diff --git a/types/blob.go b/types/blob.go index 1435a0639d..b917d1fb7a 100644 --- a/types/blob.go +++ b/types/blob.go @@ -1,13 +1,37 @@ package types -import "io" +import ( + "bytes" + "io" -type Blob interface { - Value - Len() uint64 - Reader() io.Reader + "github.com/attic-labs/noms/ref" +) + +type Blob struct { + data []byte + cr *cachedRef +} + +func (fb Blob) Reader() io.Reader { + return bytes.NewBuffer(fb.data) +} + +func (fb Blob) Len() uint64 { + return uint64(len(fb.data)) +} + +func (fb Blob) Ref() ref.Ref { + return fb.cr.Ref(fb) +} + +func (fb Blob) Equals(other Value) bool { + if other == nil { + return false + } else { + return fb.Ref() == other.Ref() + } } func NewBlob(data []byte) Blob { - return flatBlob{data, &cachedRef{}} + return Blob{data, &cachedRef{}} } diff --git a/types/flat_blob.go b/types/flat_blob.go deleted file mode 100644 index 5252eac419..0000000000 --- a/types/flat_blob.go +++ /dev/null @@ -1,33 +0,0 @@ -package types - -import ( - "bytes" - "io" - - "github.com/attic-labs/noms/ref" -) - -type flatBlob struct { - data []byte - cr *cachedRef -} - -func (fb flatBlob) Reader() io.Reader { - return bytes.NewBuffer(fb.data) -} - -func (fb flatBlob) Len() uint64 { - return uint64(len(fb.data)) -} - -func (fb flatBlob) Ref() ref.Ref { - return fb.cr.Ref(fb) -} - -func (fb flatBlob) Equals(other Value) bool { - if other == nil { - return false - } else { - return fb.Ref() == other.Ref() - } -} diff --git a/types/flat_list.go b/types/flat_list.go deleted file mode 100644 index f462bb357d..0000000000 --- a/types/flat_list.go +++ /dev/null @@ -1,84 +0,0 @@ -package types - -import ( - "github.com/attic-labs/noms/chunks" - . "github.com/attic-labs/noms/dbg" - "github.com/attic-labs/noms/ref" -) - -// flatList is a quick 'n easy implementation of List. -// It should eventually be replaced by a chunking implementation. -type flatList struct { - list []future - cr *cachedRef - cs chunks.ChunkSource -} - -func valuesToFutures(list []Value) []future { - f := []future{} - for _, v := range list { - f = append(f, futureFromValue(v)) - } - return f -} - -func newFlatList(list []future, cs chunks.ChunkSource) List { - return flatList{list, &cachedRef{}, cs} -} - -func (l flatList) Len() uint64 { - return uint64(len(l.list)) -} - -func (l flatList) Get(idx uint64) Value { - v, err := l.list[idx].Deref(l.cs) - // This is the kind of thing that makes me feel like hiding deref'ing is probably not the right idea. But we'll go with it for now. - Chk.NoError(err) - return v -} - -func (l flatList) Slice(start uint64, end uint64) List { - return newFlatList(l.list[start:end], l.cs) -} - -func (l flatList) Set(idx uint64, v Value) List { - b := make([]future, len(l.list)) - copy(b, l.list) - b[idx] = futureFromValue(v) - return newFlatList(b, l.cs) -} - -func (l flatList) Append(v ...Value) List { - return newFlatList(append(l.list, valuesToFutures(v)...), l.cs) -} - -func (l flatList) Insert(idx uint64, v ...Value) List { - b := make([]future, len(l.list)+len(v)) - copy(b, l.list[:idx]) - copy(b[idx:], valuesToFutures(v)) - copy(b[idx+uint64(len(v)):], l.list[idx:]) - return newFlatList(b, l.cs) -} - -func (l flatList) Remove(start uint64, end uint64) List { - b := make([]future, uint64(len(l.list))-(end-start)) - copy(b, l.list[:start]) - copy(b[start:], l.list[end:]) - return newFlatList(b, l.cs) -} - -func (l flatList) RemoveAt(idx uint64) List { - return l.Remove(idx, idx+1) -} - -func (l flatList) Ref() ref.Ref { - return l.cr.Ref(l) -} - -func (l flatList) Equals(other Value) bool { - if other == nil { - return false - } else { - return l.Ref() == other.Ref() - } -} diff --git a/types/flat_map.go b/types/flat_map.go deleted file mode 100644 index 8f607d87a4..0000000000 --- a/types/flat_map.go +++ /dev/null @@ -1,88 +0,0 @@ -package types - -import ( - . "github.com/attic-labs/noms/dbg" - "github.com/attic-labs/noms/ref" -) - -type mapData map[ref.Ref]MapEntry - -type flatMap struct { - m mapData - cr *cachedRef -} - -func newFlatMap(m mapData) flatMap { - return flatMap{m, &cachedRef{}} -} - -func (fm flatMap) Len() uint64 { - return uint64(len(fm.m)) -} - -func (fm flatMap) Has(key Value) bool { - _, ok := fm.m[key.Ref()] - return ok -} - -func (fm flatMap) Get(key Value) Value { - if v, ok := fm.m[key.Ref()]; ok { - return v.Value - } else { - return nil - } -} - -func (fm flatMap) Set(key Value, val Value) Map { - return newFlatMap(buildMapData(fm.m, key, val)) -} - -func (fm flatMap) SetM(kv ...Value) Map { - return newFlatMap(buildMapData(fm.m, kv...)) -} - -func (fm flatMap) Remove(k Value) Map { - m := copyMapData(fm.m) - delete(m, k.Ref()) - return newFlatMap(m) -} - -func (fm flatMap) Iter(cb mapIterCallback) { - for _, v := range fm.m { - if cb(v) { - break - } - } -} - -func (fm flatMap) Ref() ref.Ref { - return fm.cr.Ref(fm) -} - -func (fm flatMap) Equals(other Value) (res bool) { - if other == nil { - return false - } else { - return fm.Ref() == other.Ref() - } -} - -func copyMapData(m mapData) mapData { - r := mapData{} - for k, v := range m { - r[k] = v - } - return r -} - -func buildMapData(oldData mapData, kv ...Value) mapData { - Chk.Equal(0, len(kv)%2, "Must specify even number of key/value pairs") - - m := copyMapData(oldData) - for i := 0; i < len(kv); i += 2 { - k := kv[i] - v := kv[i+1] - m[k.Ref()] = MapEntry{k, v} - } - return m -} diff --git a/types/flat_set.go b/types/flat_set.go deleted file mode 100644 index f12ccbd6cd..0000000000 --- a/types/flat_set.go +++ /dev/null @@ -1,112 +0,0 @@ -package types - -import ( - "github.com/attic-labs/noms/ref" -) - -type setData map[ref.Ref]Value - -type flatSet struct { - m setData - cr *cachedRef -} - -func newFlatSet(m setData) flatSet { - return flatSet{ - m: m, - cr: &cachedRef{}, - } -} - -func (fs flatSet) Empty() bool { - return fs.Len() == uint64(0) -} - -func (fs flatSet) Len() uint64 { - return uint64(len(fs.m)) -} - -func (fs flatSet) Has(v Value) bool { - _, ok := fs.m[v.Ref()] - return ok -} - -func (fs flatSet) Insert(values ...Value) Set { - return newFlatSet(buildSetData(fs.m, values)) -} - -func (fs flatSet) Remove(values ...Value) Set { - m2 := copySetData(fs.m) - for _, v := range values { - if v != nil { - delete(m2, v.Ref()) - } - } - return newFlatSet(m2) -} - -func (fs flatSet) Union(others ...Set) (result Set) { - result = fs - for _, other := range others { - other.Iter(func(v Value) (stop bool) { - result = result.Insert(v) - return - }) - } - return result -} - -func (fs flatSet) Subtract(others ...Set) (result Set) { - result = fs - for _, other := range others { - other.Iter(func(v Value) (stop bool) { - result = result.Remove(v) - return - }) - } - return result -} - -func (fm flatSet) Iter(cb setIterCallback) { - // TODO: sort iteration order - for _, v := range fm.m { - if cb(v) { - break - } - } -} - -func (fm flatSet) Any() Value { - for _, v := range fm.m { - return v - } - return nil -} - -func (fs flatSet) Ref() ref.Ref { - return fs.cr.Ref(fs) -} - -func (fs flatSet) Equals(other Value) bool { - if other == nil { - return false - } else { - return fs.Ref() == other.Ref() - } -} - -func copySetData(m setData) setData { - r := setData{} - for k, v := range m { - r[k] = v - } - return r -} - -func buildSetData(old setData, values []Value) setData { - m := copySetData(old) - for _, v := range values { - m[v.Ref()] = v - } - return m -} diff --git a/types/flat_string.go b/types/flat_string.go deleted file mode 100644 index 1cd5052697..0000000000 --- a/types/flat_string.go +++ /dev/null @@ -1,31 +0,0 @@ -package types - -import ( - "github.com/attic-labs/noms/ref" -) - -// Stupid inefficient temporary implementation of the String interface. -type flatString struct { - s string - cr *cachedRef -} - -func (fs flatString) Blob() Blob { - return NewBlob([]byte(fs.s)) -} - -func (fs flatString) String() string { - return fs.s -} - -func (fs flatString) Ref() ref.Ref { - return fs.cr.Ref(fs) -} - -func (fs flatString) Equals(other Value) bool { - if other == nil { - return false - } else { - return fs.Ref() == other.Ref() - } -} diff --git a/types/json_decode.go b/types/json_decode.go index a64ce17961..9c1231afcb 100644 --- a/types/json_decode.go +++ b/types/json_decode.go @@ -109,7 +109,7 @@ func jsonDecodeList(input []interface{}, s chunks.ChunkSource) (future, error) { } output = append(output, outVal) } - return futureFromValue(newFlatList(output, s)), nil + return futureFromValue(listFromFutures(output, s)), nil } func jsonDecodeSet(input []interface{}, s chunks.ChunkSource) (future, error) { diff --git a/types/list.go b/types/list.go index 07678ecd2d..7a6fb22277 100644 --- a/types/list.go +++ b/types/list.go @@ -1,19 +1,86 @@ package types -// TODO: I'm not sure we even want this interface in the long term, because noms is strongly-typed, so we should actually have List. -type List interface { - Value - Len() uint64 - Get(idx uint64) Value - // TODO: iterator - Slice(idx uint64, end uint64) List - Set(idx uint64, v Value) List - Append(v ...Value) List - Insert(idx uint64, v ...Value) List - Remove(start uint64, end uint64) List - RemoveAt(idx uint64) List +import ( + "github.com/attic-labs/noms/chunks" + . "github.com/attic-labs/noms/dbg" + "github.com/attic-labs/noms/ref" +) + +type List struct { + list []future + cr *cachedRef + cs chunks.ChunkSource } func NewList(v ...Value) List { - return newFlatList(valuesToFutures(v), nil) + return listFromFutures(valuesToFutures(v), nil) +} + +func valuesToFutures(list []Value) []future { + f := []future{} + for _, v := range list { + f = append(f, futureFromValue(v)) + } + return f +} + +func listFromFutures(list []future, cs chunks.ChunkSource) List { + return List{list, &cachedRef{}, cs} +} + +func (l List) Len() uint64 { + return uint64(len(l.list)) +} + +func (l List) Get(idx uint64) Value { + v, err := l.list[idx].Deref(l.cs) + // This is the kind of thing that makes me feel like hiding deref'ing is probably not the right idea. But we'll go with it for now. + Chk.NoError(err) + return v +} + +func (l List) Slice(start uint64, end uint64) List { + return listFromFutures(l.list[start:end], l.cs) +} + +func (l List) Set(idx uint64, v Value) List { + b := make([]future, len(l.list)) + copy(b, l.list) + b[idx] = futureFromValue(v) + return listFromFutures(b, l.cs) +} + +func (l List) Append(v ...Value) List { + return listFromFutures(append(l.list, valuesToFutures(v)...), l.cs) +} + +func (l List) Insert(idx uint64, v ...Value) List { + b := make([]future, len(l.list)+len(v)) + copy(b, l.list[:idx]) + copy(b[idx:], valuesToFutures(v)) + copy(b[idx+uint64(len(v)):], l.list[idx:]) + return listFromFutures(b, l.cs) +} + +func (l List) Remove(start uint64, end uint64) List { + b := make([]future, uint64(len(l.list))-(end-start)) + copy(b, l.list[:start]) + copy(b[start:], l.list[end:]) + return listFromFutures(b, l.cs) +} + +func (l List) RemoveAt(idx uint64) List { + return l.Remove(idx, idx+1) +} + +func (l List) Ref() ref.Ref { + return l.cr.Ref(l) +} + +func (l List) Equals(other Value) bool { + if other == nil { + return false + } else { + return l.Ref() == other.Ref() + } } diff --git a/types/map.go b/types/map.go index 3e52032df4..7c2834b795 100644 --- a/types/map.go +++ b/types/map.go @@ -1,27 +1,77 @@ package types -import "github.com/attic-labs/noms/ref" +import ( + . "github.com/attic-labs/noms/dbg" + "github.com/attic-labs/noms/ref" +) -type MapEntry struct { - Key Value - Value Value +type mapData map[ref.Ref]MapEntry + +type Map struct { + m mapData + cr *cachedRef +} + +func NewMap(kv ...Value) Map { + return newMapFromData(buildMapData(mapData{}, kv...)) +} + +func (fm Map) Len() uint64 { + return uint64(len(fm.m)) +} + +func (fm Map) Has(key Value) bool { + _, ok := fm.m[key.Ref()] + return ok +} + +func (fm Map) Get(key Value) Value { + if v, ok := fm.m[key.Ref()]; ok { + return v.Value + } else { + return nil + } +} + +func (fm Map) Set(key Value, val Value) Map { + return newMapFromData(buildMapData(fm.m, key, val)) +} + +func (fm Map) SetM(kv ...Value) Map { + return newMapFromData(buildMapData(fm.m, kv...)) +} + +func (fm Map) Remove(k Value) Map { + m := copyMapData(fm.m) + delete(m, k.Ref()) + return newMapFromData(m) } type mapIterCallback func(entry MapEntry) bool -type Map interface { - Value - Len() uint64 - Has(k Value) bool - Get(k Value) Value - Set(k Value, v Value) Map - SetM(kv ...Value) Map - Remove(k Value) Map - Iter(mapIterCallback) +func (fm Map) Iter(cb mapIterCallback) { + for _, v := range fm.m { + if cb(v) { + break + } + } } -func NewMap(kv ...Value) Map { - return newFlatMap(buildMapData(mapData{}, kv...)) +func (fm Map) Ref() ref.Ref { + return fm.cr.Ref(fm) +} + +func (fm Map) Equals(other Value) (res bool) { + if other == nil { + return false + } else { + return fm.Ref() == other.Ref() + } +} + +type MapEntry struct { + Key Value + Value Value } type MapEntrySlice []MapEntry @@ -37,3 +87,27 @@ func (mes MapEntrySlice) Swap(i, j int) { func (mes MapEntrySlice) Less(i, j int) bool { return ref.Less(mes[i].Key.Ref(), mes[j].Key.Ref()) } + +func newMapFromData(m mapData) Map { + return Map{m, &cachedRef{}} +} + +func copyMapData(m mapData) mapData { + r := mapData{} + for k, v := range m { + r[k] = v + } + return r +} + +func buildMapData(oldData mapData, kv ...Value) mapData { + Chk.Equal(0, len(kv)%2, "Must specify even number of key/value pairs") + + m := copyMapData(oldData) + for i := 0; i < len(kv); i += 2 { + k := kv[i] + v := kv[i+1] + m[k.Ref()] = MapEntry{k, v} + } + return m +} diff --git a/types/set.go b/types/set.go index f888f8c857..47b400830b 100644 --- a/types/set.go +++ b/types/set.go @@ -1,21 +1,118 @@ package types -type setIterCallback func(v Value) bool -type setCombineCallback func(prev Set, v ...Value) Set +import ( + "github.com/attic-labs/noms/ref" +) -type Set interface { - Value - Empty() bool - Len() uint64 - Has(v Value) bool - Iter(setIterCallback) - Insert(v ...Value) Set - Remove(v ...Value) Set - Union(others ...Set) Set - Subtract(others ...Set) Set - Any() Value +type setData map[ref.Ref]Value + +type Set struct { + m setData + cr *cachedRef } func NewSet(v ...Value) Set { - return newFlatSet(buildSetData(setData{}, v)) + return newSetFromData(buildSetData(setData{}, v)) +} + +func (fs Set) Empty() bool { + return fs.Len() == uint64(0) +} + +func (fs Set) Len() uint64 { + return uint64(len(fs.m)) +} + +func (fs Set) Has(v Value) bool { + _, ok := fs.m[v.Ref()] + return ok +} + +func (fs Set) Insert(values ...Value) Set { + return newSetFromData(buildSetData(fs.m, values)) +} + +func (fs Set) Remove(values ...Value) Set { + m2 := copySetData(fs.m) + for _, v := range values { + if v != nil { + delete(m2, v.Ref()) + } + } + return newSetFromData(m2) +} + +func (fs Set) Union(others ...Set) (result Set) { + result = fs + for _, other := range others { + other.Iter(func(v Value) (stop bool) { + result = result.Insert(v) + return + }) + } + return result +} + +func (fs Set) Subtract(others ...Set) (result Set) { + result = fs + for _, other := range others { + other.Iter(func(v Value) (stop bool) { + result = result.Remove(v) + return + }) + } + return result +} + +type setIterCallback func(v Value) bool + +func (fm Set) Iter(cb setIterCallback) { + // TODO: sort iteration order + for _, v := range fm.m { + if cb(v) { + break + } + } +} + +func (fm Set) Any() Value { + for _, v := range fm.m { + return v + } + return nil +} + +func (fs Set) Ref() ref.Ref { + return fs.cr.Ref(fs) +} + +func (fs Set) Equals(other Value) bool { + if other == nil { + return false + } else { + return fs.Ref() == other.Ref() + } +} + +func newSetFromData(m setData) Set { + return Set{ + m: m, + cr: &cachedRef{}, + } +} + +func copySetData(m setData) setData { + r := setData{} + for k, v := range m { + r[k] = v + } + return r +} + +func buildSetData(old setData, values []Value) setData { + m := copySetData(old) + for _, v := range values { + m[v.Ref()] = v + } + return m } diff --git a/types/string.go b/types/string.go index 77beaad1f3..0d18c411be 100644 --- a/types/string.go +++ b/types/string.go @@ -1,14 +1,34 @@ package types -type String interface { - Value +import ( + "github.com/attic-labs/noms/ref" +) - Blob() Blob - - // Slurps the entire string into memory. You obviously don't want to do this if the string might be large. - String() string +type String struct { + s string + cr *cachedRef } func NewString(s string) String { - return flatString{s, &cachedRef{}} + return String{s, &cachedRef{}} +} + +func (fs String) Blob() Blob { + return NewBlob([]byte(fs.s)) +} + +func (fs String) String() string { + return fs.s +} + +func (fs String) Ref() ref.Ref { + return fs.cr.Ref(fs) +} + +func (fs String) Equals(other Value) bool { + if other == nil { + return false + } else { + return fs.Ref() == other.Ref() + } }