Commit 070609a7 authored by Eric Myhre's avatar Eric Myhre

Merge branch 'iterator-refactor'

parents 6c3e2490 b84e99cd
...@@ -31,18 +31,17 @@ func Marshal(n ipld.Node, sink shared.TokenSink) error { ...@@ -31,18 +31,17 @@ func Marshal(n ipld.Node, sink shared.TokenSink) error {
return err return err
} }
// Emit map contents (and recurse). // Emit map contents (and recurse).
for itr := n.Keys(); itr.HasNext(); { for itr := n.MapIterator(); !itr.Done(); {
k, err := itr.Next() k, v, err := itr.Next()
if err != nil { if err != nil {
return err return err
} }
tk.Type = tok.TString tk.Type = tok.TString
tk.Str = k tk.Str, err = k.AsString()
if _, err := sink.Step(&tk); err != nil { if err != nil {
return err return err
} }
v, err := n.TraverseField(k) if _, err := sink.Step(&tk); err != nil {
if err != nil {
return err return err
} }
if err := Marshal(v, sink); err != nil { if err := Marshal(v, sink); err != nil {
......
...@@ -31,18 +31,17 @@ func Marshal(n ipld.Node, sink shared.TokenSink) error { ...@@ -31,18 +31,17 @@ func Marshal(n ipld.Node, sink shared.TokenSink) error {
return err return err
} }
// Emit map contents (and recurse). // Emit map contents (and recurse).
for itr := n.Keys(); itr.HasNext(); { for itr := n.MapIterator(); !itr.Done(); {
k, err := itr.Next() k, v, err := itr.Next()
if err != nil { if err != nil {
return err return err
} }
tk.Type = tok.TString tk.Type = tok.TString
tk.Str = k tk.Str, err = k.AsString()
if _, err := sink.Step(&tk); err != nil { if err != nil {
return err return err
} }
v, err := n.TraverseField(k) if _, err := sink.Step(&tk); err != nil {
if err != nil {
return err return err
} }
if err := Marshal(v, sink); err != nil { if err := Marshal(v, sink); err != nil {
......
...@@ -36,18 +36,17 @@ func Marshal(n ipld.Node, sink shared.TokenSink) error { ...@@ -36,18 +36,17 @@ func Marshal(n ipld.Node, sink shared.TokenSink) error {
return err return err
} }
// Emit map contents (and recurse). // Emit map contents (and recurse).
for itr := n.Keys(); itr.HasNext(); { for itr := n.MapIterator(); !itr.Done(); {
k, err := itr.Next() k, v, err := itr.Next()
if err != nil { if err != nil {
return err return err
} }
tk.Type = tok.TString tk.Type = tok.TString
tk.Str = k tk.Str, err = k.AsString()
if _, err := sink.Step(&tk); err != nil { if err != nil {
return err return err
} }
v, err := n.TraverseField(k) if _, err := sink.Step(&tk); err != nil {
if err != nil {
return err return err
} }
if err := Marshal(v, sink); err != nil { if err := Marshal(v, sink); err != nil {
......
...@@ -17,8 +17,8 @@ type Node interface { ...@@ -17,8 +17,8 @@ type Node interface {
ReprKind() ipld.ReprKind ReprKind() ipld.ReprKind
TraverseField(path string) Node TraverseField(path string) Node
TraverseIndex(idx int) Node TraverseIndex(idx int) Node
Keys() KeyIterator MapIterator() MapIterator
KeysImmediate() []string ListIterator() ListIterator
Length() int Length() int
IsNull() bool IsNull() bool
AsBool() bool AsBool() bool
...@@ -76,21 +76,17 @@ func (n node) TraverseIndex(idx int) Node { ...@@ -76,21 +76,17 @@ func (n node) TraverseIndex(idx int) Node {
} }
return node{v, nil} return node{v, nil}
} }
func (n node) Keys() KeyIterator { func (n node) MapIterator() MapIterator {
if n.err != nil { if n.err != nil {
panic(Error{n.err}) panic(Error{n.err})
} }
return &keyIterator{n.n.Keys()} return &mapIterator{n.n.MapIterator()}
} }
func (n node) KeysImmediate() []string { func (n node) ListIterator() ListIterator {
if n.err != nil { if n.err != nil {
panic(Error{n.err}) panic(Error{n.err})
} }
v, err := n.n.KeysImmediate() return &listIterator{n.n.ListIterator()}
if err != nil {
panic(Error{err})
}
return v
} }
func (n node) Length() int { func (n node) Length() int {
if n.err != nil { if n.err != nil {
...@@ -165,22 +161,42 @@ func (n node) AsLink() ipld.Link { ...@@ -165,22 +161,42 @@ func (n node) AsLink() ipld.Link {
return v return v
} }
type KeyIterator interface { type MapIterator interface {
Next() string Next() (key Node, value Node)
HasNext() bool Done() bool
} }
type keyIterator struct { type mapIterator struct {
d ipld.KeyIterator d ipld.MapIterator
} }
func (ki *keyIterator) Next() string { func (itr *mapIterator) Next() (Node, Node) {
v, err := ki.d.Next() k, v, err := itr.d.Next()
if err != nil { if err != nil {
panic(Error{err}) panic(Error{err})
} }
return v return node{k, nil}, node{v, nil}
}
func (itr *mapIterator) Done() bool {
return itr.d.Done()
}
type ListIterator interface {
Next() (idx int, value Node)
Done() bool
}
type listIterator struct {
d ipld.ListIterator
}
func (itr *listIterator) Next() (int, Node) {
idx, v, err := itr.d.Next()
if err != nil {
panic(Error{err})
}
return idx, node{v, nil}
} }
func (ki *keyIterator) HasNext() bool { func (itr *listIterator) Done() bool {
return ki.d.HasNext() return itr.d.Done()
} }
package fluent
// AllKeyStrings is a shorthand to iterate a map node and collect all the keys
// (and convert them to strings), returning them in a slice.
func AllKeyStrings(n Node) []string {
itr := n.MapIterator()
res := make([]string, n.Length())
for i := 0; !itr.Done(); i++ {
k, _ := itr.Next()
res[i] = k.AsString()
}
return res
}
...@@ -107,11 +107,11 @@ func (n Node) NodeBuilder() ipld.NodeBuilder { ...@@ -107,11 +107,11 @@ func (n Node) NodeBuilder() ipld.NodeBuilder {
panic("NYI") panic("NYI")
} }
func (n Node) Keys() ipld.KeyIterator { func (n Node) MapIterator() ipld.MapIterator {
panic("NYI") panic("NYI")
} }
func (n Node) KeysImmediate() ([]string, error) { func (n Node) ListIterator() ipld.ListIterator {
panic("NYI") panic("NYI")
} }
......
...@@ -64,27 +64,56 @@ func (n *Node) NodeBuilder() ipld.NodeBuilder { ...@@ -64,27 +64,56 @@ func (n *Node) NodeBuilder() ipld.NodeBuilder {
return nodeBuilder{n} return nodeBuilder{n}
} }
func (n *Node) Keys() ipld.KeyIterator { func (n *Node) MapIterator() ipld.MapIterator {
return &keyIterator{n, 0} return &mapIterator{n, 0, expectTyp(ipld.ReprKind_Map, n.kind)}
} }
type keyIterator struct { type mapIterator struct {
node *Node node *Node
idx int idx int
err error
} }
func (ki *keyIterator) Next() (string, error) { func (itr *mapIterator) Next() (ipld.Node, ipld.Node, error) {
// TODO kind check and safer range handling. if itr.err != nil {
v := ki.node._mapOrd[ki.idx] return nil, nil, itr.err
ki.idx++ }
return v, nil k := itr.node._mapOrd[itr.idx]
v := itr.node._map[k]
itr.idx++
return &Node{kind: ipld.ReprKind_String, _str: k}, v, nil
}
func (itr *mapIterator) Done() bool {
if itr.err != nil {
return false
}
return itr.idx >= len(itr.node._mapOrd)
}
func (n *Node) ListIterator() ipld.ListIterator {
return &listIterator{n, 0, expectTyp(ipld.ReprKind_List, n.kind)}
} }
func (ki *keyIterator) HasNext() bool {
return len(ki.node._mapOrd) > ki.idx type listIterator struct {
node *Node
idx int
err error
} }
func (n *Node) KeysImmediate() ([]string, error) { func (itr *listIterator) Next() (int, ipld.Node, error) {
return n._mapOrd, expectTyp(ipld.ReprKind_Map, n.kind) if itr.err != nil {
return -1, nil, itr.err
}
v := itr.node._arr[itr.idx]
idx := itr.idx
itr.idx++
return idx, v, nil
}
func (itr *listIterator) Done() bool {
if itr.err != nil {
return false
}
return itr.idx >= len(itr.node._arr)
} }
func (n *Node) Length() int { func (n *Node) Length() int {
......
...@@ -27,20 +27,25 @@ type Node interface { ...@@ -27,20 +27,25 @@ type Node interface {
// If idx is out of range, a nil node and an error will be returned. // If idx is out of range, a nil node and an error will be returned.
TraverseIndex(idx int) (Node, error) TraverseIndex(idx int) (Node, error)
// Keys returns an iterator which will yield keys for traversing the node. // MapIterator returns an iterator which yields key-value pairs
// traversing the node.
// If the node kind is anything other than a map, the iterator will // If the node kind is anything other than a map, the iterator will
// yield error values. // yield error values.
Keys() KeyIterator //
// The iterator will yield every entry in the map; that is, it
// can be expected that itr.Next will be called node.Length times
// before itr.Done becomes true.
MapIterator() MapIterator
// KeysImmediate returns a slice containing all keys for traversing the node. // ListIterator returns an iterator which yields key-value pairs
// The semantics are otherwise identical to using the Keys() iterator. // traversing the node.
// If the node kind is anything other than a list, the iterator will
// yield error values.
// //
// KeysImmediate is for convenience of usage; callers should prefer to use // The iterator will yield every entry in the list; that is, it
// the iterator approach where possible, as it continues to behave well // can be expected that itr.Next will be called node.Length times
// even when using collections of extremely large size (and even when // before itr.Done becomes true.
// the collection is split between multiple serial nodes, as with ListIterator() ListIterator
// Advanced Layouts, etc).
KeysImmediate() ([]string, error)
// Length returns the length of a list, or the number of entries in a map, // Length returns the length of a list, or the number of entries in a map,
// or -1 if the node is not of list nor map kind. // or -1 if the node is not of list nor map kind.
...@@ -79,21 +84,64 @@ type Node interface { ...@@ -79,21 +84,64 @@ type Node interface {
NodeBuilder() NodeBuilder NodeBuilder() NodeBuilder
} }
// KeyIterator is an interface for traversing nodes of kind map. // MapIterator is an interface for traversing map nodes.
// Sequential calls to Next() will yield keys; HasNext() describes whether // Sequential calls to Next() will yield key-value pairs;
// iteration should continue. // Done() describes whether iteration should continue.
// //
// Iteration order is defined to be stable. // Iteration order is defined to be stable: two separate MapIterator
// created to iterate the same Node will yield the same key-value pairs
// in the same order.
// The order itself may be defined by the Node implementation: some
// Nodes may retain insertion order, and some may return iterators which
// always yield data in sorted order, for example.
type MapIterator interface {
// Next returns the next key-value pair.
//
// An error value can also be returned at any step: in the case of advanced
// data structures with incremental loading, it's possible to encounter
// cancellation or I/O errors at any point in iteration.
// If an error is returned, the boolean will always be false (so it's
// correct to check the bool first and short circuit to continuing if true).
// If an error is returned, the key and value may be nil.
Next() (key Node, value Node, err error)
// Done returns false as long as there's at least one more entry to iterate.
// When Done returns false, iteration can stop.
//
// Implementers of iterators for advanced data layouts (e.g. more than
// one chunk of backing data, which is loaded incrementally), if your
// implementation does any I/O during the Done method, and it encounters
// an error, it must return 'true', so that the following Next call
// has an opportunity to return the error.
Done() bool
}
// ListIterator is an interface for traversing list nodes.
// Sequential calls to Next() will yield index-value pairs;
// Done() describes whether iteration should continue.
// //
// REVIEW: should Next return error? // A loop which iterates from 0 to Node.Length is a valid
// Other parts of the Node interface use that for kind mismatch rejection; // alternative to using a ListIterator.
// so on those grounds, I'd say "no", because we know what the key kind is type ListIterator interface {
// (but then Node.Keys should return error). // Next returns the next index and value.
// In big nodes (composites using an AdvLayout), where do we return errors? //
// Since we might be streaming, there are questions here. // An error value can also be returned at any step: in the case of advanced
type KeyIterator interface { // data structures with incremental loading, it's possible to encounter
Next() (string, error) // cancellation or I/O errors at any point in iteration.
HasNext() bool // If an error is returned, the boolean will always be false (so it's
// correct to check the bool first and short circuit to continuing if true).
// If an error is returned, the key and value may be nil.
Next() (idx int, value Node, err error)
// Done returns false as long as there's at least one more entry to iterate.
// When Done returns false, iteration can stop.
//
// Implementers of iterators for advanced data layouts (e.g. more than
// one chunk of backing data, which is loaded incrementally), if your
// implementation does any I/O during the Done method, and it encounters
// an error, it must return 'true', so that the following Next call
// has an opportunity to return the error.
Done() bool
} }
// REVIEW: immediate-mode AsBytes() method (as opposed to e.g. returning // REVIEW: immediate-mode AsBytes() method (as opposed to e.g. returning
......
...@@ -101,7 +101,7 @@ func TestRecursiveUnmarshal(t *testing.T, nb ipld.NodeBuilder) { ...@@ -101,7 +101,7 @@ func TestRecursiveUnmarshal(t *testing.T, nb ipld.NodeBuilder) {
Require(t, err, ShouldEqual, nil) Require(t, err, ShouldEqual, nil)
Require(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_Map) Require(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_Map)
Require(t, n.Length(), ShouldEqual, 1) Require(t, n.Length(), ShouldEqual, 1)
Require(t, fluent.WrapNode(n).KeysImmediate(), ShouldEqual, []string{"asdf"}) Require(t, fluent.AllKeyStrings(fluent.WrapNode(n)), ShouldEqual, []string{"asdf"})
Wish(t, fluent.WrapNode(n).TraverseField("asdf").ReprKind(), ShouldEqual, ipld.ReprKind_String) Wish(t, fluent.WrapNode(n).TraverseField("asdf").ReprKind(), ShouldEqual, ipld.ReprKind_String)
Wish(t, fluent.WrapNode(n).TraverseField("asdf").AsString(), ShouldEqual, "zomzom") Wish(t, fluent.WrapNode(n).TraverseField("asdf").AsString(), ShouldEqual, "zomzom")
Wish(t, tb.read, ShouldEqual, 4) Wish(t, tb.read, ShouldEqual, 4)
...@@ -122,7 +122,7 @@ func TestRecursiveUnmarshal(t *testing.T, nb ipld.NodeBuilder) { ...@@ -122,7 +122,7 @@ func TestRecursiveUnmarshal(t *testing.T, nb ipld.NodeBuilder) {
Require(t, err, ShouldEqual, nil) Require(t, err, ShouldEqual, nil)
Require(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_Map) Require(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_Map)
Require(t, n.Length(), ShouldEqual, 2) Require(t, n.Length(), ShouldEqual, 2)
Require(t, fluent.WrapNode(n).KeysImmediate(), ShouldEqual, []string{"asdf", "zyzzy"}) Require(t, fluent.AllKeyStrings(fluent.WrapNode(n)), ShouldEqual, []string{"asdf", "zyzzy"})
Wish(t, fluent.WrapNode(n).TraverseField("asdf").ReprKind(), ShouldEqual, ipld.ReprKind_Map) Wish(t, fluent.WrapNode(n).TraverseField("asdf").ReprKind(), ShouldEqual, ipld.ReprKind_Map)
Wish(t, fluent.WrapNode(n).TraverseField("asdf").TraverseField("awoo").AsString(), ShouldEqual, "gah") Wish(t, fluent.WrapNode(n).TraverseField("asdf").TraverseField("awoo").AsString(), ShouldEqual, "gah")
Wish(t, fluent.WrapNode(n).TraverseField("zyzzy").AsInt(), ShouldEqual, 9) Wish(t, fluent.WrapNode(n).TraverseField("zyzzy").AsInt(), ShouldEqual, 9)
......
...@@ -3,7 +3,7 @@ package selector ...@@ -3,7 +3,7 @@ package selector
import ipld "github.com/ipld/go-ipld-prime" import ipld "github.com/ipld/go-ipld-prime"
type Selector interface { type Selector interface {
Explore(ipld.Node) (ipld.KeyIterator, Selector) Explore(ipld.Node) (ipld.MapIterator, ipld.ListIterator, Selector)
Decide(ipld.Node) bool Decide(ipld.Node) bool
} }
......
...@@ -43,17 +43,20 @@ func validate(ts Universe, t Type, node ipld.Node, pth string) []error { ...@@ -43,17 +43,20 @@ func validate(ts Universe, t Type, node ipld.Node, pth string) []error {
if node.ReprKind() != ipld.ReprKind_Map { if node.ReprKind() != ipld.ReprKind_Map {
return []error{fmt.Errorf("Schema match failed: expected type %q (which is kind %v) at path %q, but found kind %v", t2.Name(), t.ReprKind(), pth, node.ReprKind())} return []error{fmt.Errorf("Schema match failed: expected type %q (which is kind %v) at path %q, but found kind %v", t2.Name(), t.ReprKind(), pth, node.ReprKind())}
} }
keys, _ := node.KeysImmediate()
errs := []error(nil) errs := []error(nil)
for _, k := range keys { for itr := node.MapIterator(); !itr.Done(); {
k, v, err := itr.Next()
if err != nil {
return []error{err}
}
// FUTURE: if KeyType is an enum rather than string, do membership check. // FUTURE: if KeyType is an enum rather than string, do membership check.
child, _ := node.TraverseField(k) ks, _ := k.AsString()
if child.IsNull() { if v.IsNull() {
if !t2.ValueIsNullable() { if !t2.ValueIsNullable() {
errs = append(errs, fmt.Errorf("Schema match failed: map at path %q contains unpermitted null in key %q", pth, k)) errs = append(errs, fmt.Errorf("Schema match failed: map at path %q contains unpermitted null in key %q", pth, ks))
} }
} else { } else {
errs = append(errs, validate(ts, t2.ValueType(), child, path.Join(pth, k))...) errs = append(errs, validate(ts, t2.ValueType(), v, path.Join(pth, ks))...)
} }
} }
return errs return errs
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment