]> git.maquefel.me Git - brevno-suite/hugo/commitdiff
Add hash.XxHash
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Wed, 3 Jul 2024 09:20:46 +0000 (11:20 +0200)
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Sat, 6 Jul 2024 12:08:15 +0000 (14:08 +0200)
Also move the non crypto hash funcs into this new package.

This is much faster than e.g. MD5, especially for larger inputs:

```
BenchmarkXxHash/xxHash_43-10           9917955        112.2 ns/op       56 B/op        4 allocs/op
BenchmarkXxHash/mdb5_43-10             6017239        204.1 ns/op       96 B/op        3 allocs/op
BenchmarkXxHash/fnv32a_43-10          14407333         82.30 ns/op       16 B/op        1 allocs/op
BenchmarkXxHash/xxHash_4300-10         2916892        409.7 ns/op       56 B/op        4 allocs/op
BenchmarkXxHash/mdb5_4300-10            159748       7491 ns/op     4912 B/op        3 allocs/op
BenchmarkXxHash/fnv32a_4300-10          218210       5510 ns/op       16 B/op        1 allocs/op
```

Fixes #12635

go.mod
go.sum
tpl/crypto/crypto.go
tpl/crypto/init.go
tpl/hash/hash.go [new file with mode: 0644]
tpl/hash/hash_test.go [new file with mode: 0644]
tpl/tplimpl/template_funcs.go

