Commit 758d0555 authored by Robert Griesemer's avatar Robert Griesemer

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
parent 8138654a
...@@ -8,6 +8,7 @@ package big ...@@ -8,6 +8,7 @@ package big
import ( import (
"fmt" "fmt"
"os"
"rand" "rand"
) )
...@@ -393,62 +394,19 @@ func (z *Int) SetString(s string, base int) (*Int, bool) { ...@@ -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 // SetBytes interprets buf as the bytes of a big-endian unsigned
// sets z to that value. // integer, sets z to that value, and returns z.
func (z *Int) SetBytes(b []byte) *Int { func (z *Int) SetBytes(buf []byte) *Int {
const s = _S z.abs = z.abs.setBytes(buf)
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()
z.neg = false z.neg = false
return z 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 { func (z *Int) Bytes() []byte {
const s = _S buf := make([]byte, len(z.abs)*_S)
b := make([]byte, len(z.abs)*s) return buf[z.abs.bytes(buf):]
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:]
} }
...@@ -739,3 +697,34 @@ func (z *Int) Not(x *Int) *Int { ...@@ -739,3 +697,34 @@ func (z *Int) Not(x *Int) *Int {
z.neg = true // z cannot be zero if x is positive z.neg = true // z cannot be zero if x is positive
return z 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 ( ...@@ -8,6 +8,7 @@ import (
"bytes" "bytes"
"encoding/hex" "encoding/hex"
"fmt" "fmt"
"gob"
"testing" "testing"
"testing/quick" "testing/quick"
) )
...@@ -1053,3 +1054,41 @@ func TestModInverse(t *testing.T) { ...@@ -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: ...@@ -1065,3 +1065,50 @@ NextRandom:
return true 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()
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment