Skip to content

Commit

Permalink
add iterator.Skip() with count value
Browse files Browse the repository at this point in the history
  • Loading branch information
paulmach committed Jan 16, 2021
1 parent 4bc7256 commit 6062612
Show file tree
Hide file tree
Showing 2 changed files with 227 additions and 6 deletions.
38 changes: 32 additions & 6 deletions iterator.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,18 +39,38 @@ func (i *Iterator) HasNext() bool {
return i.base.index < len(i.base.data)
}

// Skip will move the interator forward 'count' value(s) without actually reading it.
// Must provide the correct wireType. For a new iteractor 'count' will move the
// pointer so the next value call with be the 'counth' value.
// double, float, fixed, sfixed are WireType32bit or WireType64bit,
// all others int, uint, sint types are WireTypeVarint.
// The function will panic for any other value.
func (i *Iterator) Skip(wireType int, count int) {
if wireType == WireTypeVarint {
for j := 0; j < count; j++ {
for i.data[i.index] >= 128 {
i.index++
}
i.index++
}
return
} else if wireType == WireType32bit {
i.index += 4 * count
return
} else if wireType == WireType64bit {
i.index += 8 * count
return
}

panic("invalid wire type for a packed repeated field")
}

// Count returns the total number of values in this repeated field.
// The answer depends on the type/encoding or the field:
// double, float, fixed, sfixed are WireType32bit or WireType64bit,
// all others int, uint, sint types are WireTypeVarint.
// The function will panic for any other value.
func (i *Iterator) Count(wireType int) int {
if wireType == WireType32bit {
return len(i.base.data) / 4
}
if wireType == WireType64bit {
return len(i.base.data) / 8
}
if wireType == WireTypeVarint {
var count int
for _, b := range i.data {
Expand All @@ -61,6 +81,12 @@ func (i *Iterator) Count(wireType int) int {

return count
}
if wireType == WireType32bit {
return len(i.base.data) / 4
}
if wireType == WireType64bit {
return len(i.base.data) / 8
}

panic("invalid wire type for a packed repeated field")
}
Expand Down
195 changes: 195 additions & 0 deletions iterator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,122 @@ func TestIterator_errors(t *testing.T) {
}
}

func TestIterator_Skip(t *testing.T) {
message := &testmsg.Packed{
Flt: make([]float32, 10),
Dbl: make([]float64, 10),
I32: make([]int32, 10),
I64: make([]int64, 10),
}

for i := 0; i < 10; i++ {
message.Flt[i] = float32(10 * i)
message.Dbl[i] = float64(15 * i)
message.I32[i] = int32(10 + 100*i)
}

message.I64[0] = int64(1 << 7)
message.I64[2] = int64(1 << 15)
message.I64[4] = int64(1 << 23)
message.I64[6] = int64(1 << 23)

data, err := proto.Marshal(message)
if err != nil {
t.Fatalf("unable to marshal: %v", err)
}

msg := New(data)
for msg.Next() {
switch msg.FieldNumber() {
case 1: // Float
iter, err := msg.Iterator(nil)
if err != nil {
t.Fatalf("unable to make iterator: %v", err)
}

iter.Skip(WireType32bit, 0)
if v, _ := iter.Float(); v != 0 {
t.Errorf("incorrect value: %v", v)
}

iter.Skip(WireType32bit, 1)
if v, _ := iter.Float(); v != 20 {
t.Errorf("incorrect value: %v", v)
}

iter.Skip(WireType32bit, 2)
if v, _ := iter.Float(); v != 50 {
t.Errorf("incorrect value: %v", v)
}
case 2:
iter, err := msg.Iterator(nil)
if err != nil {
t.Fatalf("unable to make iterator: %v", err)
}

iter.Skip(WireType64bit, 1)
if v, _ := iter.Double(); v != 15 {
t.Errorf("incorrect value: %v", v)
}

iter.Skip(WireType64bit, 1)
if v, _ := iter.Double(); v != 45 {
t.Errorf("incorrect value: %v", v)
}

iter.Skip(WireType64bit, 2)
if v, _ := iter.Double(); v != 90 {
t.Errorf("incorrect value: %v", v)
}
case 3:
iter, err := msg.Iterator(nil)
if err != nil {
t.Fatalf("unable to make iterator: %v", err)
}

iter.Skip(WireTypeVarint, 1)
if v, _ := iter.Int32(); v != 110 {
t.Errorf("incorrect value: %v", v)
}

iter.Skip(WireTypeVarint, 1)
if v, _ := iter.Int32(); v != 310 {
t.Errorf("incorrect value: %v", v)
}

iter.Skip(WireTypeVarint, 2)
if v, _ := iter.Int32(); v != 610 {
t.Errorf("incorrect value: %v", v)
}
case 4:
iter, err := msg.Iterator(nil)
if err != nil {
t.Fatalf("unable to make iterator: %v", err)
}

iter.Skip(WireTypeVarint, 0)
if v, _ := iter.Int64(); v != 128 {
t.Errorf("incorrect value: %v", v)
}

iter.Skip(WireTypeVarint, 2)
if v, _ := iter.Int64(); v != 0 {
t.Errorf("incorrect value: %v", v)
}

if v, _ := iter.Int64(); v != 0x0800000 {
t.Errorf("incorrect value: %x", v)
}
default:
msg.Skip()
}
}

if err := msg.Err(); err != nil {
t.Fatalf("read error: %v", err)
}
}

func TestIterator_FieldNumber(t *testing.T) {
message := &testmsg.Packed{
I64: make([]int64, 4000),
Expand Down Expand Up @@ -515,3 +631,82 @@ func BenchmarkInterateInt64(b *testing.B) {
}
}
}

func BenchmarkInterateSkip_single(b *testing.B) {
items := []int64{}
for i := 0; i < 100; i++ {
items = append(items, int64(50*i))
}

data, err := proto.Marshal(&testmsg.Packed{I64: items})
if err != nil {
b.Fatalf("unable to marshal: %v", err)
}

msg := New(data)
iter := &Iterator{}

b.ReportAllocs()
b.ResetTimer()

for i := 0; i < b.N; i++ {
msg.index = 0
for msg.Next() {
switch msg.FieldNumber() {
case 4:
var err error
iter, err := msg.Iterator(iter)
if err != nil {
b.Fatalf("unable to create iterator: %v", err)
}

// to match InterateInt64 benchmark
_ = make([]int64, 0, iter.Count(WireTypeVarint))
for iter.HasNext() {
iter.Skip(WireTypeVarint, 1)
}
default:
msg.Skip()
}
}
}
}

func BenchmarkInterateSkip_all(b *testing.B) {
items := []int64{}
for i := 0; i < 100; i++ {
items = append(items, int64(50*i))
}

data, err := proto.Marshal(&testmsg.Packed{I64: items})
if err != nil {
b.Fatalf("unable to marshal: %v", err)
}

msg := New(data)
iter := &Iterator{}

b.ReportAllocs()
b.ResetTimer()

for i := 0; i < b.N; i++ {
msg.index = 0
for msg.Next() {
switch msg.FieldNumber() {
case 4:
var err error
iter, err := msg.Iterator(iter)
if err != nil {
b.Fatalf("unable to create iterator: %v", err)
}

// to match InterateInt64 benchmark
c := iter.Count(WireTypeVarint)
_ = make([]int64, 0, c)
iter.Skip(WireTypeVarint, c)
default:
msg.Skip()
}
}
}
}

0 comments on commit 6062612

Please # to comment.