diff --git a/src/pkg/big/int.go b/src/pkg/big/int.go index 46e0087343..ecd70e03ef 100755 --- a/src/pkg/big/int.go +++ b/src/pkg/big/int.go @@ -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 +} diff --git a/src/pkg/big/int_test.go b/src/pkg/big/int_test.go index fc981e1da4..c0cc9accf1 100755 --- a/src/pkg/big/int_test.go +++ b/src/pkg/big/int_test.go @@ -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) + } + } + } +} diff --git a/src/pkg/big/nat.go b/src/pkg/big/nat.go index a308f69e8c..a04d3b1d9c 100755 --- a/src/pkg/big/nat.go +++ b/src/pkg/big/nat.go @@ -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() +}