Skip to content

Commit

Permalink
no latent repeated appending
Browse files Browse the repository at this point in the history
  • Loading branch information
awalterschulze committed Oct 8, 2015
1 parent cee4d9f commit 8a94939
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 0 deletions.
57 changes: 57 additions & 0 deletions merge.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,14 @@ func (this *errMerge) Error() string {
return this.fieldName + " requires merging"
}

type errLatent struct {
fieldName string
}

func (this *errLatent) Error() string {
return this.fieldName + " has latent appending or merging"
}

// The NoMerge function checks that the marshaled protocol buffer does not require any merging when unmarshaling.
// When this property holds, streaming processing is possible.
//
Expand Down Expand Up @@ -126,6 +134,55 @@ func NoMerge(buf []byte, descriptorSet *descriptor.FileDescriptorSet, rootPkg st
return nil
}

func NoLatentAppendingOrMerging(buf []byte, descriptorSet *descriptor.FileDescriptorSet, rootPkg string, rootMsg string) error {
msg := descriptorSet.GetMessage(rootPkg, rootMsg)
if msg == nil {
return &errUndefined{rootPkg, rootMsg, ""}
}
i := 0
var lookingAt int32 = -1
seen := make(map[int32]bool)
for i < len(buf) {
key, n, err := decodeVarint(buf, i)
if err != nil {
return err
}
i = i + n
fieldNum := int32(key >> 3)
wireType := int(key & 0x7)
field := getFieldNumber(descriptorSet, rootPkg, rootMsg, msg, fieldNum)
if seen[fieldNum] {
return &errLatent{msg.GetName() + "." + field.GetName()}
}
if lookingAt != fieldNum {
seen[lookingAt] = true
lookingAt = fieldNum
}
if !field.IsMessage() {
i, err = skip(buf, i, wireType)
if err != nil {
return err
}
continue
}
length, n, err := decodeVarint(buf, i)
if err != nil {
return err
}
i += n
fieldPkg, fieldMsg := descriptorSet.FindMessage(rootPkg, rootMsg, field.GetName())
if len(fieldMsg) == 0 {
return &errUndefined{rootPkg, rootMsg, field.GetName()}
}
err = NoLatentAppendingOrMerging(buf[i:i+int(length)], descriptorSet, fieldPkg, fieldMsg)
if err != nil {
return err
}
i += int(length)
}
return nil
}

func dotToUnderscore(r rune) rune {
if r == '.' {
return '_'
Expand Down
65 changes: 65 additions & 0 deletions merge_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,32 @@ func TestNoMergeNoMerge(t *testing.T) {
}
}

func TestNoLatentNoLatent(t *testing.T) {
r := rand.New(rand.NewSource(time.Now().UnixNano()))
m := test.NewPopulatedNinRepNative(r, true)
data, err := proto.Marshal(m)
if err != nil {
panic(err)
}
err = fieldpath.NoLatentAppendingOrMerging(data, test.ThetestDescription(), "test", "NinRepNative")
if err != nil {
panic(err)
}
}

func TestNoLatentNoMerge(t *testing.T) {
r := rand.New(rand.NewSource(time.Now().UnixNano()))
m := test.NewPopulatedNinOptNative(r, true)
data, err := proto.Marshal(m)
if err != nil {
panic(err)
}
err = fieldpath.NoLatentAppendingOrMerging(data, test.ThetestDescription(), "test", "NinOptNative")
if err != nil {
panic(err)
}
}

func TestNoMergeMerge(t *testing.T) {
r := rand.New(rand.NewSource(time.Now().UnixNano()))
m := test.NewPopulatedNinOptNative(r, true)
Expand All @@ -68,6 +94,45 @@ func TestNoMergeMerge(t *testing.T) {
}
}

func TestNoLatentLatent(t *testing.T) {
r := rand.New(rand.NewSource(time.Now().UnixNano()))
m := test.NewPopulatedNinRepNative(r, true)
if m.Field1 == nil {
m.Field1 = []float64{1.1, 1.2}
}
if m.Field2 == nil {
m.Field2 = []float32{1.1, 1.2}
}
data, err := proto.Marshal(m)
if err != nil {
panic(err)
}
key := byte(uint32(1)<<3 | uint32(1))
data = append(data, key, byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)))
err = fieldpath.NoLatentAppendingOrMerging(data, test.ThetestDescription(), "test", "NinRepNative")
if err == nil || !strings.Contains(err.Error(), "NinRepNative.Field1") {
t.Fatalf("Field1 should have latent appending")
}
}

func TestNoLatentMerge(t *testing.T) {
r := rand.New(rand.NewSource(time.Now().UnixNano()))
m := test.NewPopulatedNinOptNative(r, true)
if m.Field1 == nil {
m.Field1 = proto.Float64(1.1)
}
data, err := proto.Marshal(m)
if err != nil {
panic(err)
}
key := byte(uint32(1)<<3 | uint32(1))
data = append(data, key, byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)))
err = fieldpath.NoLatentAppendingOrMerging(data, test.ThetestDescription(), "test", "NinOptNative")
if err == nil || !strings.Contains(err.Error(), "NinOptNative.Field1") {
t.Fatalf("Field1 should require merging")
}
}

func TestNoMergeNestedNoMerge(t *testing.T) {
r := rand.New(rand.NewSource(time.Now().UnixNano()))
bigm := test.NewPopulatedNidOptStruct(r, true)
Expand Down

0 comments on commit 8a94939

Please sign in to comment.