From 21032ebebd164aaf2c128304ff116859096449d4 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Wed, 8 Jun 2011 09:10:01 -0700 Subject: [PATCH] big: gobs for big rats Fixes #1926. R=r CC=golang-dev https://golang.org/cl/4550122 --- src/pkg/big/int.go | 8 ++++---- src/pkg/big/int_test.go | 6 ++++-- src/pkg/big/rat.go | 43 +++++++++++++++++++++++++++++++++++++++++ src/pkg/big/rat_test.go | 34 ++++++++++++++++++++++++++++++++ 4 files changed, 85 insertions(+), 6 deletions(-) diff --git a/src/pkg/big/int.go b/src/pkg/big/int.go index e66c34a836..22bdf8d2f8 100755 --- a/src/pkg/big/int.go +++ b/src/pkg/big/int.go @@ -816,13 +816,13 @@ func (z *Int) Not(x *Int) *Int { // Gob codec version. Permits backward-compatible changes to the encoding. -const version byte = 1 +const intGobVersion 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 + buf := make([]byte, 1+len(z.abs)*_S) // extra byte for version and sign bit i := z.abs.bytes(buf) - 1 // i >= 0 - b := version << 1 // make space for sign bit + b := intGobVersion << 1 // make space for sign bit if z.neg { b |= 1 } @@ -837,7 +837,7 @@ func (z *Int) GobDecode(buf []byte) os.Error { return os.ErrorString("Int.GobDecode: no data") } b := buf[0] - if b>>1 != version { + if b>>1 != intGobVersion { return os.ErrorString(fmt.Sprintf("Int.GobDecode: encoding version %d not supported", b>>1)) } z.neg = b&1 != 0 diff --git a/src/pkg/big/int_test.go b/src/pkg/big/int_test.go index 1a492925b8..58a55030d5 100755 --- a/src/pkg/big/int_test.go +++ b/src/pkg/big/int_test.go @@ -1303,6 +1303,7 @@ func TestModInverse(t *testing.T) { } +// used by TestIntGobEncoding and TestRatGobEncoding var gobEncodingTests = []string{ "0", "1", @@ -1313,7 +1314,7 @@ var gobEncodingTests = []string{ "298472983472983471903246121093472394872319615612417471234712061", } -func TestGobEncoding(t *testing.T) { +func TestIntGobEncoding(t *testing.T) { var medium bytes.Buffer enc := gob.NewEncoder(&medium) dec := gob.NewDecoder(&medium) @@ -1321,7 +1322,8 @@ func TestGobEncoding(t *testing.T) { for j := 0; j < 2; j++ { medium.Reset() // empty buffer for each test case (in case of failures) stest := test - if j == 0 { + if j != 0 { + // negative numbers stest = "-" + test } var tx Int diff --git a/src/pkg/big/rat.go b/src/pkg/big/rat.go index b2e93f2a45..1fbf8c4596 100644 --- a/src/pkg/big/rat.go +++ b/src/pkg/big/rat.go @@ -7,6 +7,7 @@ package big import ( + "encoding/binary" "fmt" "os" "strings" @@ -354,3 +355,45 @@ func (z *Rat) FloatString(prec int) string { return s } + + +// Gob codec version. Permits backward-compatible changes to the encoding. +const ratGobVersion byte = 1 + +// GobEncode implements the gob.GobEncoder interface. +func (z *Rat) GobEncode() ([]byte, os.Error) { + buf := make([]byte, 1+4+(len(z.a.abs)+len(z.b))*_S) // extra bytes for version and sign bit (1), and numerator length (4) + i := z.b.bytes(buf) + j := z.a.abs.bytes(buf[0:i]) + n := i - j + if int(uint32(n)) != n { + // this should never happen + return nil, os.ErrorString("Rat.GobEncode: numerator too large") + } + binary.BigEndian.PutUint32(buf[j-4:j], uint32(n)) + j -= 1 + 4 + b := ratGobVersion << 1 // make space for sign bit + if z.a.neg { + b |= 1 + } + buf[j] = b + return buf[j:], nil +} + + +// GobDecode implements the gob.GobDecoder interface. +func (z *Rat) GobDecode(buf []byte) os.Error { + if len(buf) == 0 { + return os.ErrorString("Rat.GobDecode: no data") + } + b := buf[0] + if b>>1 != ratGobVersion { + return os.ErrorString(fmt.Sprintf("Rat.GobDecode: encoding version %d not supported", b>>1)) + } + const j = 1 + 4 + i := j + binary.BigEndian.Uint32(buf[j-4:j]) + z.a.neg = b&1 != 0 + z.a.abs = z.a.abs.setBytes(buf[j:i]) + z.b = z.b.setBytes(buf[i:]) + return nil +} diff --git a/src/pkg/big/rat_test.go b/src/pkg/big/rat_test.go index 4effbf8eac..e64505ea3c 100644 --- a/src/pkg/big/rat_test.go +++ b/src/pkg/big/rat_test.go @@ -7,6 +7,7 @@ package big import ( "bytes" "fmt" + "gob" "testing" ) @@ -308,3 +309,36 @@ func TestRatSetFrac64Rat(t *testing.T) { } } } + + +func TestRatGobEncoding(t *testing.T) { + var medium bytes.Buffer + enc := gob.NewEncoder(&medium) + dec := gob.NewDecoder(&medium) + for i, test := range gobEncodingTests { + for j := 0; j < 4; j++ { + medium.Reset() // empty buffer for each test case (in case of failures) + stest := test + if j&1 != 0 { + // negative numbers + stest = "-" + test + } + if j%2 != 0 { + // fractions + stest = stest + "." + test + } + var tx Rat + tx.SetString(stest) + if err := enc.Encode(&tx); err != nil { + t.Errorf("#%d%c: encoding failed: %s", i, 'a'+j, err) + } + var rx Rat + 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) + } + } + } +}