v2 / vlib / encoding / base58 / base58.v
209 lines · 171 sloc · 5.06 KB · d07243711c8db8d09c05c9d69da6fc8c348d0f2f
Raw
1// algorthim is adapted from https://github.com/mr-tron/base58 under the MIT license
2
3module base58
4
5import math
6
7// encode_int encodes any integer type to base58 string with Bitcoin alphabet
8pub fn encode_int(input int) !string {
9 return encode_int_walpha(input, btc_alphabet)
10}
11
12// encode_int_walpha any integer type to base58 string with custom alphabet
13pub fn encode_int_walpha(input int, alphabet Alphabet) !string {
14 if input <= 0 {
15 return error(@MOD + '.' + @FN + ': input must be greater than zero')
16 }
17
18 mut buffer := []u8{}
19
20 mut i := input
21 for i > 0 {
22 remainder := i % 58
23 buffer << alphabet.encode[i8(remainder)]
24 // This needs to be casted so byte inputs can
25 // be used. i8 because remainder will never be
26 // over 58.
27 i = i / 58
28 }
29
30 return buffer.reverse().bytestr()
31}
32
33// encode encodes the input string to base58 with the Bitcoin alphabet
34pub fn encode(input string) string {
35 return encode_walpha(input, btc_alphabet)
36}
37
38// encode_bytes encodes the input array to base58, with the Bitcoin alphabet
39pub fn encode_bytes(input []u8) []u8 {
40 return encode_walpha_bytes(input, btc_alphabet)
41}
42
43// encode_walpha encodes the input string to base58 with a custom aplhabet
44pub fn encode_walpha(input string, alphabet Alphabet) string {
45 if input.len == 0 {
46 return ''
47 }
48 bin := input.bytes()
49 return encode_walpha_bytes(bin, alphabet).bytestr()
50}
51
52// encode_walpha encodes the input array to base58 with a custom aplhabet
53@[direct_array_access]
54pub fn encode_walpha_bytes(input []u8, alphabet Alphabet) []u8 {
55 if input.len == 0 {
56 return []
57 }
58 mut sz := input.len
59
60 mut zcount := 0
61 for zcount < sz && input[zcount] == 0 {
62 zcount++
63 }
64
65 // It is crucial to make this as short as possible, especially for
66 // the usual case of Bitcoin addresses
67 sz = zcount + (sz - zcount) * 555 / 406 + 1
68 // integer simplification of
69 // ceil(log(256)/log(58))
70
71 mut out := []u8{len: sz}
72 mut i := 0
73 mut high := 0
74 mut carry := u32(0)
75
76 high = sz - 1
77 for b in input {
78 i = sz - 1
79 for carry = u32(b); i > high || carry != 0; i-- {
80 carry = carry + 256 * u32(out[i])
81 out[i] = u8(carry % 58)
82 carry /= 58
83 }
84 high = i
85 }
86
87 // determine additional "zero-gap" in the buffer, aside from zcount
88 for i = zcount; i < sz && out[i] == 0; i++ {}
89
90 // now encode the values with actual alphabet in-place
91 val := unsafe { out[i - zcount..] }
92 sz = val.len
93 for i = 0; i < sz; i++ {
94 out[i] = alphabet.encode[val[i]]
95 }
96
97 return out[..sz]
98}
99
100// decode_int decodes base58 string to an integer with Bitcoin alphabet
101pub fn decode_int(input string) !int {
102 return decode_int_walpha(input, btc_alphabet)
103}
104
105// decode_int_walpha decodes base58 string to an integer with custom alphabet
106pub fn decode_int_walpha(input string, alphabet Alphabet) !int {
107 mut total := 0 // to hold the results
108 b58 := input.reverse()
109 for i, ch in b58 {
110 ch_i := alphabet.encode.bytestr().index_u8(ch)
111 if ch_i == -1 {
112 return error(@MOD + '.' + @FN +
113 ': input string contains values not found in the provided alphabet')
114 }
115
116 val := ch_i * math.pow(58, i)
117
118 total += int(val)
119 }
120
121 return total
122}
123
124// decode decodes the base58 input string, using the Bitcoin alphabet
125pub fn decode(str string) !string {
126 return decode_walpha(str, btc_alphabet)
127}
128
129// decode_bytes decodes the base58 encoded input array, using the Bitcoin alphabet
130pub fn decode_bytes(input []u8) ![]u8 {
131 return decode_walpha_bytes(input, btc_alphabet)
132}
133
134// decode_walpha decodes the base58 encoded input string, using custom alphabet
135pub fn decode_walpha(input string, alphabet Alphabet) !string {
136 if input.len == 0 {
137 return ''
138 }
139 bin := input.bytes()
140 res := decode_walpha_bytes(bin, alphabet)!
141 return res.bytestr()
142}
143
144// decode_walpha_bytes decodes the base58 encoded input array using a custom alphabet
145pub fn decode_walpha_bytes(input []u8, alphabet Alphabet) ![]u8 {
146 if input.len == 0 {
147 return []
148 }
149
150 zero := alphabet.encode[0]
151 b58sz := input.len
152
153 mut zcount := 0
154 for i := 0; i < b58sz && input[i] == zero; i++ {
155 zcount++
156 }
157
158 mut t := u64(0)
159 mut c := u64(0)
160
161 // the 32-bit algorithm stretches the result up to 2x
162 mut binu := []u8{len: 2 * ((b58sz * 406 / 555) + 1)}
163 mut outi := []u32{len: (b58sz + 3) / 4}
164
165 for _, r in input {
166 if r > 127 {
167 panic(@MOD + '.' + @FN +
168 ': high-bit set on invalid digit; outside of ascii range (${r}). This should never happen.')
169 }
170 if alphabet.decode[r] == -1 {
171 return error(@MOD + '.' + @FN + ': invalid base58 digit (${r})')
172 }
173
174 c = u64(alphabet.decode[r])
175
176 for j := outi.len - 1; j >= 0; j-- {
177 t = u64(outi[j]) * 58 + c
178 c = t >> 32
179 outi[j] = u32(t & 0xffffffff)
180 }
181 }
182
183 // initial mask depend on b58sz, on further loops it always starts at 24 bits
184 mut mask := (u32(b58sz % 4) * 8)
185 if mask == 0 {
186 mask = 32
187 }
188 mask -= 8
189
190 mut out_len := 0
191 for j := 0; j < outi.len; j++ {
192 for mask < 32 {
193 binu[out_len] = u8(outi[j] >> mask)
194 mask -= 8
195 out_len++
196 }
197 mask = 24
198 }
199
200 // find the most significant byte post-decode, if any
201 for msb := zcount; msb < binu.len; msb++ { // loop relies on u32 overflow
202 if binu[msb] > 0 {
203 return binu[msb - zcount..out_len]
204 }
205 }
206
207 // it's all zeroes
208 return binu[..out_len]
209}
210