[
  {
    "path": ".gitattributes",
    "content": "* text=auto eol=lf\n\n"
  },
  {
    "path": ".gitignore",
    "content": "# Binaries for programs and plugins\n*.exe\n*.dll\n*.so\n*.dylib\n\n# Test binary, build with `go test -c`\n*.test\n\n# Output of the go coverage tool, specifically when used with LiteIDE\n*.out\n\n# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736\n.glide/\n\n# Misc\n/.idea\n/vendor\n"
  },
  {
    "path": "LICENSE",
    "content": "BSD 3-Clause License\n\nCopyright (c) 2018-2022, Edwin Geng\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above copyright notice, this\n  list of conditions and the following disclaimer.\n\n* Redistributions in binary form must reproduce the above copyright notice,\n  this list of conditions and the following disclaimer in the documentation\n  and/or other materials provided with the distribution.\n\n* Neither the name of the copyright holder nor the names of its\n  contributors may be used to endorse or promote products derived from\n  this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "README.md",
    "content": "# Overview\n- `WUID` is a universal unique identifier generator.\n- `WUID` is much faster than traditional UUID. Each `WUID` instance can even generate 100M unique identifiers in a single second.\n- In the nutshell, `WUID` generates 64-bit integers in sequence. The high 28 bits are loaded from a data source. By now, Redis, MySQL, MongoDB and Callback are supported.\n- The uniqueness is guaranteed as long as all `WUID` instances share a same data source or each group of them has a different section ID.\n- `WUID` automatically renews the high 28 bits when the low 36 bits are about to run out.\n- `WUID` is thread-safe, and lock free.\n- Obfuscation is supported.\n\n# Benchmarks\n```\nBenchmarkWUID           159393580          7.661 ns/op        0 B/op       0 allocs/op\nBenchmarkRand           100000000         14.95 ns/op         0 B/op       0 allocs/op\nBenchmarkTimestamp      164224915          7.359 ns/op        0 B/op       0 allocs/op\nBenchmarkUUID_V1        23629536          43.42 ns/op         0 B/op       0 allocs/op\nBenchmarkUUID_V2        29351550          43.96 ns/op         0 B/op       0 allocs/op\nBenchmarkUUID_V3         4703044         254.2 ns/op        144 B/op       4 allocs/op\nBenchmarkUUID_V4         5796310         210.0 ns/op         16 B/op       1 allocs/op\nBenchmarkUUID_V5         4051291         310.7 ns/op        168 B/op       4 allocs/op\nBenchmarkRedis              2996       38725 ns/op          160 B/op       5 allocs/op\nBenchmarkSnowflake       1000000        2092 ns/op            0 B/op       0 allocs/op\nBenchmarkULID            5660170         207.7 ns/op         16 B/op       1 allocs/op\nBenchmarkXID            49639082          26.21 ns/op         0 B/op       0 allocs/op\nBenchmarkShortID         1312386         922.2 ns/op        320 B/op      11 allocs/op\nBenchmarkKsuid          19717675          59.79 ns/op         0 B/op       0 allocs/op\n```\n\n# Getting Started\n``` bash\ngo get -u github.com/edwingeng/wuid\n```\n\n# Usages\n### Redis\n``` go\nimport \"github.com/edwingeng/wuid/redis/v8/wuid\"\n\nnewClient := func() (redis.UniversalClient, bool, error) {\n    var client redis.UniversalClient\n    // ...\n    return client, true, nil\n}\n\n// Setup\nw := NewWUID(\"alpha\", nil)\nerr := w.LoadH28FromRedis(newClient, \"wuid\")\nif err != nil {\n    panic(err)\n}\n\n// Generate\nfor i := 0; i < 10; i++ {\n    fmt.Printf(\"%#016x\\n\", w.Next())\n}\n```\n\n### MySQL\n``` go\nimport \"github.com/edwingeng/wuid/mysql/wuid\"\n\nopenDB := func() (*sql.DB, bool, error) {\n    var db *sql.DB\n    // ...\n    return db, true, nil\n}\n\n// Setup\nw := NewWUID(\"alpha\", nil)\nerr := w.LoadH28FromMysql(openDB, \"wuid\")\nif err != nil {\n    panic(err)\n}\n\n// Generate\nfor i := 0; i < 10; i++ {\n    fmt.Printf(\"%#016x\\n\", w.Next())\n}\n```\n\n### MongoDB\n``` go\nimport \"github.com/edwingeng/wuid/mongo/wuid\"\n\nnewClient := func() (*mongo.Client, bool, error) {\n    var client *mongo.Client\n    // ...\n    return client, true, nil\n}\n\n// Setup\nw := NewWUID(\"alpha\", nil)\nerr := w.LoadH28FromMongo(newClient, \"test\", \"wuid\", \"default\")\nif err != nil {\n    panic(err)\n}\n\n// Generate\nfor i := 0; i < 10; i++ {\n    fmt.Printf(\"%#016x\\n\", w.Next())\n}\n```\n\n### Callback\n``` go\nimport \"github.com/edwingeng/wuid/callback/wuid\"\n\ncallback := func() (int64, func(), error) {\n    var h28 int64\n    // ...\n    return h28, nil, nil\n}\n\n// Setup\nw := NewWUID(\"alpha\", nil)\nerr := w.LoadH28WithCallback(callback)\nif err != nil {\n    panic(err)\n}\n\n// Generate\nfor i := 0; i < 10; i++ {\n    fmt.Printf(\"%#016x\\n\", w.Next())\n}\n```\n\n# Mysql Table Creation\n``` sql\nCREATE TABLE IF NOT EXISTS `wuid` (\n    `h` int(10) NOT NULL AUTO_INCREMENT,\n    `x` tinyint(4) NOT NULL DEFAULT '0',\n    PRIMARY KEY (`x`),\n    UNIQUE KEY `h` (`h`)\n) ENGINE=InnoDB DEFAULT CHARSET=latin1;\n```\n\n# Options\n\n- `WithSection` brands a section ID on each generated number. A section ID must be in between [0, 7].\n- `WithStep` sets the step and the floor for each generated number.\n- `WithObfuscation` enables number obfuscation.\n\n# Attentions\nIt is highly recommended to pass a logger to `wuid.NewWUID` and keep an eye on the warnings that include \"renew failed\". It indicates that the low 36 bits are about to run out in hours to hundreds of hours, and the renewal program failed for some reason. `WUID` will make many renewal attempts until succeeded. \n\n# Special thanks\n- [dustinfog](https://github.com/dustinfog)\n\n# Ports\n- swift - https://github.com/ekscrypto/SwiftWUID\n"
  },
  {
    "path": "callback/wuid/coverage.sh",
    "content": "#!/usr/bin/env bash\n\n[[ \"$TRACE\" ]] && set -x\npushd `dirname \"$0\"` > /dev/null\ntrap __EXIT EXIT\n\ncolorful=false\ntput setaf 7 > /dev/null 2>&1\nif [[ $? -eq 0 ]]; then\n    colorful=true\nfi\n\nfunction __EXIT() {\n    popd > /dev/null\n}\n\nfunction printError() {\n    $colorful && tput setaf 1\n    >&2 echo \"Error: $@\"\n    $colorful && tput setaf 7\n}\n\nfunction printImportantMessage() {\n    $colorful && tput setaf 3\n    >&2 echo \"$@\"\n    $colorful && tput setaf 7\n}\n\nfunction printUsage() {\n    $colorful && tput setaf 3\n    >&2 echo \"$@\"\n    $colorful && tput setaf 7\n}\n\ngo test -cover -coverprofile=c.out -v \"$@\" && go tool cover -html=c.out\n"
  },
  {
    "path": "callback/wuid/vet.sh",
    "content": "#!/usr/bin/env bash\n\n[[ \"$TRACE\" ]] && set -x\npushd `dirname \"$0\"` > /dev/null\ntrap __EXIT EXIT\n\ncolorful=false\ntput setaf 7 > /dev/null 2>&1\nif [[ $? -eq 0 ]]; then\n    colorful=true\nfi\n\nfunction __EXIT() {\n    popd > /dev/null\n}\n\nfunction printError() {\n    $colorful && tput setaf 1\n    >&2 echo \"Error: $@\"\n    $colorful && tput setaf 7\n}\n\nfunction printImportantMessage() {\n    $colorful && tput setaf 3\n    >&2 echo \"$@\"\n    $colorful && tput setaf 7\n}\n\nfunction printUsage() {\n    $colorful && tput setaf 3\n    >&2 echo \"$@\"\n    $colorful && tput setaf 7\n}\n\nprintImportantMessage \"====== gofmt\"\ngofmt -w .\n\nprintImportantMessage \"====== go vet\"\ngo vet ./...\n\nprintImportantMessage \"====== gocyclo\"\ngocyclo -over 15 .\n\nprintImportantMessage \"====== ineffassign\"\nineffassign ./...\n\nprintImportantMessage \"====== misspell\"\nmisspell *\n"
  },
  {
    "path": "callback/wuid/wuid.go",
    "content": "package wuid\n\nimport (\n\t\"errors\"\n\t\"github.com/edwingeng/slog\"\n\t\"github.com/edwingeng/wuid/internal\"\n)\n\n// WUID is an extremely fast universal unique identifier generator.\ntype WUID struct {\n\tw internal.WUID\n}\n\n// NewWUID creates a new WUID instance.\nfunc NewWUID(name string, logger slog.Logger, opts ...Option) *WUID {\n\treturn &WUID{w: *internal.NewWUID(name, logger, opts...)}\n}\n\n// Next returns a unique identifier.\nfunc (w *WUID) Next() int64 {\n\treturn w.w.Next()\n}\n\ntype H28Callback func() (h28 int64, cleanUp func(), err error)\n\n// LoadH28WithCallback invokes cb to acquire a number. The number is used as the high 28 bits\n// of all generated numbers. In addition, cb is saved for future renewal.\nfunc (w *WUID) LoadH28WithCallback(cb H28Callback) error {\n\tif cb == nil {\n\t\treturn errors.New(\"cb cannot be nil\")\n\t}\n\n\th28, cleanUp, err := cb()\n\tif err != nil {\n\t\treturn err\n\t}\n\tif cleanUp != nil {\n\t\tdefer cleanUp()\n\t}\n\n\tif err = w.w.VerifyH28(h28); err != nil {\n\t\treturn err\n\t}\n\n\tw.w.Reset(h28 << 36)\n\tw.w.Infof(\"<wuid> new h28: %d. name: %s\", h28, w.w.Name)\n\n\tw.w.Lock()\n\tdefer w.w.Unlock()\n\n\tif w.w.Renew != nil {\n\t\treturn nil\n\t}\n\tw.w.Renew = func() error {\n\t\treturn w.LoadH28WithCallback(cb)\n\t}\n\n\treturn nil\n}\n\n// RenewNow reacquires the high 28 bits immediately.\nfunc (w *WUID) RenewNow() error {\n\treturn w.w.RenewNow()\n}\n\ntype Option = internal.Option\n\n// WithH28Verifier adds an extra verifier for the high 28 bits.\nfunc WithH28Verifier(cb func(h28 int64) error) Option {\n\treturn internal.WithH28Verifier(cb)\n}\n\n// WithSection brands a section ID on each generated number. A section ID must be in between [0, 7].\nfunc WithSection(section int8) Option {\n\treturn internal.WithSection(section)\n}\n\n// WithStep sets the step and the floor for each generated number.\nfunc WithStep(step int64, floor int64) Option {\n\treturn internal.WithStep(step, floor)\n}\n\n// WithObfuscation enables number obfuscation.\nfunc WithObfuscation(seed int) Option {\n\treturn internal.WithObfuscation(seed)\n}\n"
  },
  {
    "path": "callback/wuid/wuid_test.go",
    "content": "package wuid\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/edwingeng/slog\"\n\t\"github.com/edwingeng/wuid/internal\"\n\t\"math/rand\"\n\t\"strings\"\n\t\"sync/atomic\"\n\t\"testing\"\n\t\"time\"\n)\n\nvar (\n\tdumb = slog.NewDumbLogger()\n)\n\nfunc TestWUID_LoadH28WithCallback_Error(t *testing.T) {\n\tw := NewWUID(\"alpha\", dumb)\n\terr1 := w.LoadH28WithCallback(nil)\n\tif err1 == nil {\n\t\tt.Fatal(\"LoadH28WithCallback should fail when cb is nil\")\n\t}\n\n\terr2 := w.LoadH28WithCallback(func() (int64, func(), error) {\n\t\treturn 0, nil, errors.New(\"foo\")\n\t})\n\tif err2 == nil {\n\t\tt.Fatal(\"LoadH28WithCallback should fail when cb returns an error\")\n\t}\n\n\terr3 := w.LoadH28WithCallback(func() (int64, func(), error) {\n\t\treturn 0, nil, nil\n\t})\n\tif err3 == nil {\n\t\tt.Fatal(\"LoadH28WithCallback should fail when cb returns an invalid h28\")\n\t}\n}\n\nfunc TestWUID_LoadH28WithCallback(t *testing.T) {\n\tvar h28, counter int64\n\tdone := func() {\n\t\tcounter++\n\t}\n\tcb := func() (int64, func(), error) {\n\t\treturn atomic.AddInt64(&h28, 1), done, nil\n\t}\n\n\tw := NewWUID(\"alpha\", dumb)\n\terr := w.LoadH28WithCallback(cb)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tfor i := 1; i < 1000; i++ {\n\t\tif err := w.RenewNow(); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tv := (int64(i) + 1) << 36\n\t\tif atomic.LoadInt64(&w.w.N) != v {\n\t\t\tt.Fatalf(\"w.w.N is %d, while it should be %d. i: %d\", atomic.LoadInt64(&w.w.N), v, i)\n\t\t}\n\t\tn := rand.Intn(10)\n\t\tfor j := 0; j < n; j++ {\n\t\t\tw.Next()\n\t\t}\n\t}\n\n\tif counter != 1000 {\n\t\tt.Fatalf(\"the callback done do not work as expected. counter: %d\", counter)\n\t}\n}\n\nfunc TestWUID_LoadH28WithCallback_Section(t *testing.T) {\n\tvar h28 int64\n\tcb := func() (int64, func(), error) {\n\t\treturn atomic.AddInt64(&h28, 1), nil, nil\n\t}\n\n\tw := NewWUID(\"alpha\", dumb, WithSection(1))\n\tfor i := 0; i < 1000; i++ {\n\t\terr := w.LoadH28WithCallback(cb)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tv := (int64(i) + 1 + 0x1000000) << 36\n\t\tif atomic.LoadInt64(&w.w.N) != v {\n\t\t\tt.Fatalf(\"w.w.N is %d, while it should be %d. i: %d\", atomic.LoadInt64(&w.w.N), v, i)\n\t\t}\n\t\tn := rand.Intn(10)\n\t\tfor j := 0; j < n; j++ {\n\t\t\tw.Next()\n\t\t}\n\t}\n}\n\nfunc TestWUID_LoadH28WithCallback_Same(t *testing.T) {\n\tcb := func() (int64, func(), error) {\n\t\treturn 100, nil, nil\n\t}\n\n\tw1 := NewWUID(\"alpha\", dumb)\n\t_ = w1.LoadH28WithCallback(cb)\n\tif err := w1.LoadH28WithCallback(cb); err == nil {\n\t\tt.Fatal(\"LoadH28WithCallback should return an error\")\n\t}\n\n\tw2 := NewWUID(\"alpha\", dumb, WithSection(1))\n\t_ = w2.LoadH28WithCallback(cb)\n\tif err := w2.LoadH28WithCallback(cb); err == nil {\n\t\tt.Fatal(\"LoadH28WithCallback should return an error\")\n\t}\n}\n\nfunc waitUntilNumRenewedReaches(t *testing.T, w *WUID, expected int64) {\n\tt.Helper()\n\tstartTime := time.Now()\n\tfor time.Since(startTime) < time.Second {\n\t\tif atomic.LoadInt64(&w.w.Stats.NumRenewed) == expected {\n\t\t\treturn\n\t\t}\n\t\ttime.Sleep(time.Millisecond * 10)\n\t}\n\tt.Fatal(\"timeout\")\n}\n\nfunc TestWUID_Renew(t *testing.T) {\n\tw := NewWUID(\"alpha\", slog.NewScavenger())\n\terr := w.LoadH28WithCallback(func() (h28 int64, clean func(), err error) {\n\t\treturn (atomic.LoadInt64(&w.w.N) >> 36) + 1, nil, nil\n\t})\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\th28 := atomic.LoadInt64(&w.w.N) >> 36\n\tatomic.StoreInt64(&w.w.N, (h28<<36)|internal.Bye)\n\tn1a := w.Next()\n\tif n1a>>36 != h28 {\n\t\tt.Fatal(`n1a>>36 != h28`)\n\t}\n\n\twaitUntilNumRenewedReaches(t, w, 1)\n\tn1b := w.Next()\n\tif n1b != (h28+1)<<36+1 {\n\t\tt.Fatal(`n1b != (h28+1)<<36+1`)\n\t}\n\n\tatomic.StoreInt64(&w.w.N, ((h28+1)<<36)|internal.Bye)\n\tn2a := w.Next()\n\tif n2a>>36 != h28+1 {\n\t\tt.Fatal(`n2a>>36 != h28+1`)\n\t}\n\n\twaitUntilNumRenewedReaches(t, w, 2)\n\tn2b := w.Next()\n\tif n2b != (h28+2)<<36+1 {\n\t\tt.Fatal(`n2b != (h28+2)<<36+1`)\n\t}\n\n\tatomic.StoreInt64(&w.w.N, ((h28+2)<<36)|internal.Bye)\n\tn3a := w.Next()\n\tif n3a>>36 != h28+2 {\n\t\tt.Fatal(`n3a>>36 != h28+2`)\n\t}\n\n\twaitUntilNumRenewedReaches(t, w, 3)\n\tn3b := w.Next()\n\tif n3b != (h28+3)<<36+1 {\n\t\tt.Fatal(`n3b != (h28+3)<<36+1`)\n\t}\n\n\tatomic.StoreInt64(&w.w.N, ((h28+2)<<36)+internal.Bye+1)\n\tfor i := 0; i < 100; i++ {\n\t\tw.Next()\n\t}\n\tif atomic.LoadInt64(&w.w.Stats.NumRenewAttempts) != 3 {\n\t\tt.Fatal(`atomic.LoadInt64(&w.w.Stats.NumRenewAttempts) != 3`)\n\t}\n\n\tvar num int\n\tsc := w.w.Logger.(*slog.Scavenger)\n\tsc.Filter(func(level, msg string) bool {\n\t\tif level == slog.LevelInfo && strings.Contains(msg, \"renew succeeded\") {\n\t\t\tnum++\n\t\t}\n\t\treturn true\n\t})\n\tif num != 3 {\n\t\tt.Fatal(`num != 3`)\n\t}\n}\n\nfunc Example() {\n\tcallback := func() (int64, func(), error) {\n\t\tvar h28 int64\n\t\t// ...\n\t\treturn h28, nil, nil\n\t}\n\n\t// Setup\n\tw := NewWUID(\"alpha\", nil)\n\terr := w.LoadH28WithCallback(callback)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Generate\n\tfor i := 0; i < 10; i++ {\n\t\tfmt.Printf(\"%#016x\\n\", w.Next())\n\t}\n}\n"
  },
  {
    "path": "check.sh",
    "content": "#!/usr/bin/env bash\n\n[[ \"$TRACE\" ]] && set -x\npushd `dirname \"$0\"` > /dev/null\ntrap __EXIT EXIT\n\ncolorful=false\ntput setaf 7 > /dev/null 2>&1\nif [[ $? -eq 0 ]]; then\n    colorful=true\nfi\n\nfunction __EXIT() {\n    popd > /dev/null\n}\n\nfunction printError() {\n    $colorful && tput setaf 1\n    >&2 echo \"Error: $@\"\n    $colorful && tput setaf 7\n}\n\nfunction printImportantMessage() {\n    $colorful && tput setaf 3\n    >&2 echo \"$@\"\n    $colorful && tput setaf 7\n}\n\nfunction printUsage() {\n    $colorful && tput setaf 3\n    >&2 echo \"$@\"\n    $colorful && tput setaf 7\n}\n\nfind . -name coverage.sh | xargs -n 1 bash -c \\\n    'cmp --silent internal/coverage.sh \"$0\" || echo \"files are different. file: $0\"'\nfind . -name vet.sh | xargs -n 1 bash -c \\\n    'cmp --silent internal/vet.sh \"$0\" || echo \"files are different. file: $0\"'\n"
  },
  {
    "path": "go.mod",
    "content": "module github.com/edwingeng/wuid\n\ngo 1.18\n\nrequire (\n\tgithub.com/edwingeng/slog v0.0.0-20221027170832-482f0dfb6247\n\tgithub.com/go-redis/redis v6.15.9+incompatible\n\tgithub.com/go-redis/redis/v8 v8.11.5\n\tgithub.com/go-sql-driver/mysql v1.6.0\n\tgo.mongodb.org/mongo-driver v1.10.2\n)\n\nrequire (\n\tgithub.com/cespare/xxhash/v2 v2.1.2 // indirect\n\tgithub.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect\n\tgithub.com/golang/snappy v0.0.1 // indirect\n\tgithub.com/klauspost/compress v1.13.6 // indirect\n\tgithub.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect\n\tgithub.com/pkg/errors v0.9.1 // indirect\n\tgithub.com/xdg-go/pbkdf2 v1.0.0 // indirect\n\tgithub.com/xdg-go/scram v1.1.1 // indirect\n\tgithub.com/xdg-go/stringprep v1.0.3 // indirect\n\tgithub.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect\n\tgo.uber.org/atomic v1.10.0 // indirect\n\tgo.uber.org/multierr v1.8.0 // indirect\n\tgo.uber.org/zap v1.23.0 // indirect\n\tgolang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect\n\tgolang.org/x/net v0.7.0 // indirect\n\tgolang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect\n\tgolang.org/x/sys v0.5.0 // indirect\n\tgolang.org/x/text v0.7.0 // indirect\n)\n"
  },
  {
    "path": "go.sum",
    "content": "github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=\ngithub.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=\ngithub.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=\ngithub.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=\ngithub.com/edwingeng/slog v0.0.0-20221027170832-482f0dfb6247 h1:1Vb/cbeFfMh9q+CxEMLSJlHciX9x3JHdaNyHlvhGxhk=\ngithub.com/edwingeng/slog v0.0.0-20221027170832-482f0dfb6247/go.mod h1:mfngKiTrPWlUpIkQjkVzlumITH8chNhxsAiMklqH4Bo=\ngithub.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=\ngithub.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg=\ngithub.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=\ngithub.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=\ngithub.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=\ngithub.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=\ngithub.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=\ngithub.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=\ngithub.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=\ngithub.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=\ngithub.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0=\ngithub.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=\ngithub.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=\ngithub.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=\ngithub.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=\ngithub.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=\ngithub.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=\ngithub.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=\ngithub.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=\ngithub.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=\ngithub.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=\ngithub.com/xdg-go/scram v1.1.1 h1:VOMT+81stJgXW3CpHyqHN3AXDYIMsx56mEFrB37Mb/E=\ngithub.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g=\ngithub.com/xdg-go/stringprep v1.0.3 h1:kdwGpVNwPFtjs98xCGkHjQtGKh86rDcRZN17QEMCOIs=\ngithub.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8=\ngithub.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA=\ngithub.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=\ngo.mongodb.org/mongo-driver v1.10.2 h1:4Wk3cnqOrQCn0P92L3/mmurMxzdvWWs5J9jinAVKD+k=\ngo.mongodb.org/mongo-driver v1.10.2/go.mod h1:z4XpeoU6w+9Vht+jAFyLgVrD+jGSQQe0+CBWFHNiHt8=\ngo.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=\ngo.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=\ngo.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=\ngo.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=\ngo.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8=\ngo.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=\ngo.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY=\ngo.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY=\ngolang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY=\ngolang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=\ngolang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=\ngolang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=\ngolang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=\ngolang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\n"
  },
  {
    "path": "internal/coverage.sh",
    "content": "#!/usr/bin/env bash\n\n[[ \"$TRACE\" ]] && set -x\npushd `dirname \"$0\"` > /dev/null\ntrap __EXIT EXIT\n\ncolorful=false\ntput setaf 7 > /dev/null 2>&1\nif [[ $? -eq 0 ]]; then\n    colorful=true\nfi\n\nfunction __EXIT() {\n    popd > /dev/null\n}\n\nfunction printError() {\n    $colorful && tput setaf 1\n    >&2 echo \"Error: $@\"\n    $colorful && tput setaf 7\n}\n\nfunction printImportantMessage() {\n    $colorful && tput setaf 3\n    >&2 echo \"$@\"\n    $colorful && tput setaf 7\n}\n\nfunction printUsage() {\n    $colorful && tput setaf 3\n    >&2 echo \"$@\"\n    $colorful && tput setaf 7\n}\n\ngo test -cover -coverprofile=c.out -v \"$@\" && go tool cover -html=c.out\n"
  },
  {
    "path": "internal/vet.sh",
    "content": "#!/usr/bin/env bash\n\n[[ \"$TRACE\" ]] && set -x\npushd `dirname \"$0\"` > /dev/null\ntrap __EXIT EXIT\n\ncolorful=false\ntput setaf 7 > /dev/null 2>&1\nif [[ $? -eq 0 ]]; then\n    colorful=true\nfi\n\nfunction __EXIT() {\n    popd > /dev/null\n}\n\nfunction printError() {\n    $colorful && tput setaf 1\n    >&2 echo \"Error: $@\"\n    $colorful && tput setaf 7\n}\n\nfunction printImportantMessage() {\n    $colorful && tput setaf 3\n    >&2 echo \"$@\"\n    $colorful && tput setaf 7\n}\n\nfunction printUsage() {\n    $colorful && tput setaf 3\n    >&2 echo \"$@\"\n    $colorful && tput setaf 7\n}\n\nprintImportantMessage \"====== gofmt\"\ngofmt -w .\n\nprintImportantMessage \"====== go vet\"\ngo vet ./...\n\nprintImportantMessage \"====== gocyclo\"\ngocyclo -over 15 .\n\nprintImportantMessage \"====== ineffassign\"\nineffassign ./...\n\nprintImportantMessage \"====== misspell\"\nmisspell *\n"
  },
  {
    "path": "internal/wuid.go",
    "content": "package internal\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/edwingeng/slog\"\n\t\"sync\"\n\t\"sync/atomic\"\n)\n\nconst (\n\t// PanicValue indicates when Next starts to panic.\n\tPanicValue int64 = ((1 << 36) * 96 / 100) & ^1023\n\t// CriticalValue indicates when to renew the high 28 bits.\n\tCriticalValue int64 = ((1 << 36) * 80 / 100) & ^1023\n\t// RenewIntervalMask indicates the 'time' between two renewal attempts.\n\tRenewIntervalMask int64 = 0x20000000 - 1\n)\n\nconst (\n\tBye = ((CriticalValue + RenewIntervalMask) & ^RenewIntervalMask) - 1\n)\n\nconst (\n\tH28Mask = 0x07FFFFFF << 36\n\tL36Mask = 0x0FFFFFFFFF\n)\n\ntype WUID struct {\n\tN     int64\n\tStep  int64\n\tFloor int64\n\n\tFlags           int8\n\tObfuscation     bool\n\tMonolithic      bool\n\tObfuscationMask int64\n\tSection         int64\n\n\tslog.Logger\n\tName        string\n\tH28Verifier func(h28 int64) error\n\n\tsync.Mutex\n\tRenew func() error\n\n\tStats struct {\n\t\tNumRenewAttempts int64\n\t\tNumRenewed       int64\n\t}\n}\n\nfunc NewWUID(name string, logger slog.Logger, opts ...Option) (w *WUID) {\n\tw = &WUID{Step: 1, Name: name, Monolithic: true}\n\tif logger != nil {\n\t\tw.Logger = logger\n\t} else {\n\t\tw.Logger = slog.NewDevelopmentConfig().MustBuild()\n\t}\n\tfor _, opt := range opts {\n\t\topt(w)\n\t}\n\tif !w.Obfuscation || w.Floor == 0 {\n\t\treturn\n\t}\n\n\tones := w.Step - 1\n\tw.ObfuscationMask |= ones\n\treturn\n}\n\nfunc (w *WUID) Next() int64 {\n\tv1 := atomic.AddInt64(&w.N, w.Step)\n\tv2 := v1 & L36Mask\n\tif v2 >= PanicValue {\n\t\tpanicValue := v1&H28Mask | PanicValue\n\t\tatomic.CompareAndSwapInt64(&w.N, v1, panicValue)\n\t\tpanic(fmt.Errorf(\"the low 36 bits are about to run out\"))\n\t}\n\tif v2 >= CriticalValue && v2&RenewIntervalMask == 0 {\n\t\tgo renewImpl(w)\n\t}\n\n\tswitch w.Flags {\n\tcase 0:\n\t\treturn v1\n\tcase 1:\n\t\tx := v1 ^ w.ObfuscationMask\n\t\tr := v1&H28Mask | x&L36Mask\n\t\treturn r\n\tcase 2:\n\t\tr := v1 / w.Floor * w.Floor\n\t\treturn r\n\tcase 3:\n\t\tx := v1 ^ w.ObfuscationMask\n\t\tq := v1&H28Mask | x&L36Mask\n\t\tr := q / w.Floor * w.Floor\n\t\treturn r\n\tdefault:\n\t\tpanic(\"impossible\")\n\t}\n}\n\nfunc renewImpl(w *WUID) {\n\tdefer func() {\n\t\tatomic.AddInt64(&w.Stats.NumRenewAttempts, 1)\n\t}()\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tw.Warnf(\"<wuid> panic, renew failed. name: %s, reason: %+v\", w.Name, r)\n\t\t}\n\t}()\n\n\terr := w.RenewNow()\n\tif err != nil {\n\t\tw.Warnf(\"<wuid> renew failed. name: %s, reason: %+v\", w.Name, err)\n\t} else {\n\t\tw.Infof(\"<wuid> renew succeeded. name: %s\", w.Name)\n\t\tatomic.AddInt64(&w.Stats.NumRenewed, 1)\n\t}\n}\n\nfunc (w *WUID) RenewNow() error {\n\tw.Lock()\n\tf := w.Renew\n\tw.Unlock()\n\treturn f()\n}\n\nfunc (w *WUID) Reset(n int64) {\n\tif n < 0 {\n\t\tpanic(\"n cannot be negative\")\n\t}\n\tif n&L36Mask >= PanicValue {\n\t\tpanic(\"n is too old\")\n\t}\n\n\tif w.Monolithic {\n\t\t// Empty\n\t} else {\n\t\tconst L60Mask = 0x0FFFFFFFFFFFFFFF\n\t\tn = n&L60Mask | w.Section\n\t}\n\tif w.Floor > 1 {\n\t\tif n&(w.Step-1) == 0 {\n\t\t\tatomic.StoreInt64(&w.N, n)\n\t\t} else {\n\t\t\tatomic.StoreInt64(&w.N, n&^(w.Step-1)+w.Step)\n\t\t}\n\t} else {\n\t\tatomic.StoreInt64(&w.N, n)\n\t}\n}\n\nfunc (w *WUID) VerifyH28(h28 int64) error {\n\tif h28 <= 0 {\n\t\treturn errors.New(\"h28 must be positive\")\n\t}\n\n\tif w.Monolithic {\n\t\tif h28 > 0x07FFFFFF {\n\t\t\treturn errors.New(\"h28 should not exceed 0x07FFFFFF\")\n\t\t}\n\t} else {\n\t\tif h28 > 0x00FFFFFF {\n\t\t\treturn errors.New(\"h28 should not exceed 0x00FFFFFF\")\n\t\t}\n\t}\n\n\tcurrent := atomic.LoadInt64(&w.N) >> 36\n\tif w.Monolithic {\n\t\tif h28 == current {\n\t\t\treturn fmt.Errorf(\"h28 should be a different value other than %d\", h28)\n\t\t}\n\t} else {\n\t\tif h28 == current&0x00FFFFFF {\n\t\t\treturn fmt.Errorf(\"h28 should be a different value other than %d\", h28)\n\t\t}\n\t}\n\n\tif w.H28Verifier != nil {\n\t\tif err := w.H28Verifier(h28); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn nil\n}\n\ntype Option func(w *WUID)\n\nfunc WithH28Verifier(cb func(h28 int64) error) Option {\n\treturn func(w *WUID) {\n\t\tw.H28Verifier = cb\n\t}\n}\n\nfunc WithSection(section int8) Option {\n\tif section < 0 || section > 7 {\n\t\tpanic(\"section must be in between [0, 7]\")\n\t}\n\treturn func(w *WUID) {\n\t\tw.Monolithic = false\n\t\tw.Section = int64(section) << 60\n\t}\n}\n\nfunc WithStep(step int64, floor int64) Option {\n\tswitch step {\n\tcase 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024:\n\tdefault:\n\t\tpanic(\"the step must be one of these values: 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024\")\n\t}\n\tif floor != 0 && (floor < 0 || floor >= step) {\n\t\tpanic(fmt.Errorf(\"floor must be in between [0, %d)\", step))\n\t}\n\treturn func(w *WUID) {\n\t\tif w.Step != 1 {\n\t\t\tpanic(\"a second WithStep detected\")\n\t\t}\n\t\tw.Step = step\n\t\tif floor >= 2 {\n\t\t\tw.Floor = floor\n\t\t\tw.Flags |= 2\n\t\t}\n\t}\n}\n\nfunc WithObfuscation(seed int) Option {\n\tif seed == 0 {\n\t\tpanic(\"seed cannot be zero\")\n\t}\n\treturn func(w *WUID) {\n\t\tw.Obfuscation = true\n\t\tx := uint64(seed)\n\t\tx = (x ^ (x >> 30)) * uint64(0xbf58476d1ce4e5b9)\n\t\tx = (x ^ (x >> 27)) * uint64(0x94d049bb133111eb)\n\t\tx = (x ^ (x >> 31)) & 0x7FFFFFFFFFFFFFFF\n\t\tw.ObfuscationMask = int64(x)\n\t\tw.Flags |= 1\n\t}\n}\n"
  },
  {
    "path": "internal/wuid_test.go",
    "content": "package internal\n\nimport (\n\t\"errors\"\n\t\"github.com/edwingeng/slog\"\n\t\"math/rand\"\n\t\"sort\"\n\t\"strings\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc (w *WUID) Scavenger() *slog.Scavenger {\n\treturn w.Logger.(*slog.Scavenger)\n}\n\nfunc TestWUID_Next(t *testing.T) {\n\tfor i := 0; i < 100; i++ {\n\t\tw := NewWUID(\"alpha\", nil)\n\t\tw.Reset(int64(i+1) << 36)\n\t\tv := atomic.LoadInt64(&w.N)\n\t\tfor j := 0; j < 100; j++ {\n\t\t\tv++\n\t\t\tif id := w.Next(); id != v {\n\t\t\t\tt.Fatalf(\"the id is %d, while it should be %d\", id, v)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc TestWUID_Next_Concurrent(t *testing.T) {\n\tw := NewWUID(\"alpha\", nil)\n\tvar mu sync.Mutex\n\tconst N1 = 100\n\tconst N2 = 100\n\ta := make([]int64, 0, N1*N2)\n\n\tvar wg sync.WaitGroup\n\tfor i := 0; i < N1; i++ {\n\t\twg.Add(1)\n\t\tgo func() {\n\t\t\tdefer wg.Done()\n\t\t\tfor j := 0; j < N2; j++ {\n\t\t\t\tid := w.Next()\n\t\t\t\tmu.Lock()\n\t\t\t\ta = append(a, id)\n\t\t\t\tmu.Unlock()\n\t\t\t}\n\t\t}()\n\t}\n\n\twg.Wait()\n\tsort.Slice(a, func(i, j int) bool {\n\t\treturn a[i] < a[j]\n\t})\n\n\tfor i := 0; i < N1*N2-1; i++ {\n\t\tif a[i] == a[i+1] {\n\t\t\tt.Fatalf(\"duplication detected\")\n\t\t}\n\t}\n}\n\nfunc TestWUID_Next_Panic(t *testing.T) {\n\tconst total = 100\n\tw := NewWUID(\"alpha\", nil)\n\tatomic.StoreInt64(&w.N, PanicValue)\n\n\tch := make(chan int64, total)\n\tfor i := 0; i < total; i++ {\n\t\tgo func() {\n\t\t\tdefer func() {\n\t\t\t\tif r := recover(); r != nil {\n\t\t\t\t\tch <- 0\n\t\t\t\t}\n\t\t\t}()\n\n\t\t\tch <- w.Next()\n\t\t}()\n\t}\n\n\tfor i := 0; i < total; i++ {\n\t\tv := <-ch\n\t\tif v != 0 {\n\t\t\tt.Fatal(\"something is wrong with Next()\")\n\t\t}\n\t}\n}\n\nfunc waitUntilNumRenewAttemptsReaches(t *testing.T, w *WUID, expected int64) {\n\tt.Helper()\n\tstartTime := time.Now()\n\tfor time.Since(startTime) < time.Second {\n\t\tif atomic.LoadInt64(&w.Stats.NumRenewAttempts) == expected {\n\t\t\treturn\n\t\t}\n\t\ttime.Sleep(time.Millisecond * 10)\n\t}\n\tt.Fatal(\"timeout\")\n}\n\nfunc waitUntilNumRenewedReaches(t *testing.T, w *WUID, expected int64) {\n\tt.Helper()\n\tstartTime := time.Now()\n\tfor time.Since(startTime) < time.Second {\n\t\tif atomic.LoadInt64(&w.Stats.NumRenewed) == expected {\n\t\t\treturn\n\t\t}\n\t\ttime.Sleep(time.Millisecond * 10)\n\t}\n\tt.Fatal(\"timeout\")\n}\n\nfunc TestWUID_Renew(t *testing.T) {\n\tw := NewWUID(\"alpha\", slog.NewScavenger())\n\tw.Renew = func() error {\n\t\tw.Reset(((atomic.LoadInt64(&w.N) >> 36) + 1) << 36)\n\t\treturn nil\n\t}\n\n\tw.Reset(Bye)\n\tn1a := w.Next()\n\tif n1a>>36 != 0 {\n\t\tt.Fatal(`n1a>>36 != 0`)\n\t}\n\n\twaitUntilNumRenewedReaches(t, w, 1)\n\tn1b := w.Next()\n\tif n1b != 1<<36+1 {\n\t\tt.Fatal(`n1b != 1<<36+1`)\n\t}\n\n\tw.Reset(1<<36 | Bye)\n\tn2a := w.Next()\n\tif n2a>>36 != 1 {\n\t\tt.Fatal(`n2a>>36 != 1`)\n\t}\n\n\twaitUntilNumRenewedReaches(t, w, 2)\n\tn2b := w.Next()\n\tif n2b != 2<<36+1 {\n\t\tt.Fatal(`n2b != 2<<36+1`)\n\t}\n\n\tw.Reset(2<<36 | Bye + RenewIntervalMask + 1)\n\tn3a := w.Next()\n\tif n3a>>36 != 2 {\n\t\tt.Fatal(`n3a>>36 != 2`)\n\t}\n\n\twaitUntilNumRenewedReaches(t, w, 3)\n\tn3b := w.Next()\n\tif n3b != 3<<36+1 {\n\t\tt.Fatal(`n3b != 3<<36+1`)\n\t}\n\n\tw.Reset(Bye + 1)\n\tfor i := 0; i < 100; i++ {\n\t\tw.Next()\n\t}\n\tif atomic.LoadInt64(&w.Stats.NumRenewAttempts) != 3 {\n\t\tt.Fatal(`atomic.LoadInt64(&w.Stats.NumRenewAttempts) != 3`)\n\t}\n\n\tvar num int\n\tw.Scavenger().Filter(func(level, msg string) bool {\n\t\tif level == slog.LevelInfo && strings.Contains(msg, \"renew succeeded\") {\n\t\t\tnum++\n\t\t}\n\t\treturn true\n\t})\n\tif num != 3 {\n\t\tt.Fatal(`num != 3`)\n\t}\n}\n\nfunc TestWUID_Renew_Error(t *testing.T) {\n\tw := NewWUID(\"alpha\", slog.NewScavenger())\n\tw.Renew = func() error {\n\t\treturn errors.New(\"foo\")\n\t}\n\n\tw.Reset((1 >> 36 << 36) | Bye)\n\tw.Next()\n\twaitUntilNumRenewAttemptsReaches(t, w, 1)\n\tw.Next()\n\n\tw.Reset((2 >> 36 << 36) | Bye)\n\tw.Next()\n\twaitUntilNumRenewAttemptsReaches(t, w, 2)\n\n\tfor i := 0; i < 100; i++ {\n\t\tw.Next()\n\t}\n\tif atomic.LoadInt64(&w.Stats.NumRenewAttempts) != 2 {\n\t\tt.Fatal(`atomic.LoadInt64(&w.Stats.NumRenewAttempts) != 2`)\n\t}\n\tif atomic.LoadInt64(&w.Stats.NumRenewed) != 0 {\n\t\tt.Fatal(`atomic.LoadInt64(&w.Stats.NumRenewed) != 0`)\n\t}\n\n\tvar num int\n\tw.Scavenger().Filter(func(level, msg string) bool {\n\t\tif level == slog.LevelWarn && strings.Contains(msg, \"renew failed\") && strings.Contains(msg, \"foo\") {\n\t\t\tnum++\n\t\t}\n\t\treturn true\n\t})\n\tif num != 2 {\n\t\tt.Fatal(`num != 2`)\n\t}\n}\n\nfunc TestWUID_Renew_Panic(t *testing.T) {\n\tw := NewWUID(\"alpha\", slog.NewScavenger())\n\tw.Renew = func() error {\n\t\tpanic(\"foo\")\n\t}\n\n\tw.Reset((1 >> 36 << 36) | Bye)\n\tw.Next()\n\twaitUntilNumRenewAttemptsReaches(t, w, 1)\n\tw.Next()\n\n\tw.Reset((2 >> 36 << 36) | Bye)\n\tw.Next()\n\twaitUntilNumRenewAttemptsReaches(t, w, 2)\n\n\tfor i := 0; i < 100; i++ {\n\t\tw.Next()\n\t}\n\tif atomic.LoadInt64(&w.Stats.NumRenewAttempts) != 2 {\n\t\tt.Fatal(`atomic.LoadInt64(&w.Stats.NumRenewAttempts) != 2`)\n\t}\n\tif atomic.LoadInt64(&w.Stats.NumRenewed) != 0 {\n\t\tt.Fatal(`atomic.LoadInt64(&w.Stats.NumRenewed) != 0`)\n\t}\n\n\tvar num int\n\tw.Scavenger().Filter(func(level, msg string) bool {\n\t\tif level == slog.LevelWarn && strings.Contains(msg, \"renew failed\") && strings.Contains(msg, \"foo\") {\n\t\t\tnum++\n\t\t}\n\t\treturn true\n\t})\n\tif num != 2 {\n\t\tt.Fatal(`num != 2`)\n\t}\n}\n\nfunc TestWUID_Step(t *testing.T) {\n\tconst step = 16\n\tw := NewWUID(\"alpha\", slog.NewScavenger(), WithStep(step, 0))\n\tw.Reset(17 << 36)\n\n\tw.Renew = func() error {\n\t\tw.Reset(((atomic.LoadInt64(&w.N) >> 36) + 1) << 36)\n\t\treturn nil\n\t}\n\n\tfor i := int64(1); i < 100; i++ {\n\t\tif w.Next()&L36Mask != step*i {\n\t\t\tt.Fatal(\"w.Next()&L36Mask != step*i\")\n\t\t}\n\t}\n\n\tn1 := w.Next()\n\tw.Reset(((n1 >> 36 << 36) | Bye) & ^(step - 1))\n\tw.Next()\n\twaitUntilNumRenewedReaches(t, w, 1)\n\tn2 := w.Next()\n\n\tw.Reset(((n2 >> 36 << 36) | Bye) & ^(step - 1))\n\tw.Next()\n\twaitUntilNumRenewedReaches(t, w, 2)\n\tn3 := w.Next()\n\n\tif n2>>36-n1>>36 != 1 || n3>>36-n2>>36 != 1 {\n\t\tt.Fatalf(\"the renew mechanism does not work as expected: %x, %x, %x\", n1>>36, n2>>36, n3>>36)\n\t}\n\n\tvar num int\n\tw.Scavenger().Filter(func(level, msg string) bool {\n\t\tif level == slog.LevelInfo && strings.Contains(msg, \"renew succeeded\") {\n\t\t\tnum++\n\t\t}\n\t\treturn true\n\t})\n\tif num != 2 {\n\t\tt.Fatal(`num != 2`)\n\t}\n\n\tfunc() {\n\t\tdefer func() {\n\t\t\t_ = recover()\n\t\t}()\n\t\tNewWUID(\"alpha\", nil, WithStep(5, 0))\n\t\tt.Fatal(\"WithStep should have panicked\")\n\t}()\n}\n\nfunc TestWUID_Floor(t *testing.T) {\n\tr := rand.New(rand.NewSource(time.Now().Unix()))\n\tallSteps := []int64{1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024}\n\tfor loop := 0; loop < 10000; loop++ {\n\t\tstep := allSteps[r.Intn(len(allSteps))]\n\t\tvar floor = r.Int63n(step)\n\t\tw := NewWUID(\"alpha\", slog.NewScavenger(), WithStep(step, floor))\n\t\tif floor < 2 {\n\t\t\tif w.Flags != 0 {\n\t\t\t\tt.Fatal(`w.Flags != 0`)\n\t\t\t}\n\t\t} else {\n\t\t\tif w.Flags != 2 {\n\t\t\t\tt.Fatal(`w.Flags != 2`)\n\t\t\t}\n\t\t}\n\n\t\tw.Reset(r.Int63n(100) << 36)\n\t\tbaseValue := atomic.LoadInt64(&w.N)\n\n\t\tfor i := int64(1); i < 100; i++ {\n\t\t\tx := w.Next()\n\t\t\tif floor != 0 {\n\t\t\t\tif reminder := x % floor; reminder != 0 {\n\t\t\t\t\tt.Fatal(\"reminder != 0\")\n\t\t\t\t}\n\t\t\t}\n\t\t\tif x <= baseValue+i*step-step || x > baseValue+i*step {\n\t\t\t\tt.Fatal(\"x <= baseValue+i*step-step || x > baseValue+i*step\")\n\t\t\t}\n\t\t}\n\t}\n\n\tfunc() {\n\t\tdefer func() {\n\t\t\t_ = recover()\n\t\t}()\n\t\tNewWUID(\"alpha\", nil, WithStep(1024, 2000))\n\t\tt.Fatal(\"WithStep should have panicked\")\n\t}()\n\n\tfunc() {\n\t\tdefer func() {\n\t\t\t_ = recover()\n\t\t}()\n\t\tNewWUID(\"alpha\", nil, WithStep(1024, 0), WithStep(128, 0))\n\t\tt.Fatal(\"WithStep should have panicked\")\n\t}()\n}\n\nfunc TestWUID_VerifyH28(t *testing.T) {\n\tw1 := NewWUID(\"alpha\", nil)\n\tw1.Reset(H28Mask)\n\tif err := w1.VerifyH28(100); err != nil {\n\t\tt.Fatalf(\"VerifyH28 does not work as expected. n: 100, error: %s\", err)\n\t}\n\tif err := w1.VerifyH28(0); err == nil {\n\t\tt.Fatalf(\"VerifyH28 does not work as expected. n: 0\")\n\t}\n\tif err := w1.VerifyH28(0x08000000); err == nil {\n\t\tt.Fatalf(\"VerifyH28 does not work as expected. n: 0x08000000\")\n\t}\n\tif err := w1.VerifyH28(0x07FFFFFF); err == nil {\n\t\tt.Fatalf(\"VerifyH28 does not work as expected. n: 0x07FFFFFF\")\n\t}\n\n\tw2 := NewWUID(\"alpha\", nil, WithSection(1))\n\tw2.Reset(H28Mask)\n\tif err := w2.VerifyH28(100); err != nil {\n\t\tt.Fatalf(\"VerifyH28 does not work as expected. section: 1, n: 100, error: %s\", err)\n\t}\n\tif err := w2.VerifyH28(0); err == nil {\n\t\tt.Fatalf(\"VerifyH28 does not work as expected. section: 1, n: 0\")\n\t}\n\tif err := w2.VerifyH28(0x01000000); err == nil {\n\t\tt.Fatalf(\"VerifyH28 does not work as expected. section: 1, n: 0x01000000\")\n\t}\n\tif err := w2.VerifyH28(0x00FFFFFF); err == nil {\n\t\tt.Fatalf(\"VerifyH28 does not work as expected. section: 1, n: 0x00FFFFFF\")\n\t}\n}\n\nfunc TestWithSection_Panic(t *testing.T) {\n\tfor i := -100; i <= 100; i++ {\n\t\tfunc(j int8) {\n\t\t\tdefer func() {\n\t\t\t\t_ = recover()\n\t\t\t}()\n\t\t\tWithSection(j)\n\t\t\tif j >= 8 {\n\t\t\t\tt.Fatalf(\"WithSection should only accept the values in [0, 7]. j: %d\", j)\n\t\t\t}\n\t\t}(int8(i))\n\t}\n}\n\nfunc TestWithSection_Reset(t *testing.T) {\n\tfor i := 0; i < 28; i++ {\n\t\tn := int64(1) << (uint(i) + 36)\n\t\tfunc() {\n\t\t\tdefer func() {\n\t\t\t\tif r := recover(); r != nil {\n\t\t\t\t\tif i != 27 {\n\t\t\t\t\t\tt.Fatal(r)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}()\n\t\t\tfor j := int8(1); j < 8; j++ {\n\t\t\t\tw := NewWUID(\"alpha\", nil, WithSection(j))\n\t\t\t\tw.Reset(n)\n\t\t\t\tv := atomic.LoadInt64(&w.N)\n\t\t\t\tif v>>60 != int64(j) {\n\t\t\t\t\tt.Fatalf(\"w.Section does not work as expected. w.N: %x, n: %x, i: %d, j: %d\", v, n, i, j)\n\t\t\t\t}\n\t\t\t}\n\t\t}()\n\t}\n\n\tfunc() {\n\t\tdefer func() {\n\t\t\t_ = recover()\n\t\t}()\n\t\tw := NewWUID(\"alpha\", nil)\n\t\tw.Reset((1 << 36) | PanicValue)\n\t\tt.Fatal(\"Reset should have panicked\")\n\t}()\n}\n\nfunc TestWithH28Verifier(t *testing.T) {\n\tw := NewWUID(\"alpha\", nil, WithH28Verifier(func(h28 int64) error {\n\t\tif h28 >= 20 {\n\t\t\treturn errors.New(\"bomb\")\n\t\t}\n\t\treturn nil\n\t}))\n\tif err := w.VerifyH28(10); err != nil {\n\t\tt.Fatal(\"the H28Verifier should not return error\")\n\t}\n\tif err := w.VerifyH28(20); err == nil || err.Error() != \"bomb\" {\n\t\tt.Fatal(\"the H28Verifier was not called\")\n\t}\n}\n\n//gocyclo:ignore\nfunc TestWithObfuscation(t *testing.T) {\n\tw1 := NewWUID(\"alpha\", nil, WithObfuscation(1))\n\tif w1.Flags != 1 {\n\t\tt.Fatal(`w1.Flags != 1`)\n\t}\n\tif w1.ObfuscationMask == 0 {\n\t\tt.Fatal(`w1.ObfuscationMask == 0`)\n\t}\n\n\tw1.Reset(1 << 36)\n\tfor i := 1; i < 100; i++ {\n\t\tv := w1.Next()\n\t\tif v&H28Mask != 1<<36 {\n\t\t\tt.Fatal(`v&H28Mask != 1<<36`)\n\t\t}\n\t\ttmp := v ^ w1.ObfuscationMask\n\t\tif tmp&L36Mask != int64(i) {\n\t\t\tt.Fatal(`tmp&L36Mask != int64(i)`)\n\t\t}\n\t}\n\n\tw2 := NewWUID(\"alpha\", nil, WithObfuscation(1), WithStep(128, 100))\n\tif w2.Flags != 3 {\n\t\tt.Fatal(`w2.Flags != 3`)\n\t}\n\tif w2.ObfuscationMask == 0 {\n\t\tt.Fatal(`w2.ObfuscationMask == 0`)\n\t}\n\n\tw2.Reset(1 << 36)\n\tfor i := 1; i < 100; i++ {\n\t\tv := w2.Next()\n\t\tif v%w2.Floor != 0 {\n\t\t\tt.Fatal(`v%w2.Floor != 0`)\n\t\t}\n\t\tif v&H28Mask != 1<<36 {\n\t\t\tt.Fatal(`v&H28Mask != 1<<36`)\n\t\t}\n\t\ttmp := v ^ w2.ObfuscationMask\n\t\tif tmp&L36Mask&^(w2.Step-1) != w2.Step*int64(i) {\n\t\t\tt.Fatal(`tmp&L36Mask&^(w2.Step-1) != w2.Step*int64(i)`)\n\t\t}\n\t}\n\n\tw3 := NewWUID(\"alpha\", nil, WithObfuscation(1), WithStep(1024, 659))\n\tif w3.Flags != 3 {\n\t\tt.Fatal(`w3.Flags != 3`)\n\t}\n\tif w3.ObfuscationMask == 0 {\n\t\tt.Fatal(`w3.ObfuscationMask == 0`)\n\t}\n\n\tw3.Reset(1<<36 + 1)\n\tfor i := 1; i < 100; i++ {\n\t\tv := w3.Next()\n\t\tif v%w3.Floor != 0 {\n\t\t\tt.Fatal(`v%w3.Floor != 0`)\n\t\t}\n\t\tif v&H28Mask != 1<<36 {\n\t\t\tt.Fatal(`v&H28Mask != 1<<36`)\n\t\t}\n\t\ttmp := v ^ w3.ObfuscationMask\n\t\tif tmp&L36Mask&^(w3.Step-1) != w3.Step*int64(i+1) {\n\t\t\tt.Fatal(`tmp&L36Mask&^(w3.Step-1) != w3.Step*int64(i+1)`)\n\t\t}\n\t}\n\n\tfunc() {\n\t\tdefer func() {\n\t\t\t_ = recover()\n\t\t}()\n\t\tNewWUID(\"alpha\", nil, WithObfuscation(0))\n\t\tt.Fatal(\"WithObfuscation should have panicked\")\n\t}()\n}\n"
  },
  {
    "path": "mongo/docker-mongo-client.sh",
    "content": "#!/usr/bin/env bash\n\n[[ \"$TRACE\" ]] && set -x\npushd `dirname \"$0\"` > /dev/null\ntrap __EXIT EXIT\n\ncolorful=false\ntput setaf 7 > /dev/null 2>&1\nif [[ $? -eq 0 ]]; then\n    colorful=true\nfi\n\nfunction __EXIT() {\n    popd > /dev/null\n}\n\nfunction printError() {\n    $colorful && tput setaf 1\n    >&2 echo \"Error: $@\"\n    $colorful && tput setaf 7\n}\n\nfunction printImportantMessage() {\n    $colorful && tput setaf 3\n    >&2 echo \"$@\"\n    $colorful && tput setaf 7\n}\n\nfunction printUsage() {\n    $colorful && tput setaf 3\n    >&2 echo \"$@\"\n    $colorful && tput setaf 7\n}\n\ndocker run -it --rm mongo mongosh host.docker.internal\n"
  },
  {
    "path": "mongo/docker-mongo-server.sh",
    "content": "#!/usr/bin/env bash\n\n[[ \"$TRACE\" ]] && set -x\npushd `dirname \"$0\"` > /dev/null\ntrap __EXIT EXIT\n\ncolorful=false\ntput setaf 7 > /dev/null 2>&1\nif [[ $? -eq 0 ]]; then\n    colorful=true\nfi\n\nfunction __EXIT() {\n    popd > /dev/null\n}\n\nfunction printError() {\n    $colorful && tput setaf 1\n    >&2 echo \"Error: $@\"\n    $colorful && tput setaf 7\n}\n\nfunction printImportantMessage() {\n    $colorful && tput setaf 3\n    >&2 echo \"$@\"\n    $colorful && tput setaf 7\n}\n\nfunction printUsage() {\n    $colorful && tput setaf 3\n    >&2 echo \"$@\"\n    $colorful && tput setaf 7\n}\n\ndocker run --name mongo-server -p 27017:27017 -d mongo\n"
  },
  {
    "path": "mongo/wuid/coverage.sh",
    "content": "#!/usr/bin/env bash\n\n[[ \"$TRACE\" ]] && set -x\npushd `dirname \"$0\"` > /dev/null\ntrap __EXIT EXIT\n\ncolorful=false\ntput setaf 7 > /dev/null 2>&1\nif [[ $? -eq 0 ]]; then\n    colorful=true\nfi\n\nfunction __EXIT() {\n    popd > /dev/null\n}\n\nfunction printError() {\n    $colorful && tput setaf 1\n    >&2 echo \"Error: $@\"\n    $colorful && tput setaf 7\n}\n\nfunction printImportantMessage() {\n    $colorful && tput setaf 3\n    >&2 echo \"$@\"\n    $colorful && tput setaf 7\n}\n\nfunction printUsage() {\n    $colorful && tput setaf 3\n    >&2 echo \"$@\"\n    $colorful && tput setaf 7\n}\n\ngo test -cover -coverprofile=c.out -v \"$@\" && go tool cover -html=c.out\n"
  },
  {
    "path": "mongo/wuid/vet.sh",
    "content": "#!/usr/bin/env bash\n\n[[ \"$TRACE\" ]] && set -x\npushd `dirname \"$0\"` > /dev/null\ntrap __EXIT EXIT\n\ncolorful=false\ntput setaf 7 > /dev/null 2>&1\nif [[ $? -eq 0 ]]; then\n    colorful=true\nfi\n\nfunction __EXIT() {\n    popd > /dev/null\n}\n\nfunction printError() {\n    $colorful && tput setaf 1\n    >&2 echo \"Error: $@\"\n    $colorful && tput setaf 7\n}\n\nfunction printImportantMessage() {\n    $colorful && tput setaf 3\n    >&2 echo \"$@\"\n    $colorful && tput setaf 7\n}\n\nfunction printUsage() {\n    $colorful && tput setaf 3\n    >&2 echo \"$@\"\n    $colorful && tput setaf 7\n}\n\nprintImportantMessage \"====== gofmt\"\ngofmt -w .\n\nprintImportantMessage \"====== go vet\"\ngo vet ./...\n\nprintImportantMessage \"====== gocyclo\"\ngocyclo -over 15 .\n\nprintImportantMessage \"====== ineffassign\"\nineffassign ./...\n\nprintImportantMessage \"====== misspell\"\nmisspell *\n"
  },
  {
    "path": "mongo/wuid/wuid.go",
    "content": "package wuid\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"github.com/edwingeng/slog\"\n\t\"github.com/edwingeng/wuid/internal\"\n\t\"go.mongodb.org/mongo-driver/bson\"\n\t\"go.mongodb.org/mongo-driver/mongo\"\n\t\"go.mongodb.org/mongo-driver/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/mongo/readconcern\"\n\t\"go.mongodb.org/mongo-driver/mongo/readpref\"\n\t\"go.mongodb.org/mongo-driver/mongo/writeconcern\"\n\t\"time\"\n)\n\n// WUID is an extremely fast universal unique identifier generator.\ntype WUID struct {\n\tw *internal.WUID\n}\n\n// NewWUID creates a new WUID instance.\nfunc NewWUID(name string, logger slog.Logger, opts ...Option) *WUID {\n\treturn &WUID{w: internal.NewWUID(name, logger, opts...)}\n}\n\n// Next returns a unique identifier.\nfunc (w *WUID) Next() int64 {\n\treturn w.w.Next()\n}\n\ntype NewClient func() (client *mongo.Client, autoDisconnect bool, err error)\n\n// LoadH28FromMongo adds 1 to a specific number in MongoDB and fetches its new value.\n// The new value is used as the high 28 bits of all generated numbers. In addition, all the\n// arguments passed in are saved for future renewal.\nfunc (w *WUID) LoadH28FromMongo(newClient NewClient, dbName, coll, docID string) error {\n\tif len(dbName) == 0 {\n\t\treturn errors.New(\"dbName cannot be empty\")\n\t}\n\tif len(coll) == 0 {\n\t\treturn errors.New(\"coll cannot be empty\")\n\t}\n\tif len(docID) == 0 {\n\t\treturn errors.New(\"docID cannot be empty\")\n\t}\n\n\tclient, autoDisconnect, err := newClient()\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer func() {\n\t\tif autoDisconnect {\n\t\t\tctx2, cancel2 := context.WithTimeout(context.Background(), time.Second*5)\n\t\t\tdefer cancel2()\n\t\t\t_ = client.Disconnect(ctx2)\n\t\t}\n\t}()\n\n\tctx1, cancel1 := context.WithTimeout(context.Background(), time.Second*5)\n\tdefer cancel1()\n\tif err := client.Ping(ctx1, readpref.Primary()); err != nil {\n\t\treturn err\n\t}\n\n\tcollOpts := &options.CollectionOptions{\n\t\tReadConcern:    readconcern.Majority(),\n\t\tWriteConcern:   writeconcern.New(writeconcern.WMajority()),\n\t\tReadPreference: readpref.Primary(),\n\t}\n\n\tvar doc struct {\n\t\tN int32\n\t}\n\n\tfilter := bson.D{\n\t\t{Key: \"_id\", Value: docID},\n\t}\n\tupdate := bson.D{\n\t\t{\n\t\t\tKey: \"$inc\",\n\t\t\tValue: bson.D{\n\t\t\t\t{Key: \"n\", Value: int32(1)},\n\t\t\t},\n\t\t},\n\t}\n\n\tvar findOneAndUpdateOptions options.FindOneAndUpdateOptions\n\tfindOneAndUpdateOptions.SetUpsert(true).SetReturnDocument(options.After)\n\tc := client.Database(dbName).Collection(coll, collOpts)\n\terr = c.FindOneAndUpdate(ctx1, filter, update, &findOneAndUpdateOptions).Decode(&doc)\n\tif err != nil {\n\t\treturn err\n\t}\n\th28 := int64(doc.N)\n\tif err = w.w.VerifyH28(h28); err != nil {\n\t\treturn err\n\t}\n\n\tw.w.Reset(h28 << 36)\n\tw.w.Logger.Infof(\"<wuid> new h28: %d. name: %s\", h28, w.w.Name)\n\n\tw.w.Lock()\n\tdefer w.w.Unlock()\n\n\tif w.w.Renew != nil {\n\t\treturn nil\n\t}\n\tw.w.Renew = func() error {\n\t\treturn w.LoadH28FromMongo(newClient, dbName, coll, docID)\n\t}\n\n\treturn nil\n}\n\n// RenewNow reacquires the high 28 bits immediately.\nfunc (w *WUID) RenewNow() error {\n\treturn w.w.RenewNow()\n}\n\ntype Option = internal.Option\n\n// WithH28Verifier adds an extra verifier for the high 28 bits.\nfunc WithH28Verifier(cb func(h28 int64) error) Option {\n\treturn internal.WithH28Verifier(cb)\n}\n\n// WithSection brands a section ID on each generated number. A section ID must be in between [0, 7].\nfunc WithSection(section int8) Option {\n\treturn internal.WithSection(section)\n}\n\n// WithStep sets the step and the floor for each generated number.\nfunc WithStep(step int64, floor int64) Option {\n\treturn internal.WithStep(step, floor)\n}\n\n// WithObfuscation enables number obfuscation.\nfunc WithObfuscation(seed int) Option {\n\treturn internal.WithObfuscation(seed)\n}\n"
  },
  {
    "path": "mongo/wuid/wuid_test.go",
    "content": "package wuid\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/edwingeng/slog\"\n\t\"github.com/edwingeng/wuid/internal\"\n\t\"go.mongodb.org/mongo-driver/mongo\"\n\t\"go.mongodb.org/mongo-driver/mongo/options\"\n\t\"math/rand\"\n\t\"strings\"\n\t\"sync/atomic\"\n\t\"testing\"\n\t\"time\"\n)\n\nvar (\n\tdumb = slog.NewDumbLogger()\n)\n\nvar (\n\tcfg struct {\n\t\taddr   string\n\t\tdbName string\n\t\tcoll   string\n\t\tdocID  string\n\t}\n)\n\nfunc init() {\n\tcfg.addr = \"127.0.0.1:27017\"\n\tcfg.dbName = \"test\"\n\tcfg.coll = \"wuid\"\n\tcfg.docID = \"default\"\n}\n\nfunc connectMongodb() (*mongo.Client, error) {\n\tctx1, cancel1 := context.WithTimeout(context.Background(), time.Second*3)\n\tdefer cancel1()\n\turi := fmt.Sprintf(\"mongodb://%s\", cfg.addr)\n\treturn mongo.Connect(ctx1, options.Client().ApplyURI(uri))\n}\n\nfunc TestWUID_LoadH28FromMongo(t *testing.T) {\n\tnewClient := func() (*mongo.Client, bool, error) {\n\t\tclient, err := connectMongodb()\n\t\treturn client, true, err\n\t}\n\n\tw := NewWUID(cfg.docID, dumb)\n\terr := w.LoadH28FromMongo(newClient, cfg.dbName, cfg.coll, cfg.docID)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tinitial := atomic.LoadInt64(&w.w.N)\n\tfor i := 1; i < 100; i++ {\n\t\tif err := w.RenewNow(); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\texpected := ((initial >> 36) + int64(i)) << 36\n\t\tif atomic.LoadInt64(&w.w.N) != expected {\n\t\t\tt.Fatalf(\"w.w.N is %d, while it should be %d. i: %d\", atomic.LoadInt64(&w.w.N), expected, i)\n\t\t}\n\t\tn := rand.Intn(10)\n\t\tfor j := 0; j < n; j++ {\n\t\t\tw.Next()\n\t\t}\n\t}\n}\n\nfunc TestWUID_LoadH28FromMongo_Error(t *testing.T) {\n\tw := NewWUID(cfg.docID, dumb)\n\tif w.LoadH28FromMongo(nil, \"\", cfg.coll, cfg.docID) == nil {\n\t\tt.Fatal(\"dbName is not properly checked\")\n\t}\n\tif w.LoadH28FromMongo(nil, cfg.dbName, \"\", cfg.docID) == nil {\n\t\tt.Fatal(\"coll is not properly checked\")\n\t}\n\tif w.LoadH28FromMongo(nil, cfg.dbName, cfg.coll, \"\") == nil {\n\t\tt.Fatal(\"docID is not properly checked\")\n\t}\n\n\tnewErrorClient := func() (*mongo.Client, bool, error) {\n\t\treturn nil, true, errors.New(\"beta\")\n\t}\n\tif w.LoadH28FromMongo(newErrorClient, cfg.dbName, cfg.coll, cfg.docID) == nil {\n\t\tt.Fatal(`w.LoadH28FromMongo(newErrorClient, cfg.dbName, cfg.coll, cfg.docID) == nil`)\n\t}\n}\n\nfunc waitUntilNumRenewedReaches(t *testing.T, w *WUID, expected int64) {\n\tt.Helper()\n\tstartTime := time.Now()\n\tfor time.Since(startTime) < time.Second*3 {\n\t\tif atomic.LoadInt64(&w.w.Stats.NumRenewed) == expected {\n\t\t\treturn\n\t\t}\n\t\ttime.Sleep(time.Millisecond * 10)\n\t}\n\tt.Fatal(\"timeout\")\n}\n\nfunc TestWUID_Renew(t *testing.T) {\n\tclient, err := connectMongodb()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tnewClient := func() (*mongo.Client, bool, error) {\n\t\treturn client, false, err\n\t}\n\n\tw := NewWUID(cfg.docID, slog.NewScavenger())\n\terr = w.LoadH28FromMongo(newClient, cfg.dbName, cfg.coll, cfg.docID)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\th28 := atomic.LoadInt64(&w.w.N) >> 36\n\tatomic.StoreInt64(&w.w.N, (h28<<36)|internal.Bye)\n\tn1a := w.Next()\n\tif n1a>>36 != h28 {\n\t\tt.Fatal(`n1a>>36 != h28`)\n\t}\n\n\twaitUntilNumRenewedReaches(t, w, 1)\n\tn1b := w.Next()\n\tif n1b != (h28+1)<<36+1 {\n\t\tt.Fatal(`n1b != (h28+1)<<36+1`)\n\t}\n\n\tatomic.StoreInt64(&w.w.N, ((h28+1)<<36)|internal.Bye)\n\tn2a := w.Next()\n\tif n2a>>36 != h28+1 {\n\t\tt.Fatal(`n2a>>36 != h28+1`)\n\t}\n\n\twaitUntilNumRenewedReaches(t, w, 2)\n\tn2b := w.Next()\n\tif n2b != (h28+2)<<36+1 {\n\t\tt.Fatal(`n2b != (h28+2)<<36+1`)\n\t}\n\n\tatomic.StoreInt64(&w.w.N, ((h28+2)<<36)|internal.Bye)\n\tn3a := w.Next()\n\tif n3a>>36 != h28+2 {\n\t\tt.Fatal(`n3a>>36 != h28+2`)\n\t}\n\n\twaitUntilNumRenewedReaches(t, w, 3)\n\tn3b := w.Next()\n\tif n3b != (h28+3)<<36+1 {\n\t\tt.Fatal(`n3b != (h28+3)<<36+1`)\n\t}\n\n\tatomic.StoreInt64(&w.w.N, ((h28+2)<<36)+internal.Bye+1)\n\tfor i := 0; i < 100; i++ {\n\t\tw.Next()\n\t}\n\tif atomic.LoadInt64(&w.w.Stats.NumRenewAttempts) != 3 {\n\t\tt.Fatal(`atomic.LoadInt64(&w.w.Stats.NumRenewAttempts) != 3`)\n\t}\n\n\tvar num int\n\tsc := w.w.Logger.(*slog.Scavenger)\n\tsc.Filter(func(level, msg string) bool {\n\t\tif level == slog.LevelInfo && strings.Contains(msg, \"renew succeeded\") {\n\t\t\tnum++\n\t\t}\n\t\treturn true\n\t})\n\tif num != 3 {\n\t\tt.Fatal(`num != 3`)\n\t}\n}\n\nfunc Example() {\n\tnewClient := func() (*mongo.Client, bool, error) {\n\t\tvar client *mongo.Client\n\t\t// ...\n\t\treturn client, true, nil\n\t}\n\n\t// Setup\n\tw := NewWUID(\"alpha\", nil)\n\terr := w.LoadH28FromMongo(newClient, \"test\", \"wuid\", \"default\")\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Generate\n\tfor i := 0; i < 10; i++ {\n\t\tfmt.Printf(\"%#016x\\n\", w.Next())\n\t}\n}\n"
  },
  {
    "path": "mysql/db.sql",
    "content": "CREATE DATABASE IF NOT EXISTS test;\n\nuse test;\n\nCREATE TABLE IF NOT EXISTS `wuid` (\n    `h` int(10) NOT NULL AUTO_INCREMENT,\n    `x` tinyint(4) NOT NULL DEFAULT '0',\n    PRIMARY KEY (`x`),\n    UNIQUE KEY `h` (`h`)\n) ENGINE=InnoDB DEFAULT CHARSET=latin1;\n"
  },
  {
    "path": "mysql/docker-mysql-client.sh",
    "content": "#!/usr/bin/env bash\n\n[[ \"$TRACE\" ]] && set -x\npushd `dirname \"$0\"` > /dev/null\ntrap __EXIT EXIT\n\ncolorful=false\ntput setaf 7 > /dev/null 2>&1\nif [[ $? -eq 0 ]]; then\n    colorful=true\nfi\n\nfunction __EXIT() {\n    popd > /dev/null\n}\n\nfunction printError() {\n    $colorful && tput setaf 1\n    >&2 echo \"Error: $@\"\n    $colorful && tput setaf 7\n}\n\nfunction printImportantMessage() {\n    $colorful && tput setaf 3\n    >&2 echo \"$@\"\n    $colorful && tput setaf 7\n}\n\nfunction printUsage() {\n    $colorful && tput setaf 3\n    >&2 echo \"$@\"\n    $colorful && tput setaf 7\n}\n\ndocker run -it --rm mysql mysql -h host.docker.internal -u root -phello test\n"
  },
  {
    "path": "mysql/docker-mysql-server.sh",
    "content": "#!/usr/bin/env bash\n\n[[ \"$TRACE\" ]] && set -x\npushd `dirname \"$0\"` > /dev/null\ntrap __EXIT EXIT\n\ncolorful=false\ntput setaf 7 > /dev/null 2>&1\nif [[ $? -eq 0 ]]; then\n    colorful=true\nfi\n\nfunction __EXIT() {\n    popd > /dev/null\n}\n\nfunction printError() {\n    $colorful && tput setaf 1\n    >&2 echo \"Error: $@\"\n    $colorful && tput setaf 7\n}\n\nfunction printImportantMessage() {\n    $colorful && tput setaf 3\n    >&2 echo \"$@\"\n    $colorful && tput setaf 7\n}\n\nfunction printUsage() {\n    $colorful && tput setaf 3\n    >&2 echo \"$@\"\n    $colorful && tput setaf 7\n}\n\ndocker run --name mysql-server -p 3306:3306 -e MYSQL_ROOT_PASSWORD=hello -d mysql\n[[ $? -ne 0 ]] && exit 1\n\nprintImportantMessage \"It may take quite a few seconds to get ready.\"\nfor ((i=0;i<1000;i++)); do\n    docker run -it --rm mysql mysqladmin ping -h host.docker.internal --silent\n    [[ $? -eq 0 ]] && echo \"Ready.\" && break\n    echo \"Waiting $((i+1))...\"\n    sleep 1\ndone\n\nsleep 1\ndocker run -v `pwd`/db.sql:/tmp/db.sql -it --rm mysql /bin/bash -c 'cat /tmp/db.sql | mysql -h host.docker.internal -u root -phello'\n[[ $? -ne 0 ]] && exit 1\n\necho \"Job done.\"\n"
  },
  {
    "path": "mysql/wuid/coverage.sh",
    "content": "#!/usr/bin/env bash\n\n[[ \"$TRACE\" ]] && set -x\npushd `dirname \"$0\"` > /dev/null\ntrap __EXIT EXIT\n\ncolorful=false\ntput setaf 7 > /dev/null 2>&1\nif [[ $? -eq 0 ]]; then\n    colorful=true\nfi\n\nfunction __EXIT() {\n    popd > /dev/null\n}\n\nfunction printError() {\n    $colorful && tput setaf 1\n    >&2 echo \"Error: $@\"\n    $colorful && tput setaf 7\n}\n\nfunction printImportantMessage() {\n    $colorful && tput setaf 3\n    >&2 echo \"$@\"\n    $colorful && tput setaf 7\n}\n\nfunction printUsage() {\n    $colorful && tput setaf 3\n    >&2 echo \"$@\"\n    $colorful && tput setaf 7\n}\n\ngo test -cover -coverprofile=c.out -v \"$@\" && go tool cover -html=c.out\n"
  },
  {
    "path": "mysql/wuid/vet.sh",
    "content": "#!/usr/bin/env bash\n\n[[ \"$TRACE\" ]] && set -x\npushd `dirname \"$0\"` > /dev/null\ntrap __EXIT EXIT\n\ncolorful=false\ntput setaf 7 > /dev/null 2>&1\nif [[ $? -eq 0 ]]; then\n    colorful=true\nfi\n\nfunction __EXIT() {\n    popd > /dev/null\n}\n\nfunction printError() {\n    $colorful && tput setaf 1\n    >&2 echo \"Error: $@\"\n    $colorful && tput setaf 7\n}\n\nfunction printImportantMessage() {\n    $colorful && tput setaf 3\n    >&2 echo \"$@\"\n    $colorful && tput setaf 7\n}\n\nfunction printUsage() {\n    $colorful && tput setaf 3\n    >&2 echo \"$@\"\n    $colorful && tput setaf 7\n}\n\nprintImportantMessage \"====== gofmt\"\ngofmt -w .\n\nprintImportantMessage \"====== go vet\"\ngo vet ./...\n\nprintImportantMessage \"====== gocyclo\"\ngocyclo -over 15 .\n\nprintImportantMessage \"====== ineffassign\"\nineffassign ./...\n\nprintImportantMessage \"====== misspell\"\nmisspell *\n"
  },
  {
    "path": "mysql/wuid/wuid.go",
    "content": "package wuid\n\nimport (\n\t\"database/sql\"\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/edwingeng/slog\"\n\t\"github.com/edwingeng/wuid/internal\"\n\t_ \"github.com/go-sql-driver/mysql\"\n)\n\n// WUID is an extremely fast universal unique identifier generator.\ntype WUID struct {\n\tw *internal.WUID\n}\n\n// NewWUID creates a new WUID instance.\nfunc NewWUID(name string, logger slog.Logger, opts ...Option) *WUID {\n\treturn &WUID{w: internal.NewWUID(name, logger, opts...)}\n}\n\n// Next returns a unique identifier.\nfunc (w *WUID) Next() int64 {\n\treturn w.w.Next()\n}\n\ntype OpenDB func() (client *sql.DB, autoClose bool, err error)\n\n// LoadH28FromMysql adds 1 to a specific number in MySQL and fetches its new value.\n// The new value is used as the high 28 bits of all generated numbers. In addition, all the\n// arguments passed in are saved for future renewal.\nfunc (w *WUID) LoadH28FromMysql(openDB OpenDB, table string) error {\n\tif len(table) == 0 {\n\t\treturn errors.New(\"table cannot be empty\")\n\t}\n\n\tdb, autoClose, err := openDB()\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer func() {\n\t\tif autoClose {\n\t\t\t_ = db.Close()\n\t\t}\n\t}()\n\n\tresult, err := db.Exec(fmt.Sprintf(\"REPLACE INTO %s (x) VALUES (0)\", table))\n\tif err != nil {\n\t\treturn err\n\t}\n\th28, err := result.LastInsertId()\n\tif err != nil {\n\t\treturn err\n\t}\n\tif err = w.w.VerifyH28(h28); err != nil {\n\t\treturn err\n\t}\n\n\tw.w.Reset(h28 << 36)\n\tw.w.Logger.Infof(\"<wuid> new h28: %d. name: %s\", h28, w.w.Name)\n\n\tw.w.Lock()\n\tdefer w.w.Unlock()\n\n\tif w.w.Renew != nil {\n\t\treturn nil\n\t}\n\tw.w.Renew = func() error {\n\t\treturn w.LoadH28FromMysql(openDB, table)\n\t}\n\n\treturn nil\n}\n\n// RenewNow reacquires the high 28 bits immediately.\nfunc (w *WUID) RenewNow() error {\n\treturn w.w.RenewNow()\n}\n\ntype Option = internal.Option\n\n// WithH28Verifier adds an extra verifier for the high 28 bits.\nfunc WithH28Verifier(cb func(h28 int64) error) Option {\n\treturn internal.WithH28Verifier(cb)\n}\n\n// WithSection brands a section ID on each generated number. A section ID must be in between [0, 7].\nfunc WithSection(section int8) Option {\n\treturn internal.WithSection(section)\n}\n\n// WithStep sets the step and the floor for each generated number.\nfunc WithStep(step int64, floor int64) Option {\n\treturn internal.WithStep(step, floor)\n}\n\n// WithObfuscation enables number obfuscation.\nfunc WithObfuscation(seed int) Option {\n\treturn internal.WithObfuscation(seed)\n}\n"
  },
  {
    "path": "mysql/wuid/wuid_test.go",
    "content": "package wuid\n\nimport (\n\t\"database/sql\"\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/edwingeng/slog\"\n\t\"github.com/edwingeng/wuid/internal\"\n\t_ \"github.com/go-sql-driver/mysql\"\n\t\"math/rand\"\n\t\"strings\"\n\t\"sync/atomic\"\n\t\"testing\"\n\t\"time\"\n)\n\nvar (\n\tdumb = slog.NewDumbLogger()\n)\n\nvar (\n\tcfg struct {\n\t\taddr   string\n\t\tuser   string\n\t\tpass   string\n\t\tdbName string\n\t\ttable  string\n\t}\n)\n\nfunc init() {\n\tcfg.addr = \"127.0.0.1:3306\"\n\tcfg.user = \"root\"\n\tcfg.pass = \"hello\"\n\tcfg.dbName = \"test\"\n\tcfg.table = \"wuid\"\n}\n\nfunc connect() (*sql.DB, error) {\n\tdsn := cfg.user\n\tif len(cfg.pass) > 0 {\n\t\tdsn += \":\" + cfg.pass\n\t}\n\tdsn += \"@tcp(\" + cfg.addr + \")/\" + cfg.dbName\n\treturn sql.Open(\"mysql\", dsn)\n}\n\nfunc TestWUID_LoadH28FromMysql(t *testing.T) {\n\topenDB := func() (*sql.DB, bool, error) {\n\t\tdb, err := connect()\n\t\treturn db, true, err\n\t}\n\tw := NewWUID(\"alpha\", dumb)\n\terr := w.LoadH28FromMysql(openDB, cfg.table)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tinitial := atomic.LoadInt64(&w.w.N)\n\tfor i := 1; i < 100; i++ {\n\t\tif err := w.RenewNow(); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\texpected := ((initial >> 36) + int64(i)) << 36\n\t\tif atomic.LoadInt64(&w.w.N) != expected {\n\t\t\tt.Fatalf(\"w.w.N is %d, while it should be %d. i: %d\", atomic.LoadInt64(&w.w.N), expected, i)\n\t\t}\n\t\tn := rand.Intn(10)\n\t\tfor j := 0; j < n; j++ {\n\t\t\tw.Next()\n\t\t}\n\t}\n}\n\nfunc TestWUID_LoadH28FromMysql_Error(t *testing.T) {\n\tw := NewWUID(\"alpha\", dumb)\n\tif w.LoadH28FromMysql(nil, \"\") == nil {\n\t\tt.Fatal(\"table is not properly checked\")\n\t}\n\n\tnewErrorDB := func() (client *sql.DB, autoClose bool, err error) {\n\t\treturn nil, true, errors.New(\"beta\")\n\t}\n\tif w.LoadH28FromMysql(newErrorDB, \"beta\") == nil {\n\t\tt.Fatal(`w.LoadH28FromMysql(newErrorDB, \"beta\") == nil`)\n\t}\n}\n\nfunc waitUntilNumRenewedReaches(t *testing.T, w *WUID, expected int64) {\n\tt.Helper()\n\tstartTime := time.Now()\n\tfor time.Since(startTime) < time.Second*3 {\n\t\tif atomic.LoadInt64(&w.w.Stats.NumRenewed) == expected {\n\t\t\treturn\n\t\t}\n\t\ttime.Sleep(time.Millisecond * 10)\n\t}\n\tt.Fatal(\"timeout\")\n}\n\nfunc TestWUID_Next_Renew(t *testing.T) {\n\tdb, err := connect()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\topenDB := func() (*sql.DB, bool, error) {\n\t\treturn db, false, err\n\t}\n\n\tw := NewWUID(\"alpha\", slog.NewScavenger())\n\terr = w.LoadH28FromMysql(openDB, cfg.table)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\th28 := atomic.LoadInt64(&w.w.N) >> 36\n\tatomic.StoreInt64(&w.w.N, (h28<<36)|internal.Bye)\n\tn1a := w.Next()\n\tif n1a>>36 != h28 {\n\t\tt.Fatal(`n1a>>36 != h28`)\n\t}\n\n\twaitUntilNumRenewedReaches(t, w, 1)\n\tn1b := w.Next()\n\tif n1b != (h28+1)<<36+1 {\n\t\tt.Fatal(`n1b != (h28+1)<<36+1`)\n\t}\n\n\tatomic.StoreInt64(&w.w.N, ((h28+1)<<36)|internal.Bye)\n\tn2a := w.Next()\n\tif n2a>>36 != h28+1 {\n\t\tt.Fatal(`n2a>>36 != h28+1`)\n\t}\n\n\twaitUntilNumRenewedReaches(t, w, 2)\n\tn2b := w.Next()\n\tif n2b != (h28+2)<<36+1 {\n\t\tt.Fatal(`n2b != (h28+2)<<36+1`)\n\t}\n\n\tatomic.StoreInt64(&w.w.N, ((h28+2)<<36)|internal.Bye)\n\tn3a := w.Next()\n\tif n3a>>36 != h28+2 {\n\t\tt.Fatal(`n3a>>36 != h28+2`)\n\t}\n\n\twaitUntilNumRenewedReaches(t, w, 3)\n\tn3b := w.Next()\n\tif n3b != (h28+3)<<36+1 {\n\t\tt.Fatal(`n3b != (h28+3)<<36+1`)\n\t}\n\n\tatomic.StoreInt64(&w.w.N, ((h28+2)<<36)+internal.Bye+1)\n\tfor i := 0; i < 100; i++ {\n\t\tw.Next()\n\t}\n\tif atomic.LoadInt64(&w.w.Stats.NumRenewAttempts) != 3 {\n\t\tt.Fatal(`atomic.LoadInt64(&w.w.Stats.NumRenewAttempts) != 3`)\n\t}\n\n\tvar num int\n\tsc := w.w.Logger.(*slog.Scavenger)\n\tsc.Filter(func(level, msg string) bool {\n\t\tif level == slog.LevelInfo && strings.Contains(msg, \"renew succeeded\") {\n\t\t\tnum++\n\t\t}\n\t\treturn true\n\t})\n\tif num != 3 {\n\t\tt.Fatal(`num != 3`)\n\t}\n}\n\nfunc Example() {\n\topenDB := func() (*sql.DB, bool, error) {\n\t\tvar db *sql.DB\n\t\t// ...\n\t\treturn db, true, nil\n\t}\n\n\t// Setup\n\tw := NewWUID(\"alpha\", nil)\n\terr := w.LoadH28FromMysql(openDB, \"wuid\")\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Generate\n\tfor i := 0; i < 10; i++ {\n\t\tfmt.Printf(\"%#016x\\n\", w.Next())\n\t}\n}\n"
  },
  {
    "path": "redis/docker-redis-client.sh",
    "content": "#!/usr/bin/env bash\n\n[[ \"$TRACE\" ]] && set -x\npushd `dirname \"$0\"` > /dev/null\ntrap __EXIT EXIT\n\ncolorful=false\ntput setaf 7 > /dev/null 2>&1\nif [[ $? -eq 0 ]]; then\n    colorful=true\nfi\n\nfunction __EXIT() {\n    popd > /dev/null\n}\n\nfunction printError() {\n    $colorful && tput setaf 1\n    >&2 echo \"Error: $@\"\n    $colorful && tput setaf 7\n}\n\nfunction printImportantMessage() {\n    $colorful && tput setaf 3\n    >&2 echo \"$@\"\n    $colorful && tput setaf 7\n}\n\nfunction printUsage() {\n    $colorful && tput setaf 3\n    >&2 echo \"$@\"\n    $colorful && tput setaf 7\n}\n\ndocker run -it --rm redis redis-cli -h host.docker.internal\n"
  },
  {
    "path": "redis/docker-redis-server.sh",
    "content": "#!/usr/bin/env bash\n\n[[ \"$TRACE\" ]] && set -x\npushd `dirname \"$0\"` > /dev/null\ntrap __EXIT EXIT\n\ncolorful=false\ntput setaf 7 > /dev/null 2>&1\nif [[ $? -eq 0 ]]; then\n    colorful=true\nfi\n\nfunction __EXIT() {\n    popd > /dev/null\n}\n\nfunction printError() {\n    $colorful && tput setaf 1\n    >&2 echo \"Error: $@\"\n    $colorful && tput setaf 7\n}\n\nfunction printImportantMessage() {\n    $colorful && tput setaf 3\n    >&2 echo \"$@\"\n    $colorful && tput setaf 7\n}\n\nfunction printUsage() {\n    $colorful && tput setaf 3\n    >&2 echo \"$@\"\n    $colorful && tput setaf 7\n}\n\ndocker run --name redis-server -d -p 6379-6383:6379 redis\n"
  },
  {
    "path": "redis/v8/wuid/coverage.sh",
    "content": "#!/usr/bin/env bash\n\n[[ \"$TRACE\" ]] && set -x\npushd `dirname \"$0\"` > /dev/null\ntrap __EXIT EXIT\n\ncolorful=false\ntput setaf 7 > /dev/null 2>&1\nif [[ $? -eq 0 ]]; then\n    colorful=true\nfi\n\nfunction __EXIT() {\n    popd > /dev/null\n}\n\nfunction printError() {\n    $colorful && tput setaf 1\n    >&2 echo \"Error: $@\"\n    $colorful && tput setaf 7\n}\n\nfunction printImportantMessage() {\n    $colorful && tput setaf 3\n    >&2 echo \"$@\"\n    $colorful && tput setaf 7\n}\n\nfunction printUsage() {\n    $colorful && tput setaf 3\n    >&2 echo \"$@\"\n    $colorful && tput setaf 7\n}\n\ngo test -cover -coverprofile=c.out -v \"$@\" && go tool cover -html=c.out\n"
  },
  {
    "path": "redis/v8/wuid/vet.sh",
    "content": "#!/usr/bin/env bash\n\n[[ \"$TRACE\" ]] && set -x\npushd `dirname \"$0\"` > /dev/null\ntrap __EXIT EXIT\n\ncolorful=false\ntput setaf 7 > /dev/null 2>&1\nif [[ $? -eq 0 ]]; then\n    colorful=true\nfi\n\nfunction __EXIT() {\n    popd > /dev/null\n}\n\nfunction printError() {\n    $colorful && tput setaf 1\n    >&2 echo \"Error: $@\"\n    $colorful && tput setaf 7\n}\n\nfunction printImportantMessage() {\n    $colorful && tput setaf 3\n    >&2 echo \"$@\"\n    $colorful && tput setaf 7\n}\n\nfunction printUsage() {\n    $colorful && tput setaf 3\n    >&2 echo \"$@\"\n    $colorful && tput setaf 7\n}\n\nprintImportantMessage \"====== gofmt\"\ngofmt -w .\n\nprintImportantMessage \"====== go vet\"\ngo vet ./...\n\nprintImportantMessage \"====== gocyclo\"\ngocyclo -over 15 .\n\nprintImportantMessage \"====== ineffassign\"\nineffassign ./...\n\nprintImportantMessage \"====== misspell\"\nmisspell *\n"
  },
  {
    "path": "redis/v8/wuid/wuid.go",
    "content": "package wuid\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"github.com/edwingeng/slog\"\n\t\"github.com/edwingeng/wuid/internal\"\n\t\"github.com/go-redis/redis/v8\"\n\t\"time\"\n)\n\n// WUID is an extremely fast universal unique identifier generator.\ntype WUID struct {\n\tw *internal.WUID\n}\n\n// NewWUID creates a new WUID instance.\nfunc NewWUID(name string, logger slog.Logger, opts ...Option) *WUID {\n\treturn &WUID{w: internal.NewWUID(name, logger, opts...)}\n}\n\n// Next returns a unique identifier.\nfunc (w *WUID) Next() int64 {\n\treturn w.w.Next()\n}\n\ntype NewClient func() (client redis.UniversalClient, autoClose bool, err error)\n\n// LoadH28FromRedis adds 1 to a specific number in Redis and fetches its new value.\n// The new value is used as the high 28 bits of all generated numbers. In addition, all the\n// arguments passed in are saved for future renewal.\nfunc (w *WUID) LoadH28FromRedis(newClient NewClient, key string) error {\n\tif len(key) == 0 {\n\t\treturn errors.New(\"key cannot be empty\")\n\t}\n\n\tclient, autoClose, err := newClient()\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer func() {\n\t\tif autoClose {\n\t\t\t_ = client.Close()\n\t\t}\n\t}()\n\n\tctx1, cancel1 := context.WithTimeout(context.Background(), time.Second*5)\n\tdefer cancel1()\n\th28, err := client.Incr(ctx1, key).Result()\n\tif err != nil {\n\t\treturn err\n\t}\n\tif err = w.w.VerifyH28(h28); err != nil {\n\t\treturn err\n\t}\n\n\tw.w.Reset(h28 << 36)\n\tw.w.Logger.Infof(\"<wuid> new h28: %d. name: %s\", h28, w.w.Name)\n\n\tw.w.Lock()\n\tdefer w.w.Unlock()\n\n\tif w.w.Renew != nil {\n\t\treturn nil\n\t}\n\tw.w.Renew = func() error {\n\t\treturn w.LoadH28FromRedis(newClient, key)\n\t}\n\n\treturn nil\n}\n\n// RenewNow reacquires the high 28 bits immediately.\nfunc (w *WUID) RenewNow() error {\n\treturn w.w.RenewNow()\n}\n\ntype Option = internal.Option\n\n// WithH28Verifier adds an extra verifier for the high 28 bits.\nfunc WithH28Verifier(cb func(h28 int64) error) Option {\n\treturn internal.WithH28Verifier(cb)\n}\n\n// WithSection brands a section ID on each generated number. A section ID must be in between [0, 7].\nfunc WithSection(section int8) Option {\n\treturn internal.WithSection(section)\n}\n\n// WithStep sets the step and the floor for each generated number.\nfunc WithStep(step int64, floor int64) Option {\n\treturn internal.WithStep(step, floor)\n}\n\n// WithObfuscation enables number obfuscation.\nfunc WithObfuscation(seed int) Option {\n\treturn internal.WithObfuscation(seed)\n}\n"
  },
  {
    "path": "redis/v8/wuid/wuid_test.go",
    "content": "package wuid\n\nimport (\n\t\"errors\"\n\t\"flag\"\n\t\"fmt\"\n\t\"github.com/edwingeng/slog\"\n\t\"github.com/edwingeng/wuid/internal\"\n\t\"github.com/go-redis/redis/v8\"\n\t\"math/rand\"\n\t\"strings\"\n\t\"sync/atomic\"\n\t\"testing\"\n\t\"time\"\n)\n\nvar redisCluster = flag.Bool(\"cluster\", false, \"\")\n\nvar (\n\tdumb = slog.NewDumbLogger()\n)\n\nvar (\n\tcfg struct {\n\t\taddrs    []string\n\t\tpassword string\n\t\tkey      string\n\t}\n)\n\nfunc init() {\n\tcfg.addrs = []string{\"127.0.0.1:6379\", \"127.0.0.1:6380\", \"127.0.0.1:6381\"}\n\tcfg.key = \"v8:wuid\"\n}\n\nfunc connect() redis.UniversalClient {\n\tif *redisCluster {\n\t\treturn redis.NewClusterClient(&redis.ClusterOptions{\n\t\t\tAddrs:    cfg.addrs,\n\t\t\tPassword: cfg.password,\n\t\t})\n\t} else {\n\t\treturn redis.NewClient(&redis.Options{\n\t\t\tAddr:     cfg.addrs[0],\n\t\t\tPassword: cfg.password,\n\t\t})\n\t}\n}\n\nfunc TestWUID_LoadH28FromRedis(t *testing.T) {\n\tnewClient := func() (redis.UniversalClient, bool, error) {\n\t\treturn connect(), true, nil\n\t}\n\tw := NewWUID(\"alpha\", dumb)\n\terr := w.LoadH28FromRedis(newClient, cfg.key)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tinitial := atomic.LoadInt64(&w.w.N)\n\tfor i := 1; i < 100; i++ {\n\t\tif err := w.RenewNow(); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\texpected := ((initial >> 36) + int64(i)) << 36\n\t\tif atomic.LoadInt64(&w.w.N) != expected {\n\t\t\tt.Fatalf(\"w.w.N is %d, while it should be %d. i: %d\", atomic.LoadInt64(&w.w.N), expected, i)\n\t\t}\n\t\tn := rand.Intn(10)\n\t\tfor j := 0; j < n; j++ {\n\t\t\tw.Next()\n\t\t}\n\t}\n}\n\nfunc TestWUID_LoadH28FromRedis_Error(t *testing.T) {\n\tw := NewWUID(\"alpha\", dumb)\n\tif w.LoadH28FromRedis(nil, \"\") == nil {\n\t\tt.Fatal(\"key is not properly checked\")\n\t}\n\n\tnewErrorClient := func() (redis.UniversalClient, bool, error) {\n\t\treturn nil, true, errors.New(\"beta\")\n\t}\n\tif w.LoadH28FromRedis(newErrorClient, \"beta\") == nil {\n\t\tt.Fatal(`w.LoadH28FromRedis(newErrorClient, \"beta\") == nil`)\n\t}\n}\n\nfunc waitUntilNumRenewedReaches(t *testing.T, w *WUID, expected int64) {\n\tt.Helper()\n\tstartTime := time.Now()\n\tfor time.Since(startTime) < time.Second*3 {\n\t\tif atomic.LoadInt64(&w.w.Stats.NumRenewed) == expected {\n\t\t\treturn\n\t\t}\n\t\ttime.Sleep(time.Millisecond * 10)\n\t}\n\tt.Fatal(\"timeout\")\n}\n\nfunc TestWUID_Next_Renew(t *testing.T) {\n\tclient := connect()\n\tnewClient := func() (redis.UniversalClient, bool, error) {\n\t\treturn client, false, nil\n\t}\n\n\tw := NewWUID(\"alpha\", slog.NewScavenger())\n\terr := w.LoadH28FromRedis(newClient, cfg.key)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\th28 := atomic.LoadInt64(&w.w.N) >> 36\n\tatomic.StoreInt64(&w.w.N, (h28<<36)|internal.Bye)\n\tn1a := w.Next()\n\tif n1a>>36 != h28 {\n\t\tt.Fatal(`n1a>>36 != h28`)\n\t}\n\n\twaitUntilNumRenewedReaches(t, w, 1)\n\tn1b := w.Next()\n\tif n1b != (h28+1)<<36+1 {\n\t\tt.Fatal(`n1b != (h28+1)<<36+1`)\n\t}\n\n\tatomic.StoreInt64(&w.w.N, ((h28+1)<<36)|internal.Bye)\n\tn2a := w.Next()\n\tif n2a>>36 != h28+1 {\n\t\tt.Fatal(`n2a>>36 != h28+1`)\n\t}\n\n\twaitUntilNumRenewedReaches(t, w, 2)\n\tn2b := w.Next()\n\tif n2b != (h28+2)<<36+1 {\n\t\tt.Fatal(`n2b != (h28+2)<<36+1`)\n\t}\n\n\tatomic.StoreInt64(&w.w.N, ((h28+2)<<36)|internal.Bye)\n\tn3a := w.Next()\n\tif n3a>>36 != h28+2 {\n\t\tt.Fatal(`n3a>>36 != h28+2`)\n\t}\n\n\twaitUntilNumRenewedReaches(t, w, 3)\n\tn3b := w.Next()\n\tif n3b != (h28+3)<<36+1 {\n\t\tt.Fatal(`n3b != (h28+3)<<36+1`)\n\t}\n\n\tatomic.StoreInt64(&w.w.N, ((h28+2)<<36)+internal.Bye+1)\n\tfor i := 0; i < 100; i++ {\n\t\tw.Next()\n\t}\n\tif atomic.LoadInt64(&w.w.Stats.NumRenewAttempts) != 3 {\n\t\tt.Fatal(`atomic.LoadInt64(&w.w.Stats.NumRenewAttempts) != 3`)\n\t}\n\n\tvar num int\n\tsc := w.w.Logger.(*slog.Scavenger)\n\tsc.Filter(func(level, msg string) bool {\n\t\tif level == slog.LevelInfo && strings.Contains(msg, \"renew succeeded\") {\n\t\t\tnum++\n\t\t}\n\t\treturn true\n\t})\n\tif num != 3 {\n\t\tt.Fatal(`num != 3`)\n\t}\n}\n\nfunc Example() {\n\tnewClient := func() (redis.UniversalClient, bool, error) {\n\t\tvar client redis.UniversalClient\n\t\t// ...\n\t\treturn client, true, nil\n\t}\n\n\t// Setup\n\tw := NewWUID(\"alpha\", nil)\n\terr := w.LoadH28FromRedis(newClient, \"wuid\")\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Generate\n\tfor i := 0; i < 10; i++ {\n\t\tfmt.Printf(\"%#016x\\n\", w.Next())\n\t}\n}\n"
  },
  {
    "path": "redis/wuid/coverage.sh",
    "content": "#!/usr/bin/env bash\n\n[[ \"$TRACE\" ]] && set -x\npushd `dirname \"$0\"` > /dev/null\ntrap __EXIT EXIT\n\ncolorful=false\ntput setaf 7 > /dev/null 2>&1\nif [[ $? -eq 0 ]]; then\n    colorful=true\nfi\n\nfunction __EXIT() {\n    popd > /dev/null\n}\n\nfunction printError() {\n    $colorful && tput setaf 1\n    >&2 echo \"Error: $@\"\n    $colorful && tput setaf 7\n}\n\nfunction printImportantMessage() {\n    $colorful && tput setaf 3\n    >&2 echo \"$@\"\n    $colorful && tput setaf 7\n}\n\nfunction printUsage() {\n    $colorful && tput setaf 3\n    >&2 echo \"$@\"\n    $colorful && tput setaf 7\n}\n\ngo test -cover -coverprofile=c.out -v \"$@\" && go tool cover -html=c.out\n"
  },
  {
    "path": "redis/wuid/vet.sh",
    "content": "#!/usr/bin/env bash\n\n[[ \"$TRACE\" ]] && set -x\npushd `dirname \"$0\"` > /dev/null\ntrap __EXIT EXIT\n\ncolorful=false\ntput setaf 7 > /dev/null 2>&1\nif [[ $? -eq 0 ]]; then\n    colorful=true\nfi\n\nfunction __EXIT() {\n    popd > /dev/null\n}\n\nfunction printError() {\n    $colorful && tput setaf 1\n    >&2 echo \"Error: $@\"\n    $colorful && tput setaf 7\n}\n\nfunction printImportantMessage() {\n    $colorful && tput setaf 3\n    >&2 echo \"$@\"\n    $colorful && tput setaf 7\n}\n\nfunction printUsage() {\n    $colorful && tput setaf 3\n    >&2 echo \"$@\"\n    $colorful && tput setaf 7\n}\n\nprintImportantMessage \"====== gofmt\"\ngofmt -w .\n\nprintImportantMessage \"====== go vet\"\ngo vet ./...\n\nprintImportantMessage \"====== gocyclo\"\ngocyclo -over 15 .\n\nprintImportantMessage \"====== ineffassign\"\nineffassign ./...\n\nprintImportantMessage \"====== misspell\"\nmisspell *\n"
  },
  {
    "path": "redis/wuid/wuid.go",
    "content": "package wuid\n\nimport (\n\t\"errors\"\n\t\"github.com/edwingeng/slog\"\n\t\"github.com/edwingeng/wuid/internal\"\n\t\"github.com/go-redis/redis\"\n)\n\n// WUID is an extremely fast universal unique identifier generator.\ntype WUID struct {\n\tw *internal.WUID\n}\n\n// NewWUID creates a new WUID instance.\nfunc NewWUID(name string, logger slog.Logger, opts ...Option) *WUID {\n\treturn &WUID{w: internal.NewWUID(name, logger, opts...)}\n}\n\n// Next returns a unique identifier.\nfunc (w *WUID) Next() int64 {\n\treturn w.w.Next()\n}\n\ntype NewClient func() (client redis.UniversalClient, autoClose bool, err error)\n\n// LoadH28FromRedis adds 1 to a specific number in Redis and fetches its new value.\n// The new value is used as the high 28 bits of all generated numbers. In addition, all the\n// arguments passed in are saved for future renewal.\nfunc (w *WUID) LoadH28FromRedis(newClient NewClient, key string) error {\n\tif len(key) == 0 {\n\t\treturn errors.New(\"key cannot be empty\")\n\t}\n\n\tclient, autoClose, err := newClient()\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer func() {\n\t\tif autoClose {\n\t\t\t_ = client.Close()\n\t\t}\n\t}()\n\n\th28, err := client.Incr(key).Result()\n\tif err != nil {\n\t\treturn err\n\t}\n\tif err = w.w.VerifyH28(h28); err != nil {\n\t\treturn err\n\t}\n\n\tw.w.Reset(h28 << 36)\n\tw.w.Logger.Infof(\"<wuid> new h28: %d. name: %s\", h28, w.w.Name)\n\n\tw.w.Lock()\n\tdefer w.w.Unlock()\n\n\tif w.w.Renew != nil {\n\t\treturn nil\n\t}\n\tw.w.Renew = func() error {\n\t\treturn w.LoadH28FromRedis(newClient, key)\n\t}\n\n\treturn nil\n}\n\n// RenewNow reacquires the high 28 bits immediately.\nfunc (w *WUID) RenewNow() error {\n\treturn w.w.RenewNow()\n}\n\ntype Option = internal.Option\n\n// WithH28Verifier adds an extra verifier for the high 28 bits.\nfunc WithH28Verifier(cb func(h28 int64) error) Option {\n\treturn internal.WithH28Verifier(cb)\n}\n\n// WithSection brands a section ID on each generated number. A section ID must be in between [0, 7].\nfunc WithSection(section int8) Option {\n\treturn internal.WithSection(section)\n}\n\n// WithStep sets the step and the floor for each generated number.\nfunc WithStep(step int64, floor int64) Option {\n\treturn internal.WithStep(step, floor)\n}\n\n// WithObfuscation enables number obfuscation.\nfunc WithObfuscation(seed int) Option {\n\treturn internal.WithObfuscation(seed)\n}\n"
  },
  {
    "path": "redis/wuid/wuid_test.go",
    "content": "package wuid\n\nimport (\n\t\"errors\"\n\t\"flag\"\n\t\"fmt\"\n\t\"github.com/edwingeng/slog\"\n\t\"github.com/edwingeng/wuid/internal\"\n\t\"github.com/go-redis/redis\"\n\t\"math/rand\"\n\t\"strings\"\n\t\"sync/atomic\"\n\t\"testing\"\n\t\"time\"\n)\n\nvar redisCluster = flag.Bool(\"cluster\", false, \"\")\n\nvar (\n\tdumb = slog.NewDumbLogger()\n)\n\nvar (\n\tcfg struct {\n\t\taddrs    []string\n\t\tpassword string\n\t\tkey      string\n\t}\n)\n\nfunc init() {\n\tcfg.addrs = []string{\"127.0.0.1:6379\", \"127.0.0.1:6380\", \"127.0.0.1:6381\"}\n\tcfg.key = \"wuid\"\n}\n\nfunc connect() redis.UniversalClient {\n\tif *redisCluster {\n\t\treturn redis.NewClusterClient(&redis.ClusterOptions{\n\t\t\tAddrs:    cfg.addrs,\n\t\t\tPassword: cfg.password,\n\t\t})\n\t} else {\n\t\treturn redis.NewClient(&redis.Options{\n\t\t\tAddr:     cfg.addrs[0],\n\t\t\tPassword: cfg.password,\n\t\t})\n\t}\n}\n\nfunc TestWUID_LoadH28FromRedis(t *testing.T) {\n\tnewClient := func() (redis.UniversalClient, bool, error) {\n\t\treturn connect(), true, nil\n\t}\n\tw := NewWUID(\"alpha\", dumb)\n\terr := w.LoadH28FromRedis(newClient, cfg.key)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tinitial := atomic.LoadInt64(&w.w.N)\n\tfor i := 1; i < 100; i++ {\n\t\tif err := w.RenewNow(); err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\texpected := ((initial >> 36) + int64(i)) << 36\n\t\tif atomic.LoadInt64(&w.w.N) != expected {\n\t\t\tt.Fatalf(\"w.w.N is %d, while it should be %d. i: %d\", atomic.LoadInt64(&w.w.N), expected, i)\n\t\t}\n\t\tn := rand.Intn(10)\n\t\tfor j := 0; j < n; j++ {\n\t\t\tw.Next()\n\t\t}\n\t}\n}\n\nfunc TestWUID_LoadH28FromRedis_Error(t *testing.T) {\n\tw := NewWUID(\"alpha\", dumb)\n\tif w.LoadH28FromRedis(nil, \"\") == nil {\n\t\tt.Fatal(\"key is not properly checked\")\n\t}\n\n\tnewErrorClient := func() (redis.UniversalClient, bool, error) {\n\t\treturn nil, true, errors.New(\"beta\")\n\t}\n\tif w.LoadH28FromRedis(newErrorClient, \"beta\") == nil {\n\t\tt.Fatal(`w.LoadH28FromRedis(newErrorClient, \"beta\") == nil`)\n\t}\n}\n\nfunc waitUntilNumRenewedReaches(t *testing.T, w *WUID, expected int64) {\n\tt.Helper()\n\tstartTime := time.Now()\n\tfor time.Since(startTime) < time.Second*3 {\n\t\tif atomic.LoadInt64(&w.w.Stats.NumRenewed) == expected {\n\t\t\treturn\n\t\t}\n\t\ttime.Sleep(time.Millisecond * 10)\n\t}\n\tt.Fatal(\"timeout\")\n}\n\nfunc TestWUID_Next_Renew(t *testing.T) {\n\tclient := connect()\n\tnewClient := func() (redis.UniversalClient, bool, error) {\n\t\treturn client, false, nil\n\t}\n\n\tw := NewWUID(\"alpha\", slog.NewScavenger())\n\terr := w.LoadH28FromRedis(newClient, cfg.key)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\th28 := atomic.LoadInt64(&w.w.N) >> 36\n\tatomic.StoreInt64(&w.w.N, (h28<<36)|internal.Bye)\n\tn1a := w.Next()\n\tif n1a>>36 != h28 {\n\t\tt.Fatal(`n1a>>36 != h28`)\n\t}\n\n\twaitUntilNumRenewedReaches(t, w, 1)\n\tn1b := w.Next()\n\tif n1b != (h28+1)<<36+1 {\n\t\tt.Fatal(`n1b != (h28+1)<<36+1`)\n\t}\n\n\tatomic.StoreInt64(&w.w.N, ((h28+1)<<36)|internal.Bye)\n\tn2a := w.Next()\n\tif n2a>>36 != h28+1 {\n\t\tt.Fatal(`n2a>>36 != h28+1`)\n\t}\n\n\twaitUntilNumRenewedReaches(t, w, 2)\n\tn2b := w.Next()\n\tif n2b != (h28+2)<<36+1 {\n\t\tt.Fatal(`n2b != (h28+2)<<36+1`)\n\t}\n\n\tatomic.StoreInt64(&w.w.N, ((h28+2)<<36)|internal.Bye)\n\tn3a := w.Next()\n\tif n3a>>36 != h28+2 {\n\t\tt.Fatal(`n3a>>36 != h28+2`)\n\t}\n\n\twaitUntilNumRenewedReaches(t, w, 3)\n\tn3b := w.Next()\n\tif n3b != (h28+3)<<36+1 {\n\t\tt.Fatal(`n3b != (h28+3)<<36+1`)\n\t}\n\n\tatomic.StoreInt64(&w.w.N, ((h28+2)<<36)+internal.Bye+1)\n\tfor i := 0; i < 100; i++ {\n\t\tw.Next()\n\t}\n\tif atomic.LoadInt64(&w.w.Stats.NumRenewAttempts) != 3 {\n\t\tt.Fatal(`atomic.LoadInt64(&w.w.Stats.NumRenewAttempts) != 3`)\n\t}\n\n\tvar num int\n\tsc := w.w.Logger.(*slog.Scavenger)\n\tsc.Filter(func(level, msg string) bool {\n\t\tif level == slog.LevelInfo && strings.Contains(msg, \"renew succeeded\") {\n\t\t\tnum++\n\t\t}\n\t\treturn true\n\t})\n\tif num != 3 {\n\t\tt.Fatal(`num != 3`)\n\t}\n}\n\nfunc Example() {\n\tnewClient := func() (redis.UniversalClient, bool, error) {\n\t\tvar client redis.UniversalClient\n\t\t// ...\n\t\treturn client, true, nil\n\t}\n\n\t// Setup\n\tw := NewWUID(\"alpha\", nil)\n\terr := w.LoadH28FromRedis(newClient, \"wuid\")\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Generate\n\tfor i := 0; i < 10; i++ {\n\t\tfmt.Printf(\"%#016x\\n\", w.Next())\n\t}\n}\n"
  },
  {
    "path": "wuid.go",
    "content": "package wuid\n\ntype WUID interface {\n\tNext() int64\n}\n"
  }
]