monero-base58/base58.go

143 lines
4.2 KiB
Go

package base58
import (
"encoding/binary"
)
const base58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
const reverseBase58 =
// 0 1 2 3 4 5 6 7 8 9 a b c d e f */
/* 00 */ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +
/* 10 */ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +
/* 20 */ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +
/* 30 */ "\xff\x00\x01\x02\x03\x04\x05\x06\x07\x08\xff\xff\xff\xff\xff\xff" +
/* 40 */ "\xff\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\xff\x11\x12\x13\x14\x15\xff" +
/* 50 */ "\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\xff\xff\xff\xff\xff" +
/* 60 */ "\xff\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\xff\x2c\x2d\x2e" +
/* 70 */ "\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\xff\xff\xff\xff\xff" +
/* 80 */ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +
/* 90 */ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +
/* a0 */ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +
/* b0 */ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +
/* c0 */ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +
/* d0 */ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +
/* e0 */ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +
/* f0 */ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
func encodeChunk(raw []byte, buf []byte) []byte {
intToDecode := binary.BigEndian.Uint64(raw[:])
for intToDecode > 0 {
buf = append(buf, base58[intToDecode%58])
intToDecode /= 58
}
for len(buf) < 11 {
buf = append(buf, '1')
}
for i, j := 0, len(buf)-1; i < j; i, j = i+1, j-1 {
buf[i], buf[j] = buf[j], buf[i]
}
return buf
}
func encodeChunkTail(raw []byte, buf []byte) []byte {
var data [8]byte
copy(data[8-len(raw):], raw)
intToDecode := binary.BigEndian.Uint64(data[:])
for intToDecode > 0 {
buf = append(buf, base58[intToDecode%58])
intToDecode /= 58
}
for len(buf) < 7 {
buf = append(buf, '1')
}
for i, j := 0, len(buf)-1; i < j; i, j = i+1, j-1 {
buf[i], buf[j] = buf[j], buf[i]
}
return buf
}
func decodeChunk(buf []byte, encoded []byte) []byte {
var intResult uint64
currentMultiplier := uint64(1)
for i := len(encoded) - 1; i >= 0; i-- {
intResult += currentMultiplier * uint64(reverseBase58[encoded[i]])
// this can overflow, but only on the last iteration when i == 0 when all data is valid
currentMultiplier *= 58
}
var result [8]byte
binary.BigEndian.PutUint64(result[:], intResult)
switch len(encoded) {
case 0:
return append(buf, result[8:]...)
case 2:
return append(buf, result[7:]...)
case 3:
return append(buf, result[6:]...)
case 5:
return append(buf, result[5:]...)
case 6:
return append(buf, result[4:]...)
case 7:
return append(buf, result[3:]...)
case 9:
return append(buf, result[2:]...)
case 10:
return append(buf, result[1:]...)
case 11:
return append(buf, result[:]...)
default:
}
return nil
}
func EncodeMoneroBase58(data ...[]byte) []byte {
return EncodeMoneroBase58PreAllocated(make([]byte, 0, func() (result int) {
for _, v := range data {
result += len(v)
}
return
}()), data...)
}
func EncodeMoneroBase58PreAllocated(buf []byte, data ...[]byte) []byte {
//preallocate common case
combined := make([]byte, 0, 96)
for _, item := range data {
combined = append(combined, item...)
}
result := buf
tmpBuf := make([]byte, 0, len(combined)*2)
length := len(combined)
rounds := length / 8
for i := 0; i < rounds; i++ {
result = append(result, encodeChunk(combined[i*8:(i+1)*8], tmpBuf[:0])...)
}
if length%8 > 0 {
result = append(result, encodeChunkTail(combined[rounds*8:], tmpBuf[:0])...)
}
return result
}
func DecodeMoneroBase58(data []byte) (result []byte) {
//common case
return DecodeMoneroBase58PreAllocated(make([]byte, 0, 69), data)
}
func DecodeMoneroBase58PreAllocated(buf []byte, data []byte) (result []byte) {
result = buf
length := len(data)
rounds := length / 11
for i := 0; i < rounds; i++ {
result = decodeChunk(result, data[i*11:i*11+11])
}
if length%11 > 0 {
result = decodeChunk(result, data[rounds*11:])
}
return
}