143 lines
4.2 KiB
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
|
|
}
|