Skip to content

Commit

Permalink
fixup! feat: introduce UIntNode interface, use it exclusively for >int64
Browse files Browse the repository at this point in the history
  • Loading branch information
rvagg committed May 31, 2022
1 parent 6c64f2d commit 166435d
Show file tree
Hide file tree
Showing 6 changed files with 46 additions and 64 deletions.
4 changes: 2 additions & 2 deletions codec/dagcbor/marshal.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
23 changes: 7 additions & 16 deletions codec/dagcbor/roundtrip_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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) {
Expand All @@ -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)
})
}
2 changes: 1 addition & 1 deletion codec/dagcbor/unmarshal.go
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
8 changes: 4 additions & 4 deletions datamodel/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
71 changes: 31 additions & 40 deletions node/basicnode/int.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{}
Expand All @@ -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.
Expand Down Expand Up @@ -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 -->
Expand Down

0 comments on commit 166435d

Please sign in to comment.