mirror of
https://github.com/golang/go
synced 2024-11-19 21:04:43 -07:00
big: implemented custom Gob(En/De)coder for Int type
- factored implementation of Int.Bytes, Int.SetBytes and replaced existing code with much simpler cores - use the shared bytes, setBytes routines for Gob (en/de)coding Fixes #1496. R=r, eds CC=golang-dev https://golang.org/cl/4249063
This commit is contained in:
parent
8138654aa8
commit
758d05551d
@ -8,6 +8,7 @@ package big
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"rand"
|
||||
)
|
||||
|
||||
@ -393,62 +394,19 @@ func (z *Int) SetString(s string, base int) (*Int, bool) {
|
||||
}
|
||||
|
||||
|
||||
// SetBytes interprets b as the bytes of a big-endian, unsigned integer and
|
||||
// sets z to that value.
|
||||
func (z *Int) SetBytes(b []byte) *Int {
|
||||
const s = _S
|
||||
z.abs = z.abs.make((len(b) + s - 1) / s)
|
||||
|
||||
j := 0
|
||||
for len(b) >= s {
|
||||
var w Word
|
||||
|
||||
for i := s; i > 0; i-- {
|
||||
w <<= 8
|
||||
w |= Word(b[len(b)-i])
|
||||
}
|
||||
|
||||
z.abs[j] = w
|
||||
j++
|
||||
b = b[0 : len(b)-s]
|
||||
}
|
||||
|
||||
if len(b) > 0 {
|
||||
var w Word
|
||||
|
||||
for i := len(b); i > 0; i-- {
|
||||
w <<= 8
|
||||
w |= Word(b[len(b)-i])
|
||||
}
|
||||
|
||||
z.abs[j] = w
|
||||
}
|
||||
|
||||
z.abs = z.abs.norm()
|
||||
// SetBytes interprets buf as the bytes of a big-endian unsigned
|
||||
// integer, sets z to that value, and returns z.
|
||||
func (z *Int) SetBytes(buf []byte) *Int {
|
||||
z.abs = z.abs.setBytes(buf)
|
||||
z.neg = false
|
||||
return z
|
||||
}
|
||||
|
||||
|
||||
// Bytes returns the absolute value of x as a big-endian byte array.
|
||||
// Bytes returns the absolute value of z as a big-endian byte slice.
|
||||
func (z *Int) Bytes() []byte {
|
||||
const s = _S
|
||||
b := make([]byte, len(z.abs)*s)
|
||||
|
||||
for i, w := range z.abs {
|
||||
wordBytes := b[(len(z.abs)-i-1)*s : (len(z.abs)-i)*s]
|
||||
for j := s - 1; j >= 0; j-- {
|
||||
wordBytes[j] = byte(w)
|
||||
w >>= 8
|
||||
}
|
||||
}
|
||||
|
||||
i := 0
|
||||
for i < len(b) && b[i] == 0 {
|
||||
i++
|
||||
}
|
||||
|
||||
return b[i:]
|
||||
buf := make([]byte, len(z.abs)*_S)
|
||||
return buf[z.abs.bytes(buf):]
|
||||
}
|
||||
|
||||
|
||||
@ -739,3 +697,34 @@ func (z *Int) Not(x *Int) *Int {
|
||||
z.neg = true // z cannot be zero if x is positive
|
||||
return z
|
||||
}
|
||||
|
||||
|
||||
// Gob codec version. Permits backward-compatible changes to the encoding.
|
||||
const version byte = 1
|
||||
|
||||
// GobEncode implements the gob.GobEncoder interface.
|
||||
func (z *Int) GobEncode() ([]byte, os.Error) {
|
||||
buf := make([]byte, len(z.abs)*_S+1) // extra byte for version and sign bit
|
||||
i := z.abs.bytes(buf) - 1 // i >= 0
|
||||
b := version << 1 // make space for sign bit
|
||||
if z.neg {
|
||||
b |= 1
|
||||
}
|
||||
buf[i] = b
|
||||
return buf[i:], nil
|
||||
}
|
||||
|
||||
|
||||
// GobDecode implements the gob.GobDecoder interface.
|
||||
func (z *Int) GobDecode(buf []byte) os.Error {
|
||||
if len(buf) == 0 {
|
||||
return os.NewError("Int.GobDecode: no data")
|
||||
}
|
||||
b := buf[0]
|
||||
if b>>1 != version {
|
||||
return os.NewError(fmt.Sprintf("Int.GobDecode: encoding version %d not supported", b>>1))
|
||||
}
|
||||
z.neg = b&1 != 0
|
||||
z.abs = z.abs.setBytes(buf[1:])
|
||||
return nil
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"gob"
|
||||
"testing"
|
||||
"testing/quick"
|
||||
)
|
||||
@ -1053,3 +1054,41 @@ func TestModInverse(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var gobEncodingTests = []string{
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
"10",
|
||||
"42",
|
||||
"1234567890",
|
||||
"298472983472983471903246121093472394872319615612417471234712061",
|
||||
}
|
||||
|
||||
func TestGobEncoding(t *testing.T) {
|
||||
var medium bytes.Buffer
|
||||
enc := gob.NewEncoder(&medium)
|
||||
dec := gob.NewDecoder(&medium)
|
||||
for i, test := range gobEncodingTests {
|
||||
for j := 0; j < 2; j++ {
|
||||
medium.Reset() // empty buffer for each test case (in case of failures)
|
||||
stest := test
|
||||
if j == 0 {
|
||||
stest = "-" + test
|
||||
}
|
||||
var tx Int
|
||||
tx.SetString(stest, 10)
|
||||
if err := enc.Encode(&tx); err != nil {
|
||||
t.Errorf("#%d%c: encoding failed: %s", i, 'a'+j, err)
|
||||
}
|
||||
var rx Int
|
||||
if err := dec.Decode(&rx); err != nil {
|
||||
t.Errorf("#%d%c: decoding failed: %s", i, 'a'+j, err)
|
||||
}
|
||||
if rx.Cmp(&tx) != 0 {
|
||||
t.Errorf("#%d%c: transmission failed: got %s want %s", i, 'a'+j, &rx, &tx)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1065,3 +1065,50 @@ NextRandom:
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
// bytes writes the value of z into buf using big-endian encoding.
|
||||
// len(buf) must be >= len(z)*_S. The value of z is encoded in the
|
||||
// slice buf[i:]. The number i of unused bytes at the beginning of
|
||||
// buf is returned as result.
|
||||
func (z nat) bytes(buf []byte) (i int) {
|
||||
i = len(buf)
|
||||
for _, d := range z {
|
||||
for j := 0; j < _S; j++ {
|
||||
i--
|
||||
buf[i] = byte(d)
|
||||
d >>= 8
|
||||
}
|
||||
}
|
||||
|
||||
for i < len(buf) && buf[i] == 0 {
|
||||
i++
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// setBytes interprets buf as the bytes of a big-endian unsigned
|
||||
// integer, sets z to that value, and returns z.
|
||||
func (z nat) setBytes(buf []byte) nat {
|
||||
z = z.make((len(buf) + _S - 1) / _S)
|
||||
|
||||
k := 0
|
||||
s := uint(0)
|
||||
var d Word
|
||||
for i := len(buf); i > 0; i-- {
|
||||
d |= Word(buf[i-1]) << s
|
||||
if s += 8; s == _S*8 {
|
||||
z[k] = d
|
||||
k++
|
||||
s = 0
|
||||
d = 0
|
||||
}
|
||||
}
|
||||
if k < len(z) {
|
||||
z[k] = d
|
||||
}
|
||||
|
||||
return z.norm()
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user