mirror of
https://github.com/dolthub/dolt.git
synced 2026-05-12 19:39:32 -05:00
add types.Set
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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.
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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 {
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
@@ -37,6 +37,7 @@ func TestCachedRef(t *testing.T) {
|
||||
NewList(),
|
||||
NewString(""),
|
||||
NewMap(),
|
||||
NewSet(),
|
||||
}
|
||||
for i := 0; i < 2; i++ {
|
||||
for j, v := range values {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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))
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user