Files
gofakes3/s3mem/versionid.go
Henry d6dfd4186c
Some checks failed
build / go1.20 (push) Has been cancelled
build / go1.21 (push) Has been cancelled
build / linux (push) Has been cancelled
build / linux_386 (push) Has been cancelled
build / mac_amd64 (push) Has been cancelled
build / mac_arm64 (push) Has been cancelled
build / windows (push) Has been cancelled
build / lint (push) Has been cancelled
chore: update package name
2025-06-27 16:37:33 +08:00

69 lines
2.0 KiB
Go

package s3mem
import (
"encoding/base32"
"fmt"
"math/big"
"sync"
"github.com/itsHenry35/gofakes3"
)
var add1 = new(big.Int).SetInt64(1)
type versionGenerator struct {
state uint64
size int
next *big.Int
mu sync.Mutex
}
func newVersionGenerator(seed uint64, size int) *versionGenerator {
if size <= 0 {
size = 64
}
_ = size // FIXME size is unused here - maybe it should go into versionGenerator
return &versionGenerator{next: new(big.Int), state: seed}
}
func (v *versionGenerator) Next(scratch []byte) (gofakes3.VersionID, []byte) {
v.mu.Lock()
v.next.Add(v.next, add1)
idb := []byte(fmt.Sprintf("%030d", v.next))
neat := v.size/8*8 + 8 // cheap and nasty way to ensure a multiple of 8 definitely greater than size
scratchLen := len(idb) + neat + 1
if len(scratch) < scratchLen {
scratch = make([]byte, scratchLen)
}
copy(scratch, idb)
b := scratch[len(idb)+1:]
// This is a simple inline implementation of http://xoshiro.di.unimi.it/splitmix64.c.
// It may not ultimately be the right tool for this job but with a large
// enough size the collision risk should still be minuscule.
for i := 0; i < neat; i += 8 {
v.state += 0x9E3779B97F4A7C15
z := v.state
z = (z ^ (z >> 30)) * 0xBF58476D1CE4E5B9
z = (z ^ (z >> 27)) * 0x94D049BB133111EB
b[i], b[i+1], b[i+2], b[i+3], b[i+4], b[i+5], b[i+6], b[i+7] =
byte(z), byte(z>>8), byte(z>>16), byte(z>>24), byte(z>>32), byte(z>>40), byte(z>>48), byte(z>>56)
}
v.mu.Unlock()
// The version IDs that come out of S3 appear to start with '3/' and follow
// with a base64-URL encoded blast of god knows what. There didn't appear
// to be any explanation of the format beyond that, but let's copy it anyway.
//
// Base64 is not sortable though, and we need our versions to be lexicographically
// sortable for the SkipList key, so we have to encode it as base32hex, which _is_
// sortable, and just pretend that it's "Base64". Phew!
return gofakes3.VersionID(fmt.Sprintf("3/%s", base32.HexEncoding.EncodeToString(scratch))), scratch
}