mirror of
https://github.com/dolthub/dolt.git
synced 2026-05-24 02:43:42 -05:00
8dfbe399a3
This should mean that we calculate the hash of read Values only one, when pulling the chunk out of storage. It means we need to be careful about making sure that we actually do calculate hashes of chunks we read, or that we get the hash from a trusted place.
249 lines
5.3 KiB
Go
249 lines
5.3 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
|
|
}
|
|
|
|
func (m Map) hashPointer() *hash.Hash {
|
|
return m.h
|
|
}
|
|
|
|
// 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 EnsureHash(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
|
|
}
|
|
}
|