diff --git a/bigint.go b/bigint.go index c65ef28..fbf7acd 100644 --- a/bigint.go +++ b/bigint.go @@ -242,6 +242,20 @@ func (z *BigInt) updateInnerFromUint(val uint, neg bool) { } } +const ( + bigIntSize = unsafe.Sizeof(BigInt{}) + mathBigIntSize = unsafe.Sizeof(big.Int{}) + mathWordSize = unsafe.Sizeof(big.Word(0)) +) + +// Size returns the total memory footprint of z in bytes. +func (z *BigInt) Size() uintptr { + if z.isInline() { + return bigIntSize + } + return bigIntSize + mathBigIntSize + uintptr(cap(z._inner.Bits()))*mathWordSize +} + /////////////////////////////////////////////////////////////////////////////// // inline arithmetic for small values // /////////////////////////////////////////////////////////////////////////////// diff --git a/decimal.go b/decimal.go index 76d3699..b42ac7f 100644 --- a/decimal.go +++ b/decimal.go @@ -17,6 +17,7 @@ package apd import ( "strconv" "strings" + "unsafe" "database/sql/driver" "github.com/pkg/errors" @@ -737,6 +738,13 @@ func (d *Decimal) Reduce(x *Decimal) (*Decimal, int) { return d, nd } +const decimalSize = unsafe.Sizeof(Decimal{}) + +// Size returns the total memory footprint of d in bytes. +func (d *Decimal) Size() uintptr { + return decimalSize - bigIntSize + d.Coeff.Size() +} + // Value implements the database/sql/driver.Valuer interface. It converts d to a // string. func (d Decimal) Value() (driver.Value, error) { diff --git a/decimal_test.go b/decimal_test.go index 726e76d..29342e3 100644 --- a/decimal_test.go +++ b/decimal_test.go @@ -801,6 +801,41 @@ func TestSizeof(t *testing.T) { } } +// TestSize tests the Size method on BigInt and Decimal. Unlike Sizeof, which +// returns the shallow size of the structs, the Size method reports the total +// memory footprint of each struct and all referenced objects. +func TestSize(t *testing.T) { + var d Decimal + if e, s := uintptr(32), d.Size(); e != s { + t.Errorf("(*Decimal).Size() != %d: %d", e, s) + } + if e, s := uintptr(24), d.Coeff.Size(); e != s { + t.Errorf("(*BigInt).Size() != %d: %d", e, s) + } + // Set to an inlinable value. + d.SetInt64(1234) + if e, s := uintptr(32), d.Size(); e != s { + t.Errorf("(*Decimal).Size() != %d: %d", e, s) + } + if e, s := uintptr(24), d.Coeff.Size(); e != s { + t.Errorf("(*BigInt).Size() != %d: %d", e, s) + } + // Set to a non-inlinable value. + if _, _, err := d.SetString("123456789123456789123456789.123456789123456789"); err != nil { + t.Fatal(err) + } + if d.Coeff.isInline() { + // Sanity-check, in case inlineWords changes. + t.Fatal("BigInt inlined large value. Did inlineWords change?") + } + if e, s := uintptr(120), d.Size(); e != s { + t.Errorf("(*Decimal).Size() != %d: %d", e, s) + } + if e, s := uintptr(112), d.Coeff.Size(); e != s { + t.Errorf("(*BigInt).Size() != %d: %d", e, s) + } +} + func TestJSONEncoding(t *testing.T) { var encodingTests = []string{ "0",