Skip to content

Commit

Permalink
Make maps shareable (#47)
Browse files Browse the repository at this point in the history
When serializing two maps that point to the same data, link them back
together.
  • Loading branch information
pelletier authored Sep 20, 2023
2 parents 8835ec6 + b5d2301 commit e1dfb73
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 5 deletions.
34 changes: 29 additions & 5 deletions internal/serde/reflect.go
Original file line number Diff line number Diff line change
Expand Up @@ -294,13 +294,23 @@ func deserializePointedAt(d *Deserializer, t reflect.Type) reflect.Value {
}

func serializeMap(s *Serializer, t reflect.Type, p unsafe.Pointer) {
size := 0
r := reflect.NewAt(t, p).Elem()

if r.IsNil() {
size = -1
} else {
size = r.Len()
serializeVarint(s, 0)
return
}

mapptr := r.UnsafePointer()

id, new := s.assignPointerID(mapptr)
serializeVarint(s, int(id))
if !new {
return
}

size := r.Len()

serializeVarint(s, size)

// TODO: allocs
Expand All @@ -316,13 +326,27 @@ func serializeMap(s *Serializer, t reflect.Type, p unsafe.Pointer) {
}

func deserializeMap(d *Deserializer, t reflect.Type, p unsafe.Pointer) {
r := reflect.NewAt(t, p)

ptr, id := d.readPtr()
if id == 0 {
// nil map
return
}
if ptr != nil {
// already deserialized at ptr
existing := reflect.NewAt(t, ptr).Elem()
r.Elem().Set(existing)
return
}

n := deserializeVarint(d)
if n < 0 { // nil map
return
}
nv := reflect.MakeMapWithSize(t, n)
r := reflect.NewAt(t, p)
r.Elem().Set(nv)
d.store(id, p)
for i := 0; i < n; i++ {
k := reflect.New(t.Key())
DeserializeAny(d, t.Key(), k.UnsafePointer())
Expand Down
1 change: 1 addition & 0 deletions internal/serde/scan.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ func (c *containers) fixup(i int) {
c.remove(i + 1)
return
}
c.remove(i + 1)
// Array fully contains next container. Nothing to do
return
}
Expand Down
26 changes: 26 additions & 0 deletions serde/serde_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,32 @@ func TestReflectCustom(t *testing.T) {
}

func TestReflectSharing(t *testing.T) {
testReflect(t, "maps of ints", func(t *testing.T) {
m := map[int]int{1: 2, 3: 4}

type X struct {
a map[int]int
b map[int]int
}

x := X{
a: m,
b: m,
}

// make sure map is shared beforehand
x.a[5] = 6
assertEqual(t, 6, x.b[5])

serde.RegisterType[X]()

out := assertRoundTrip(t, x)

// check map is shared after
out.a[7] = 8
assertEqual(t, 8, out.b[7])
})

testReflect(t, "slice backing array", func(t *testing.T) {
data := make([]int, 10)
for i := range data {
Expand Down

0 comments on commit e1dfb73

Please sign in to comment.