mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
encoding: add base58 support (#11288)
This commit is contained in:
181
vlib/encoding/base58/base58.v
Normal file
181
vlib/encoding/base58/base58.v
Normal file
@@ -0,0 +1,181 @@
|
||||
// algorthim is adapted from https://github.com/mr-tron/base58 under the MIT license
|
||||
|
||||
module base58
|
||||
|
||||
import math
|
||||
|
||||
// encode_int encodes any integer type to base58 string with Bitcoin alphabet
|
||||
pub fn encode_int(input int) ?string {
|
||||
return encode_int_walpha(input, alphabets['btc'])
|
||||
}
|
||||
|
||||
// encode_int_walpha any integer type to base58 string with custom alphabet
|
||||
pub fn encode_int_walpha(input int, alphabet Alphabet) ?string {
|
||||
if input <= 0 {
|
||||
return error(@MOD + '.' + @FN + ': input must be greater than zero')
|
||||
}
|
||||
|
||||
mut buffer := []byte{}
|
||||
|
||||
mut i := input
|
||||
for i > 0 {
|
||||
remainder := i % 58
|
||||
buffer << alphabet.encode[i8(remainder)]
|
||||
// This needs to be casted so byte inputs can
|
||||
// be used. i8 because remainder will never be
|
||||
// over 58.
|
||||
i = i / 58
|
||||
}
|
||||
|
||||
return buffer.reverse().bytestr()
|
||||
}
|
||||
|
||||
// encode encodes byte array to base58 with Bitcoin alphabet
|
||||
pub fn encode(input string) string {
|
||||
return encode_walpha(input, alphabets['btc'])
|
||||
}
|
||||
|
||||
// encode_walpha encodes byte array to base58 with custom aplhabet
|
||||
pub fn encode_walpha(input string, alphabet Alphabet) string {
|
||||
if input.len == 0 {
|
||||
return ''
|
||||
}
|
||||
|
||||
bin := input.bytes()
|
||||
mut sz := bin.len
|
||||
|
||||
mut zcount := 0
|
||||
for zcount < sz && bin[zcount] == 0 {
|
||||
zcount++
|
||||
}
|
||||
|
||||
// It is crucial to make this as short as possible, especially for
|
||||
// the usual case of Bitcoin addresses
|
||||
sz = zcount + (sz - zcount) * 555 / 406 + 1
|
||||
// integer simplification of
|
||||
// ceil(log(256)/log(58))
|
||||
|
||||
mut out := []byte{len: sz}
|
||||
mut i := 0
|
||||
mut high := 0
|
||||
mut carry := u32(0)
|
||||
|
||||
high = sz - 1
|
||||
for b in bin {
|
||||
i = sz - 1
|
||||
for carry = u32(b); i > high || carry != 0; i-- {
|
||||
carry = carry + 256 * u32(out[i])
|
||||
out[i] = byte(carry % 58)
|
||||
carry /= 58
|
||||
}
|
||||
high = 1
|
||||
}
|
||||
|
||||
// determine additional "zero-gap" in the buffer, aside from zcount
|
||||
for i = zcount; i < sz && out[i] == 0; i++ {}
|
||||
|
||||
// now encode the values with actual alphabet in-place
|
||||
val := out[i - zcount..]
|
||||
sz = val.len
|
||||
for i = 0; i < sz; i++ {
|
||||
out[i] = alphabet.encode[val[i]]
|
||||
}
|
||||
|
||||
return out[..sz].bytestr()
|
||||
}
|
||||
|
||||
// decode_int decodes base58 string to an integer with Bitcoin alphabet
|
||||
pub fn decode_int(input string) ?int {
|
||||
return decode_int_walpha(input, alphabets['btc'])
|
||||
}
|
||||
|
||||
// decode_int_walpha decodes base58 string to an integer with custom alphabet
|
||||
pub fn decode_int_walpha(input string, alphabet Alphabet) ?int {
|
||||
mut total := 0 // to hold the results
|
||||
b58 := input.reverse()
|
||||
for i, ch in b58 {
|
||||
ch_i := alphabet.encode.bytestr().index_byte(ch)
|
||||
if ch_i == -1 {
|
||||
return error(@MOD + '.' + @FN +
|
||||
': input string contains values not found in the provided alphabet')
|
||||
}
|
||||
|
||||
val := ch_i * math.pow(58, i)
|
||||
|
||||
total += int(val)
|
||||
}
|
||||
|
||||
return total
|
||||
}
|
||||
|
||||
// decode decodes base58 string using the Bitcoin alphabet
|
||||
pub fn decode(str string) ?string {
|
||||
return decode_walpha(str, alphabets['btc'])
|
||||
}
|
||||
|
||||
// decode_walpha decodes base58 string using custom alphabet
|
||||
pub fn decode_walpha(str string, alphabet Alphabet) ?string {
|
||||
if str.len == 0 {
|
||||
return ''
|
||||
}
|
||||
|
||||
zero := alphabet.encode[0]
|
||||
b58sz := str.len
|
||||
|
||||
mut zcount := 0
|
||||
for i := 0; i < b58sz && str[i] == zero; i++ {
|
||||
zcount++
|
||||
}
|
||||
|
||||
mut t := u64(0)
|
||||
mut c := u64(0)
|
||||
|
||||
// the 32-bit algorithm stretches the result up to 2x
|
||||
mut binu := []byte{len: 2 * ((b58sz * 406 / 555) + 1)}
|
||||
mut outi := []u32{len: (b58sz + 3) / 4}
|
||||
|
||||
for _, r in str {
|
||||
if r > 127 {
|
||||
panic(@MOD + '.' + @FN +
|
||||
': high-bit set on invalid digit; outside of ascii range ($r). This should never happen.')
|
||||
}
|
||||
if alphabet.decode[r] == -1 {
|
||||
return error(@MOD + '.' + @FN + ': invalid base58 digit ($r)')
|
||||
}
|
||||
|
||||
c = u64(alphabet.decode[r])
|
||||
|
||||
for j := outi.len - 1; j >= 0; j-- {
|
||||
t = u64(outi[j]) * 58 + c
|
||||
c = t >> 32
|
||||
outi[j] = u32(t & 0xffffffff)
|
||||
}
|
||||
}
|
||||
|
||||
// initial mask depend on b58sz, on further loops it always starts at 24 bits
|
||||
mut mask := (u32(b58sz % 4) * 8)
|
||||
if mask == 0 {
|
||||
mask = 32
|
||||
}
|
||||
mask -= 8
|
||||
|
||||
mut out_len := 0
|
||||
for j := 0; j < outi.len; j++ {
|
||||
for mask < 32 {
|
||||
binu[out_len] = byte(outi[j] >> mask)
|
||||
mask -= 8
|
||||
out_len++
|
||||
}
|
||||
mask = 24
|
||||
}
|
||||
|
||||
// find the most significant byte post-decode, if any
|
||||
for msb := zcount; msb < binu.len; msb++ { // loop relies on u32 overflow
|
||||
if binu[msb] > 0 {
|
||||
return binu[msb - zcount..out_len].bytestr()
|
||||
}
|
||||
}
|
||||
|
||||
// it's all zeroes
|
||||
return binu[..out_len].bytestr()
|
||||
}
|
||||
Reference in New Issue
Block a user