add types.Set

This commit is contained in:
Aaron Boodman
2015-06-12 22:26:32 -07:00
parent ed62644cda
commit 08a53bb48f
13 changed files with 316 additions and 5 deletions
+16
View File
@@ -73,6 +73,10 @@ func jsonDecodeTaggedValue(m map[string]interface{}, s store.ChunkSource) (types
if v, ok := v.([]interface{}); ok {
return jsonDecodeList(v, s)
}
case "set":
if v, ok := v.([]interface{}); ok {
return jsonDecodeSet(v, s)
}
case "map":
if v, ok := v.(map[string]interface{}); ok {
return jsonDecodeMap(v, s)
@@ -99,6 +103,18 @@ func jsonDecodeList(input []interface{}, s store.ChunkSource) (types.Value, erro
return output, nil
}
func jsonDecodeSet(input []interface{}, s store.ChunkSource) (types.Value, error) {
vals := []types.Value{}
for _, inVal := range input {
outVal, err := jsonDecodeValue(inVal, s)
if err != nil {
return nil, err
}
vals = append(vals, outVal)
}
return types.NewSet(vals...), nil
}
func jsonDecodeMap(input map[string]interface{}, s store.ChunkSource) (types.Value, error) {
output := types.NewMap()
for k, inVal := range input {
+6
View File
@@ -70,5 +70,11 @@ func TestJSONDecode(t *testing.T) {
testDecode(`j {"map":{"bool":false,"int32":{"int32":42},"list":{"ref":"sha1-58bdf8e374b39f9b1e8a64784cf5c09601f4b7ea"},"map":{"ref":"sha1-fa8026bf44f60b64ab674c49cda31a697467973c"},"string":"hotdog"}}
//`, types.NewMap("bool", types.Bool(false), "int32", types.Int32(42), "string", types.NewString("hotdog"), "list", types.NewList(), "map", types.NewMap()))
// Sets
testDecode(`j {"set":[]}
`, types.NewSet())
testDecode(`j {"set":[{"int32":42},"hotdog",{"ref":"sha1-58bdf8e374b39f9b1e8a64784cf5c09601f4b7ea"},false,{"ref":"sha1-fa8026bf44f60b64ab674c49cda31a697467973c"}]}
`, types.NewSet(types.Bool(false), types.Int32(42), types.NewString("hotdog"), types.NewList(), types.NewMap()))
// referenced blobs?
}
+35 -2
View File
@@ -3,6 +3,7 @@ package enc
import (
"encoding/json"
"fmt"
"sort"
. "github.com/attic-labs/noms/dbg"
"github.com/attic-labs/noms/ref"
@@ -61,6 +62,8 @@ func getJSON(v types.Value, s store.ChunkSink) (interface{}, error) {
return getJSONList(v, s)
case types.Map:
return getJSONMap(v, s)
case types.Set:
return getJSONSet(v, s)
case types.String:
return v.String(), nil
case types.UInt16:
@@ -117,16 +120,46 @@ func getJSONMap(m types.Map, s store.ChunkSink) (r interface{}, err error) {
return
}
func getJSONSet(set types.Set, s store.ChunkSink) (r interface{}, err error) {
// Iteration through Set is random, but we need a deterministic order for serialization. Let's order using the refs of the values in the set.
lookup := map[ref.Ref]types.Value{}
order := ref.RefSlice{}
set.Iter(func(v types.Value) (stop bool) {
order = append(order, v.Ref())
lookup[v.Ref()] = v
return
})
sort.Sort(order)
j := []interface{}{}
for _, r := range order {
v := lookup[r]
var cj interface{}
cj, err = getChildJSON(v, s)
if err != nil {
return nil, err
}
j = append(j, cj)
}
r = map[string]interface{}{
"set": j,
}
return
}
func getChildJSON(v types.Value, s store.ChunkSink) (interface{}, error) {
var r ref.Ref
var err error
switch v := v.(type) {
// Blobs, maps, and lists are always out-of-line
// Blobs, lists, maps, and sets are always out-of-line
case types.Blob:
r, err = WriteValue(v, s)
case types.List:
r, err = WriteValue(v, s)
case types.Map:
r, err = WriteValue(v, s)
case types.List:
case types.Set:
r, err = WriteValue(v, s)
default:
// Other types are always inline.
+4
View File
@@ -83,4 +83,8 @@ func TestJsonEncode(t *testing.T) {
testEncode(`j {"map":{"bool":false,"int32":{"int32":42},"list":{"ref":"sha1-58bdf8e374b39f9b1e8a64784cf5c09601f4b7ea"},"map":{"ref":"sha1-fa8026bf44f60b64ab674c49cda31a697467973c"},"string":"hotdog"}}
`, types.NewMap("bool", types.Bool(false), "int32", types.Int32(42), "string", types.NewString("hotdog"), "list", types.NewList(), "map", types.NewMap()))
assertChildVals()
// Sets
testEncode(`j {"set":[]}
`, types.NewSet())
}
+43
View File
@@ -0,0 +1,43 @@
package ref
import (
. "github.com/attic-labs/noms/dbg"
)
type RefSlice []Ref
func (rs RefSlice) Len() int {
return len(rs)
}
func (rs RefSlice) Less(i, j int) bool {
d1, d2 := rs[i].digest, rs[j].digest
Chk.Equal(len(d1), len(d2))
for k := 0; k < len(d1); k++ {
b1, b2 := d1[k], d2[k]
if b1 < b2 {
return true
} else if b1 > b2 {
return false
}
}
return false
}
func (rs RefSlice) Swap(i, j int) {
t := rs[j]
rs[j] = rs[i]
rs[i] = t
}
func (rs RefSlice) Equals(other RefSlice) bool {
if len(rs) != len(other) {
return false
}
for i := 0; i < len(rs); i++ {
if rs[i] != other[i] {
return false
}
}
return true
}
+31
View File
@@ -0,0 +1,31 @@
package ref
import (
"sort"
"testing"
"github.com/stretchr/testify/assert"
)
func TestRefSliceSort(t *testing.T) {
assert := assert.New(t)
rs := RefSlice{}
for i := 1; i <= 3; i++ {
for j := 1; j <= 3; j++ {
d := Sha1Digest{}
for k := 1; k <= j; k++ {
d[k-1] = byte(i)
}
rs = append(rs, New(d))
}
}
rs2 := RefSlice(make([]Ref, len(rs)))
copy(rs2, rs)
sort.Sort(sort.Reverse(rs2))
assert.False(rs.Equals(rs2))
sort.Sort(rs2)
assert.True(rs.Equals(rs2))
}
+1 -1
View File
@@ -52,7 +52,7 @@ func (fm flatMap) Remove(k string) Map {
return newFlatMap(m)
}
func (fm flatMap) Iter(cb IterCallback) {
func (fm flatMap) Iter(cb mapIterCallback) {
for k, v := range fm.m {
if cb(k, v) {
break
+77
View File
@@ -0,0 +1,77 @@
package types
import (
"github.com/attic-labs/noms/ref"
)
type flatSet struct {
m setInternalMap
cr *cachedRef
}
type setInternalMap map[ref.Ref]Value
func newFlatSet(m setInternalMap) flatSet {
return flatSet{
m: m,
cr: &cachedRef{},
}
}
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(buildInternalMap(fs.m, values))
}
func (fs flatSet) Remove(values ...Value) Set {
m2 := copyInternalMap(fs.m)
for _, v := range values {
delete(m2, v.Ref())
}
return newFlatSet(m2)
}
func (fm flatSet) Iter(cb setIterCallback) {
// TODO: sort iteration order
for _, v := range fm.m {
if cb(v) {
break
}
}
}
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 copyInternalMap(m setInternalMap) setInternalMap {
r := setInternalMap{}
for k, v := range m {
r[k] = v
}
return r
}
func buildInternalMap(old setInternalMap, values []Value) setInternalMap {
m := copyInternalMap(old)
for _, v := range values {
m[v.Ref()] = v
}
return m
}
+2 -2
View File
@@ -1,6 +1,6 @@
package types
type IterCallback func(k string, v Value) bool
type mapIterCallback func(k string, v Value) bool
type Map interface {
Value
@@ -11,7 +11,7 @@ type Map interface {
Set(k string, v Value) Map
SetM(kv ...interface{}) Map
Remove(k string) Map
Iter(IterCallback)
Iter(mapIterCallback)
}
func NewMap(kv ...interface{}) Map {
+16
View File
@@ -0,0 +1,16 @@
package types
type setIterCallback func(v Value) bool
type Set interface {
Value
Len() uint64
Has(v Value) bool
Iter(setIterCallback)
Insert(v ...Value) Set
Remove(v ...Value) Set
}
func NewSet(v ...Value) Set {
return newFlatSet(buildInternalMap(setInternalMap{}, v))
}
+1
View File
@@ -37,6 +37,7 @@ func TestCachedRef(t *testing.T) {
NewList(),
NewString(""),
NewMap(),
NewSet(),
}
for i := 0; i < 2; i++ {
for j, v := range values {
+2
View File
@@ -47,6 +47,8 @@ func TestPrimitiveEquals(t *testing.T) {
func() types.Value { return types.NewList(types.NewString("bar")) },
func() types.Value { return types.NewMap() },
func() types.Value { return types.NewMap("a", types.NewString("a")) },
func() types.Value { return types.NewSet() },
func() types.Value { return types.NewSet(types.NewString("hi")) },
}
for i, f1 := range values {
+82
View File
@@ -0,0 +1,82 @@
package test
import (
"testing"
_ "github.com/attic-labs/noms/enc"
. "github.com/attic-labs/noms/types"
"github.com/stretchr/testify/assert"
)
func TestSetLen(t *testing.T) {
assert := assert.New(t)
s1 := NewSet(Bool(true), Int32(1), NewString("hi"))
assert.Equal(uint64(3), s1.Len())
s2 := s1.Insert(Bool(false))
assert.Equal(uint64(4), s2.Len())
s3 := s2.Remove(Bool(true))
assert.Equal(uint64(3), s3.Len())
}
func TestSetHas(t *testing.T) {
assert := assert.New(t)
s1 := NewSet(Bool(true), Int32(1), NewString("hi"))
assert.True(s1.Has(Bool(true)))
assert.False(s1.Has(Bool(false)))
assert.True(s1.Has(Int32(1)))
assert.False(s1.Has(Int32(0)))
assert.True(s1.Has(NewString("hi")))
assert.False(s1.Has(NewString("ho")))
s2 := s1.Insert(Bool(false))
assert.True(s2.Has(Bool(false)))
assert.True(s2.Has(Bool(true)))
assert.True(s1.Has(Bool(true)))
assert.False(s1.Has(Bool(false)))
}
func TestSetInsert(t *testing.T) {
assert := assert.New(t)
s := NewSet()
v1 := Bool(false)
v2 := Bool(true)
v3 := Int32(0)
assert.False(s.Has(v1))
s = s.Insert(v1)
assert.True(s.Has(v1))
s = s.Insert(v2)
assert.True(s.Has(v1))
assert.True(s.Has(v2))
s2 := s.Insert(v3)
assert.True(s.Has(v1))
assert.True(s.Has(v2))
assert.False(s.Has(v3))
assert.True(s2.Has(v1))
assert.True(s2.Has(v2))
assert.True(s2.Has(v3))
}
func TestSetRemove(t *testing.T) {
assert := assert.New(t)
v1 := Bool(false)
v2 := Bool(true)
v3 := Int32(0)
s := NewSet(v1, v2, v3)
assert.True(s.Has(v1))
assert.True(s.Has(v2))
assert.True(s.Has(v3))
s = s.Remove(v1)
assert.False(s.Has(v1))
assert.True(s.Has(v2))
assert.True(s.Has(v3))
s2 := s.Remove(v2)
assert.False(s.Has(v1))
assert.True(s.Has(v2))
assert.True(s.Has(v3))
assert.False(s2.Has(v1))
assert.False(s2.Has(v2))
assert.True(s2.Has(v3))
}