Skip to content

Commit d8b85a2

Browse files
committed
Support certain cases of unsafe.Pointer
Nil pointers can be serialized. Pointers that refer to objects in the serialization graph are also supported.
1 parent 8227dcf commit d8b85a2

File tree

2 files changed

+50
-2
lines changed

2 files changed

+50
-2
lines changed

types/reflect.go

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,14 +69,15 @@ func serializeAny(s *Serializer, t reflect.Type, p unsafe.Pointer) {
6969
serializeMap(s, t, p)
7070
case reflect.Pointer:
7171
serializePointer(s, t, p)
72+
case reflect.UnsafePointer:
73+
serializeUnsafePointer(s, p)
7274
case reflect.Slice:
7375
serializeSlice(s, t, p)
7476
case reflect.Struct:
7577
serializeStruct(s, t, p)
7678
case reflect.Func:
7779
serializeFunc(s, t, p)
7880
// Chan
79-
// UnsafePointer
8081
default:
8182
panic(fmt.Errorf("reflection cannot serialize type %s", t))
8283
}
@@ -129,6 +130,8 @@ func deserializeAny(d *Deserializer, t reflect.Type, p unsafe.Pointer) {
129130
deserializeInterface(d, t, p)
130131
case reflect.Pointer:
131132
deserializePointer(d, t, p)
133+
case reflect.UnsafePointer:
134+
deserializeUnsafePointer(d, p)
132135
case reflect.Array:
133136
deserializeArray(d, t, p)
134137
case reflect.Slice:
@@ -174,6 +177,9 @@ func serializePointedAt(s *Serializer, t reflect.Type, p unsafe.Pointer) {
174177
// If this pointer does not belong to any region, write a negative
175178
// offset to flag it is on its own, and write its data.
176179
if !r.valid() {
180+
if t == nil {
181+
panic("cannot serialize unsafe.Pointer pointing to region of unknown size")
182+
}
177183
serializeVarint(s, -1)
178184
serializeAny(s, t, p)
179185
return
@@ -359,6 +365,26 @@ func deserializePointer(d *Deserializer, t reflect.Type, p unsafe.Pointer) {
359365
r.Elem().Set(ep)
360366
}
361367

368+
func serializeUnsafePointer(s *Serializer, p unsafe.Pointer) {
369+
if p == nil {
370+
serializePointedAt(s, nil, nil)
371+
} else {
372+
serializePointedAt(s, nil, *(*unsafe.Pointer)(p))
373+
}
374+
}
375+
376+
var unsafePointerType = reflect.TypeOf(unsafe.Pointer(nil))
377+
378+
func deserializeUnsafePointer(d *Deserializer, p unsafe.Pointer) {
379+
r := reflect.NewAt(unsafePointerType, p)
380+
381+
ep := deserializePointedAt(d, unsafePointerType)
382+
if !ep.IsNil() {
383+
up := ep.UnsafePointer()
384+
r.Elem().Set(reflect.ValueOf(up))
385+
}
386+
}
387+
362388
func serializeStruct(s *Serializer, t reflect.Type, p unsafe.Pointer) {
363389
serializeStructFields(s, p, t.NumField(), t.Field)
364390
}
@@ -476,9 +502,11 @@ func deserializeInterface(d *Deserializer, t reflect.Type, p unsafe.Pointer) {
476502
ep := deserializePointedAt(d, et)
477503

478504
// Store the result in the interface
505+
r := reflect.NewAt(t, p)
479506
if !ep.IsNil() {
480-
r := reflect.NewAt(t, p)
481507
r.Elem().Set(ep.Elem())
508+
} else {
509+
r.Elem().Set(reflect.Zero(et))
482510
}
483511
}
484512

types/serde_test.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ func TestReflect(t *testing.T) {
110110
"",
111111
struct{}{},
112112
errors.New("test"),
113+
unsafe.Pointer(nil),
113114
}
114115

115116
for _, x := range cases {
@@ -143,6 +144,25 @@ func TestReflect(t *testing.T) {
143144
})
144145
}
145146

147+
func TestReflectUnsafePointer(t *testing.T) {
148+
type unsafePointerStruct struct{ p unsafe.Pointer }
149+
var selfRef unsafePointerStruct
150+
selfRef.p = unsafe.Pointer(&selfRef)
151+
152+
b := Serialize(&selfRef)
153+
out, b, err := Deserialize(b)
154+
if err != nil {
155+
t.Fatal(err)
156+
} else if len(b) > 0 {
157+
t.Fatalf("leftover bytes: %d", len(b))
158+
}
159+
160+
res := out.(*unsafePointerStruct)
161+
if unsafe.Pointer(res) != unsafe.Pointer(res.p) {
162+
t.Errorf("unsafe.Pointer was not restored correctly")
163+
}
164+
}
165+
146166
func TestErrors(t *testing.T) {
147167
s := struct {
148168
X5 error

0 commit comments

Comments
 (0)