diff --git a/vlib/encoding/base58/alphabet.v b/vlib/encoding/base58/alphabet.v index 42cb5c6e9b..8f28cefbf2 100644 --- a/vlib/encoding/base58/alphabet.v +++ b/vlib/encoding/base58/alphabet.v @@ -1,23 +1,24 @@ module base58 -// alphabets is a map of common base58 alphabets -pub const alphabets = init_alphabets() +const impossible = 'this should never happen' -// init_alphabet instantiates the preconfigured `Alphabet`s and returns them as `map[string]Alphabet`. -// This is a temporary function. Setting const alphabets to the value returned in this function -// causes a C error right now. -fn init_alphabets() map[string]Alphabet { - return { - 'btc': new_alphabet('123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz') or { - panic(@MOD + '.' + @FN + ': this should never happen') - } - 'flickr': new_alphabet('123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ') or { - panic(@MOD + '.' + @FN + ': this should never happen') - } - 'ripple': new_alphabet('rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz') or { - panic(@MOD + '.' + @FN + ': this should never happen') - } - } +pub const btc_alphabet = new_alphabet('123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz') or { + panic(impossible) +} + +pub const flickr_alphabet = new_alphabet('123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ') or { + panic(impossible) +} + +pub const ripple_alphabet = new_alphabet('rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz') or { + panic(impossible) +} + +// alphabets is a map of common base58 alphabets: +pub const alphabets = { + 'btc': btc_alphabet + 'flickr': flickr_alphabet + 'ripple': ripple_alphabet } // Alphabet is the series of characters that an input diff --git a/vlib/encoding/base58/base58.v b/vlib/encoding/base58/base58.v index 30f6f58a51..a5a86bf5ca 100644 --- a/vlib/encoding/base58/base58.v +++ b/vlib/encoding/base58/base58.v @@ -6,7 +6,7 @@ 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']) + return encode_int_walpha(input, btc_alphabet) } // encode_int_walpha any integer type to base58 string with custom alphabet @@ -30,22 +30,34 @@ pub fn encode_int_walpha(input int, alphabet Alphabet) ?string { return buffer.reverse().bytestr() } -// encode encodes byte array to base58 with Bitcoin alphabet +// encode encodes the input string to base58 with the Bitcoin alphabet pub fn encode(input string) string { - return encode_walpha(input, alphabets['btc']) + return encode_walpha(input, btc_alphabet) } -// encode_walpha encodes byte array to base58 with custom aplhabet +// encode_bytes encodes the input array to base58, with the Bitcoin alphabet +pub fn encode_bytes(input []u8) []u8 { + return encode_walpha_bytes(input, btc_alphabet) +} + +// encode_walpha encodes the input string to base58 with a custom aplhabet pub fn encode_walpha(input string, alphabet Alphabet) string { if input.len == 0 { return '' } - bin := input.bytes() - mut sz := bin.len + return encode_walpha_bytes(bin, alphabet).bytestr() +} + +// encode_walpha encodes the input array to base58 with a custom aplhabet +pub fn encode_walpha_bytes(input []u8, alphabet Alphabet) []u8 { + if input.len == 0 { + return [] + } + mut sz := input.len mut zcount := 0 - for zcount < sz && bin[zcount] == 0 { + for zcount < sz && input[zcount] == 0 { zcount++ } @@ -61,7 +73,7 @@ pub fn encode_walpha(input string, alphabet Alphabet) string { mut carry := u32(0) high = sz - 1 - for b in bin { + for b in input { i = sz - 1 for carry = u32(b); i > high || carry != 0; i-- { carry = carry + 256 * u32(out[i]) @@ -81,12 +93,12 @@ pub fn encode_walpha(input string, alphabet Alphabet) string { out[i] = alphabet.encode[val[i]] } - return out[..sz].bytestr() + return out[..sz] } // 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']) + return decode_int_walpha(input, btc_alphabet) } // decode_int_walpha decodes base58 string to an integer with custom alphabet @@ -108,22 +120,37 @@ pub fn decode_int_walpha(input string, alphabet Alphabet) ?int { return total } -// decode decodes base58 string using the Bitcoin alphabet +// decode decodes the base58 input string, using the Bitcoin alphabet pub fn decode(str string) ?string { - return decode_walpha(str, alphabets['btc']) + return decode_walpha(str, btc_alphabet) } -// decode_walpha decodes base58 string using custom alphabet -pub fn decode_walpha(str string, alphabet Alphabet) ?string { - if str.len == 0 { +// decode_bytes decodes the base58 encoded input array, using the Bitcoin alphabet +pub fn decode_bytes(input []u8) ?[]u8 { + return decode_walpha_bytes(input, btc_alphabet) +} + +// decode_walpha decodes the base58 encoded input string, using custom alphabet +pub fn decode_walpha(input string, alphabet Alphabet) ?string { + if input.len == 0 { return '' } + bin := input.bytes() + res := decode_walpha_bytes(bin, alphabet)? + return res.bytestr() +} + +// decode_walpha_bytes decodes the base58 encoded input array using a custom alphabet +pub fn decode_walpha_bytes(input []u8, alphabet Alphabet) ?[]u8 { + if input.len == 0 { + return [] + } zero := alphabet.encode[0] - b58sz := str.len + b58sz := input.len mut zcount := 0 - for i := 0; i < b58sz && str[i] == zero; i++ { + for i := 0; i < b58sz && input[i] == zero; i++ { zcount++ } @@ -134,7 +161,7 @@ pub fn decode_walpha(str string, alphabet Alphabet) ?string { mut binu := []u8{len: 2 * ((b58sz * 406 / 555) + 1)} mut outi := []u32{len: (b58sz + 3) / 4} - for _, r in str { + for _, r in input { if r > 127 { panic(@MOD + '.' + @FN + ': high-bit set on invalid digit; outside of ascii range ($r). This should never happen.') @@ -172,10 +199,10 @@ pub fn decode_walpha(str string, alphabet Alphabet) ?string { // 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() + return binu[msb - zcount..out_len] } } // it's all zeroes - return binu[..out_len].bytestr() + return binu[..out_len] } diff --git a/vlib/encoding/base58/base58_usage_test.v b/vlib/encoding/base58/base58_usage_test.v new file mode 100644 index 0000000000..b0eb8c8001 --- /dev/null +++ b/vlib/encoding/base58/base58_usage_test.v @@ -0,0 +1,42 @@ +import encoding.base58 +import encoding.hex + +fn test_encode() ? { + for input, expected in { + '': '' + '6263': '2PMCRQ' + 'hello world': 'StV1DL6CwTryKyV' + '\x00\x00hello world': '11StV1DL6CwTryKyV' + } { + output := base58.encode(input) + println('> input: `$input` | $input.bytes().hex() | => output: `$output`') + assert output == expected + } +} + +fn test_encode_int() ? { + for input, expected in { + 0x6263: '8VG' + 0x61: '2g' + 0x626262: 'a3gV' + 0x636363: 'aPEr' + } { + output := base58.encode_int(input)? + println('> input: 0x${input:x} | => output: `$output`') + assert output == expected + } +} + +fn test_decode() ? { + for output, expected in { + 'USm3fpXnKG5EUBx2ndxBDMPVciP5hGey2Jh4NDv6gmeo1LkMeiKrLJUUBk6Z': 'The quick brown fox jumps over the lazy dog.' + '11StV1DL6CwTryKyV': '\x00\x00hello world' + '2NEpo7TZRRrLZSi2U': 'Hello World!' + '14cxpo3MBCYYWCgF74SWTdcmxipnGUsPw3': hex.decode('0027b5891b01da2db74cde1689a97a2acbe23d5fb1c0205bf6')?.bytestr() + '3vQB7B6MrGQZaxCuFg4oh': hex.decode('68656c6c6f20776f726c64bc62d4b8')?.bytestr() + } { + input := base58.decode(output)? + println('> output: `$output` | decoded input: `$input` | bytes: $input.bytes().hex()') + assert input.bytes().hex() == expected.bytes().hex() + } +}