Skip to content

Commit

Permalink
restore legacy hashing behavior alongside new hashing behavior to avo…
Browse files Browse the repository at this point in the history
…id hash collisions
  • Loading branch information
tjons committed Jun 26, 2024
1 parent bdcd500 commit e0c8232
Show file tree
Hide file tree
Showing 16 changed files with 1,044 additions and 1,134 deletions.
2 changes: 0 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"github.com/solo-io/protoc-gen-ext/module/equal"
"github.com/solo-io/protoc-gen-ext/module/hash"
"github.com/solo-io/protoc-gen-ext/module/merge"
"github.com/solo-io/protoc-gen-ext/module/uniquehash"
)

func main() {
Expand All @@ -19,7 +18,6 @@ func main() {
pgs.SupportedFeatures(&feat),
).RegisterModule(
hash.Hash(),
uniquehash.Hash(),
equal.Equal(),
merge.Merge(),
clone.Clone(),
Expand Down
56 changes: 0 additions & 56 deletions module/uniquehash/uniquehash.go

This file was deleted.

3 changes: 2 additions & 1 deletion templates/hash/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ var (
)
{{ range .AllMessages }}
{{ template "msg" . }}
{{ template "hash" . }}
{{ template "uniquehash" . }}
{{ end }}
`
35 changes: 35 additions & 0 deletions templates/hash/messages.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,23 @@ package hash

const messageTpl = `
if h, ok := interface{}({{ .FieldAccessor }}).(safe_hasher.SafeHasher); ok {
{{ if .UniqueHash }}
if _, err = {{ .Hasher }}.Write([]byte("{{ .FieldName }}")); err != nil {
return 0, err
}
{{ end }}
if _, err = h.Hash({{ .Hasher }}); err != nil {
return 0, err
}
} else {
if fieldValue, err := hashstructure.Hash({{ .FieldAccessor }}, nil); err != nil {
return 0, err
} else {
{{ if .UniqueHash }}
if _, err = {{ .Hasher }}.Write([]byte("{{ .FieldName }}")); err != nil {
return 0, err
}
{{ end }}
if err := binary.Write({{ .Hasher }}, binary.LittleEndian, fieldValue); err != nil {
return 0, err
}
Expand All @@ -17,17 +27,32 @@ const messageTpl = `
`

const primitiveTmpl = `
{{ if .UniqueHash }}
if _, err = {{ .Hasher }}.Write([]byte("{{ .FieldName }}")); err != nil {
return 0, err
}
{{ end }}
err = binary.Write({{ .Hasher }}, binary.LittleEndian, {{ .FieldAccessor }} )
if err != nil {return 0, err}
`

const bytesTpl = `
{{ if .UniqueHash }}
if _, err = {{ .Hasher }}.Write([]byte("{{ .FieldName }}")); err != nil {
return 0, err
}
{{ end }}
if _, err = {{ .Hasher }}.Write({{ .FieldAccessor }}); err != nil {
return 0, err
}
`

const stringTpl = `
{{ if .UniqueHash }}
if _, err = {{ .Hasher }}.Write([]byte("{{ .FieldName }}")); err != nil {
return 0, err
}
{{ end }}
if _, err = {{ .Hasher }}.Write([]byte({{ .FieldAccessor }})); err != nil {
return 0, err
}
Expand All @@ -54,7 +79,17 @@ const mapTpl = `
`

const repeatedTpl = `
{{ if .UniqueHash }}
if _, err = hasher.Write([]byte("{{ .FieldName }}")); err != nil {
return 0, err
}
for i, v := range {{ .FieldAccessor }} {
if _, err = hasher.Write([]byte(strconv.Itoa(i))); err != nil {
return 0, err
}
{{ else }}
for _, v := range {{ .FieldAccessor }} {
{{ end }}
{{ .InnerTemplates.Value }}
}
`
45 changes: 40 additions & 5 deletions templates/hash/msg.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package hash

const msgTpl = `
const hashTpl = `
// Hash function
// Deprecated due to hashing implemention only using field values. The omission
// of the field name in the hash calculation can lead to hash collisions.
// Use the UniqueHash function instead.
// Use the HashUnique function instead.
func (m {{ (msgTyp .).Pointer }}) Hash(hasher hash.Hash64) (uint64, error) {
if m == nil { return 0, nil }
if hasher == nil { hasher = fnv.New64() }
Expand All @@ -14,17 +14,52 @@ func (m {{ (msgTyp .).Pointer }}) Hash(hasher hash.Hash64) (uint64, error) {
}
{{ range .NonOneOfFields }}
{{ render . }}
{{ render . false }}
{{ end }}
{{ range .SyntheticOneOfFields }}
{{ render . }}
{{ render . false }}
{{ end }}
{{ range .RealOneOfs }}
switch m.{{ name . }}.(type) {
{{ range .Fields }}
case {{ oneof . }}:
{{ render . }}
{{ render . false }}
{{ end }}
}
{{ end }}
return hasher.Sum64(), nil
}
`

const uniqueHashTpl = `
// HashUnique function generates a hash of the object that is unique to the object by
// hashing field name and value pairs.
// Replaces Hash due to original hashing implemention only using field values. The omission
// of the field name in the hash calculation can lead to hash collisions.
func (m {{ (msgTyp .).Pointer }}) HashUnique(hasher hash.Hash64) (uint64, error) {
if m == nil { return 0, nil }
if hasher == nil { hasher = fnv.New64() }
var err error
if _, err = hasher.Write([]byte("{{ fullPackageName . }}")); err != nil {
return 0, err
}
{{ range .NonOneOfFields }}
{{ render . true }}
{{ end }}
{{ range .SyntheticOneOfFields }}
{{ render . true }}
{{ end }}
{{ range .RealOneOfs }}
switch m.{{ name . }}.(type) {
{{ range .Fields }}
case {{ oneof . }}:
{{ render . true }}
{{ end }}
}
{{ end }}
Expand Down
3 changes: 2 additions & 1 deletion templates/hash/register.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ import (
func Register(tpl *template.Template, params pgs.Parameters) {
register(tpl, params)
template.Must(tpl.Parse(fileTpl))
template.Must(tpl.New("msg").Parse(msgTpl))
template.Must(tpl.New("hash").Parse(hashTpl))
template.Must(tpl.New("uniquehash").Parse(uniqueHashTpl))
}
29 changes: 19 additions & 10 deletions templates/hash/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,16 @@ import (

type Value struct {
FieldAccessor string
FieldName string
Hasher string
InnerTemplates struct {
Key string
Value string
}
UniqueHash bool
}

func (fns goSharedFuncs) render(field pgs.Field) (string, error) {
func (fns goSharedFuncs) render(field pgs.Field, hashUnique bool) (string, error) {
var tpl *template.Template

// check if skip hash is set on a given field
Expand All @@ -37,9 +39,9 @@ func (fns goSharedFuncs) render(field pgs.Field) (string, error) {

tpl = template.Must(fns.tpl.New("primitive").Parse(primitiveTmpl))
} else if field.Type().IsMap() {
return fns.renderMap(field)
return fns.renderMap(field, hashUnique)
} else if field.Type().IsRepeated() {
return fns.renderRepeated(field)
return fns.renderRepeated(field, hashUnique)
} else {
switch field.Type().ProtoType() {
case pgs.BytesT:
Expand All @@ -56,25 +58,28 @@ func (fns goSharedFuncs) render(field pgs.Field) (string, error) {
var b bytes.Buffer
err = tpl.Execute(&b, Value{
FieldAccessor: fns.accessor(field),
FieldName: fns.fieldName(field),
Hasher: "hasher",
UniqueHash: hashUnique,
})
return b.String(), err
}

func (fns goSharedFuncs) renderMap(field pgs.Field) (string, error) {

func (fns goSharedFuncs) renderMap(field pgs.Field, uniqueHash bool) (string, error) {
var b bytes.Buffer
valueTemplate, err := fns.simpleRender(field.Type().Element(), "v", "innerHash")
valueTemplate, err := fns.simpleRender(field.Type().Element(), "v", "innerHash", uniqueHash)
if err != nil {
return "", err
}
keyTemplate, err := fns.simpleRender(field.Type().Key(), "k", "innerHash")
keyTemplate, err := fns.simpleRender(field.Type().Key(), "k", "innerHash", uniqueHash)
if err != nil {
return "", err
}
values := Value{
FieldAccessor: fns.accessor(field),
FieldName: fns.fieldName(field),
Hasher: "innerHash",
UniqueHash: uniqueHash,
InnerTemplates: struct {
Key string
Value string
Expand All @@ -85,15 +90,17 @@ func (fns goSharedFuncs) renderMap(field pgs.Field) (string, error) {
return b.String(), err
}

func (fns goSharedFuncs) renderRepeated(field pgs.Field) (string, error) {
func (fns goSharedFuncs) renderRepeated(field pgs.Field, hashUnique bool) (string, error) {
var b bytes.Buffer
innerTemplate, err := fns.simpleRender(field.Type().Element(), "v", "hasher")
innerTemplate, err := fns.simpleRender(field.Type().Element(), "v", "hasher", hashUnique)
if err != nil {
return "", err
}
values := Value{
FieldName: fns.fieldName(field),
FieldAccessor: fns.accessor(field),
Hasher: "innerHash",
UniqueHash: hashUnique,
InnerTemplates: struct {
Key string
Value string
Expand All @@ -104,7 +111,7 @@ func (fns goSharedFuncs) renderRepeated(field pgs.Field) (string, error) {
return b.String(), err
}

func (fns goSharedFuncs) simpleRender(field pgs.FieldTypeElem, valueName, hasherName string) (string, error) {
func (fns goSharedFuncs) simpleRender(field pgs.FieldTypeElem, valueName, hasherName string, uniqueHash bool) (string, error) {
var tpl *template.Template
if field.ProtoType().IsNumeric() ||
field.ProtoType() == pgs.BoolT ||
Expand All @@ -125,8 +132,10 @@ func (fns goSharedFuncs) simpleRender(field pgs.FieldTypeElem, valueName, hasher

var b bytes.Buffer
err := tpl.Execute(&b, Value{
FieldName: valueName,
FieldAccessor: valueName,
Hasher: hasherName,
UniqueHash: uniqueHash,
})
return b.String(), err
}
42 changes: 0 additions & 42 deletions templates/uniquehash/file.go

This file was deleted.

Loading

0 comments on commit e0c8232

Please sign in to comment.