forked from mattheath/base62
-
Notifications
You must be signed in to change notification settings - Fork 0
/
base62.go
227 lines (184 loc) · 4.97 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
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
// 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, error) {
return StdEncoding.DecodeToInt64(s)
}
// MustDecodeToInt64 decodes a base62 encoded string using the StdEncoding
// panics in the case of an error
func MustDecodeToInt64(s string) int64 {
return StdEncoding.MustDecodeToInt64(s)
}
// DecodeToBigInt returns an arbitrary precision integer from the base62
// encoded string using the StdEncoding
func DecodeToBigInt(s string) (*big.Int, error) {
return StdEncoding.DecodeToBigInt(s)
}
type ErrInvalidCharacter struct{ error }
// MustDecodeToInt64 decodes a base62 encoded string,
// it panics in the case of an error
func (e *Encoding) MustDecodeToInt64(s string) int64 {
v, err := e.DecodeToInt64(s)
if err != nil {
panic(err)
}
return v
}
// DecodeToInt64 decodes a base62 encoded string
func (e *Encoding) DecodeToInt64(s string) (int64, error) {
var (
n int64
c int64
idx int
power int
)
for i, v := range s {
idx = strings.IndexRune(e.encode, v)
if idx == -1 {
return 0, ErrInvalidCharacter{fmt.Errorf("Invalid character %c at %d", v, i)}
}
// 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), nil
}
// DecodeToBigInt returns an arbitrary precision integer from the base62 encoded string
func (e *Encoding) DecodeToBigInt(s string) (*big.Int, error) {
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 {
pos := strings.IndexRune(e.encode, v)
if pos == -1 {
return nil, ErrInvalidCharacter{fmt.Errorf("Invalid character %c at %d",
v, i)}
}
// Get index/position of the rune as a big int
idx.SetInt64(int64(pos))
// 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, nil
}
// 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)
}