-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathversion.go
More file actions
72 lines (63 loc) · 2.4 KB
/
Copy pathversion.go
File metadata and controls
72 lines (63 loc) · 2.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
package fs
import (
"crypto/rand"
"encoding/binary"
"encoding/hex"
"time"
)
// NewVersionID returns a fresh version identifier.
//
// # The format is frozen
//
// 32 lowercase hex characters: the first 16 are the bitwise complement of the
// creation time in unix nanoseconds, the last 16 are random.
//
// Complementing the timestamp is what makes plain ascending lexical order put
// the *newest* version first — which is the order every read wants, because
// resolving "the current version" is then "take the first entry", and a
// directory listing or a sorted key range already arrives that way. No index,
// no cached pointer, no separate ordering column.
//
// The random half breaks ties between versions created in the same
// nanosecond and keeps IDs unguessable, so one version ID does not let a
// caller derive its neighbors.
//
// This format cannot change once written. Every stored version is named by it
// and every listing sorts on it, so a later change means migrating live data —
// the exact trap SeaweedFS fell into by shipping IDs that did not sort and
// then having to convert them mid-flight. It is fixed here deliberately, at
// the cost of getting it right before the first release rather than after.
func NewVersionID() string {
var id [16]byte
// ^uint64: newest sorts first. Nanosecond resolution keeps the ordering
// meaningful for writes to the same key in quick succession, which is
// exactly when ordering matters.
binary.BigEndian.PutUint64(id[:8], ^uint64(time.Now().UnixNano())) //nolint:gosec // Complement of a positive time.
if _, err := rand.Read(id[8:]); err != nil {
// crypto/rand does not fail in practice; if it ever does, a
// timestamp-only ID is still unique per nanosecond and correctly
// ordered, which is what the read path depends on.
binary.BigEndian.PutUint64(id[8:], uint64(time.Now().UnixNano())) //nolint:gosec // Fallback only.
}
return hex.EncodeToString(id[:])
}
// ValidVersionID reports whether s is a version identifier this server could
// have produced, or the reserved "null".
//
// Callers send version IDs back to us, so this is the guard between a client's
// string and a filesystem path or a lookup key.
func ValidVersionID(s string) bool {
if s == NullVersionID {
return true
}
if len(s) != 32 {
return false
}
for i := range len(s) {
c := s[i]
if (c < '0' || c > '9') && (c < 'a' || c > 'f') {
return false
}
}
return true
}