package langext import ( "crypto/rand" "errors" "math" "math/big" ) type AnyBaseConverter struct { base uint64 charset []rune } func NewAnyBaseConverter(cs string) AnyBaseConverter { rcs := []rune(cs) return AnyBaseConverter{ base: uint64(len(rcs)), charset: rcs, } } func (bc AnyBaseConverter) Rand(rlen int) string { biBase := big.NewInt(int64(bc.base)) randMax := big.NewInt(math.MaxInt64) r := "" for i := 0; i < rlen; i++ { v, err := rand.Int(rand.Reader, randMax) if err != nil { panic(err) } r += string(bc.charset[v.Mod(v, biBase).Int64()]) } return r } func (bc AnyBaseConverter) EncodeUInt64(num uint64) string { if num == 0 { return "0" } b := "" // loop as long the num is bigger than zero for num > 0 { r := num % bc.base num -= r num /= base62Base b += string(bc.charset[int(r)]) } return b } func (bc AnyBaseConverter) DecodeUInt64(str string) (uint64, error) { if str == "" { return 0, errors.New("empty string") } result := uint64(0) for _, v := range str { result *= base62Base pos := ArrFirstIndex(bc.charset, v) if pos == -1 { return 0, errors.New("invalid character: " + string(v)) } result += uint64(pos) } return result, nil } func (bc AnyBaseConverter) Encode(src []byte) string { value := new(big.Int) value.SetBytes(src) return bc.EncodeBigInt(value) } func (bc AnyBaseConverter) EncodeBigInt(src *big.Int) string { value := new(big.Int) value.Set(src) isneg := value.Sign() < 0 answer := "" if isneg { value.Neg(value) } biBase := big.NewInt(int64(bc.base)) rem := new(big.Int) for value.Sign() > 0 { value.QuoRem(value, biBase, rem) answer = string(bc.charset[rem.Int64()]) + answer } if isneg { return "-" + answer } else { return answer } } func (bc AnyBaseConverter) Decode(src string) ([]byte, error) { value, err := bc.DecodeToBigInt(src) if err != nil { return nil, err } return value.Bytes(), nil } func (bc AnyBaseConverter) DecodeToBigInt(_src string) (*big.Int, error) { result := new(big.Int) result.SetInt64(0) src := []rune(_src) if len(src) == 0 { return nil, errors.New("string is empty") } if bc.base < 2 { return nil, errors.New("not enough digits") } i := 0 sign := new(big.Int) sign.SetInt64(1) if src[i] == '+' { i++ } else if src[i] == '-' { i++ sign.SetInt64(-1) } if i >= len(src) { return nil, errors.New("no digits in input") } biBase := big.NewInt(int64(bc.base)) oldResult := new(big.Int) for ; i < len(src); i++ { n := ArrFirstIndex(bc.charset, src[i]) if n < 0 { return nil, errors.New("invalid characters in input") } oldResult.Set(result) result.Mul(result, biBase) result.Add(result, big.NewInt(int64(n))) if result.Cmp(oldResult) < 0 { return nil, errors.New("overflow") } } if sign.Cmp(big.NewInt(0)) < 0 { result.Neg(result) } return result, nil }