diff --git a/go.mod b/go.mod
index 2cff6b5e0791841552c3de67ffd58f3897a367cc..5a10b2480aabaa53a15410e3ace4c0d45a4904d9 100644 (file)
--- a/go.mod
+++ b/go.mod
@@ -21,6 +21,7 @@ require (
        github.com/bep/overlayfs v0.9.2
        github.com/bep/simplecobra v0.4.0
        github.com/bep/tmc v0.5.1
+       github.com/cespare/xxhash/v2 v2.3.0
        github.com/clbanning/mxj/v2 v2.7.0
        github.com/cli/safeexec v1.0.1
        github.com/disintegration/gift v1.2.1
diff --git a/go.sum b/go.sum
index 0498c7f8d058b41569d248dffb47578394d0c66d..ea4788a9727492ea42a936a8d801f79c8dfa5aa9 100644 (file)
--- a/go.sum
+++ b/go.sum
@@ -147,6 +147,8 @@ github.com/bep/tmc v0.5.1/go.mod h1:tGYHN8fS85aJPhDLgXETVKp+PR382OvFi2+q2GkGsq0=
 github.com/bep/workers v1.0.0 h1:U+H8YmEaBCEaFZBst7GcRVEoqeRC9dzH2dWOwGmOchg=
 github.com/bep/workers v1.0.0/go.mod h1:7kIESOB86HfR2379pwoMWNy8B50D7r99fRLUyPSNyCs=
 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
+github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
 github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
 github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
index 412a192128bdcabc627a8fbba676ee6074e4e492..677f59139ff4bee5a4322ef08ca6e9abafee1e63 100644 (file)
@@ -25,6 +25,7 @@ import (
        "hash"
        "hash/fnv"
 
+       "github.com/gohugoio/hugo/common/hugo"
        "github.com/spf13/cast"
 )
 
@@ -72,6 +73,7 @@ func (ns *Namespace) SHA256(v any) (string, error) {
 // FNV32a hashes v using fnv32a algorithm.
 // <docsmeta>{"newIn": "0.98.0" }</docsmeta>
 func (ns *Namespace) FNV32a(v any) (int, error) {
+       hugo.Deprecate("crypto.FNV32a", "Use hash.FNV32a.", "v0.129.0")
        conv, err := cast.ToStringE(v)
        if err != nil {
                return 0, err
index 418fbd9fb472ae2cb551970d2d9f22a98f8f8834..0527fba06d062cfd6ecb661265131e3987e3e533 100644 (file)
@@ -53,13 +53,6 @@ func init() {
                        },
                )
 
-               ns.AddMethodMapping(ctx.FNV32a,
-                       nil,
-                       [][2]string{
-                               {`{{ crypto.FNV32a "Hugo Rocks!!" }}`, `1515779328`},
-                       },
-               )
-
                ns.AddMethodMapping(ctx.HMAC,
                        []string{"hmac"},
                        [][2]string{
diff --git a/tpl/hash/hash.go b/tpl/hash/hash.go
new file mode 100644 (file)
index 0000000..d4a80b3
--- /dev/null
@@ -0,0 +1,93 @@
+// Copyright 2024 The Hugo Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package hash provides non-cryptographic hash functions for template use.
+package hash
+
+import (
+       "context"
+       "encoding/hex"
+       "hash/fnv"
+
+       "github.com/cespare/xxhash/v2"
+       "github.com/gohugoio/hugo/deps"
+       "github.com/gohugoio/hugo/tpl/internal"
+       "github.com/spf13/cast"
+)
+
+// New returns a new instance of the hash-namespaced template functions.
+func New() *Namespace {
+       return &Namespace{}
+}
+
+// Namespace provides template functions for the "hash" namespace.
+type Namespace struct{}
+
+// FNV32a hashes v using fnv32a algorithm.
+func (ns *Namespace) FNV32a(v any) (int, error) {
+       conv, err := cast.ToStringE(v)
+       if err != nil {
+               return 0, err
+       }
+       algorithm := fnv.New32a()
+       algorithm.Write([]byte(conv))
+       return int(algorithm.Sum32()), nil
+}
+
+// XxHash returns the xxHash of the input string.
+func (ns *Namespace) XxHash(v any) (string, error) {
+       conv, err := cast.ToStringE(v)
+       if err != nil {
+               return "", err
+       }
+
+       hasher := xxhash.New()
+
+       _, err = hasher.WriteString(conv)
+       if err != nil {
+               return "", err
+       }
+       hash := hasher.Sum(nil)
+       return hex.EncodeToString(hash), nil
+}
+
+const name = "hash"
+
+func init() {
+       f := func(d *deps.Deps) *internal.TemplateFuncsNamespace {
+               ctx := New()
+
+               ns := &internal.TemplateFuncsNamespace{
+                       Name:    name,
+                       Context: func(cctx context.Context, args ...any) (any, error) { return ctx, nil },
+               }
+
+               ns.AddMethodMapping(ctx.XxHash,
+                       []string{"xxhash"},
+                       [][2]string{
+                               {`{{ hash.XxHash "The quick brown fox jumps over the lazy dog" }}`, `0b242d361fda71bc`},
+                       },
+               )
+
+               ns.AddMethodMapping(ctx.FNV32a,
+                       nil,
+                       [][2]string{
+                               {`{{ hash.FNV32a "Hugo Rocks!!" }}`, `1515779328`},
+                       },
+               )
+
+               return ns
+       }
+
+       internal.AddTemplateFuncsNamespace(f)
+}
diff --git a/tpl/hash/hash_test.go b/tpl/hash/hash_test.go
new file mode 100644 (file)
index 0000000..ff5d59a
--- /dev/null
@@ -0,0 +1,84 @@
+// Copyright 2024 The Hugo Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package hash provides non-cryptographic hash functions for template use.
+package hash
+
+import (
+       "crypto/md5"
+       "encoding/hex"
+       "fmt"
+       "strings"
+       "testing"
+
+       qt "github.com/frankban/quicktest"
+       "github.com/spf13/cast"
+)
+
+func TestXxHash(t *testing.T) {
+       t.Parallel()
+       c := qt.New(t)
+
+       ns := New()
+
+       h, err := ns.XxHash("The quick brown fox jumps over the lazy dog")
+       c.Assert(err, qt.IsNil)
+       // Facit: https://asecuritysite.com/encryption/xxhash?val=The%20quick%20brown%20fox%20jumps%20over%20the%20lazy%20dog
+       c.Assert(h, qt.Equals, "0b242d361fda71bc")
+}
+
+func BenchmarkXxHash(b *testing.B) {
+       const inputSmall = "The quick brown fox jumps over the lazy dog"
+       inputLarge := strings.Repeat(inputSmall, 100)
+
+       runBench := func(name, input string, b *testing.B, fn func(v any)) {
+               b.Run(fmt.Sprintf("%s_%d", name, len(input)), func(b *testing.B) {
+                       for i := 0; i < b.N; i++ {
+                               fn(input)
+                       }
+               })
+       }
+
+       ns := New()
+       fnXxHash := func(v any) {
+               _, err := ns.XxHash(v)
+               if err != nil {
+                       panic(err)
+               }
+       }
+
+       fnFNv32a := func(v any) {
+               _, err := ns.FNV32a(v)
+               if err != nil {
+                       panic(err)
+               }
+       }
+
+       // Copied from the crypto tpl/crypto package,
+       // just to have something to compare the above with.
+       fnMD5 := func(v any) {
+               conv, err := cast.ToStringE(v)
+               if err != nil {
+                       panic(err)
+               }
+
+               hash := md5.Sum([]byte(conv))
+               _ = hex.EncodeToString(hash[:])
+       }
+
+       for _, input := range []string{inputSmall, inputLarge} {
+               runBench("xxHash", input, b, fnXxHash)
+               runBench("mdb5", input, b, fnMD5)
+               runBench("fnv32a", input, b, fnFNv32a)
+       }
+}
index 815270631499fb12a8596a88c7be919507c285e3..3daba74a03e5f234d86333d9ebcb0847f67576ab 100644 (file)
@@ -43,6 +43,7 @@ import (
        _ "github.com/gohugoio/hugo/tpl/diagrams"
        _ "github.com/gohugoio/hugo/tpl/encoding"
        _ "github.com/gohugoio/hugo/tpl/fmt"
+       _ "github.com/gohugoio/hugo/tpl/hash"
        _ "github.com/gohugoio/hugo/tpl/hugo"
        _ "github.com/gohugoio/hugo/tpl/images"
        _ "github.com/gohugoio/hugo/tpl/inflect"