From d5495c64ef6d60a8522ad02e9f31f27dd3e528a8 Mon Sep 17 00:00:00 2001 From: Rafael Weinstein Date: Thu, 5 May 2016 19:17:09 -0700 Subject: [PATCH] Align go & js sequence cursor (#1435) Align go & js sequence cursor --- types/blob.go | 2 +- types/blob_leaf.go | 11 +- types/compound_blob.go | 5 +- types/compound_list.go | 12 +- types/compound_map.go | 12 +- types/compound_map_test.go | 2 +- types/compound_set.go | 18 +-- types/compound_set_test.go | 2 +- types/encode_noms_value.go | 3 +- types/equals_test.go | 4 +- types/list_leaf.go | 11 +- types/map_leaf.go | 11 +- types/meta_sequence.go | 46 +++--- types/meta_sequence_cursor_test.go | 57 -------- types/sequence_chunker.go | 2 - types/sequence_cursor.go | 77 ++++++---- types/sequence_cursor_test.go | 222 +++++++---------------------- types/set_leaf.go | 24 ++-- 18 files changed, 180 insertions(+), 341 deletions(-) delete mode 100644 types/meta_sequence_cursor_test.go diff --git a/types/blob.go b/types/blob.go index d61f241fb1..eaf22e9852 100644 --- a/types/blob.go +++ b/types/blob.go @@ -22,7 +22,7 @@ type Blob interface { } func NewEmptyBlob() Blob { - return newBlobLeaf([]byte{}) + return newBlobLeaf([]byte{}).(Blob) } func newBlobLeafBoundaryChecker() boundaryChecker { diff --git a/types/blob_leaf.go b/types/blob_leaf.go index a7e0265736..ba32613c7a 100644 --- a/types/blob_leaf.go +++ b/types/blob_leaf.go @@ -14,10 +14,19 @@ type blobLeaf struct { ref *ref.Ref } -func newBlobLeaf(data []byte) blobLeaf { +func newBlobLeaf(data []byte) sequence { return blobLeaf{data, &ref.Ref{}} } +// sequence +func (bl blobLeaf) getItem(idx int) sequenceItem { + return bl.data[idx] +} + +func (bl blobLeaf) seqLen() int { + return len(bl.data) +} + // Reader implements the Blob interface func (bl blobLeaf) Reader() io.ReadSeeker { return bytes.NewReader(bl.data) diff --git a/types/compound_blob.go b/types/compound_blob.go index 108b008791..6e6a0edce8 100644 --- a/types/compound_blob.go +++ b/types/compound_blob.go @@ -14,16 +14,15 @@ type compoundBlob struct { metaSequenceObject length uint64 ref *ref.Ref - vr ValueReader } func newCompoundBlob(tuples metaSequenceData, vr ValueReader) compoundBlob { return buildCompoundBlob(tuples, BlobType, vr).(compoundBlob) } -func buildCompoundBlob(tuples metaSequenceData, t *Type, vr ValueReader) Value { +func buildCompoundBlob(tuples metaSequenceData, t *Type, vr ValueReader) metaSequence { d.Chk.True(t.Equals(BlobType)) - return compoundBlob{metaSequenceObject{tuples, BlobType}, tuples.uint64ValuesSum(), &ref.Ref{}, vr} + return compoundBlob{metaSequenceObject{tuples, BlobType, vr}, tuples.uint64ValuesSum(), &ref.Ref{}} } func init() { diff --git a/types/compound_list.go b/types/compound_list.go index bdc3f5e3fa..8cf5ab8ae9 100644 --- a/types/compound_list.go +++ b/types/compound_list.go @@ -17,11 +17,10 @@ type compoundList struct { metaSequenceObject length uint64 ref *ref.Ref - vr ValueReader } -func buildCompoundList(tuples metaSequenceData, t *Type, vr ValueReader) Value { - return compoundList{metaSequenceObject{tuples, t}, tuples.uint64ValuesSum(), &ref.Ref{}, vr} +func buildCompoundList(tuples metaSequenceData, t *Type, vr ValueReader) metaSequence { + return compoundList{metaSequenceObject{tuples, t, vr}, tuples.uint64ValuesSum(), &ref.Ref{}} } func listAsSequenceItems(ls listLeaf) []sequenceItem { @@ -139,12 +138,7 @@ func (cl compoundList) Insert(idx uint64, vs ...Value) List { func (cl compoundList) sequenceCursorAtIndex(idx uint64) *sequenceCursor { // TODO: An optimisation would be to decide at each level whether to step forward or backward across the node to find the insertion point, depending on which is closer. This would make Append much faster. metaCur, leaf, start := cl.cursorAt(idx) - return &sequenceCursor{metaCur, leaf, int(idx - start), len(leaf.values), func(otherLeaf sequenceItem, idx int) sequenceItem { - return otherLeaf.(listLeaf).values[idx] - }, func(mt sequenceItem) (sequenceItem, int) { - otherLeaf := readMetaTupleValue(mt, cl.vr).(listLeaf) - return otherLeaf, len(otherLeaf.values) - }} + return newSequenceCursor(metaCur, leaf, int(idx-start)) } func (cl compoundList) sequenceChunkerAtIndex(idx uint64) *sequenceChunker { diff --git a/types/compound_map.go b/types/compound_map.go index 05b59d47e7..16a3c9d88b 100644 --- a/types/compound_map.go +++ b/types/compound_map.go @@ -14,11 +14,10 @@ type compoundMap struct { metaSequenceObject numLeaves uint64 ref *ref.Ref - vr ValueReader } -func buildCompoundMap(tuples metaSequenceData, t *Type, vr ValueReader) Value { - return compoundMap{metaSequenceObject{tuples, t}, tuples.numLeavesSum(), &ref.Ref{}, vr} +func buildCompoundMap(tuples metaSequenceData, t *Type, vr ValueReader) metaSequence { + return compoundMap{metaSequenceObject{tuples, t, vr}, tuples.numLeavesSum(), &ref.Ref{}} } func init() { @@ -97,12 +96,7 @@ func (cm compoundMap) Remove(k Value) Map { func (cm compoundMap) sequenceChunkerAtKey(k Value) (*sequenceChunker, bool) { metaCur, leaf, idx := cm.findLeaf(k) - cur := &sequenceCursor{metaCur, leaf, idx, len(leaf.data), func(otherLeaf sequenceItem, idx int) sequenceItem { - return otherLeaf.(mapLeaf).data[idx] - }, func(mt sequenceItem) (sequenceItem, int) { - otherLeaf := readMetaTupleValue(mt, cm.vr).(mapLeaf) - return otherLeaf, len(otherLeaf.data) - }} + cur := newSequenceCursor(metaCur, leaf, idx) seq := newSequenceChunker(cur, makeMapLeafChunkFn(cm.t, cm.vr), newOrderedMetaSequenceChunkFn(cm.t, cm.vr), newMapLeafBoundaryChecker(), newOrderedMetaSequenceBoundaryChecker) found := idx < len(leaf.data) && leaf.data[idx].key.Equals(k) diff --git a/types/compound_map_test.go b/types/compound_map_test.go index 21772ebabb..1a1f92af0b 100644 --- a/types/compound_map_test.go +++ b/types/compound_map_test.go @@ -446,7 +446,7 @@ func TestCompoundMapModifyAfterRead(t *testing.T) { func deriveCompoundMapHeight(m compoundMap) uint64 { // Note: not using mt.childRef.Height() because the purpose of this method is to be redundant. height := uint64(1) - if m2, ok := m.tupleAt(0).child.(compoundMap); ok { + if m2, ok := m.getItem(0).(metaTuple).child.(compoundMap); ok { height += deriveCompoundMapHeight(m2) } return height diff --git a/types/compound_set.go b/types/compound_set.go index 480b44e6d2..0d92a36c8f 100644 --- a/types/compound_set.go +++ b/types/compound_set.go @@ -15,11 +15,10 @@ type compoundSet struct { metaSequenceObject numLeaves uint64 ref *ref.Ref - vr ValueReader } -func buildCompoundSet(tuples metaSequenceData, t *Type, vr ValueReader) Value { - return compoundSet{metaSequenceObject{tuples, t}, tuples.numLeavesSum(), &ref.Ref{}, vr} +func buildCompoundSet(tuples metaSequenceData, t *Type, vr ValueReader) metaSequence { + return compoundSet{metaSequenceObject{tuples, t, vr}, tuples.numLeavesSum(), &ref.Ref{}} } func init() { @@ -88,20 +87,11 @@ func (cs compoundSet) Remove(values ...Value) Set { func (cs compoundSet) sequenceCursorAtValue(v Value) (*sequenceCursor, bool) { metaCur, leaf, idx := cs.findLeaf(v) - cur := newSetSequenceCursorAtPosition(metaCur, leaf, idx, cs.vr) + cur := newSequenceCursor(metaCur, leaf, idx) found := idx < len(leaf.data) && leaf.data[idx].Equals(v) return cur, found } -func newSetSequenceCursorAtPosition(metaCur *sequenceCursor, leaf setLeaf, idx int, cs ValueReader) *sequenceCursor { - return &sequenceCursor{metaCur, leaf, idx, len(leaf.data), func(otherLeaf sequenceItem, idx int) sequenceItem { - return otherLeaf.(setLeaf).data[idx] - }, func(mt sequenceItem) (sequenceItem, int) { - otherLeaf := readMetaTupleValue(mt, cs).(setLeaf) - return otherLeaf, len(otherLeaf.data) - }} -} - func (cs compoundSet) sequenceChunkerAtValue(v Value) (*sequenceChunker, bool) { cur, found := cs.sequenceCursorAtValue(v) seq := newSequenceChunker(cur, makeSetLeafChunkFn(cs.t, cs.vr), newOrderedMetaSequenceChunkFn(cs.t, cs.vr), newSetLeafBoundaryChecker(), newOrderedMetaSequenceBoundaryChecker) @@ -165,7 +155,7 @@ func (cs compoundSet) elemType() *Type { func (cs compoundSet) sequenceCursorAtFirst() *sequenceCursor { metaCur, leaf := newMetaSequenceCursor(cs, cs.vr) - return newSetSequenceCursorAtPosition(metaCur, leaf.(setLeaf), 0, cs.vr) + return newSequenceCursor(metaCur, leaf.(setLeaf), 0) } func (cs compoundSet) valueReader() ValueReader { diff --git a/types/compound_set_test.go b/types/compound_set_test.go index 3083a06cf4..baa765777d 100644 --- a/types/compound_set_test.go +++ b/types/compound_set_test.go @@ -401,7 +401,7 @@ func TestCompoundSetModifyAfterRead(t *testing.T) { func deriveCompoundSetHeight(s compoundSet) uint64 { // Note: not using mt.childRef.Height() because the purpose of this method is to be redundant. height := uint64(1) - if s2, ok := s.tupleAt(0).child.(compoundSet); ok { + if s2, ok := s.getItem(0).(metaTuple).child.(compoundSet); ok { height += deriveCompoundSetHeight(s2) } return height diff --git a/types/encode_noms_value.go b/types/encode_noms_value.go index 8541dde1bb..e88ee5303f 100644 --- a/types/encode_noms_value.go +++ b/types/encode_noms_value.go @@ -109,7 +109,8 @@ func (w *jsonArrayWriter) maybeWriteMetaSequence(v Value, tr *Type) bool { w.write(true) // a meta sequence w2 := newJSONArrayWriter(w.vw) - for _, tuple := range ms.(metaSequence).data() { + for i := 0; i < ms.seqLen(); i++ { + tuple := ms.getItem(i).(metaTuple) if tuple.child != nil && w.vw != nil { // Write unwritten chunked sequences. Chunks are lazily written so that intermediate chunked structures like NewList().Append(x).Append(y) don't cause unnecessary churn. w.vw.WriteValue(tuple.child) diff --git a/types/equals_test.go b/types/equals_test.go index bf40ba7fb6..0a023e44bb 100644 --- a/types/equals_test.go +++ b/types/equals_test.go @@ -33,8 +33,8 @@ func TestValueEquals(t *testing.T) { b1 := NewBlob(bytes.NewBufferString("hi")) b2 := NewBlob(bytes.NewBufferString("bye")) return newCompoundBlob([]metaTuple{ - newMetaTuple(Number(uint64(2)), b1, NewTypedRefFromValue(b1), 2), - newMetaTuple(Number(uint64(5)), b2, NewTypedRefFromValue(b2), 5), + newMetaTuple(Number(uint64(2)), b1.(sequence), NewTypedRefFromValue(b1), 2), + newMetaTuple(Number(uint64(5)), b2.(sequence), NewTypedRefFromValue(b2), 5), }, nil) }, func() Value { return NewList() }, diff --git a/types/list_leaf.go b/types/list_leaf.go index 6211d631f4..f9c0cf00ca 100644 --- a/types/list_leaf.go +++ b/types/list_leaf.go @@ -13,11 +13,20 @@ type listLeaf struct { ref *ref.Ref } -func newListLeaf(t *Type, v ...Value) List { +func newListLeaf(t *Type, v ...Value) sequence { d.Chk.Equal(ListKind, t.Kind()) return listLeaf{v, t, &ref.Ref{}} } +// sequence +func (l listLeaf) getItem(idx int) sequenceItem { + return l.values[idx] +} + +func (l listLeaf) seqLen() int { + return len(l.values) +} + func (l listLeaf) Len() uint64 { return uint64(len(l.values)) } diff --git a/types/map_leaf.go b/types/map_leaf.go index 432fa3f993..d6fc651b33 100644 --- a/types/map_leaf.go +++ b/types/map_leaf.go @@ -22,7 +22,7 @@ type mapEntry struct { value Value } -func newMapLeaf(t *Type, data ...mapEntry) Map { +func newMapLeaf(t *Type, data ...mapEntry) sequence { return mapLeaf{data, getIndexFnForMapType(t), t, &ref.Ref{}} } @@ -34,6 +34,15 @@ func (m mapLeaf) First() (Value, Value) { return entry.key, entry.value } +// sequence +func (m mapLeaf) getItem(idx int) sequenceItem { + return m.data[idx] +} + +func (m mapLeaf) seqLen() int { + return len(m.data) +} + func (m mapLeaf) Len() uint64 { return uint64(len(m.data)) } diff --git a/types/meta_sequence.go b/types/meta_sequence.go index 05a418d3bd..ea28e86324 100644 --- a/types/meta_sequence.go +++ b/types/meta_sequence.go @@ -10,21 +10,18 @@ const ( // metaSequence is a logical abstraction, but has no concrete "base" implementation. A Meta Sequence is a non-leaf (internal) node of a "probably" tree, which results from the chunking of an ordered or unordered sequence of values. type metaSequence interface { - Value - data() metaSequenceData - tupleAt(idx int) metaTuple - tupleSlice(to int) []metaTuple - tupleCount() int + sequence + getChildSequence(idx int) sequence } -func newMetaTuple(value, child Value, childRef Ref, numLeaves uint64) metaTuple { +func newMetaTuple(value Value, child sequence, childRef Ref, numLeaves uint64) metaTuple { d.Chk.NotEqual(Ref{}, childRef) return metaTuple{child, childRef, value, numLeaves} } // metaTuple is a node in a Prolly Tree, consisting of data in the node (either tree leaves or other metaSequences), and a Value annotation for exploring the tree (e.g. the largest item if this an ordered sequence). type metaTuple struct { - child Value // may be nil + child sequence // may be nil childRef Ref value Value numLeaves uint64 @@ -61,18 +58,26 @@ func (msd metaSequenceData) last() metaTuple { type metaSequenceObject struct { tuples metaSequenceData t *Type + vr ValueReader } -func (ms metaSequenceObject) tupleAt(idx int) metaTuple { +// sequence +func (ms metaSequenceObject) getItem(idx int) sequenceItem { return ms.tuples[idx] } -func (ms metaSequenceObject) tupleSlice(to int) []metaTuple { - return ms.tuples[:to] +func (ms metaSequenceObject) seqLen() int { + return len(ms.tuples) } -func (ms metaSequenceObject) tupleCount() int { - return len(ms.tuples) +func (ms metaSequenceObject) getChildSequence(idx int) sequence { + mt := ms.tuples[idx] + if mt.child != nil { + return mt.child + } + + child := mt.childRef.TargetValue(ms.vr) + return child.(sequence) } func (ms metaSequenceObject) data() metaSequenceData { @@ -98,7 +103,7 @@ func (ms metaSequenceObject) Type() *Type { return ms.t } -type metaBuilderFunc func(tuples metaSequenceData, t *Type, vr ValueReader) Value +type metaBuilderFunc func(tuples metaSequenceData, t *Type, vr ValueReader) metaSequence var metaFuncMap = map[NomsKind]metaBuilderFunc{} @@ -106,7 +111,7 @@ func registerMetaValue(k NomsKind, bf metaBuilderFunc) { metaFuncMap[k] = bf } -func newMetaSequenceFromData(tuples metaSequenceData, t *Type, vr ValueReader) Value { +func newMetaSequenceFromData(tuples metaSequenceData, t *Type, vr ValueReader) metaSequence { if bf, ok := metaFuncMap[t.Kind()]; ok { return bf(tuples, t, vr) } @@ -118,21 +123,12 @@ func newMetaSequenceFromData(tuples metaSequenceData, t *Type, vr ValueReader) V func newMetaSequenceCursor(root metaSequence, vr ValueReader) (*sequenceCursor, Value) { d.Chk.NotNil(root) - newCursor := func(parent *sequenceCursor, ms metaSequence) *sequenceCursor { - return &sequenceCursor{parent, ms, 0, ms.tupleCount(), func(otherMs sequenceItem, idx int) sequenceItem { - return otherMs.(metaSequence).tupleAt(idx) - }, func(item sequenceItem) (sequenceItem, int) { - otherMs := readMetaTupleValue(item, vr).(metaSequence) - return otherMs, otherMs.tupleCount() - }} - } - - cursors := []*sequenceCursor{newCursor(nil, root)} + cursors := []*sequenceCursor{newSequenceCursor(nil, root, 0)} for { cursor := cursors[len(cursors)-1] val := readMetaTupleValue(cursor.current(), vr) if ms, ok := val.(metaSequence); ok { - cursors = append(cursors, newCursor(cursor, ms)) + cursors = append(cursors, newSequenceCursor(cursor, ms, 0)) } else { return cursor, val } diff --git a/types/meta_sequence_cursor_test.go b/types/meta_sequence_cursor_test.go deleted file mode 100644 index 65e9289952..0000000000 --- a/types/meta_sequence_cursor_test.go +++ /dev/null @@ -1,57 +0,0 @@ -package types - -import ( - "testing" - - "github.com/attic-labs/noms/ref" - "github.com/stretchr/testify/assert" -) - -func TestMeta(t *testing.T) { - assert := assert.New(t) - - vs := NewTestValueStore() - - flatList := []Value{Number(0), Number(1), Number(2), Number(3), Number(4), Number(5), Number(6), Number(7)} - l0 := NewList(flatList[0]) - lr0 := vs.WriteValue(l0) - l1 := NewList(flatList[1]) - lr1 := vs.WriteValue(l1) - l2 := NewList(flatList[2]) - lr2 := vs.WriteValue(l2) - l3 := NewList(flatList[3]) - lr3 := vs.WriteValue(l3) - l4 := NewList(flatList[4]) - lr4 := vs.WriteValue(l4) - l5 := NewList(flatList[5]) - lr5 := vs.WriteValue(l5) - l6 := NewList(flatList[6]) - lr6 := vs.WriteValue(l6) - l7 := NewList(flatList[7]) - lr7 := vs.WriteValue(l7) - - mtr := l0.Type() - - m0 := compoundList{metaSequenceObject{metaSequenceData{{l0, lr0, Number(1), 1}, {l1, lr1, Number(2), 2}}, mtr}, 0, &ref.Ref{}, vs} - lm0 := vs.WriteValue(m0) - m1 := compoundList{metaSequenceObject{metaSequenceData{{l2, lr2, Number(1), 1}, {l3, lr3, Number(2), 2}}, mtr}, 0, &ref.Ref{}, vs} - lm1 := vs.WriteValue(m1) - m2 := compoundList{metaSequenceObject{metaSequenceData{{l4, lr4, Number(1), 1}, {l5, lr5, Number(2), 2}}, mtr}, 0, &ref.Ref{}, vs} - lm2 := vs.WriteValue(m2) - m3 := compoundList{metaSequenceObject{metaSequenceData{{l6, lr6, Number(1), 1}, {l7, lr7, Number(2), 2}}, mtr}, 0, &ref.Ref{}, vs} - lm3 := vs.WriteValue(m3) - - m00 := compoundList{metaSequenceObject{metaSequenceData{{m0, lm0, Number(2), 2}, {m1, lm1, Number(4), 4}}, mtr}, 0, &ref.Ref{}, vs} - lm00 := vs.WriteValue(m00) - m01 := compoundList{metaSequenceObject{metaSequenceData{{m2, lm2, Number(2), 2}, {m3, lm3, Number(4), 4}}, mtr}, 0, &ref.Ref{}, vs} - lm01 := vs.WriteValue(m01) - - rootList := compoundList{metaSequenceObject{metaSequenceData{{m00, lm00, Number(4), 4}, {m01, lm01, Number(8), 8}}, mtr}, 0, &ref.Ref{}, vs} - rootRef := vs.WriteValue(rootList).TargetRef() - - rootList = vs.ReadValue(rootRef).(compoundList) - - rootList.IterAll(func(v Value, index uint64) { - assert.Equal(flatList[index], v) - }) -} diff --git a/types/sequence_chunker.go b/types/sequence_chunker.go index e131d9605b..c27637488b 100644 --- a/types/sequence_chunker.go +++ b/types/sequence_chunker.go @@ -2,8 +2,6 @@ package types import "github.com/attic-labs/noms/d" -type sequenceItem interface{} - type boundaryChecker interface { // Write takes an item and returns true if the sequence should chunk after this item, false if not. Write(sequenceItem) bool diff --git a/types/sequence_cursor.go b/types/sequence_cursor.go index a8be48a4d3..66784e7e70 100644 --- a/types/sequence_cursor.go +++ b/types/sequence_cursor.go @@ -6,20 +6,41 @@ import ( "github.com/attic-labs/noms/d" ) -// sequenceCursor explores a tree of sequence items. -type sequenceCursor struct { - parent *sequenceCursor - item sequenceItem - idx, length int - getItem getItemFn - readChunk readChunkFn +type sequenceItem interface{} + +type sequence interface { + Value + getItem(idx int) sequenceItem + seqLen() int } -// getItemFn takes a parent in the sequence and an index into that parent, and returns the child item, equivalent to `child = parent[idx]`. The parent and the child aren't necessarily the same type. -type getItemFn func(parent sequenceItem, idx int) (child sequenceItem) +// sequenceCursor explores a tree of sequence items. +type sequenceCursor struct { + parent *sequenceCursor + seq sequence + idx int +} -// readChunkFn takes an item in the sequence which references another sequence of items, and returns that sequence along with its length. -type readChunkFn func(reference sequenceItem) (sequence sequenceItem, length int) +func newSequenceCursor(parent *sequenceCursor, seq sequence, idx int) *sequenceCursor { + return &sequenceCursor{parent, seq, idx} +} + +func (cur *sequenceCursor) length() int { + return cur.seq.seqLen() +} + +func (cur *sequenceCursor) getItem(idx int) sequenceItem { + return cur.seq.getItem(idx) +} + +func (cur *sequenceCursor) sync() { + d.Chk.NotNil(cur.parent) + cur.seq = cur.parent.getChildSequence() +} + +func (cur *sequenceCursor) getChildSequence() sequence { + return cur.seq.(metaSequence).getChildSequence(cur.idx) +} // Returns the value the cursor refers to. Fails an assertion if the cursor doesn't point to a value. func (cur *sequenceCursor) current() sequenceItem { @@ -30,11 +51,11 @@ func (cur *sequenceCursor) current() sequenceItem { // Returns the value the cursor refers to, if any. If the cursor doesn't point to a value, returns (nil, false). func (cur *sequenceCursor) maybeCurrent() (sequenceItem, bool) { - d.Chk.True(cur.idx >= -1 && cur.idx <= cur.length) - if cur.idx == -1 || cur.idx == cur.length { + d.Chk.True(cur.idx >= -1 && cur.idx <= cur.length()) + if cur.idx == -1 || cur.idx == cur.length() { return nil, false } - return cur.getItem(cur.item, cur.idx), true + return cur.getItem(cur.idx), true } func (cur *sequenceCursor) indexInChunk() int { @@ -46,15 +67,15 @@ func (cur *sequenceCursor) advance() bool { } func (cur *sequenceCursor) advanceMaybeAllowPastEnd(allowPastEnd bool) bool { - if cur.idx < cur.length-1 { + if cur.idx < cur.length()-1 { cur.idx++ return true } - if cur.idx == cur.length { + if cur.idx == cur.length() { return false } if cur.parent != nil && cur.parent.advanceMaybeAllowPastEnd(false) { - cur.item, cur.length = cur.readChunk(cur.parent.current()) + cur.sync() cur.idx = 0 return true } @@ -78,8 +99,8 @@ func (cur *sequenceCursor) retreatMaybeAllowBeforeStart(allowBeforeStart bool) b } d.Chk.Equal(0, cur.idx) if cur.parent != nil && cur.parent.retreatMaybeAllowBeforeStart(false) { - cur.item, cur.length = cur.readChunk(cur.parent.current()) - cur.idx = cur.length - 1 + cur.sync() + cur.idx = cur.length() - 1 return true } if allowBeforeStart { @@ -93,7 +114,7 @@ func (cur *sequenceCursor) clone() *sequenceCursor { if cur.parent != nil { parent = cur.parent.clone() } - return &sequenceCursor{parent, cur.item, cur.idx, cur.length, cur.getItem, cur.readChunk} + return &sequenceCursor{parent, cur.seq, cur.idx} } type sequenceCursorSeekBinaryCompareFn func(item sequenceItem) bool @@ -104,15 +125,15 @@ func (cur *sequenceCursor) seekBinary(compare sequenceCursorSeekBinaryCompareFn) if cur.parent != nil { cur.parent.seekBinary(compare) - cur.item, cur.length = cur.readChunk(cur.parent.current()) + cur.sync() } - cur.idx = sort.Search(cur.length, func(i int) bool { - return compare(cur.getItem(cur.item, i)) + cur.idx = sort.Search(cur.length(), func(i int) bool { + return compare(cur.getItem(i)) }) - if cur.idx == cur.length { - cur.idx = cur.length - 1 + if cur.idx == cur.length() { + cur.idx = cur.length() - 1 } } @@ -124,12 +145,12 @@ func (cur *sequenceCursor) seekLinear(step sequenceCursorSeekLinearStepFn, carry if cur.parent != nil { carry = cur.parent.seekLinear(step, carry) - cur.item, cur.length = cur.readChunk(cur.parent.current()) + cur.sync() } cur.idx = 0 - for i := 0; i < cur.length-1; i++ { - found, carryOut := step(carry, cur.getItem(cur.item, i)) + for i := 0; i < cur.length()-1; i++ { + found, carryOut := step(carry, cur.getItem(i)) if found { break } diff --git a/types/sequence_cursor_test.go b/types/sequence_cursor_test.go index be69e8d293..d35f72d540 100644 --- a/types/sequence_cursor_test.go +++ b/types/sequence_cursor_test.go @@ -3,42 +3,57 @@ package types import ( "testing" + "github.com/attic-labs/noms/ref" "github.com/stretchr/testify/assert" ) -func newTestSequenceCursor(items [][]int) *sequenceCursor { - parent := &sequenceCursor{nil, items, 0, len(items), func(item sequenceItem, idx int) sequenceItem { - return item.([][]int)[idx] // item should be == items - }, func(item sequenceItem) (sequenceItem, int) { - panic("not reachable") - }} +type testSequence struct { + items []interface{} +} - return &sequenceCursor{parent, items[0], 0, len(items[0]), func(item sequenceItem, idx int) sequenceItem { - return item.([]int)[idx] - }, func(item sequenceItem) (sequenceItem, int) { - return item, len(item.([]int)) - }} +// sequence +func (ts testSequence) getItem(idx int) sequenceItem { + return ts.items[idx] +} + +func (ts testSequence) seqLen() int { + return len(ts.items) +} + +func (ts testSequence) getChildSequence(idx int) sequence { + child := ts.items[idx] + return testSequence{child.([]interface{})} +} + +func (ts testSequence) Equals(other Value) bool { + panic("not reached") +} +func (ts testSequence) Ref() ref.Ref { + panic("not reached") +} +func (ts testSequence) ChildValues() []Value { + panic("not reached") +} +func (ts testSequence) Chunks() []Ref { + panic("not reached") +} +func (ts testSequence) Type() *Type { + panic("not reached") +} + +func newTestSequenceCursor(items []interface{}) *sequenceCursor { + parent := newSequenceCursor(nil, testSequence{items}, 0) + items = items[0].([]interface{}) + return newSequenceCursor(parent, testSequence{items}, 0) } // TODO: Convert all tests to use newTestSequenceCursor3. -func newTestSequenceCursor3(items [][][]int) *sequenceCursor { - top := &sequenceCursor{nil, items, 0, len(items), func(item sequenceItem, idx int) sequenceItem { - return item.([][][]int)[idx] // item should be == items - }, func(item sequenceItem) (sequenceItem, int) { - panic("not reachable") - }} - - middle := &sequenceCursor{top, items[0], 0, len(items[0]), func(item sequenceItem, idx int) sequenceItem { - return item.([][]int)[idx] - }, func(item sequenceItem) (sequenceItem, int) { - return item, len(item.([][]int)) - }} - - return &sequenceCursor{middle, items[0][0], 0, len(items[0][0]), func(item sequenceItem, idx int) sequenceItem { - return item.([]int)[idx] - }, func(item sequenceItem) (sequenceItem, int) { - return item, len(item.([]int)) - }} +func newTestSequenceCursor3(items []interface{}) *sequenceCursor { + top := newSequenceCursor(nil, testSequence{items}, 0) + items = items[0].([]interface{}) + middle := newSequenceCursor(top, testSequence{items}, 0) + items = items[0].([]interface{}) + return newSequenceCursor(middle, testSequence{items}, 0) } func TestTestCursor(t *testing.T) { @@ -46,7 +61,7 @@ func TestTestCursor(t *testing.T) { var cur *sequenceCursor reset := func() { - cur = newTestSequenceCursor([][]int{[]int{100, 101}, []int{102}}) + cur = newTestSequenceCursor([]interface{}{[]interface{}{100, 101}, []interface{}{102}}) } expect := func(expectIdx, expectParentIdx int, expectOk bool, expectVal sequenceItem) { assert.Equal(expectIdx, cur.indexInChunk()) @@ -112,14 +127,14 @@ func TestTestCursor(t *testing.T) { func TestCursorGetMaxNPrevItemsWithEmptySequence(t *testing.T) { assert := assert.New(t) - cur := newTestSequenceCursor([][]int{[]int{}}) + cur := newTestSequenceCursor([]interface{}{[]interface{}{}}) assert.Equal([]sequenceItem{}, cur.maxNPrevItems(0)) assert.Equal([]sequenceItem{}, cur.maxNPrevItems(1)) } func TestCursorGetMaxNPrevItemsWithSingleItemSequence(t *testing.T) { assert := assert.New(t) - cur := newTestSequenceCursor([][]int{[]int{100}, []int{101}, []int{102}}) + cur := newTestSequenceCursor([]interface{}{[]interface{}{100}, []interface{}{101}, []interface{}{102}}) assert.Equal([]sequenceItem{}, cur.maxNPrevItems(0)) assert.Equal([]sequenceItem{}, cur.maxNPrevItems(1)) @@ -152,9 +167,9 @@ func TestCursorGetMaxNPrevItemsWithSingleItemSequence(t *testing.T) { func TestCursorGetMaxNPrevItemsWithMultiItemSequence(t *testing.T) { assert := assert.New(t) - cur := newTestSequenceCursor([][]int{ - []int{100, 101, 102, 103}, - []int{104, 105, 106, 107}, + cur := newTestSequenceCursor([]interface{}{ + []interface{}{100, 101, 102, 103}, + []interface{}{104, 105, 106, 107}, }) assert.Equal([]sequenceItem{}, cur.maxNPrevItems(0)) @@ -226,140 +241,3 @@ func TestCursorGetMaxNPrevItemsWithMultiItemSequence(t *testing.T) { assert.Equal([]sequenceItem{100, 101, 102, 103, 104, 105, 106, 107}, cur.maxNPrevItems(8)) assert.Equal([]sequenceItem{100, 101, 102, 103, 104, 105, 106, 107}, cur.maxNPrevItems(9)) } - -func TestCursorSeekBinary(t *testing.T) { - assert := assert.New(t) - - var cur *sequenceCursor - reset := func() { - cur = newTestSequenceCursor([][]int{ - []int{100, 101, 102, 103}, - []int{104, 105, 106, 107}, - }) - } - - assertSeeksTo := func(expected sequenceItem, seekTo int) { - cur.seekBinary(func(val sequenceItem) bool { - switch val := val.(type) { - case []int: - return val[len(val)-1] >= seekTo - case int: - return val >= seekTo - default: - panic("illegal") - } - }) - assert.Equal(expected, cur.current()) - } - - // Test seeking immediately to values on cursor construction. - reset() - assertSeeksTo(sequenceItem(100), 99) - for i := 100; i <= 107; i++ { - reset() - assertSeeksTo(sequenceItem(i), i) - } - reset() - assertSeeksTo(sequenceItem(107), 108) - - // Test reusing an existing cursor to seek all over the place. - reset() - assertSeeksTo(sequenceItem(100), 99) - for i := 100; i <= 107; i++ { - assertSeeksTo(sequenceItem(i), i) - } - assertSeeksTo(sequenceItem(107), 108) - assertSeeksTo(sequenceItem(100), 99) - for i := 100; i <= 107; i++ { - assertSeeksTo(sequenceItem(i), i) - } - assertSeeksTo(sequenceItem(107), 108) -} - -func TestCursorSeekLinear(t *testing.T) { - assert := assert.New(t) - - var cur *sequenceCursor - - assertSeeksTo := func(reset bool, expectedPos sequenceItem, expectedSumUpto, seekTo int) { - if reset { - cur = newTestSequenceCursor3( - [][][]int{ - [][]int{ - []int{100, 101, 102, 103}, - []int{104, 105, 106, 107}, - }, - [][]int{ - []int{108, 109, 110, 111}, - []int{112, 113, 114, 115}, - }, - }, - ) - } - sumUpto := cur.seekLinear(func(carry interface{}, item sequenceItem) (bool, interface{}) { - switch item := item.(type) { - case [][]int: - last := item[len(item)-1] - return seekTo <= last[len(last)-1], carry - case []int: - return seekTo <= item[len(item)-1], carry - case int: - return seekTo <= item, item + carry.(int) - } - panic("illegal") - }, 0) - pos, _ := cur.maybeCurrent() - assert.Equal(expectedPos, pos) - assert.Equal(expectedSumUpto, sumUpto) - } - - // Test seeking immediately to values on cursor construction. - assertSeeksTo(true, sequenceItem(100), 0, 99) - - assertSeeksTo(true, sequenceItem(100), 0, 100) - assertSeeksTo(true, sequenceItem(101), 100, 101) - assertSeeksTo(true, sequenceItem(102), 201, 102) - assertSeeksTo(true, sequenceItem(103), 303, 103) - - assertSeeksTo(true, sequenceItem(104), 0, 104) - assertSeeksTo(true, sequenceItem(105), 104, 105) - assertSeeksTo(true, sequenceItem(106), 209, 106) - assertSeeksTo(true, sequenceItem(107), 315, 107) - - assertSeeksTo(true, sequenceItem(108), 0, 108) - assertSeeksTo(true, sequenceItem(109), 108, 109) - assertSeeksTo(true, sequenceItem(110), 217, 110) - assertSeeksTo(true, sequenceItem(111), 327, 111) - - assertSeeksTo(true, sequenceItem(112), 0, 112) - assertSeeksTo(true, sequenceItem(113), 112, 113) - assertSeeksTo(true, sequenceItem(114), 225, 114) - assertSeeksTo(true, sequenceItem(115), 339, 115) - - assertSeeksTo(true, sequenceItem(115), 339, 116) - - // Test reusing an existing cursor to seek all over the place. - assertSeeksTo(false, sequenceItem(100), 0, 99) - - assertSeeksTo(false, sequenceItem(100), 0, 100) - assertSeeksTo(false, sequenceItem(101), 100, 101) - assertSeeksTo(false, sequenceItem(102), 201, 102) - assertSeeksTo(false, sequenceItem(103), 303, 103) - - assertSeeksTo(false, sequenceItem(104), 0, 104) - assertSeeksTo(false, sequenceItem(105), 104, 105) - assertSeeksTo(false, sequenceItem(106), 209, 106) - assertSeeksTo(false, sequenceItem(107), 315, 107) - - assertSeeksTo(false, sequenceItem(108), 0, 108) - assertSeeksTo(false, sequenceItem(109), 108, 109) - assertSeeksTo(false, sequenceItem(110), 217, 110) - assertSeeksTo(false, sequenceItem(111), 327, 111) - - assertSeeksTo(false, sequenceItem(112), 0, 112) - assertSeeksTo(false, sequenceItem(113), 112, 113) - assertSeeksTo(false, sequenceItem(114), 225, 114) - assertSeeksTo(false, sequenceItem(115), 339, 115) - - assertSeeksTo(false, sequenceItem(115), 339, 116) -} diff --git a/types/set_leaf.go b/types/set_leaf.go index a2a2abb10b..0ede9cc8cf 100644 --- a/types/set_leaf.go +++ b/types/set_leaf.go @@ -17,10 +17,19 @@ type setLeaf struct { type setData []Value -func newSetLeaf(t *Type, m ...Value) setLeaf { +func newSetLeaf(t *Type, m ...Value) sequence { return setLeaf{m, getIndexFnForSetType(t), t, &ref.Ref{}} } +// sequence +func (s setLeaf) getItem(idx int) sequenceItem { + return s.data[idx] +} + +func (s setLeaf) seqLen() int { + return len(s.data) +} + func (s setLeaf) Empty() bool { return s.Len() == uint64(0) } @@ -197,18 +206,7 @@ func makeSetLeafChunkFn(t *Type, vr ValueReader) makeChunkFn { } func (s setLeaf) sequenceCursorAtFirst() *sequenceCursor { - return &sequenceCursor{ - nil, - s.data, - 0, - len(s.data), - func(parent sequenceItem, idx int) sequenceItem { - return s.data[idx] - }, - func(reference sequenceItem) (sequence sequenceItem, length int) { - panic("unreachable") - }, - } + return newSequenceCursor(nil, s, 0) } func (s setLeaf) valueReader() ValueReader {