]> Cypherpunks repositories - gostls13.git/commitdiff
big: implemented custom Gob(En/De)coder for Int type
authorRobert Griesemer <gri@golang.org>
Wed, 9 Mar 2011 01:27:44 +0000 (17:27 -0800)
committerRobert Griesemer <gri@golang.org>
Wed, 9 Mar 2011 01:27:44 +0000 (17:27 -0800)
- 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

src/pkg/big/int.go
src/pkg/big/int_test.go
src/pkg/big/nat.go

index 46e0087343a9a0bce422cd0a637b8e4d52efecd8..ecd70e03ef10b954c6a011d5c6408839d24e9f7b 100755 (executable)
@@ -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
+}
index fc981e1da46c4fab2ff04e0d61a3e3082f8539a4..c0cc9accf1a86103b6596d40dd35eda95b41aaad 100755 (executable)
@@ -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)
+                       }
+               }
+       }
+}
index a308f69e8cd7ea8605128f5d69f0f42bfad6f61f..a04d3b1d9c1e4cc21552d72228fac0246a98fe76 100755 (executable)
@@ -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()
+}