-
Notifications
You must be signed in to change notification settings - Fork 18
/
LexiBase64.go
98 lines (87 loc) · 2.06 KB
/
LexiBase64.go
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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
package S
import (
"sync/atomic"
"time"
"github.com/kokizzu/rand"
"golang.org/x/exp/constraints"
"github.com/kokizzu/gotro/L"
)
type int64orUint64 interface {
~int64 | ~uint64
}
const i2c_cb63 = `-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz`
const MaxStrLenCB63 = 11
var c2i_cb63 map[rune]int64
var ModCB63 []uint64
var atom int64
func init() {
c2i_cb63 = map[rune]int64{}
for i, ch := range i2c_cb63 {
c2i_cb63[ch] = int64(i)
}
ModCB63 = []uint64{0}
mod := uint64(1)
for i := 1; i < MaxStrLenCB63; i++ {
mod *= 64
ModCB63 = append(ModCB63, mod)
}
ModCB63 = append(ModCB63, 9223372036854775808)
atom = time.Now().UnixNano()
}
// EncodeCB63 convert integer to custom base-63 encoding that lexicographically correct, positive integer only
//
// 0 -
// 1..10 0..9
// 11..36 A..Z
// 37 _
// 38..63 a..z
// S.EncodeCB63(11,1) // `A`
// S.EncodeCB63(1,3) // `--0`
func EncodeCB63[T int64orUint64, L constraints.Integer](id T, minLen L) string {
if minLen < 1 {
minLen = 1
}
str := make([]byte, 0, 12)
for id > 0 {
mod := rune(id % 64)
str = append(str, i2c_cb63[mod])
id /= 64
}
for L(len(str)) < minLen {
str = append(str, i2c_cb63[0])
}
l := len(str)
for i, j := 0, l-1; i < l/2; i, j = i+1, j-1 {
str[i], str[j] = str[j], str[i]
}
return string(str)
}
// DecodeCB63 convert custom base-63 encoding to int64
//
// S.DecodeCB63(`--0`) // 1, true
// S.DecodeCB64(`(*&#$`) // 0, false
func DecodeCB63[T int64orUint64](str string) (T, bool) {
res := T(0)
for _, ch := range str {
res *= 64
val, ok := c2i_cb63[ch]
if L.CheckIf(!ok, `Invalid character for CB63: `+string(ch)) {
return 0, false
}
res += T(val)
}
return res, true
}
// RandomCB63 random CB63 n-times, the result is n*MaxStrLenCB63 bytes
func RandomCB63[T constraints.Integer](len T) string {
if len == 1 {
return EncodeCB63(rand.Uint64(), MaxStrLenCB63)
}
res := ``
for z := T(0); z < len-1; z++ {
res += EncodeCB63(rand.Uint64(), MaxStrLenCB63)
}
now := atomic.AddInt64(&atom, 1)
res += EncodeCB63(now, MaxStrLenCB63)
return res
}