Align go & js sequence cursor (#1435)

Align go & js sequence cursor
This commit is contained in:
Rafael Weinstein
2016-05-05 19:17:09 -07:00
parent e6bbc8de19
commit d5495c64ef
18 changed files with 180 additions and 341 deletions
+1 -1
View File
@@ -22,7 +22,7 @@ type Blob interface {
}
func NewEmptyBlob() Blob {
return newBlobLeaf([]byte{})
return newBlobLeaf([]byte{}).(Blob)
}
func newBlobLeafBoundaryChecker() boundaryChecker {
+10 -1
View File
@@ -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)
+2 -3
View File
@@ -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() {
+3 -9
View File
@@ -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 {
+3 -9
View File
@@ -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)
+1 -1
View File
@@ -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
+4 -14
View File
@@ -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 {
+1 -1
View File
@@ -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
+2 -1
View File
@@ -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)
+2 -2
View File
@@ -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() },
+10 -1
View File
@@ -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))
}
+10 -1
View File
@@ -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))
}
+21 -25
View File
@@ -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
}
-57
View File
@@ -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)
})
}
-2
View File
@@ -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
+49 -28
View File
@@ -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
}
+50 -172
View File
@@ -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)
}
+11 -13
View File
@@ -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 {