-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathbase62.go
202 lines (161 loc) · 4.26 KB
/
base62.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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
// Package base62 implements base62 encoding
package base62
import (
"fmt"
"math"
"math/big"
"strconv"
"strings"
)
const base = 62
type Encoding struct {
encode string
padding int
}
// Option sets a number of optional parameters on the encoding
func (e *Encoding) Option(opts ...option) *Encoding {
for _, opt := range opts {
opt(e)
}
// Return the encoding to allow chaining
return e
}
const encodeStd = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
// NewEncoding returns a new Encoding defined by the given alphabet
func NewEncoding(encoder string) *Encoding {
return &Encoding{
encode: encoder,
}
}
// NewStdEncoding returns an Encoding preconfigured with the standard base62 alphabet
func NewStdEncoding() *Encoding {
return NewEncoding(encodeStd)
}
// StdEncoding is the standard base62 encoding
var StdEncoding = NewStdEncoding()
// Configurable options for an Encoding
type option func(*Encoding)
// Padding sets the minimum string length returned when encoding
// strings shorter than this will be left padded with zeros
func Padding(n int) option {
return func(e *Encoding) {
e.padding = n
}
}
/**
* Encoder
*/
// EncodeInt64 returns the base62 encoding of n using the StdEncoding
func EncodeInt64(n int64) string {
return StdEncoding.EncodeInt64(n)
}
// EncodeBigInt returns the base62 encoding of an arbitrary precision integer using the StdEncoding
func EncodeBigInt(n *big.Int) string {
return StdEncoding.EncodeBigInt(n)
}
// EncodeInt64 returns the base62 encoding of n
func (e *Encoding) EncodeInt64(n int64) string {
var (
b = make([]byte, 0)
rem int64
)
// Progressively divide by base, store remainder each time
// Prepend as an additional character is the higher power
for n > 0 {
rem = n % base
n = n / base
b = append([]byte{e.encode[rem]}, b...)
}
s := string(b)
if e.padding > 0 {
s = e.pad(s, e.padding)
}
return s
}
// EncodeBigInt returns the base62 encoding of an arbitrary precision integer
func (e *Encoding) EncodeBigInt(n *big.Int) string {
var (
b = make([]byte, 0)
rem = new(big.Int)
bse = new(big.Int)
zero = new(big.Int)
)
bse.SetInt64(base)
zero.SetInt64(0)
// Progressively divide by base, until we hit zero
// store remainder each time
// Prepend as an additional character is the higher power
for n.Cmp(zero) == 1 {
n, rem = n.DivMod(n, bse, rem)
b = append([]byte{e.encode[rem.Int64()]}, b...)
}
s := string(b)
if e.padding > 0 {
s = e.pad(s, e.padding)
}
return s
}
/**
* Decoder
*/
// DecodeToInt64 decodes a base62 encoded string using the StdEncoding
func DecodeToInt64(s string) int64 {
return StdEncoding.DecodeToInt64(s)
}
// DecodeToBigInt returns an arbitrary precision integer from the base62
// encoded string using the StdEncoding
func DecodeToBigInt(s string) *big.Int {
return StdEncoding.DecodeToBigInt(s)
}
// DecodeToInt64 decodes a base62 encoded string
func (e *Encoding) DecodeToInt64(s string) int64 {
var (
n int64
c int64
idx int
power int
)
for i, v := range s {
idx = strings.IndexRune(e.encode, v)
// Work downwards through powers of our base
power = len(s) - (i + 1)
// Calculate value at this position and add
c = int64(idx) * int64(math.Pow(float64(base), float64(power)))
n = n + c
}
return int64(n)
}
// DecodeToBigInt returns an arbitrary precision integer from the base62 encoded string
func (e *Encoding) DecodeToBigInt(s string) *big.Int {
var (
n = new(big.Int)
c = new(big.Int)
idx = new(big.Int)
power = new(big.Int)
exp = new(big.Int)
bse = new(big.Int)
)
bse.SetInt64(base)
// Run through each character to decode
for i, v := range s {
// Get index/position of the rune as a big int
idx.SetInt64(int64(strings.IndexRune(e.encode, v)))
// Work downwards through exponents
exp.SetInt64(int64(len(s) - (i + 1)))
// Calculate power for this exponent
power.Exp(bse, exp, nil)
// Multiplied by our index, gives us the value for this character
c = c.Mul(idx, power)
// Finally add to running total
n.Add(n, c)
}
return n
}
// pad a string to a minimum length with zero characters
func (e *Encoding) pad(s string, minlen int) string {
if len(s) >= minlen {
return s
}
format := fmt.Sprint(`%0`, strconv.Itoa(minlen), "s")
return fmt.Sprintf(format, s)
}