mirror of
https://github.com/gohugoio/hugo.git
synced 2024-11-14 20:37:55 -05:00
Add hash.XxHash
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
This commit is contained in:
parent
edeed52fc5
commit
644d55475d
7 changed files with 183 additions and 7 deletions
1
go.mod
1
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
|
||||
|
|
2
go.sum
2
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=
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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{
|
||||
|
|
93
tpl/hash/hash.go
Normal file
93
tpl/hash/hash.go
Normal file
|
@ -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)
|
||||
}
|
84
tpl/hash/hash_test.go
Normal file
84
tpl/hash/hash_test.go
Normal file
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
|
|
Loading…
Reference in a new issue