Skip to content

Commit

Permalink
Remove the FormatV1/FormatV2 option
Browse files Browse the repository at this point in the history
This is a new fork so we can start fresh.
  • Loading branch information
bep committed Jul 30, 2024
1 parent 8c47e87 commit 7c8cdd1
Show file tree
Hide file tree
Showing 4 changed files with 26 additions and 95 deletions.
11 changes: 1 addition & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,6 @@ Standard `go get`:
$ go get github.com/gohugoio/hashstructure
```

**Note on v2:** It is highly recommended you use the "v2" release since this
fixes some significant hash collisions issues from v1. In practice, we used
v1 for many years in real projects at HashiCorp and never had issues, but it
is highly dependent on the shape of the data you're hashing and how you use
those hashes.

When using v2+, you can still generate weaker v1 hashes by using the
`FormatV1` format when calling `Hash`.

## Usage & Example

For usage and examples see the [Godoc](http://godoc.org/github.com/mitchellh/hashstructure).
Expand All @@ -65,7 +56,7 @@ v := ComplexStruct{
},
}

hash, err := hashstructure.Hash(v, hashstructure.FormatV2, nil)
hash, err := hashstructure.Hash(v, nil)
if err != nil {
panic(err)
}
Expand Down
43 changes: 6 additions & 37 deletions hashstructure.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,25 +39,6 @@ type HashOptions struct {
UseStringer bool
}

// Format specifies the hashing process used. Different formats typically
// generate different hashes for the same value and have different properties.
type Format uint

const (
// To disallow the zero value
formatInvalid Format = iota

// FormatV1 is the format used in v1.x of this library. This has the
// downsides noted in issue #18 but allows simultaneous v1/v2 usage.
FormatV1

// FormatV2 is the current recommended format and fixes the issues
// noted in FormatV1.
FormatV2

formatMax // so we can easily find the end
)

// Hash returns the hash value of an arbitrary value.
//
// If opts is nil, then default options will be used. See HashOptions
Expand Down Expand Up @@ -94,12 +75,7 @@ const (
//
// - "string" - The field will be hashed as a string, only works when the
// field implements fmt.Stringer
func Hash(v interface{}, format Format, opts *HashOptions) (uint64, error) {
// Validate our format
if format <= formatInvalid || format >= formatMax {
return 0, &ErrFormat{}
}

func Hash(v interface{}, opts *HashOptions) (uint64, error) {
// Create default options
if opts == nil {
opts = &HashOptions{}
Expand All @@ -116,7 +92,6 @@ func Hash(v interface{}, format Format, opts *HashOptions) (uint64, error) {

// Create our walker and walk the structure
w := &walker{
format: format,
h: opts.Hasher,
tag: opts.TagName,
zeronil: opts.ZeroNil,
Expand All @@ -128,7 +103,6 @@ func Hash(v interface{}, format Format, opts *HashOptions) (uint64, error) {
}

type walker struct {
format Format
h hash.Hash64
tag string
zeronil bool
Expand Down Expand Up @@ -267,10 +241,8 @@ func (w *walker) visit(v reflect.Value, opts *visitOpts) (uint64, error) {
h = hashUpdateUnordered(h, fieldHash)
}

if w.format != FormatV1 {
// Important: read the docs for hashFinishUnordered
h = hashFinishUnordered(w.h, h)
}
// Important: read the docs for hashFinishUnordered
h = hashFinishUnordered(w.h, h)

return h, nil

Expand Down Expand Up @@ -373,11 +345,8 @@ func (w *walker) visit(v reflect.Value, opts *visitOpts) (uint64, error) {
fieldHash := hashUpdateOrdered(w.h, kh, vh)
h = hashUpdateUnordered(h, fieldHash)
}

if w.format != FormatV1 {
// Important: read the docs for hashFinishUnordered
h = hashFinishUnordered(w.h, h)
}
// Important: read the docs for hashFinishUnordered
h = hashFinishUnordered(w.h, h)
}

return h, nil
Expand Down Expand Up @@ -405,7 +374,7 @@ func (w *walker) visit(v reflect.Value, opts *visitOpts) (uint64, error) {
}
}

if set && w.format != FormatV1 {
if set {
// Important: read the docs for hashFinishUnordered
h = hashFinishUnordered(w.h, h)
}
Expand Down
29 changes: 1 addition & 28 deletions hashstructure_examples_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func ExampleHash() {
},
}

hash, err := Hash(v, FormatV2, nil)
hash, err := Hash(v, nil)
if err != nil {
panic(err)
}
Expand All @@ -30,30 +30,3 @@ func ExampleHash() {
// Output:
// 1839806922502695369
}

func ExampleHash_v1() {
type ComplexStruct struct {
Name string
Age uint
Metadata map[string]interface{}
}

v := ComplexStruct{
Name: "mitchellh",
Age: 64,
Metadata: map[string]interface{}{
"car": true,
"location": "California",
"siblings": []string{"Bob", "John"},
},
}

hash, err := Hash(v, FormatV1, nil)
if err != nil {
panic(err)
}

fmt.Printf("%d", hash)
// Output:
// 6691276962590150517
}
38 changes: 18 additions & 20 deletions hashstructure_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ import (
"time"
)

var testFormat = FormatV2

func TestHash_identity(t *testing.T) {
cases := []interface{}{
nil,
Expand Down Expand Up @@ -42,7 +40,7 @@ func TestHash_identity(t *testing.T) {
// in the runtime in terms of ordering.
valuelist := make([]uint64, 100)
for i := range valuelist {
v, err := Hash(tc, testFormat, nil)
v, err := Hash(tc, nil)
if err != nil {
t.Fatalf("Error: %s\n\n%#v", err, tc)
}
Expand Down Expand Up @@ -171,13 +169,13 @@ func TestHash_equal(t *testing.T) {
for i, tc := range cases {
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
t.Logf("Hashing: %#v", tc.One)
one, err := Hash(tc.One, testFormat, nil)
one, err := Hash(tc.One, nil)
t.Logf("Result: %d", one)
if err != nil {
t.Fatalf("Failed to hash %#v: %s", tc.One, err)
}
t.Logf("Hashing: %#v", tc.Two)
two, err := Hash(tc.Two, testFormat, nil)
two, err := Hash(tc.Two, nil)
t.Logf("Result: %d", two)
if err != nil {
t.Fatalf("Failed to hash %#v: %s", tc.Two, err)
Expand Down Expand Up @@ -271,11 +269,11 @@ func TestHash_equalIgnore(t *testing.T) {
}

for _, tc := range cases {
one, err := Hash(tc.One, testFormat, nil)
one, err := Hash(tc.One, nil)
if err != nil {
t.Fatalf("Failed to hash %#v: %s", tc.One, err)
}
two, err := Hash(tc.Two, testFormat, nil)
two, err := Hash(tc.Two, nil)
if err != nil {
t.Fatalf("Failed to hash %#v: %s", tc.Two, err)
}
Expand Down Expand Up @@ -327,7 +325,7 @@ func TestHash_stringTagError(t *testing.T) {
}

for _, tc := range cases {
_, err := Hash(tc.Test, testFormat, nil)
_, err := Hash(tc.Test, nil)
if err != nil {
if ens, ok := err.(*ErrNotStringer); ok {
if ens.Field != tc.Field {
Expand Down Expand Up @@ -400,11 +398,11 @@ func TestHash_equalNil(t *testing.T) {
}

for _, tc := range cases {
one, err := Hash(tc.One, testFormat, &HashOptions{ZeroNil: tc.ZeroNil})
one, err := Hash(tc.One, &HashOptions{ZeroNil: tc.ZeroNil})
if err != nil {
t.Fatalf("Failed to hash %#v: %s", tc.One, err)
}
two, err := Hash(tc.Two, testFormat, &HashOptions{ZeroNil: tc.ZeroNil})
two, err := Hash(tc.Two, &HashOptions{ZeroNil: tc.ZeroNil})
if err != nil {
t.Fatalf("Failed to hash %#v: %s", tc.Two, err)
}
Expand Down Expand Up @@ -445,11 +443,11 @@ func TestHash_equalSet(t *testing.T) {
}

for _, tc := range cases {
one, err := Hash(tc.One, testFormat, nil)
one, err := Hash(tc.One, nil)
if err != nil {
t.Fatalf("Failed to hash %#v: %s", tc.One, err)
}
two, err := Hash(tc.Two, testFormat, nil)
two, err := Hash(tc.Two, nil)
if err != nil {
t.Fatalf("Failed to hash %#v: %s", tc.Two, err)
}
Expand Down Expand Up @@ -491,11 +489,11 @@ func TestHash_includable(t *testing.T) {
}

for _, tc := range cases {
one, err := Hash(tc.One, testFormat, nil)
one, err := Hash(tc.One, nil)
if err != nil {
t.Fatalf("Failed to hash %#v: %s", tc.One, err)
}
two, err := Hash(tc.Two, testFormat, nil)
two, err := Hash(tc.Two, nil)
if err != nil {
t.Fatalf("Failed to hash %#v: %s", tc.Two, err)
}
Expand Down Expand Up @@ -542,11 +540,11 @@ func TestHash_ignoreZeroValue(t *testing.T) {
}

for _, tc := range cases {
hashA, err := Hash(structA, testFormat, &HashOptions{IgnoreZeroValue: tc.IgnoreZeroValue})
hashA, err := Hash(structA, &HashOptions{IgnoreZeroValue: tc.IgnoreZeroValue})
if err != nil {
t.Fatalf("Failed to hash %#v: %s", structA, err)
}
hashB, err := Hash(structB, testFormat, &HashOptions{IgnoreZeroValue: tc.IgnoreZeroValue})
hashB, err := Hash(structB, &HashOptions{IgnoreZeroValue: tc.IgnoreZeroValue})
if err != nil {
t.Fatalf("Failed to hash %#v: %s", structB, err)
}
Expand Down Expand Up @@ -581,11 +579,11 @@ func TestHash_includableMap(t *testing.T) {
}

for _, tc := range cases {
one, err := Hash(tc.One, testFormat, nil)
one, err := Hash(tc.One, nil)
if err != nil {
t.Fatalf("Failed to hash %#v: %s", tc.One, err)
}
two, err := Hash(tc.Two, testFormat, nil)
two, err := Hash(tc.Two, nil)
if err != nil {
t.Fatalf("Failed to hash %#v: %s", tc.Two, err)
}
Expand Down Expand Up @@ -643,7 +641,7 @@ func TestHash_hashable(t *testing.T) {

for i, tc := range cases {
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
one, err := Hash(tc.One, testFormat, nil)
one, err := Hash(tc.One, nil)
if tc.Err != "" {
if err == nil {
t.Fatal("expected error")
Expand All @@ -659,7 +657,7 @@ func TestHash_hashable(t *testing.T) {
t.Fatalf("Failed to hash %#v: %s", tc.One, err)
}

two, err := Hash(tc.Two, testFormat, nil)
two, err := Hash(tc.Two, nil)
if err != nil {
t.Fatalf("Failed to hash %#v: %s", tc.Two, err)
}
Expand Down

0 comments on commit 7c8cdd1

Please sign in to comment.