mirror of
https://github.com/dolthub/dolt.git
synced 2026-02-10 18:49:02 -06:00
245 lines
5.2 KiB
Go
245 lines
5.2 KiB
Go
package types
|
|
|
|
import (
|
|
"crypto/sha1"
|
|
"sort"
|
|
|
|
"github.com/attic-labs/noms/d"
|
|
"github.com/attic-labs/noms/hash"
|
|
)
|
|
|
|
const (
|
|
mapWindowSize = 1
|
|
mapPattern = uint32(1<<6 - 1) // Average size of 64 elements
|
|
)
|
|
|
|
type Map struct {
|
|
seq orderedSequence
|
|
h *hash.Hash
|
|
}
|
|
|
|
func newMap(seq orderedSequence) Map {
|
|
return Map{seq, &hash.Hash{}}
|
|
}
|
|
|
|
func NewMap(kv ...Value) Map {
|
|
entries := buildMapData(kv)
|
|
seq := newEmptySequenceChunker(makeMapLeafChunkFn(nil), newOrderedMetaSequenceChunkFn(MapKind, nil), newMapLeafBoundaryChecker(), newOrderedMetaSequenceBoundaryChecker)
|
|
|
|
for _, entry := range entries {
|
|
seq.Append(entry)
|
|
}
|
|
|
|
return seq.Done().(Map)
|
|
}
|
|
|
|
func (m Map) Diff(last Map) (added []Value, removed []Value, modified []Value) {
|
|
return orderedSequenceDiff(last.sequence().(orderedSequence), m.sequence().(orderedSequence))
|
|
}
|
|
|
|
// Collection interface
|
|
func (m Map) Len() uint64 {
|
|
return m.seq.numLeaves()
|
|
}
|
|
|
|
func (m Map) Empty() bool {
|
|
return m.Len() == 0
|
|
}
|
|
|
|
func (m Map) sequence() sequence {
|
|
return m.seq
|
|
}
|
|
|
|
// Value interface
|
|
func (m Map) Equals(other Value) bool {
|
|
return other != nil && m.Hash() == other.Hash()
|
|
}
|
|
|
|
func (m Map) Less(other Value) bool {
|
|
return valueLess(m, other)
|
|
}
|
|
|
|
func (m Map) Hash() hash.Hash {
|
|
return EnsureRef(m.h, m)
|
|
}
|
|
|
|
func (m Map) ChildValues() (values []Value) {
|
|
m.IterAll(func(k, v Value) {
|
|
values = append(values, k, v)
|
|
})
|
|
return
|
|
}
|
|
|
|
func (m Map) Chunks() []Ref {
|
|
return m.seq.Chunks()
|
|
}
|
|
|
|
func (m Map) Type() *Type {
|
|
return m.seq.Type()
|
|
}
|
|
|
|
func (m Map) First() (Value, Value) {
|
|
cur := newCursorAtKey(m.seq, nil, false, false)
|
|
if !cur.valid() {
|
|
return nil, nil
|
|
}
|
|
entry := cur.current().(mapEntry)
|
|
return entry.key, entry.value
|
|
}
|
|
|
|
func (m Map) MaybeGet(key Value) (v Value, ok bool) {
|
|
cur := newCursorAtKey(m.seq, key, false, false)
|
|
if !cur.valid() {
|
|
return nil, false
|
|
}
|
|
entry := cur.current().(mapEntry)
|
|
if !entry.key.Equals(key) {
|
|
return nil, false
|
|
}
|
|
|
|
return entry.value, true
|
|
}
|
|
|
|
func (m Map) Set(key Value, val Value) Map {
|
|
return m.SetM(key, val)
|
|
}
|
|
|
|
func (m Map) SetM(kv ...Value) Map {
|
|
if len(kv) == 0 {
|
|
return m
|
|
}
|
|
d.Chk.True(len(kv)%2 == 0)
|
|
|
|
k, v, tail := kv[0], kv[1], kv[2:]
|
|
|
|
cur, found := m.getCursorAtValue(k)
|
|
deleteCount := uint64(0)
|
|
if found {
|
|
deleteCount = 1
|
|
}
|
|
return m.splice(cur, deleteCount, mapEntry{k, v}).SetM(tail...)
|
|
}
|
|
|
|
func (m Map) Remove(k Value) Map {
|
|
if cur, found := m.getCursorAtValue(k); found {
|
|
return m.splice(cur, 1)
|
|
}
|
|
return m
|
|
}
|
|
|
|
func (m Map) splice(cur *sequenceCursor, deleteCount uint64, vs ...mapEntry) Map {
|
|
ch := newSequenceChunker(cur, makeMapLeafChunkFn(m.seq.valueReader()), newOrderedMetaSequenceChunkFn(MapKind, m.seq.valueReader()), newMapLeafBoundaryChecker(), newOrderedMetaSequenceBoundaryChecker)
|
|
for deleteCount > 0 {
|
|
ch.Skip()
|
|
deleteCount--
|
|
}
|
|
|
|
for _, v := range vs {
|
|
ch.Append(v)
|
|
}
|
|
return ch.Done().(Map)
|
|
}
|
|
|
|
func (m Map) getCursorAtValue(v Value) (cur *sequenceCursor, found bool) {
|
|
cur = newCursorAtKey(m.seq, v, true, false)
|
|
found = cur.idx < cur.seq.seqLen() && cur.current().(mapEntry).key.Equals(v)
|
|
return
|
|
}
|
|
|
|
func (m Map) Has(key Value) bool {
|
|
cur := newCursorAtKey(m.seq, key, false, false)
|
|
if !cur.valid() {
|
|
return false
|
|
}
|
|
entry := cur.current().(mapEntry)
|
|
return entry.key.Equals(key)
|
|
}
|
|
|
|
func (m Map) Get(key Value) Value {
|
|
v, _ := m.MaybeGet(key)
|
|
return v
|
|
}
|
|
|
|
type mapIterCallback func(key, value Value) (stop bool)
|
|
|
|
func (m Map) Iter(cb mapIterCallback) {
|
|
cur := newCursorAtKey(m.seq, nil, false, false)
|
|
cur.iter(func(v interface{}) bool {
|
|
entry := v.(mapEntry)
|
|
return cb(entry.key, entry.value)
|
|
})
|
|
}
|
|
|
|
type mapIterAllCallback func(key, value Value)
|
|
|
|
func (m Map) IterAll(cb mapIterAllCallback) {
|
|
cur := newCursorAtKey(m.seq, nil, false, false)
|
|
cur.iter(func(v interface{}) bool {
|
|
entry := v.(mapEntry)
|
|
cb(entry.key, entry.value)
|
|
return false
|
|
})
|
|
}
|
|
|
|
func (m Map) elemTypes() []*Type {
|
|
return m.Type().Desc.(CompoundDesc).ElemTypes
|
|
}
|
|
|
|
func buildMapData(values []Value) mapEntrySlice {
|
|
if len(values) == 0 {
|
|
return mapEntrySlice{}
|
|
}
|
|
|
|
// Sadly, d.Chk.Equals() costs too much. BUG #83
|
|
d.Chk.True(0 == len(values)%2, "Must specify even number of key/value pairs")
|
|
kvs := make(mapEntrySlice, len(values)/2)
|
|
|
|
for i := 0; i < len(values); i += 2 {
|
|
entry := mapEntry{values[i], values[i+1]}
|
|
kvs[i/2] = entry
|
|
}
|
|
|
|
uniqueSorted := make(mapEntrySlice, 0, len(kvs))
|
|
sort.Stable(kvs)
|
|
last := kvs[0]
|
|
for i := 1; i < len(kvs); i++ {
|
|
kv := kvs[i]
|
|
if !kv.key.Equals(last.key) {
|
|
uniqueSorted = append(uniqueSorted, last)
|
|
}
|
|
|
|
last = kv
|
|
}
|
|
|
|
return append(uniqueSorted, last)
|
|
}
|
|
|
|
func newMapLeafBoundaryChecker() boundaryChecker {
|
|
return newBuzHashBoundaryChecker(mapWindowSize, sha1.Size, mapPattern, func(item sequenceItem) []byte {
|
|
digest := item.(mapEntry).key.Hash().Digest()
|
|
return digest[:]
|
|
})
|
|
}
|
|
|
|
func makeMapLeafChunkFn(vr ValueReader) makeChunkFn {
|
|
return func(items []sequenceItem) (metaTuple, Collection) {
|
|
mapData := make([]mapEntry, len(items), len(items))
|
|
|
|
for i, v := range items {
|
|
mapData[i] = v.(mapEntry)
|
|
}
|
|
|
|
m := newMap(newMapLeafSequence(vr, mapData...))
|
|
|
|
var indexValue Value
|
|
if len(mapData) > 0 {
|
|
indexValue = mapData[len(mapData)-1].key
|
|
if !isKindOrderedByValue(indexValue.Type().Kind()) {
|
|
indexValue = NewRef(indexValue)
|
|
}
|
|
}
|
|
|
|
return newMetaTuple(indexValue, m, NewRef(m), uint64(len(items))), m
|
|
}
|
|
}
|