From 166435dd74058412f69dd5b2859219f6bd146004 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Tue, 31 May 2022 16:49:10 +1000 Subject: [PATCH] fixup! feat: introduce UIntNode interface, use it exclusively for >int64 --- .ipld | 2 +- codec/dagcbor/marshal.go | 4 +- codec/dagcbor/roundtrip_test.go | 23 ++++------- codec/dagcbor/unmarshal.go | 2 +- datamodel/node.go | 8 ++-- node/basicnode/int.go | 71 ++++++++++++++------------------- 6 files changed, 46 insertions(+), 64 deletions(-) diff --git a/.ipld b/.ipld index caedc8d7..b1723047 160000 --- a/.ipld +++ b/.ipld @@ -1 +1 @@ -Subproject commit caedc8d768e027f4722c3ac3d4f743d5ec43b5d7 +Subproject commit b172304754744c50447bb92cba39d46e266d1097 diff --git a/codec/dagcbor/marshal.go b/codec/dagcbor/marshal.go index b3ffa45a..40896fba 100644 --- a/codec/dagcbor/marshal.go +++ b/codec/dagcbor/marshal.go @@ -100,10 +100,10 @@ func marshal(n datamodel.Node, tk *tok.Token, sink shared.TokenSink, options Enc return err case datamodel.Kind_Int: var v uint64 - var positive bool + positive := true if uin, ok := n.(datamodel.UintNode); ok { var err error - v, positive, err = uin.AsUint() + v, err = uin.AsUint() if err != nil { return err } diff --git a/codec/dagcbor/roundtrip_test.go b/codec/dagcbor/roundtrip_test.go index 56a4cbb5..ef7dcf0e 100644 --- a/codec/dagcbor/roundtrip_test.go +++ b/codec/dagcbor/roundtrip_test.go @@ -135,10 +135,9 @@ func TestInts(t *testing.T) { // get real, underlying value uin, ok := n.(datamodel.UintNode) qt.Assert(t, ok, qt.IsTrue) - val, positive, err := uin.AsUint() + val, err := uin.AsUint() qt.Assert(t, err, qt.IsNil) qt.Assert(t, val, qt.Equals, uint64(math.MaxUint64)) - qt.Assert(t, positive, qt.IsTrue) var byts bytes.Buffer err = Encode(n, &byts) @@ -158,13 +157,9 @@ func TestInts(t *testing.T) { qt.Assert(t, err, qt.IsNil) qt.Assert(t, ii, qt.Equals, int64(math.MaxInt64)) - // get uint form - uin, ok := n.(datamodel.UintNode) - qt.Assert(t, ok, qt.IsTrue) - val, positive, err := uin.AsUint() - qt.Assert(t, err, qt.IsNil) - qt.Assert(t, val, qt.Equals, uint64(math.MaxInt64)) - qt.Assert(t, positive, qt.IsTrue) + // doesn't need to be a uint + _, ok := n.(datamodel.UintNode) + qt.Assert(t, ok, qt.IsFalse) }) t.Run("min int64", func(t *testing.T) { @@ -179,12 +174,8 @@ func TestInts(t *testing.T) { qt.Assert(t, err, qt.IsNil) qt.Assert(t, ii, qt.Equals, int64(math.MinInt64)) - // get uint form - uin, ok := n.(datamodel.UintNode) - qt.Assert(t, ok, qt.IsTrue) - val, positive, err := uin.AsUint() - qt.Assert(t, err, qt.IsNil) - qt.Assert(t, val, qt.Equals, uint64(math.MaxInt64)+1) - qt.Assert(t, positive, qt.IsFalse) + // doesn't need to be a uint + _, ok := n.(datamodel.UintNode) + qt.Assert(t, ok, qt.IsFalse) }) } diff --git a/codec/dagcbor/unmarshal.go b/codec/dagcbor/unmarshal.go index 56658367..40e8f3f9 100644 --- a/codec/dagcbor/unmarshal.go +++ b/codec/dagcbor/unmarshal.go @@ -277,7 +277,7 @@ func unmarshal2(na datamodel.NodeAssembler, tokSrc shared.TokenSource, tk *tok.T return ErrAllocationBudgetExceeded } if tk.Uint > uint64(math.MaxInt64) { - return na.AssignNode(basicnode.NewUInt(tk.Uint, true)) + return na.AssignNode(basicnode.NewUInt(tk.Uint)) } return na.AssignInt(int64(tk.Uint)) case tok.TFloat64: diff --git a/datamodel/node.go b/datamodel/node.go index 4d6b430f..8bfe93c8 100644 --- a/datamodel/node.go +++ b/datamodel/node.go @@ -172,10 +172,10 @@ type Node interface { type UintNode interface { Node - // AsUint returns a uint64 representing the underlying integer as well as a - // boolean to indicate whether the number is positive (true) or negative - // (false). - AsUint() (uint64, bool, error) + // AsUint returns a uint64 representing the underlying integer if possible. + // This may return an error if the Node represents a negative integer that + // cannot be represented as a uint64. + AsUint() (uint64, error) } // LargeBytesNode is an optional interface extending a Bytes node that allows its diff --git a/node/basicnode/int.go b/node/basicnode/int.go index c0b48891..bf4e242d 100644 --- a/node/basicnode/int.go +++ b/node/basicnode/int.go @@ -10,9 +10,8 @@ import ( var ( _ datamodel.Node = plainInt(0) - _ datamodel.UintNode = plainInt(0) - _ datamodel.Node = uintNode{} - _ datamodel.UintNode = uintNode{} + _ datamodel.Node = plainUint(0) + _ datamodel.UintNode = plainUint(0) _ datamodel.NodePrototype = Prototype__Int{} _ datamodel.NodeBuilder = &plainInt__Builder{} _ datamodel.NodeAssembler = &plainInt__Assembler{} @@ -22,14 +21,8 @@ func NewInt(value int64) datamodel.Node { return plainInt(value) } -func NewUInt(value uint64, positive bool) datamodel.Node { - v := uintNode{value, positive} - return &v -} - -type uintNode struct { - value uint64 - positive bool +func NewUInt(value uint64) datamodel.Node { + return plainUint(value) } // plainInt is a simple boxed int that complies with datamodel.Node. @@ -89,74 +82,72 @@ func (plainInt) Prototype() datamodel.NodePrototype { return Prototype__Int{} } -// allows plainInt to conform to the UintNode interface +// plainUint is a simple boxed uint64 that complies with datamodel.Node, +// allowing representation of the uint64 range above the int64 maximum via the +// UintNode interface +type plainUint uint64 -func (n plainInt) AsUint() (uint64, bool, error) { - sign := (n >> 63) - return uint64((n ^ sign) - sign), sign == 0, nil -} +// -- Node interface methods for plainUint --> -// -- Node interface methods for uintNode --> - -func (uintNode) Kind() datamodel.Kind { +func (plainUint) Kind() datamodel.Kind { return datamodel.Kind_Int } -func (uintNode) LookupByString(string) (datamodel.Node, error) { +func (plainUint) LookupByString(string) (datamodel.Node, error) { return mixins.Int{TypeName: "int"}.LookupByString("") } -func (uintNode) LookupByNode(key datamodel.Node) (datamodel.Node, error) { +func (plainUint) LookupByNode(key datamodel.Node) (datamodel.Node, error) { return mixins.Int{TypeName: "int"}.LookupByNode(nil) } -func (uintNode) LookupByIndex(idx int64) (datamodel.Node, error) { +func (plainUint) LookupByIndex(idx int64) (datamodel.Node, error) { return mixins.Int{TypeName: "int"}.LookupByIndex(0) } -func (uintNode) LookupBySegment(seg datamodel.PathSegment) (datamodel.Node, error) { +func (plainUint) LookupBySegment(seg datamodel.PathSegment) (datamodel.Node, error) { return mixins.Int{TypeName: "int"}.LookupBySegment(seg) } -func (uintNode) MapIterator() datamodel.MapIterator { +func (plainUint) MapIterator() datamodel.MapIterator { return nil } -func (uintNode) ListIterator() datamodel.ListIterator { +func (plainUint) ListIterator() datamodel.ListIterator { return nil } -func (uintNode) Length() int64 { +func (plainUint) Length() int64 { return -1 } -func (uintNode) IsAbsent() bool { +func (plainUint) IsAbsent() bool { return false } -func (uintNode) IsNull() bool { +func (plainUint) IsNull() bool { return false } -func (uintNode) AsBool() (bool, error) { +func (plainUint) AsBool() (bool, error) { return mixins.Int{TypeName: "int"}.AsBool() } -func (n uintNode) AsInt() (int64, error) { - if n.value > uint64(math.MaxInt64) { +func (n plainUint) AsInt() (int64, error) { + if uint64(n) > uint64(math.MaxInt64) { return -1, fmt.Errorf("unsigned integer out of range of int64 type") } - return int64(n.value), nil + return int64(n), nil } -func (uintNode) AsFloat() (float64, error) { +func (plainUint) AsFloat() (float64, error) { return mixins.Int{TypeName: "int"}.AsFloat() } -func (uintNode) AsString() (string, error) { +func (plainUint) AsString() (string, error) { return mixins.Int{TypeName: "int"}.AsString() } -func (uintNode) AsBytes() ([]byte, error) { +func (plainUint) AsBytes() ([]byte, error) { return mixins.Int{TypeName: "int"}.AsBytes() } -func (uintNode) AsLink() (datamodel.Link, error) { +func (plainUint) AsLink() (datamodel.Link, error) { return mixins.Int{TypeName: "int"}.AsLink() } -func (uintNode) Prototype() datamodel.NodePrototype { +func (plainUint) Prototype() datamodel.NodePrototype { return Prototype__Int{} } -// allows uintNode to conform to the UintNode interface +// allows plainUint to conform to the plainUint interface -func (n uintNode) AsUint() (uint64, bool, error) { - return n.value, n.positive, nil +func (n plainUint) AsUint() (uint64, error) { + return uint64(n), nil } // -- NodePrototype -->