-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathexample_ptr_struct_test.go
127 lines (110 loc) · 3.25 KB
/
example_ptr_struct_test.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
package lexy_test
import (
"fmt"
"github.com/phiryll/lexy"
)
type BigStruct struct {
name string
// ... big fields, inefficient to copy
}
type Container struct {
big *BigStruct
// ... other fields, but not large ones
// If Container were also large, use the same technique
// to create a Codec[*Container] instead.
}
var (
PtrToBigStructCodec lexy.Codec[*BigStruct] = ptrToBigStructCodec{}
ContainerCodec lexy.Codec[Container] = containterCodec{}
)
type ptrToBigStructCodec struct{}
func (ptrToBigStructCodec) Append(buf []byte, value *BigStruct) []byte {
done, buf := lexy.PrefixNilsFirst.Append(buf, value == nil)
if done {
return buf
}
buf = lexy.TerminatedString().Append(buf, value.name)
// Append other fields.
return buf
}
func (ptrToBigStructCodec) Put(buf []byte, value *BigStruct) []byte {
done, buf := lexy.PrefixNilsFirst.Put(buf, value == nil)
if done {
return buf
}
buf = lexy.TerminatedString().Put(buf, value.name)
// Put other fields.
return buf
}
func (ptrToBigStructCodec) Get(buf []byte) (*BigStruct, []byte) {
done, buf := lexy.PrefixNilsFirst.Get(buf)
if done {
return nil, buf
}
name, buf := lexy.TerminatedString().Get(buf)
// Get other fields.
return &BigStruct{name /* , other fields ... */}, buf
}
func (ptrToBigStructCodec) RequiresTerminator() bool {
return false
}
type containterCodec struct{}
func (containterCodec) Append(buf []byte, value Container) []byte {
buf = PtrToBigStructCodec.Append(buf, value.big)
// Append other fields.
return buf
}
func (containterCodec) Put(buf []byte, value Container) []byte {
buf = PtrToBigStructCodec.Put(buf, value.big)
// Put other fields.
// buf = someCodec.Put(buf, someValue)
return buf
}
func (containterCodec) Get(buf []byte) (Container, []byte) {
big, buf := PtrToBigStructCodec.Get(buf)
// Get other fields.
// someValue, buf := someCodec.Get(buf)
// ...
return Container{big /* , other fields ... */}, buf
}
func (containterCodec) RequiresTerminator() bool {
return false
}
// This is only used for printing output in the example.
func containerEquals(a, b Container) bool {
if a.big == nil && b.big == nil {
return true
}
if a.big == nil || b.big == nil {
return false
}
return *a.big == *b.big
}
// ExamplePointerToStruct shows how to use pointers for efficiency
// in a user-defined Codec, to avoid unnecessarily copying large data structures.
// Note that types in Go other than structs and arrays do not have this problem.
// Complex numbers, strings, pointers, slices, and maps
// all have a relatively small footprint when passed by value.
// The same is true of [time.Time] and [time.Duration] instances.
//
// Normally, a Codec[BigStruct] would be defined and Container's Codec
// would use it as lexy.PointerTo(bigStructCodec).
// However, calls to a Codec[BigStruct] will pass BigStruct instances by value,
// even though the wrapping pointer Codec is only copying pointers.
//
// The order isn't relevant for this example, so other fields are not shown.
func Example_pointerToStruct() {
for _, value := range []Container{
{nil},
{&BigStruct{""}},
{&BigStruct{"abc"}},
} {
buf := ContainerCodec.Append(nil, value)
decoded, _ := ContainerCodec.Get(buf)
fmt.Println(containerEquals(value, decoded))
}
// Output:
// true
// true
// true
}