mirror of
https://github.com/dolthub/dolt.git
synced 2026-01-29 10:41:05 -06:00
Merge pull request #774 from kalman/set-map-insert-remove
Implement chunked insert/remove for sets and maps.
This commit is contained in:
@@ -172,11 +172,11 @@ 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(list sequenceItem, idx int) sequenceItem {
|
||||
return list.(listLeaf).values[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) {
|
||||
list := readMetaTupleValue(mt, cl.cs).(listLeaf)
|
||||
return list, len(list.values)
|
||||
otherLeaf := readMetaTupleValue(mt, cl.cs).(listLeaf)
|
||||
return otherLeaf, len(otherLeaf.values)
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,8 +34,12 @@ func (cm compoundMap) Ref() ref.Ref {
|
||||
return EnsureRef(cm.ref, cm)
|
||||
}
|
||||
|
||||
func (cm compoundMap) Len() uint64 {
|
||||
panic("not implemented")
|
||||
func (cm compoundMap) Len() (length uint64) {
|
||||
// https://github.com/attic-labs/noms/issues/764
|
||||
cm.IterAll(func(k, v Value) {
|
||||
length++
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
func (cm compoundMap) Empty() bool {
|
||||
@@ -43,31 +47,16 @@ func (cm compoundMap) Empty() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// TODO: seek should return false if it failed to find the value
|
||||
func (cm compoundMap) findLeaf(key Value) (*sequenceCursor, mapLeaf) {
|
||||
cursor, leaf := newMetaSequenceCursor(cm, cm.cs)
|
||||
|
||||
var seekFn sequenceCursorSeekBinaryCompareFn
|
||||
if orderedSequenceByIndexedType(cm.t) {
|
||||
orderedKey := key.(OrderedValue)
|
||||
|
||||
seekFn = func(mt sequenceItem) bool {
|
||||
return !mt.(metaTuple).value.(OrderedValue).Less(orderedKey)
|
||||
func (cm compoundMap) findLeaf(key Value) (*sequenceCursor, mapLeaf, int) {
|
||||
cursor, leaf, idx := findLeafInOrderedSequence(cm, cm.t, key, func(v Value) []Value {
|
||||
entries := v.(mapLeaf).data
|
||||
res := make([]Value, len(entries))
|
||||
for i, entry := range entries {
|
||||
res[i] = entry.key
|
||||
}
|
||||
} else {
|
||||
seekFn = func(mt sequenceItem) bool {
|
||||
return !mt.(metaTuple).value.(Ref).TargetRef().Less(key.Ref())
|
||||
}
|
||||
}
|
||||
|
||||
cursor.seekBinary(seekFn)
|
||||
|
||||
current := cursor.current().(metaTuple)
|
||||
if current.ref != valueFromType(cm.cs, leaf, leaf.Type()).Ref() {
|
||||
leaf = readMetaTupleValue(cursor.current(), cm.cs)
|
||||
}
|
||||
|
||||
return cursor, leaf.(mapLeaf)
|
||||
return res
|
||||
}, cm.cs)
|
||||
return cursor, leaf.(mapLeaf), idx
|
||||
}
|
||||
|
||||
func (cm compoundMap) First() (Value, Value) {
|
||||
@@ -76,19 +65,52 @@ func (cm compoundMap) First() (Value, Value) {
|
||||
}
|
||||
|
||||
func (cm compoundMap) MaybeGet(key Value) (v Value, ok bool) {
|
||||
_, leaf := cm.findLeaf(key)
|
||||
_, leaf, _ := cm.findLeaf(key)
|
||||
return leaf.MaybeGet(key)
|
||||
}
|
||||
|
||||
func (cm compoundMap) Set(key Value, val Value) Map {
|
||||
panic("Not implemented")
|
||||
return cm.SetM(key, val)
|
||||
}
|
||||
|
||||
func (cm compoundMap) SetM(kv ...Value) Map {
|
||||
panic("Not implemented")
|
||||
if len(kv) == 0 {
|
||||
return cm
|
||||
}
|
||||
d.Chk.True(len(kv)%2 == 0)
|
||||
|
||||
k, v, tail := kv[0], kv[1], kv[2:]
|
||||
|
||||
seq, found := cm.sequenceChunkerAtKey(k)
|
||||
if found {
|
||||
seq.Skip()
|
||||
}
|
||||
seq.Append(mapEntry{k, v})
|
||||
return seq.Done().(Map).SetM(tail...)
|
||||
}
|
||||
|
||||
func (cm compoundMap) Remove(k Value) Map {
|
||||
panic("Not implemented")
|
||||
if seq, found := cm.sequenceChunkerAtKey(k); found {
|
||||
seq.Skip()
|
||||
return seq.Done().(Map)
|
||||
} else {
|
||||
return cm
|
||||
}
|
||||
}
|
||||
|
||||
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.cs).(mapLeaf)
|
||||
return otherLeaf, len(otherLeaf.data)
|
||||
}}
|
||||
|
||||
seq := newSequenceChunker(cur, makeMapLeafChunkFn(cm.t, cm.cs), newMapMetaSequenceChunkFn(cm.t, cm.cs), newMapLeafBoundaryChecker(), newOrderedMetaSequenceBoundaryChecker)
|
||||
found := idx < len(leaf.data) && leaf.data[idx].key.Equals(k)
|
||||
return seq, found
|
||||
}
|
||||
|
||||
func (cm compoundMap) IterAllP(concurrency int, f mapIterAllCallback) {
|
||||
@@ -104,12 +126,12 @@ func (cm compoundMap) Filter(cb mapFilterCallback) Map {
|
||||
}
|
||||
|
||||
func (cm compoundMap) Has(key Value) bool {
|
||||
_, leaf := cm.findLeaf(key)
|
||||
_, leaf, _ := cm.findLeaf(key)
|
||||
return leaf.Has(key)
|
||||
}
|
||||
|
||||
func (cm compoundMap) Get(key Value) Value {
|
||||
_, leaf := cm.findLeaf(key)
|
||||
_, leaf, _ := cm.findLeaf(key)
|
||||
return leaf.Get(key)
|
||||
}
|
||||
|
||||
|
||||
@@ -10,10 +10,9 @@ import (
|
||||
)
|
||||
|
||||
type testMap struct {
|
||||
entries []mapEntry
|
||||
less testMapLessFn
|
||||
tr Type
|
||||
|
||||
entries []mapEntry
|
||||
less testMapLessFn
|
||||
tr Type
|
||||
knownBadKey Value
|
||||
}
|
||||
|
||||
@@ -31,6 +30,29 @@ func (tm testMap) Swap(i, j int) {
|
||||
tm.entries[i], tm.entries[j] = tm.entries[j], tm.entries[i]
|
||||
}
|
||||
|
||||
func (tm testMap) SetValue(i int, v Value) testMap {
|
||||
entries := make([]mapEntry, 0, len(tm.entries))
|
||||
entries = append(entries, tm.entries...)
|
||||
entries[i].value = v
|
||||
return testMap{entries, tm.less, tm.tr, tm.knownBadKey}
|
||||
}
|
||||
|
||||
func (tm testMap) Remove(from, to int) testMap {
|
||||
entries := make([]mapEntry, 0, len(tm.entries)-(to-from))
|
||||
entries = append(entries, tm.entries[:from]...)
|
||||
entries = append(entries, tm.entries[to:]...)
|
||||
return testMap{entries, tm.less, tm.tr, tm.knownBadKey}
|
||||
}
|
||||
|
||||
func (tm testMap) Flatten(from, to int) []Value {
|
||||
flat := make([]Value, 0, len(tm.entries)*2)
|
||||
for _, entry := range tm.entries[from:to] {
|
||||
flat = append(flat, entry.key)
|
||||
flat = append(flat, entry.value)
|
||||
}
|
||||
return flat
|
||||
}
|
||||
|
||||
func (tm testMap) toCompoundMap(cs chunks.ChunkStore) compoundMap {
|
||||
keyvals := []Value{}
|
||||
for _, entry := range tm.entries {
|
||||
@@ -59,36 +81,36 @@ func newTestMap(length int, gen testMapGenFn, less testMapLessFn, tr Type) testM
|
||||
return testMap{entries, less, MakeCompoundType(MapKind, tr, tr), gen(Int64(mask + 1))}
|
||||
}
|
||||
|
||||
func getTestNativeOrderMap() testMap {
|
||||
return newTestMap(int(mapPattern*16), func(v Int64) Value {
|
||||
func getTestNativeOrderMap(scale int) testMap {
|
||||
return newTestMap(int(mapPattern)*scale, func(v Int64) Value {
|
||||
return v
|
||||
}, func(x, y Value) bool {
|
||||
return !y.(OrderedValue).Less(x.(OrderedValue))
|
||||
}, MakePrimitiveType(Int64Kind))
|
||||
}
|
||||
|
||||
func getTestRefValueOrderMap() testMap {
|
||||
func getTestRefValueOrderMap(scale int) testMap {
|
||||
setType := MakeCompoundType(SetKind, MakePrimitiveType(Int64Kind))
|
||||
return newTestMap(int(mapPattern*2), func(v Int64) Value {
|
||||
return newTestMap(int(mapPattern)*scale, func(v Int64) Value {
|
||||
return NewTypedSet(chunks.NewMemoryStore(), setType, v)
|
||||
}, func(x, y Value) bool {
|
||||
return !y.Ref().Less(x.Ref())
|
||||
}, setType)
|
||||
}
|
||||
|
||||
func getTestRefToNativeOrderMap() testMap {
|
||||
func getTestRefToNativeOrderMap(scale int) testMap {
|
||||
refType := MakeCompoundType(RefKind, MakePrimitiveType(Int64Kind))
|
||||
return newTestMap(int(mapPattern*2), func(v Int64) Value {
|
||||
return newTestMap(int(mapPattern)*scale, func(v Int64) Value {
|
||||
return newRef(v.Ref(), refType)
|
||||
}, func(x, y Value) bool {
|
||||
return !y.(RefBase).TargetRef().Less(x.(RefBase).TargetRef())
|
||||
}, refType)
|
||||
}
|
||||
|
||||
func getTestRefToValueOrderMap() testMap {
|
||||
func getTestRefToValueOrderMap(scale int) testMap {
|
||||
setType := MakeCompoundType(SetKind, MakePrimitiveType(Int64Kind))
|
||||
refType := MakeCompoundType(RefKind, setType)
|
||||
return newTestMap(int(mapPattern*2), func(v Int64) Value {
|
||||
return newTestMap(int(mapPattern)*scale, func(v Int64) Value {
|
||||
return newRef(NewTypedSet(chunks.NewMemoryStore(), setType, v).Ref(), refType)
|
||||
}, func(x, y Value) bool {
|
||||
return !y.(RefBase).TargetRef().Less(x.(RefBase).TargetRef())
|
||||
@@ -106,10 +128,10 @@ func TestCompoundMapHas(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
doTest(getTestNativeOrderMap())
|
||||
doTest(getTestRefValueOrderMap())
|
||||
doTest(getTestRefToNativeOrderMap())
|
||||
doTest(getTestRefToValueOrderMap())
|
||||
doTest(getTestNativeOrderMap(16))
|
||||
doTest(getTestRefValueOrderMap(2))
|
||||
doTest(getTestRefToNativeOrderMap(2))
|
||||
doTest(getTestRefToValueOrderMap(2))
|
||||
}
|
||||
|
||||
func TestCompoundMapFirst(t *testing.T) {
|
||||
@@ -123,10 +145,10 @@ func TestCompoundMapFirst(t *testing.T) {
|
||||
assert.True(tm.entries[0].value.Equals(actualValue))
|
||||
}
|
||||
|
||||
doTest(getTestNativeOrderMap())
|
||||
doTest(getTestRefValueOrderMap())
|
||||
doTest(getTestRefToNativeOrderMap())
|
||||
doTest(getTestRefToValueOrderMap())
|
||||
doTest(getTestNativeOrderMap(16))
|
||||
doTest(getTestRefValueOrderMap(2))
|
||||
doTest(getTestRefToNativeOrderMap(2))
|
||||
doTest(getTestRefToValueOrderMap(2))
|
||||
}
|
||||
|
||||
func TestCompoundMapMaybeGet(t *testing.T) {
|
||||
@@ -144,10 +166,10 @@ func TestCompoundMapMaybeGet(t *testing.T) {
|
||||
assert.False(ok, "m should not contain %v", tm.knownBadKey)
|
||||
}
|
||||
|
||||
doTest(getTestNativeOrderMap())
|
||||
doTest(getTestRefValueOrderMap())
|
||||
doTest(getTestRefToNativeOrderMap())
|
||||
doTest(getTestRefToValueOrderMap())
|
||||
doTest(getTestNativeOrderMap(2))
|
||||
doTest(getTestRefValueOrderMap(2))
|
||||
doTest(getTestRefToNativeOrderMap(2))
|
||||
doTest(getTestRefToValueOrderMap(2))
|
||||
}
|
||||
|
||||
func TestCompoundMapIter(t *testing.T) {
|
||||
@@ -174,10 +196,10 @@ func TestCompoundMapIter(t *testing.T) {
|
||||
assert.Equal(endAt, idx-1)
|
||||
}
|
||||
|
||||
doTest(getTestNativeOrderMap())
|
||||
doTest(getTestRefValueOrderMap())
|
||||
doTest(getTestRefToNativeOrderMap())
|
||||
doTest(getTestRefToValueOrderMap())
|
||||
doTest(getTestNativeOrderMap(16))
|
||||
doTest(getTestRefValueOrderMap(2))
|
||||
doTest(getTestRefToNativeOrderMap(2))
|
||||
doTest(getTestRefToValueOrderMap(2))
|
||||
}
|
||||
|
||||
func TestCompoundMapIterAll(t *testing.T) {
|
||||
@@ -195,8 +217,108 @@ func TestCompoundMapIterAll(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
doTest(getTestNativeOrderMap())
|
||||
doTest(getTestRefValueOrderMap())
|
||||
doTest(getTestRefToNativeOrderMap())
|
||||
doTest(getTestRefToValueOrderMap())
|
||||
doTest(getTestNativeOrderMap(16))
|
||||
doTest(getTestRefValueOrderMap(2))
|
||||
doTest(getTestRefToNativeOrderMap(2))
|
||||
doTest(getTestRefToValueOrderMap(2))
|
||||
}
|
||||
|
||||
func TestCompoundMapSet(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
doTest := func(incr int, tm testMap) {
|
||||
cs := chunks.NewMemoryStore()
|
||||
expected := tm.toCompoundMap(cs)
|
||||
run := func(from, to int) {
|
||||
actual := tm.Remove(from, to).toCompoundMap(cs).SetM(tm.Flatten(from, to)...)
|
||||
assert.Equal(expected.Len(), actual.Len())
|
||||
assert.True(expected.Equals(actual))
|
||||
}
|
||||
for i := 0; i < len(tm.entries); i += incr {
|
||||
run(i, i+1)
|
||||
}
|
||||
// TODO: make this pass, and make it fast:
|
||||
// for i := 0; i < len(tm.entries)-incr; i += incr {
|
||||
// run(i, i+incr)
|
||||
// }
|
||||
// For example, run(256, 384) fails with the native order map.
|
||||
}
|
||||
|
||||
doTest(128, getTestNativeOrderMap(32))
|
||||
doTest(64, getTestRefValueOrderMap(4))
|
||||
doTest(64, getTestRefToNativeOrderMap(4))
|
||||
doTest(64, getTestRefToValueOrderMap(4))
|
||||
}
|
||||
|
||||
func TestCompoundMapSetExistingKeyToExistingValue(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
cs := chunks.NewMemoryStore()
|
||||
tm := getTestNativeOrderMap(2)
|
||||
original := tm.toCompoundMap(cs)
|
||||
|
||||
actual := original
|
||||
for _, entry := range tm.entries {
|
||||
actual = actual.Set(entry.key, entry.value).(compoundMap)
|
||||
}
|
||||
|
||||
assert.Equal(original.Len(), actual.Len())
|
||||
assert.True(original.Equals(actual))
|
||||
}
|
||||
|
||||
func TestCompoundMapSetExistingKeyToNewValue(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
cs := chunks.NewMemoryStore()
|
||||
tm := getTestNativeOrderMap(2)
|
||||
original := tm.toCompoundMap(cs)
|
||||
|
||||
expectedWorking := tm
|
||||
actual := original
|
||||
for i, entry := range tm.entries {
|
||||
newValue := Int64(int64(entry.value.(Int64)) + 1)
|
||||
expectedWorking = expectedWorking.SetValue(i, newValue)
|
||||
actual = actual.Set(entry.key, newValue).(compoundMap)
|
||||
}
|
||||
|
||||
expected := expectedWorking.toCompoundMap(cs)
|
||||
assert.Equal(expected.Len(), actual.Len())
|
||||
assert.True(expected.Equals(actual))
|
||||
assert.False(original.Equals(actual))
|
||||
}
|
||||
|
||||
func TestCompoundMapRemove(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
doTest := func(incr int, tm testMap) {
|
||||
cs := chunks.NewMemoryStore()
|
||||
whole := tm.toCompoundMap(cs)
|
||||
run := func(i int) {
|
||||
expected := tm.Remove(i, i+1).toCompoundMap(cs)
|
||||
actual := whole.Remove(tm.entries[i].key)
|
||||
assert.Equal(expected.Len(), actual.Len())
|
||||
assert.True(expected.Equals(actual))
|
||||
}
|
||||
for i := 0; i < len(tm.entries); i += incr {
|
||||
run(i)
|
||||
}
|
||||
run(len(tm.entries) - 1)
|
||||
}
|
||||
|
||||
doTest(128, getTestNativeOrderMap(32))
|
||||
doTest(64, getTestRefValueOrderMap(4))
|
||||
doTest(64, getTestRefToNativeOrderMap(4))
|
||||
doTest(64, getTestRefToValueOrderMap(4))
|
||||
}
|
||||
|
||||
func TestCompoundMapRemoveNonexistentKey(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
cs := chunks.NewMemoryStore()
|
||||
tm := getTestNativeOrderMap(2)
|
||||
original := tm.toCompoundMap(cs)
|
||||
actual := original.Remove(Int64(-1)) // rand.Int63 returns non-negative numbers.
|
||||
|
||||
assert.Equal(original.Len(), actual.Len())
|
||||
assert.True(original.Equals(actual))
|
||||
}
|
||||
|
||||
@@ -35,8 +35,12 @@ func (cs compoundSet) Ref() ref.Ref {
|
||||
return EnsureRef(cs.ref, cs)
|
||||
}
|
||||
|
||||
func (cs compoundSet) Len() uint64 {
|
||||
panic("not implemented")
|
||||
func (cs compoundSet) Len() (length uint64) {
|
||||
// https://github.com/attic-labs/noms/issues/764
|
||||
cs.IterAll(func(v Value) {
|
||||
length++
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
func (cs compoundSet) Empty() bool {
|
||||
@@ -50,11 +54,54 @@ func (cs compoundSet) First() Value {
|
||||
}
|
||||
|
||||
func (cs compoundSet) Insert(values ...Value) Set {
|
||||
panic("not implemented")
|
||||
if len(values) == 0 {
|
||||
return cs
|
||||
}
|
||||
|
||||
head, tail := values[0], values[1:]
|
||||
|
||||
var res Set
|
||||
if seq, found := cs.sequenceChunkerAtValue(head); !found {
|
||||
seq.Append(head)
|
||||
res = seq.Done().(Set)
|
||||
} else {
|
||||
res = cs
|
||||
}
|
||||
|
||||
return res.Insert(tail...)
|
||||
}
|
||||
|
||||
func (cs compoundSet) Remove(values ...Value) Set {
|
||||
panic("not implemented")
|
||||
if len(values) == 0 {
|
||||
return cs
|
||||
}
|
||||
|
||||
head, tail := values[0], values[1:]
|
||||
|
||||
var res Set
|
||||
if seq, found := cs.sequenceChunkerAtValue(head); found {
|
||||
seq.Skip()
|
||||
res = seq.Done().(Set)
|
||||
} else {
|
||||
res = cs
|
||||
}
|
||||
|
||||
return res.Remove(tail...)
|
||||
}
|
||||
|
||||
func (cs compoundSet) sequenceChunkerAtValue(v Value) (*sequenceChunker, bool) {
|
||||
metaCur, leaf, idx := cs.findLeaf(v)
|
||||
|
||||
cur := &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.cs).(setLeaf)
|
||||
return otherLeaf, len(otherLeaf.data)
|
||||
}}
|
||||
|
||||
seq := newSequenceChunker(cur, makeSetLeafChunkFn(cs.t, cs.cs), newSetMetaSequenceChunkFn(cs.t, cs.cs), newSetLeafBoundaryChecker(), newOrderedMetaSequenceBoundaryChecker)
|
||||
found := idx < len(leaf.data) && leaf.data[idx].Equals(v)
|
||||
return seq, found
|
||||
}
|
||||
|
||||
func (cs compoundSet) Union(others ...Set) Set {
|
||||
@@ -69,35 +116,15 @@ func (cs compoundSet) Filter(cb setFilterCallback) Set {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
// TODO: seek should return false if it failed to find the value
|
||||
func (cs compoundSet) findLeaf(key Value) (*sequenceCursor, setLeaf) {
|
||||
cursor, leaf := newMetaSequenceCursor(cs, cs.cs)
|
||||
|
||||
var seekFn sequenceCursorSeekBinaryCompareFn
|
||||
if orderedSequenceByIndexedType(cs.t) {
|
||||
orderedKey := key.(OrderedValue)
|
||||
|
||||
seekFn = func(mt sequenceItem) bool {
|
||||
return !mt.(metaTuple).value.(OrderedValue).Less(orderedKey)
|
||||
}
|
||||
} else {
|
||||
seekFn = func(mt sequenceItem) bool {
|
||||
return !mt.(metaTuple).value.(Ref).TargetRef().Less(key.Ref())
|
||||
}
|
||||
}
|
||||
|
||||
cursor.seekBinary(seekFn)
|
||||
|
||||
current := cursor.current().(metaTuple)
|
||||
if current.ref != valueFromType(cs.cs, leaf, leaf.Type()).Ref() {
|
||||
leaf = readMetaTupleValue(cursor.current(), cs.cs)
|
||||
}
|
||||
|
||||
return cursor, leaf.(setLeaf)
|
||||
func (cs compoundSet) findLeaf(key Value) (*sequenceCursor, setLeaf, int) {
|
||||
cursor, leaf, idx := findLeafInOrderedSequence(cs, cs.t, key, func(v Value) []Value {
|
||||
return v.(setLeaf).data
|
||||
}, cs.cs)
|
||||
return cursor, leaf.(setLeaf), idx
|
||||
}
|
||||
|
||||
func (cs compoundSet) Has(key Value) bool {
|
||||
_, leaf := cs.findLeaf(key)
|
||||
_, leaf, _ := cs.findLeaf(key)
|
||||
return leaf.Has(key)
|
||||
}
|
||||
|
||||
@@ -127,10 +154,6 @@ func (cs compoundSet) IterAllP(concurrency int, f setIterAllCallback) {
|
||||
})
|
||||
}
|
||||
|
||||
func orderedSequenceByIndexedType(t Type) bool {
|
||||
return t.Desc.(CompoundDesc).ElemTypes[0].IsOrdered()
|
||||
}
|
||||
|
||||
func newSetMetaSequenceChunkFn(t Type, cs chunks.ChunkStore) makeChunkFn {
|
||||
return func(items []sequenceItem) (sequenceItem, Value) {
|
||||
tuples := make(metaSequenceData, len(items))
|
||||
@@ -139,9 +162,9 @@ func newSetMetaSequenceChunkFn(t Type, cs chunks.ChunkStore) makeChunkFn {
|
||||
tuples[i] = v.(metaTuple)
|
||||
}
|
||||
|
||||
lastIndex := tuples[len(tuples)-1].value
|
||||
lastValue := tuples[len(tuples)-1].value
|
||||
meta := newMetaSequenceFromData(tuples, t, cs)
|
||||
ref := WriteValue(meta, cs)
|
||||
return metaTuple{ref, lastIndex}, meta
|
||||
return metaTuple{ref, lastValue}, meta
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,14 @@ func (ts testSet) Swap(i, j int) {
|
||||
ts.values[i], ts.values[j] = ts.values[j], ts.values[i]
|
||||
}
|
||||
|
||||
func (ts testSet) toCompoundSet(cs chunks.ChunkStore) compoundSet {
|
||||
func (ts testSet) Remove(from, to int) testSet {
|
||||
values := make([]Value, 0, len(ts.values)-(to-from))
|
||||
values = append(values, ts.values[:from]...)
|
||||
values = append(values, ts.values[to:]...)
|
||||
return testSet{values, ts.less, ts.tr}
|
||||
}
|
||||
|
||||
func (ts testSet) toCompoundSet(cs chunks.ChunkStore) Set {
|
||||
return NewTypedSet(cs, ts.tr, ts.values...).(compoundSet)
|
||||
}
|
||||
|
||||
@@ -51,36 +58,36 @@ func newTestSet(length int, gen testSetGenFn, less testSetLessFn, tr Type) testS
|
||||
return testSet{values, less, MakeCompoundType(SetKind, tr)}
|
||||
}
|
||||
|
||||
func getTestNativeOrderSet() testSet {
|
||||
return newTestSet(int(setPattern*16), func(v Int64) Value {
|
||||
func getTestNativeOrderSet(scale int) testSet {
|
||||
return newTestSet(int(setPattern)*scale, func(v Int64) Value {
|
||||
return v
|
||||
}, func(x, y Value) bool {
|
||||
return !y.(OrderedValue).Less(x.(OrderedValue))
|
||||
}, MakePrimitiveType(Int64Kind))
|
||||
}
|
||||
|
||||
func getTestRefValueOrderSet() testSet {
|
||||
func getTestRefValueOrderSet(scale int) testSet {
|
||||
setType := MakeCompoundType(SetKind, MakePrimitiveType(Int64Kind))
|
||||
return newTestSet(int(setPattern*2), func(v Int64) Value {
|
||||
return newTestSet(int(setPattern)*scale, func(v Int64) Value {
|
||||
return NewTypedSet(chunks.NewMemoryStore(), setType, v)
|
||||
}, func(x, y Value) bool {
|
||||
return !y.Ref().Less(x.Ref())
|
||||
}, setType)
|
||||
}
|
||||
|
||||
func getTestRefToNativeOrderSet() testSet {
|
||||
func getTestRefToNativeOrderSet(scale int) testSet {
|
||||
refType := MakeCompoundType(RefKind, MakePrimitiveType(Int64Kind))
|
||||
return newTestSet(int(setPattern*2), func(v Int64) Value {
|
||||
return newTestSet(int(setPattern)*scale, func(v Int64) Value {
|
||||
return newRef(v.Ref(), refType)
|
||||
}, func(x, y Value) bool {
|
||||
return !y.(RefBase).TargetRef().Less(x.(RefBase).TargetRef())
|
||||
}, refType)
|
||||
}
|
||||
|
||||
func getTestRefToValueOrderSet() testSet {
|
||||
func getTestRefToValueOrderSet(scale int) testSet {
|
||||
setType := MakeCompoundType(SetKind, MakePrimitiveType(Int64Kind))
|
||||
refType := MakeCompoundType(RefKind, setType)
|
||||
return newTestSet(int(setPattern*2), func(v Int64) Value {
|
||||
return newTestSet(int(setPattern)*scale, func(v Int64) Value {
|
||||
return newRef(NewTypedSet(chunks.NewMemoryStore(), setType, v).Ref(), refType)
|
||||
}, func(x, y Value) bool {
|
||||
return !y.(RefBase).TargetRef().Less(x.(RefBase).TargetRef())
|
||||
@@ -97,10 +104,10 @@ func TestCompoundSetHas(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
doTest(getTestNativeOrderSet())
|
||||
doTest(getTestRefValueOrderSet())
|
||||
doTest(getTestRefToNativeOrderSet())
|
||||
doTest(getTestRefToValueOrderSet())
|
||||
doTest(getTestNativeOrderSet(16))
|
||||
doTest(getTestRefValueOrderSet(2))
|
||||
doTest(getTestRefToNativeOrderSet(2))
|
||||
doTest(getTestRefToValueOrderSet(2))
|
||||
}
|
||||
|
||||
func TestCompoundSetFirst(t *testing.T) {
|
||||
@@ -113,10 +120,10 @@ func TestCompoundSetFirst(t *testing.T) {
|
||||
assert.True(ts.values[0].Equals(actual), "%v != %v", ts.values[0], actual)
|
||||
}
|
||||
|
||||
doTest(getTestNativeOrderSet())
|
||||
doTest(getTestRefValueOrderSet())
|
||||
doTest(getTestRefToNativeOrderSet())
|
||||
doTest(getTestRefToValueOrderSet())
|
||||
doTest(getTestNativeOrderSet(16))
|
||||
doTest(getTestRefValueOrderSet(2))
|
||||
doTest(getTestRefToNativeOrderSet(2))
|
||||
doTest(getTestRefToValueOrderSet(2))
|
||||
}
|
||||
|
||||
func TestCompoundSetIter(t *testing.T) {
|
||||
@@ -142,10 +149,10 @@ func TestCompoundSetIter(t *testing.T) {
|
||||
assert.Equal(endAt, idx-1)
|
||||
}
|
||||
|
||||
doTest(getTestNativeOrderSet())
|
||||
doTest(getTestRefValueOrderSet())
|
||||
doTest(getTestRefToNativeOrderSet())
|
||||
doTest(getTestRefToValueOrderSet())
|
||||
doTest(getTestNativeOrderSet(16))
|
||||
doTest(getTestRefValueOrderSet(2))
|
||||
doTest(getTestRefToNativeOrderSet(2))
|
||||
doTest(getTestRefToValueOrderSet(2))
|
||||
}
|
||||
|
||||
func TestCompoundSetIterAll(t *testing.T) {
|
||||
@@ -162,8 +169,89 @@ func TestCompoundSetIterAll(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
doTest(getTestNativeOrderSet())
|
||||
doTest(getTestRefValueOrderSet())
|
||||
doTest(getTestRefToNativeOrderSet())
|
||||
doTest(getTestRefToValueOrderSet())
|
||||
doTest(getTestNativeOrderSet(16))
|
||||
doTest(getTestRefValueOrderSet(2))
|
||||
doTest(getTestRefToNativeOrderSet(2))
|
||||
doTest(getTestRefToValueOrderSet(2))
|
||||
}
|
||||
|
||||
func TestCompoundSetInsert(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
doTest := func(incr int, ts testSet) {
|
||||
cs := chunks.NewMemoryStore()
|
||||
expected := ts.toCompoundSet(cs)
|
||||
run := func(from, to int) {
|
||||
actual := ts.Remove(from, to).toCompoundSet(cs).Insert(ts.values[from:to]...)
|
||||
assert.Equal(expected.Len(), actual.Len(), "%d-%d", from, to)
|
||||
assert.True(expected.Equals(actual))
|
||||
}
|
||||
for i := 0; i < len(ts.values); i += incr {
|
||||
run(i, i+1)
|
||||
}
|
||||
run(len(ts.values)-1, len(ts.values))
|
||||
// TODO: make this pass, and make it fast:
|
||||
// for i := 0; i < len(ts.values)-incr; i += incr {
|
||||
// run(i, i+incr)
|
||||
// }
|
||||
// For example, run(896, 960) fails for the native order set.
|
||||
}
|
||||
|
||||
doTest(64, getTestNativeOrderSet(32))
|
||||
doTest(32, getTestRefValueOrderSet(4))
|
||||
doTest(32, getTestRefToNativeOrderSet(4))
|
||||
doTest(32, getTestRefToValueOrderSet(4))
|
||||
}
|
||||
|
||||
func TestCompoundSetInsertExistingValue(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
cs := chunks.NewMemoryStore()
|
||||
ts := getTestNativeOrderSet(2)
|
||||
original := ts.toCompoundSet(cs)
|
||||
actual := original.Insert(ts.values[0])
|
||||
|
||||
assert.Equal(original.Len(), actual.Len())
|
||||
assert.True(original.Equals(actual))
|
||||
}
|
||||
|
||||
func TestCompoundSetRemove(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
doTest := func(incr int, ts testSet) {
|
||||
cs := chunks.NewMemoryStore()
|
||||
whole := ts.toCompoundSet(cs)
|
||||
run := func(from, to int) {
|
||||
expected := ts.Remove(from, to).toCompoundSet(cs)
|
||||
actual := whole.Remove(ts.values[from:to]...)
|
||||
assert.Equal(expected.Len(), actual.Len(), "%d-%d", from, to)
|
||||
assert.True(expected.Equals(actual))
|
||||
}
|
||||
for i := 0; i < len(ts.values); i += incr {
|
||||
run(i, i+1)
|
||||
}
|
||||
run(len(ts.values)-1, len(ts.values))
|
||||
// TODO: make this pass, and make it fast:
|
||||
// for i := 0; i < len(ts.values)-incr; i += incr {
|
||||
// run(i, i+incr)
|
||||
// }
|
||||
// For example, run(448, 512) fails for the native order set.
|
||||
}
|
||||
|
||||
doTest(64, getTestNativeOrderSet(32))
|
||||
doTest(32, getTestRefValueOrderSet(4))
|
||||
doTest(32, getTestRefToNativeOrderSet(4))
|
||||
doTest(32, getTestRefToValueOrderSet(4))
|
||||
}
|
||||
|
||||
func TestCompoundSetRemoveNonexistentValue(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
cs := chunks.NewMemoryStore()
|
||||
ts := getTestNativeOrderSet(2)
|
||||
original := ts.toCompoundSet(cs)
|
||||
actual := original.Remove(Int64(-1)) // rand.Int63 returns non-negative values.
|
||||
|
||||
assert.Equal(original.Len(), actual.Len())
|
||||
assert.True(original.Equals(actual))
|
||||
}
|
||||
|
||||
@@ -249,7 +249,7 @@ func makeMapLeafChunkFn(t Type, cs chunks.ChunkStore) makeChunkFn {
|
||||
var indexValue Value
|
||||
if len(mapData) > 0 {
|
||||
lastValue := mapData[len(mapData)-1]
|
||||
if orderedSequenceByIndexedType(t) {
|
||||
if isSequenceOrderedByIndexedType(t) {
|
||||
indexValue = lastValue.key
|
||||
} else {
|
||||
indexValue = NewRef(lastValue.key.Ref())
|
||||
|
||||
@@ -138,11 +138,11 @@ func newMetaSequenceCursor(root metaSequence, cs chunks.ChunkStore) (*sequenceCu
|
||||
d.Chk.NotNil(root)
|
||||
|
||||
newCursor := func(parent *sequenceCursor, ms metaSequence) *sequenceCursor {
|
||||
return &sequenceCursor{parent, ms, 0, ms.tupleCount(), func(item sequenceItem, idx int) sequenceItem {
|
||||
return item.(metaSequence).tupleAt(idx)
|
||||
return &sequenceCursor{parent, ms, 0, ms.tupleCount(), func(otherMs sequenceItem, idx int) sequenceItem {
|
||||
return otherMs.(metaSequence).tupleAt(idx)
|
||||
}, func(item sequenceItem) (sequenceItem, int) {
|
||||
ms := readMetaTupleValue(item, cs).(metaSequence)
|
||||
return ms, ms.tupleCount()
|
||||
otherMs := readMetaTupleValue(item, cs).(metaSequence)
|
||||
return otherMs, otherMs.tupleCount()
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
49
types/ordered_sequences.go
Normal file
49
types/ordered_sequences.go
Normal file
@@ -0,0 +1,49 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"github.com/attic-labs/noms/chunks"
|
||||
)
|
||||
|
||||
func isSequenceOrderedByIndexedType(t Type) bool {
|
||||
return t.Desc.(CompoundDesc).ElemTypes[0].IsOrdered()
|
||||
}
|
||||
|
||||
// Given a leaf in an ordered sequence, returns the values in that leaf which define the ordering of the sequence.
|
||||
type getLeafOrderedValuesFn func(Value) []Value
|
||||
|
||||
// Returns a cursor to |key| in |ms|, plus the leaf + index that |key| is in. |t| is the type of the ordered values.
|
||||
func findLeafInOrderedSequence(ms metaSequence, t Type, key Value, getValues getLeafOrderedValuesFn, cs chunks.ChunkStore) (cursor *sequenceCursor, leaf Value, idx int) {
|
||||
cursor, leaf = newMetaSequenceCursor(ms, cs)
|
||||
|
||||
if isSequenceOrderedByIndexedType(t) {
|
||||
orderedKey := key.(OrderedValue)
|
||||
|
||||
cursor.seekBinary(func(mt sequenceItem) bool {
|
||||
return !mt.(metaTuple).value.(OrderedValue).Less(orderedKey)
|
||||
})
|
||||
} else {
|
||||
cursor.seekBinary(func(mt sequenceItem) bool {
|
||||
return !mt.(metaTuple).value.(Ref).TargetRef().Less(key.Ref())
|
||||
})
|
||||
}
|
||||
|
||||
if current := cursor.current().(metaTuple); current.ref != valueFromType(cs, leaf, leaf.Type()).Ref() {
|
||||
leaf = readMetaTupleValue(cursor.current(), cs)
|
||||
}
|
||||
|
||||
if leafData := getValues(leaf); isSequenceOrderedByIndexedType(t) {
|
||||
orderedKey := key.(OrderedValue)
|
||||
|
||||
idx = sort.Search(len(leafData), func(i int) bool {
|
||||
return !leafData[i].(OrderedValue).Less(orderedKey)
|
||||
})
|
||||
} else {
|
||||
idx = sort.Search(len(leafData), func(i int) bool {
|
||||
return !leafData[i].Ref().Less(key.Ref())
|
||||
})
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
@@ -231,7 +231,7 @@ func makeSetLeafChunkFn(t Type, cs chunks.ChunkStore) makeChunkFn {
|
||||
var indexValue Value
|
||||
if len(setData) > 0 {
|
||||
lastValue := setData[len(setData)-1]
|
||||
if orderedSequenceByIndexedType(t) {
|
||||
if isSequenceOrderedByIndexedType(t) {
|
||||
indexValue = lastValue
|
||||
} else {
|
||||
indexValue = NewRef(lastValue.Ref())
|
||||
|
||||
Reference in New Issue
Block a user