1package s3mem 2 3import ( 4 "encoding/base32" 5 "fmt" 6 "math/big" 7 "sync" 8 9 "github.com/johannesboyne/gofakes3" 10) 11 12var add1 = new(big.Int).SetInt64(1) 13 14type versionGenerator struct { 15 state uint64 16 size int 17 next *big.Int 18 mu sync.Mutex 19} 20 21func newVersionGenerator(seed uint64, size int) *versionGenerator { 22 if size <= 0 { 23 size = 64 24 } 25 return &versionGenerator{next: new(big.Int), state: seed} 26} 27 28func (v *versionGenerator) Next(scratch []byte) (gofakes3.VersionID, []byte) { 29 v.mu.Lock() 30 31 v.next.Add(v.next, add1) 32 idb := []byte(fmt.Sprintf("%030d", v.next)) 33 34 neat := v.size/8*8 + 8 // cheap and nasty way to ensure a multiple of 8 definitely greater than size 35 36 scratchLen := len(idb) + neat + 1 37 if len(scratch) < scratchLen { 38 scratch = make([]byte, scratchLen) 39 } 40 copy(scratch, idb) 41 42 b := scratch[len(idb)+1:] 43 44 // This is a simple inline implementation of http://xoshiro.di.unimi.it/splitmix64.c. 45 // It may not ultimately be the right tool for this job but with a large 46 // enough size the collision risk should still be minuscule. 47 for i := 0; i < neat; i += 8 { 48 v.state += 0x9E3779B97F4A7C15 49 z := v.state 50 z = (z ^ (z >> 30)) * 0xBF58476D1CE4E5B9 51 z = (z ^ (z >> 27)) * 0x94D049BB133111EB 52 b[i], b[i+1], b[i+2], b[i+3], b[i+4], b[i+5], b[i+6], b[i+7] = 53 byte(z), byte(z>>8), byte(z>>16), byte(z>>24), byte(z>>32), byte(z>>40), byte(z>>48), byte(z>>56) 54 } 55 56 v.mu.Unlock() 57 58 // The version IDs that come out of S3 appear to start with '3/' and follow 59 // with a base64-URL encoded blast of god knows what. There didn't appear 60 // to be any explanation of the format beyond that, but let's copy it anyway. 61 // 62 // Base64 is not sortable though, and we need our versions to be lexicographically 63 // sortable for the SkipList key, so we have to encode it as base32hex, which _is_ 64 // sortable, and just pretend that it's "Base64". Phew! 65 66 return gofakes3.VersionID(fmt.Sprintf("3/%s", base32.HexEncoding.EncodeToString(scratch))), scratch 67} 68