29 Star 304 Fork 62

GVPdromara/dongle

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
base45.go 7.23 KB
一键复制 编辑 原始数据 按行查看 历史
kuafuRace 提交于 2022-12-30 16:32 +08:00 . Package base45 implements base45 encoding
// Package base45 implements base45 encoding, fork from https://github.com/xkmsoft/base45
package base45
import (
"encoding/binary"
"fmt"
"strings"
)
const (
base = 45
baseSquare = 45 * 45
maxUint16 = 0xFFFF
)
type InvalidLengthError struct {
length int
mod int
}
func (e InvalidLengthError) Error() string {
return fmt.Sprintf("invalid length n=%d. It should be n mod 3 = [0, 2] NOT n mod 3 = %d", e.length, e.mod)
}
type InvalidCharacterError struct {
char rune
position int
}
func (e InvalidCharacterError) Error() string {
return fmt.Sprintf("invalid character %s at position: %d\n", string(e.char), e.position)
}
type IllegalBase45ByteError struct {
position int
}
func (e IllegalBase45ByteError) Error() string {
return fmt.Sprintf("illegal base45 data at byte position %d\n", e.position)
}
var encodingMap = map[byte]rune{
byte(0): '0',
byte(1): '1',
byte(2): '2',
byte(3): '3',
byte(4): '4',
byte(5): '5',
byte(6): '6',
byte(7): '7',
byte(8): '8',
byte(9): '9',
byte(10): 'A',
byte(11): 'B',
byte(12): 'C',
byte(13): 'D',
byte(14): 'E',
byte(15): 'F',
byte(16): 'G',
byte(17): 'H',
byte(18): 'I',
byte(19): 'J',
byte(20): 'K',
byte(21): 'L',
byte(22): 'M',
byte(23): 'N',
byte(24): 'O',
byte(25): 'P',
byte(26): 'Q',
byte(27): 'R',
byte(28): 'S',
byte(29): 'T',
byte(30): 'U',
byte(31): 'V',
byte(32): 'W',
byte(33): 'X',
byte(34): 'Y',
byte(35): 'Z',
byte(36): ' ',
byte(37): '$',
byte(38): '%',
byte(39): '*',
byte(40): '+',
byte(41): '-',
byte(42): '.',
byte(43): '/',
byte(44): ':',
}
var decodingMap = map[rune]byte{
'0': byte(0),
'1': byte(1),
'2': byte(2),
'3': byte(3),
'4': byte(4),
'5': byte(5),
'6': byte(6),
'7': byte(7),
'8': byte(8),
'9': byte(9),
'A': byte(10),
'B': byte(11),
'C': byte(12),
'D': byte(13),
'E': byte(14),
'F': byte(15),
'G': byte(16),
'H': byte(17),
'I': byte(18),
'J': byte(19),
'K': byte(20),
'L': byte(21),
'M': byte(22),
'N': byte(23),
'O': byte(24),
'P': byte(25),
'Q': byte(26),
'R': byte(27),
'S': byte(28),
'T': byte(29),
'U': byte(30),
'V': byte(31),
'W': byte(32),
'X': byte(33),
'Y': byte(34),
'Z': byte(35),
' ': byte(36),
'$': byte(37),
'%': byte(38),
'*': byte(39),
'+': byte(40),
'-': byte(41),
'.': byte(42),
'/': byte(43),
':': byte(44),
}
// Encode
//
// 4. The Base45 Encoding
// A 45-character subset of US-ASCII is used; the 45 characters usable
// in a QR code in Alphanumeric mode. Base45 encodes 2 bytes in 3
// characters, compared to Base64, which encodes 3 bytes in 4
// characters.
//
// For encoding two bytes [a, b] MUST be interpreted as a number n in
// base 256, i.e. as an unsigned integer over 16 bits so that the number
// n = (a*256) + b.
//
// This number n is converted to base 45 [c, d, e] so that n = c +
// (d*45) + (e*45*45). Note the order of c, d and e which are chosen so
// that the left-most [c] is the least significant.
//
// The values c, d and e are then looked up in Table 1 to produce a
// three character string. The process is reversed when decoding.
//
// For encoding a single byte [a], it MUST be interpreted as a base 256
// number, i.e. as an unsigned integer over 8 bits. That integer MUST
// be converted to base 45 [c d] so that a = c + (45*d). The values c
// and d are then looked up in Table 1 to produce a two character
// string.
//
// A byte string [a b c d ... x y z] with arbitrary content and
// arbitrary length MUST be encoded as follows: From left to right pairs
// of bytes are encoded as described above. If the number of bytes is
// even, then the encoded form is a string with a length which is evenly
// divisible by 3. If the number of bytes is odd, then the last
// (rightmost) byte is encoded on two characters as described above.
//
// For decoding a Base45 encoded string the inverse operations are
// performed.
func Encode(in string) string {
bytes := []byte(in)
pairs := encodePairs(bytes)
var builder strings.Builder
for i, pair := range pairs {
res := encodeBase45(pair)
if i+1 == len(pairs) && res[2] == 0 {
for _, b := range res[:2] {
if c, ok := encodingMap[b]; ok {
builder.WriteRune(c)
}
}
} else {
for _, b := range res {
if c, ok := encodingMap[b]; ok {
builder.WriteRune(c)
}
}
}
}
return builder.String()
}
// Decode
//
// Decoding example 1: The string "QED8WEX0" represents, when looked up
// in Table 1, the values [26 14 13 8 32 14 33 0]. We arrange the
// numbers in chunks of three, except for the last one which can be two,
// and get [[26 14 13] [8 32 14] [33 0]]. In base 45 we get [26981
// 29798 33] where the bytes are [[105 101] [116 102] [33]]. If we look
// at the ASCII values we get the string "ietf!".
func Decode(in string) (string, error) {
size := len(in)
mod := size % 3
if mod != 0 && mod != 2 {
return "", InvalidLengthError{
length: size,
mod: mod,
}
}
bytes := make([]byte, 0, size)
for pos, char := range in {
v, ok := decodingMap[char]
if !ok {
return "", InvalidCharacterError{
char: char,
position: pos,
}
}
bytes = append(bytes, v)
}
chunks := decodeChunks(bytes)
triplets, err := decodeTriplets(chunks)
if err != nil {
return "", err
}
tripletsLength := len(triplets)
decoded := make([]byte, 0, tripletsLength*2)
for i := 0; i < tripletsLength-1; i++ {
bytes := uint16ToBytes(triplets[i])
decoded = append(decoded, bytes[0])
decoded = append(decoded, bytes[1])
}
if mod == 2 {
bytes := uint16ToBytes(triplets[tripletsLength-1])
decoded = append(decoded, bytes[1])
} else {
bytes := uint16ToBytes(triplets[tripletsLength-1])
decoded = append(decoded, bytes[0])
decoded = append(decoded, bytes[1])
}
return string(decoded), nil
}
func uint16ToBytes(in uint16) []byte {
bytes := make([]byte, 2)
binary.BigEndian.PutUint16(bytes, in)
return bytes
}
func decodeChunks(in []byte) [][]byte {
size := len(in)
ret := make([][]byte, 0, size/2)
for i := 0; i < size; i += 3 {
var f, s, l byte
if i+2 < size {
f = in[i]
s = in[i+1]
l = in[i+2]
ret = append(ret, []byte{f, s, l})
} else {
f = in[i]
s = in[i+1]
ret = append(ret, []byte{f, s})
}
}
return ret
}
func encodePairs(in []byte) [][]byte {
size := len(in)
ret := make([][]byte, 0, size/2)
for i := 0; i < size; i += 2 {
var high, low byte
if i+1 < size {
high = in[i]
low = in[i+1]
} else {
low = in[i]
}
ret = append(ret, []byte{high, low})
}
return ret
}
func encodeBase45(in []byte) []byte {
n := binary.BigEndian.Uint16(in)
c := n % base
e := (n - c) / (baseSquare)
d := (n - (c + (e * baseSquare))) / base
return []byte{byte(c), byte(d), byte(e)}
}
func decodeTriplets(in [][]byte) ([]uint16, error) {
size := len(in)
ret := make([]uint16, 0, size)
for pos, chunk := range in {
if len(chunk) == 3 {
// n = c + (d*45) + (e*45*45)
c := int(chunk[0])
d := int(chunk[1])
e := int(chunk[2])
n := c + (d * base) + (e * baseSquare)
if n > maxUint16 {
return nil, IllegalBase45ByteError{position: pos}
}
ret = append(ret, uint16(n))
}
if len(chunk) == 2 {
// n = c + (d*45)
c := uint16(chunk[0])
d := uint16(chunk[1])
n := c + (d * base)
ret = append(ret, n)
}
}
return ret, nil
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Go
1
https://gitee.com/dromara/dongle.git
git@gitee.com:dromara/dongle.git
dromara
dongle
dongle
v1.0.1

搜索帮助