From 6ed1468fb5d26c5273c838fee760832bfee9da1c Mon Sep 17 00:00:00 2001 From: 13981712066 Date: Tue, 23 Nov 2021 16:40:57 +0800 Subject: [PATCH 1/6] Add MsgPack Marshal and Unmarshal function Mote: Marshal max digits is 30. it decimal.IntPart large than 30,it will be lose extra digits. --- decimal_msgpack.go | 92 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 decimal_msgpack.go diff --git a/decimal_msgpack.go b/decimal_msgpack.go new file mode 100644 index 0000000..f0647ae --- /dev/null +++ b/decimal_msgpack.go @@ -0,0 +1,92 @@ +package decimal + +import ( + "errors" +) + +var ( + errShortBytes = errors.New("msgp: too few bytes left to read object") +) + +// MarshalMsg implements msgp.Marshaler +func (d Decimal) MarshalMsg(b []byte) (o []byte, err error) { + o = require(b, d.Msgsize()) + str := d.String() + sz := len(str) + // limit to 30 digits + // note, if d.IntPart size large than 30, will be lose. + if sz > 30 { + sz = 30 + // if last char is '.' then limit to 20 digits + if str[29] == '.' { + sz = 29 + } + + str = str[:sz] + } + + o, n := ensure(b, 1+sz) + o[n] = byte(0xa0 | sz) + n++ + + return o[:n+copy(o[n:], str)], nil +} + +// UnmarshalMsg implements msgp.Unmarshaler +func (d *Decimal) UnmarshalMsg(b []byte) (o []byte, err error) { + l := len(b) + if l < 1 { + return nil, errShortBytes + } + + sz := int(b[0] & 0x1f) + if len(b[1:]) < sz { + err = errShortBytes + return + } + if *d, err = NewFromString(string(b[1 : sz+1])); err == nil { + o = b[sz:] + } + return +} + +// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message +func (d Decimal) Msgsize() int { + return 31 +} + +// Require ensures that cap(old)-len(old) >= extra. +func require(old []byte, extra int) []byte { + l := len(old) + c := cap(old) + r := l + extra + if c >= r { + return old + } else if l == 0 { + return make([]byte, 0, extra) + } + // the new size is the greater + // of double the old capacity + // and the sum of the old length + // and the number of new bytes + // necessary. + c <<= 1 + if c < r { + c = r + } + n := make([]byte, l, c) + copy(n, old) + return n +} + +// ensure 'sz' extra bytes in 'b' btw len(b) and cap(b) +func ensure(b []byte, sz int) ([]byte, int) { + l := len(b) + c := cap(b) + if c-l < sz { + o := make([]byte, (2*c)+sz) // exponential growth + n := copy(o, b) + return o[:n+sz], n + } + return b[:l+sz], l +} From 879e52d70a0d7b72c7131dc55fd5502fe8478b38 Mon Sep 17 00:00:00 2001 From: 13981712066 Date: Mon, 6 Dec 2021 09:09:53 +0800 Subject: [PATCH 2/6] Add MsgPack Marshal and Unmarshal Test Case --- decimal_magpack_test.go | 55 +++++++++++++++++++++++++++++++++++++++++ decimal_msgpack.go | 15 +++++------ 2 files changed, 63 insertions(+), 7 deletions(-) create mode 100644 decimal_magpack_test.go diff --git a/decimal_magpack_test.go b/decimal_magpack_test.go new file mode 100644 index 0000000..41d9dd2 --- /dev/null +++ b/decimal_magpack_test.go @@ -0,0 +1,55 @@ +package decimal + +import ( + "testing" +) + +func TestMsgPack(t *testing.T) { + for _, x := range testTable { + s := x.short + // limit to 31 digits + if len(s) > 31 { + s = s[:31] + if s[30] == '.' { + s = s[:30] + } + } + + // Prepare Test Decimal Data + amount, err := NewFromString(s) + if err != nil{ + t.Error(err) + } + s = amount.String() + + // MarshalMsg + var b []byte + out, err := amount.MarshalMsg(b) + if err != nil{ + t.Errorf("error marshalMsg %s: %v", s, err) + } + + // check msg type + typ := out[0] & 0xe0 + if typ != 0xa0 { + t.Errorf("error marshalMsg, expected type = %b, got %b", 0xa0, typ) + } + + // check msg len + sz := int(out[0] & 0x1f) + if sz != len(s) { + t.Errorf("error marshalMsg, expected size = %d, got %d", len(s), sz) + } + + // UnmarshalMsg + var unmarshalAmount Decimal + _, err = unmarshalAmount.UnmarshalMsg(out) + if err != nil{ + t.Errorf("error unmarshalMsg %s: %v", s, err) + }else if !unmarshalAmount.Equal(amount) { + t.Errorf("expected %s, got %s (%s, %d)", + amount.String(), unmarshalAmount.String(), + unmarshalAmount.value.String(), unmarshalAmount.exp) + } + } +} \ No newline at end of file diff --git a/decimal_msgpack.go b/decimal_msgpack.go index f0647ae..03a335d 100644 --- a/decimal_msgpack.go +++ b/decimal_msgpack.go @@ -9,17 +9,18 @@ var ( ) // MarshalMsg implements msgp.Marshaler +// Note: limit to 31 digits, if d.IntPart size large than 31, will be lose. func (d Decimal) MarshalMsg(b []byte) (o []byte, err error) { o = require(b, d.Msgsize()) str := d.String() sz := len(str) - // limit to 30 digits - // note, if d.IntPart size large than 30, will be lose. - if sz > 30 { - sz = 30 - // if last char is '.' then limit to 20 digits - if str[29] == '.' { - sz = 29 + // limit to 31 digits + // note, if d.IntPart size large than 31, will be lose. + if sz > 31 { + sz = 31 + // if last char is '.' then limit to 30 digits + if str[30] == '.' { + sz = 30 } str = str[:sz] From 4526b4a8bd8435429854d1a12c2d24c7f2015eb5 Mon Sep 17 00:00:00 2001 From: 13981712066 Date: Mon, 6 Dec 2021 09:11:14 +0800 Subject: [PATCH 3/6] Add Test Case for MsgPack Marshal and Unmarshal --- decimal_magpack_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/decimal_magpack_test.go b/decimal_magpack_test.go index 41d9dd2..59bc549 100644 --- a/decimal_magpack_test.go +++ b/decimal_magpack_test.go @@ -20,7 +20,6 @@ func TestMsgPack(t *testing.T) { if err != nil{ t.Error(err) } - s = amount.String() // MarshalMsg var b []byte From 5f79fa610c17748ed1669dc99ed6a3fbea1ebfd4 Mon Sep 17 00:00:00 2001 From: 13981712066 Date: Mon, 6 Dec 2021 09:22:32 +0800 Subject: [PATCH 4/6] Change MsgPack MarshalMsg and UnmarshalMsg function's return value to unnamed. --- decimal_magpack_test.go | 1 + decimal_msgpack.go | 15 ++++++++------- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/decimal_magpack_test.go b/decimal_magpack_test.go index 59bc549..41d9dd2 100644 --- a/decimal_magpack_test.go +++ b/decimal_magpack_test.go @@ -20,6 +20,7 @@ func TestMsgPack(t *testing.T) { if err != nil{ t.Error(err) } + s = amount.String() // MarshalMsg var b []byte diff --git a/decimal_msgpack.go b/decimal_msgpack.go index 03a335d..c13d598 100644 --- a/decimal_msgpack.go +++ b/decimal_msgpack.go @@ -10,12 +10,12 @@ var ( // MarshalMsg implements msgp.Marshaler // Note: limit to 31 digits, if d.IntPart size large than 31, will be lose. -func (d Decimal) MarshalMsg(b []byte) (o []byte, err error) { - o = require(b, d.Msgsize()) +func (d Decimal) MarshalMsg(b []byte) ([]byte, error) { + o := require(b, d.Msgsize()) str := d.String() sz := len(str) // limit to 31 digits - // note, if d.IntPart size large than 31, will be lose. + // note, if d.IntPart size large than 3, will be lose. if sz > 31 { sz = 31 // if last char is '.' then limit to 30 digits @@ -34,7 +34,9 @@ func (d Decimal) MarshalMsg(b []byte) (o []byte, err error) { } // UnmarshalMsg implements msgp.Unmarshaler -func (d *Decimal) UnmarshalMsg(b []byte) (o []byte, err error) { +func (d *Decimal) UnmarshalMsg(b []byte) ([]byte, error) { + o, err := b, errShortBytes + l := len(b) if l < 1 { return nil, errShortBytes @@ -42,13 +44,12 @@ func (d *Decimal) UnmarshalMsg(b []byte) (o []byte, err error) { sz := int(b[0] & 0x1f) if len(b[1:]) < sz { - err = errShortBytes - return + return nil, errShortBytes } if *d, err = NewFromString(string(b[1 : sz+1])); err == nil { o = b[sz:] } - return + return o, err } // Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message From c7eaaded9cb88a18f9b497735095fa42539f9f39 Mon Sep 17 00:00:00 2001 From: 13981712066 Date: Mon, 6 Dec 2021 09:29:46 +0800 Subject: [PATCH 5/6] fix UnmarshalMsg return value. Returns the origanal data if the error occurs. --- decimal_msgpack.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/decimal_msgpack.go b/decimal_msgpack.go index c13d598..2e205f4 100644 --- a/decimal_msgpack.go +++ b/decimal_msgpack.go @@ -39,12 +39,12 @@ func (d *Decimal) UnmarshalMsg(b []byte) ([]byte, error) { l := len(b) if l < 1 { - return nil, errShortBytes + return o, err } sz := int(b[0] & 0x1f) if len(b[1:]) < sz { - return nil, errShortBytes + return o, err } if *d, err = NewFromString(string(b[1 : sz+1])); err == nil { o = b[sz:] From 6e8c4f56debad81b395dc7b0a87060835c6751ad Mon Sep 17 00:00:00 2001 From: 13981712066 Date: Mon, 6 Dec 2021 11:35:43 +0800 Subject: [PATCH 6/6] Optimize the MarshalMsgand allocate the memory only once. --- decimal_magpack_test.go | 20 ++++++++++---------- decimal_msgpack.go | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/decimal_magpack_test.go b/decimal_magpack_test.go index 41d9dd2..bc865a4 100644 --- a/decimal_magpack_test.go +++ b/decimal_magpack_test.go @@ -7,7 +7,7 @@ import ( func TestMsgPack(t *testing.T) { for _, x := range testTable { s := x.short - // limit to 31 digits + // limit to 31 digits if len(s) > 31 { s = s[:31] if s[30] == '.' { @@ -17,15 +17,15 @@ func TestMsgPack(t *testing.T) { // Prepare Test Decimal Data amount, err := NewFromString(s) - if err != nil{ + if err != nil { t.Error(err) } s = amount.String() // MarshalMsg var b []byte - out, err := amount.MarshalMsg(b) - if err != nil{ + out, err := amount.MarshalMsg(b) + if err != nil { t.Errorf("error marshalMsg %s: %v", s, err) } @@ -44,12 +44,12 @@ func TestMsgPack(t *testing.T) { // UnmarshalMsg var unmarshalAmount Decimal _, err = unmarshalAmount.UnmarshalMsg(out) - if err != nil{ + if err != nil { t.Errorf("error unmarshalMsg %s: %v", s, err) - }else if !unmarshalAmount.Equal(amount) { - t.Errorf("expected %s, got %s (%s, %d)", - amount.String(), unmarshalAmount.String(), - unmarshalAmount.value.String(), unmarshalAmount.exp) + } else if !unmarshalAmount.Equal(amount) { + t.Errorf("expected %s, got %s (%s, %d)", + amount.String(), unmarshalAmount.String(), + unmarshalAmount.value.String(), unmarshalAmount.exp) } } -} \ No newline at end of file +} diff --git a/decimal_msgpack.go b/decimal_msgpack.go index 2e205f4..55d22d2 100644 --- a/decimal_msgpack.go +++ b/decimal_msgpack.go @@ -26,7 +26,7 @@ func (d Decimal) MarshalMsg(b []byte) ([]byte, error) { str = str[:sz] } - o, n := ensure(b, 1+sz) + o, n := ensure(o, 1+sz) o[n] = byte(0xa0 | sz) n++ @@ -54,7 +54,7 @@ func (d *Decimal) UnmarshalMsg(b []byte) ([]byte, error) { // Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message func (d Decimal) Msgsize() int { - return 31 + return 32 } // Require ensures that cap(old)-len(old) >= extra.