Reputation: 65793
I want to convert a big.Int
to simple base32. Not the standard base32 stuff like the RFC 4648 implemented by base32
nor zBase32 nor Crockford I want just simple normal 5-bits per character 0-9A-V character set.
I am aware of the base32
package and it does not do what I want - it builds the result in a standard base 32 number with padding and stuff I don't want. Certainly I could use it and tear off the trailing "=" characters and hack what remains but that just seems like a brutal solution.
There is a big.SetString(string, base)
that can parse a base32 number in string form but there is no reverse - which is what I am really looking for, a big.GetString(base)
like the Java BigInteger.toString(int base)
.
There is, however, a nat.string
which does exactly what I want. How can I gain access to it?
Is there a way I could manually extend big
to implement big.GetString(base)
which trivially calls nat.string
with the correct charset
?
Is there a way I can reach into the unexported big.nat
which big
uses and call nat.string
?
Is there something else I can do?
P.S. I'd also be interested in using nat.trailingZeroBits
- I had to write my own because I didn't realise this was already done.
Upvotes: 2
Views: 158
Reputation: 65793
I have posted on the go-nuts thread. They seem happy with my suggestion.
Meanwhile - my implementation of Base32 (does NOT handle sign - like nat
)
// The digits
const charset string = "0123456789ABCDEFGHIJKLMNOPQRSTUV"
/*
Convert the big to base32
*/
func Base32(n big.Int) string {
// The bytes of the number
bytes := n.Bytes()
nBytes := len(bytes)
if nBytes == 0 {
// Special case 0.
return "0"
}
// How many digits will be in the string?
nBits := nBytes * 8
nDigits := nBits / 5
if nBits%5 > 0 {
nDigits += 1
}
// Grow the digits into an array
digits := make([]byte, nDigits)
digit := nDigits - 1
// Peel off 5 bits from the array each time.
for b, shr := nBytes-1, uint(0); b >= 0; {
// The next lot is there.
shrp5 := shr + 5
// Build my bits.
x := (bytes[b] >> shr)
if shrp5 > 8 && b > 0 {
of := shrp5 - 8
// Add in some bits from the next byte too.
x &= (1 << (8 - shr)) - 1
x |= bytes[b-1] << (5 - of)
}
// Shift 5 more.
shr = shrp5
// Should we step to next byte?
if shr >= 8 {
// Next byte
shr -= 8
b -= 1
}
x &= 0x1f
// Build my digit
digits[digit] = charset[x]
digit -= 1
}
// Skip leading zeros.
lz := 0
for digits[lz] == '0' {
lz += 1
}
// Return the string equivalent.
return string(digits[lz:])
}
Comments welcome.
Upvotes: 1
Reputation: 54079
I don't think the go team will ever export anything in nat
as that is an implementation details for big.Int
. They may look kindly upon a request to export a ToBase
function though from big.Int
.
In the mean time here is a lightly tested naive base converter for you
func ToBase(x *big.Int, base int) string {
if x.Sign() == 0 {
return "0"
}
y := new(big.Int).Set(x)
b := big.NewInt(int64(base))
charset := "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
out := make([]byte, 0, 16)
negative := false
if y.Sign() < 0 {
negative = true
y.Neg(y)
}
digit := new(big.Int)
for y.Sign() != 0 {
y.DivMod(y, b, digit)
out = append(out, charset[digit.Int64()])
}
if negative {
out = append(out, '-')
}
// Reverse out
for i, j := 0, len(out)-1; i < j; i, j = i+1, j-1 {
out[i], out[j] = out[j], out[i]
}
return string(out)
}
Upvotes: 2
Reputation: 19378
You can't access unexported functions at all. You'd need to re-write at least some portion of nat.go
in order to achieve that functionality. Some of those functions look very useful, so it may be worth sending a feature request to the golang-nuts
group asking for some of them to be exported in a future release.
You can however use strconv.FormatInt()
to do what you require.
given a big.Int
b
you can do:
strconv.FormatInt(b.Int64(), 32)
Full example:
package main
import (
"fmt"
"math/big"
"strconv"
)
func main() {
i := 3286583923486565782 // Some random integer
b := big.NewInt(int64(i))
fmt.Println(strconv.FormatInt(b.Int64(), 32))
}
Produces:
2r72al99uq9cm
Upvotes: 2