From 47bf9d6dac43345bf763fb10fef4ba534221b582 Mon Sep 17 00:00:00 2001 From: Mildred Ki'Lya Date: Fri, 5 Feb 2016 19:34:49 +0100 Subject: [PATCH] Add Reader Iterator --- iterator.go | 177 +++++++++++++++++++++++++++++++++++++++++++++++++++ links.go | 20 ++++++ path.go | 20 ++++++ reader_at.go | 18 ------ 4 files changed, 217 insertions(+), 18 deletions(-) create mode 100644 iterator.go create mode 100644 links.go diff --git a/iterator.go b/iterator.go new file mode 100644 index 0000000..bdf1f8a --- /dev/null +++ b/iterator.go @@ -0,0 +1,177 @@ +package ipld + +import ( + "math/big" +) + +type NodeIterator struct { + ReadItem + LastError error + nextError bool + items chan *ReadItem + feedback chan error + feedbackClosed bool +} + +type ReadItem struct { + Path []interface{} + TokenType ReaderToken + Value interface{} +} + +func (s *ReadItem) StringPath() []string { + return ToStringPath(s.Path) +} + +func (s *ReadItem) ToInt() (res int64, ok bool) { + ok = true + defer func() { + if r := recover(); r != nil { + ok = false + } + }() + switch s.Value.(type) { + case int: + res = int64(s.Value.(int)) + case int64: + res = s.Value.(int64) + case uint64: + res = int64(s.Value.(uint64)) + case *big.Int: + i := s.Value.(*big.Int) + if i.BitLen() > 63 { + ok = false + } else { + res = i.Int64() + } + default: + ok = false + } + return res, ok +} + +func (s *ReadItem) ToUint() (res uint64, ok bool) { + ok = true + defer func() { + if r := recover(); r != nil { + ok = false + } + }() + switch s.Value.(type) { + case int: + res = uint64(s.Value.(int)) + case int64: + res = uint64(s.Value.(int64)) + case uint64: + res = s.Value.(uint64) + case *big.Int: + i := s.Value.(*big.Int) + if i.BitLen() > 64 || i.Sign() < 0 { + ok = false + } else { + res = i.Uint64() + } + default: + ok = false + } + return res, ok +} + +func (s *ReadItem) ToFloat() (res float64, ok bool) { + ok = true + defer func() { + if r := recover(); r != nil { + ok = false + } + }() + switch s.Value.(type) { + case int: + res = float64(s.Value.(int)) + case int64: + res = float64(s.Value.(int64)) + case uint64: + res = float64(s.Value.(uint64)) + case float32: + res = float64(s.Value.(float32)) + case float64: + res = s.Value.(float64) + case *big.Int: + i := s.Value.(*big.Int) + if i.BitLen() > 64 { + ok = false + } else if i.Sign() < 0 { + res = -float64(big.NewInt(0).Abs(i).Uint64()) + } else { + res = float64(i.Uint64()) + } + default: + ok = false + } + return res, ok +} + +// Read from a NodeReader using a channel of ReadItem. +func Iterate(r NodeReader, res_error *error) *NodeIterator { + it := make(chan *ReadItem) + ce := make(chan error) + res := &NodeIterator{ReadItem{}, nil, true, it, ce, false} + + go func() { + err := r.Read(func(path []interface{}, tokenType ReaderToken, value interface{}) error { + item := &ReadItem{path, tokenType, value} + it <- item + return <-ce + }) + close(it) + if err != nil && res_error != nil { + res.LastError = err + } + }() + + return res +} + +func (s *NodeIterator) Iter() bool { + if !s.nextError { + s.feedback <- nil + } + + item := <-s.items + if item == nil { + if !s.feedbackClosed { + close(s.feedback) + s.feedbackClosed = true + } + return false + } + + s.ReadItem = *item + return true +} + +func (s *NodeIterator) Abort() { + s.StopError(NodeReadAbort) +} + +func (s *NodeIterator) Skip() { + s.StopError(NodeReadSkip) +} + +func (s *NodeIterator) StopError(e error) { + if s.nextError { + panic("Already stopped") + } + + s.feedback <- e + s.nextError = true +} + +func (s *NodeIterator) Close() error { + if !s.nextError { + s.Abort() + } + for s.Iter() { + s.Abort() + } + return s.LastError +} diff --git a/links.go b/links.go new file mode 100644 index 0000000..91d2760 --- /dev/null +++ b/links.go @@ -0,0 +1,20 @@ +package ipld + +import ( + "fmt" + mh "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" +) + +const ( + LinkKey = "@link" +) + +// Read the hash of an IPLD link. +func ReadLinkPath(value interface{}) (mh.Multihash, error) { + svalue, has_value := value.(string) + var h mh.Multihash + if has_value { + return mh.FromB58String(svalue) + } + return h, fmt.Errorf("Could not get multihash for %#v", value) +} diff --git a/path.go b/path.go index a15809b..83daba3 100644 --- a/path.go +++ b/path.go @@ -1,9 +1,29 @@ package ipld import ( + "fmt" "strings" ) +// Convert a []interface{} path to a []string path +// This should only convert array indices to strings, and such are not ambiguous +// because array indices and object keys cannot be mixed for the same prefix. +func ToStringPath(path []interface{}) []string { + res := make([]string, len(path)) + for _, e := range path { + if str, ok := e.(string); ok { + res = append(res, str) + } else if i, ok := e.(int); ok { + res = append(res, fmt.Sprintf("%d", i)) + } else if i, ok := e.(uint64); ok { + res = append(res, fmt.Sprintf("%d", i)) + } else { + res = append(res, fmt.Sprintf("%v", e)) + } + } + return res +} + // Escape path component. The special characters ("@" and "\") are escaped to // allow mixing the path component with directives (starting with "@") in IPLD // data structure. diff --git a/reader_at.go b/reader_at.go index 2fd4b96..2f9e11a 100644 --- a/reader_at.go +++ b/reader_at.go @@ -1,28 +1,10 @@ package ipld import ( - "fmt" "reflect" "strings" ) -// Convert a []interface{} path to a []string path -// This should only convert array indices to strings, and such are not ambiguous -// because array indices and object keys cannot be mixed for the same prefix. -func ToStringPath(anyPath []interface{}) []string { - var res []string - for _, e := range anyPath { - if str, ok := e.(string); ok { - res = append(res, str) - } else if i, ok := e.(int); ok { - res = append(res, fmt.Sprintf("%d", i)) - } else { - res = append(res, fmt.Sprintf("%v", e)) - } - } - return res -} - // A NodeReader that only read elements from a path prefix type NodeReaderAt struct { parent NodeReader