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