Files
dolt/types/map.go
T
cmasone-attic 8dfbe399a3 When decoding a Chunk, re-use the hash in the decoded Value (#1616)
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.
2016-05-24 17:16:16 -07:00

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
}
}