-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.go
More file actions
148 lines (131 loc) · 3.67 KB
/
Copy pathmain.go
File metadata and controls
148 lines (131 loc) · 3.67 KB
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
// Go's `encoding/json` relies on reflection to marshal arbitrary structs.
// Solod has no reflection, and uses a different approach: a token-level API.
// You read and write one JSON token at a time, and the `Encoder` and `Decoder`
// types take care of the syntax — adding commas and colons, checking UTF-8,
// and rejecting bad input.
package main
import (
"solod.dev/so/encoding/json"
"solod.dev/so/mem"
"solod.dev/so/strings"
)
type Money struct {
Currency string
Amount int64
}
func (m *Money) EncodeJSON(enc *json.Encoder) {
enc.BeginObject()
enc.Str("currency")
enc.Str(m.Currency)
enc.Str("amount")
enc.Int(m.Amount)
enc.EndObject()
}
func (m *Money) DecodeJSON(alloc mem.Allocator, dec *json.Decoder) error {
if dec.Kind() != json.KindObjBeg {
dec.Skip()
return json.ErrKind
}
for dec.Next() && dec.Kind() == json.KindString {
switch dec.Str() {
case "currency":
dec.Next()
m.Currency = strings.Clone(alloc, dec.Str())
case "amount":
dec.Next()
m.Amount = dec.Int()
default:
dec.Next()
dec.Skip()
}
}
return dec.Err()
}
func (m *Money) Free(alloc mem.Allocator) {
mem.FreeString(alloc, m.Currency)
*m = Money{}
}
type Person struct {
Name string
Balance Money
}
// EncodeJSON writes p as a JSON object.
func (p *Person) EncodeJSON(enc *json.Encoder) {
// Encoding is done through a series of calls
// that match the structure of your document.
enc.BeginObject()
enc.Str("name")
enc.Str(p.Name)
enc.Str("balance")
// A nested value encodes itself into the same encoder.
p.Balance.EncodeJSON(enc)
enc.EndObject()
}
// DecodeJSON decodes p from dec, allocating the fields p owns with alloc.
// It starts on the opening { and stops on the matching }.
// Release the fields with [Person.Free] and the same allocator.
func (p *Person) DecodeJSON(alloc mem.Allocator, dec *json.Decoder) error {
if dec.Kind() != json.KindObjBeg {
// Not an object: consume the whole value so the caller
// stays in sync, then report that nothing was decoded.
dec.Skip()
return json.ErrKind
}
// Decoding pulls one validated token at a time with `Next`.
// You can check each token with `Kind` and read its value using
// typed getters like `Str`, `Int`, or `Bool`.
for dec.Next() && dec.Kind() == json.KindString {
switch dec.Str() {
case "name":
dec.Next()
// `Str` is a view into the decoder's buffer, valid only until
// the next token, so an owned field must copy it.
p.Name = strings.Clone(alloc, dec.Str())
case "balance":
dec.Next()
// The nested value decodes itself, from the same allocator.
p.Balance.DecodeJSON(alloc, dec)
default:
dec.Next()
dec.Skip()
}
}
return dec.Err()
}
// Free releases the memory p owns. It does not free p itself.
func (p *Person) Free(alloc mem.Allocator) {
mem.FreeString(alloc, p.Name)
p.Balance.Free(alloc)
// Zeroing makes Free safe to call more than once.
*p = Person{}
}
func encode() {
out := make([]byte, 256)
sb := strings.FixedBuilder(out)
enc := json.NewEncoder(&sb)
p := &Person{Name: "Alice", Balance: Money{Currency: "USD", Amount: 100}}
p.EncodeJSON(&enc)
enc.Flush()
println(sb.String())
// {"name":"Alice","balance":{"currency":"USD","amount":100}}
}
func decode() {
src := `{"name":"Alice","balance":{"currency":"USD","amount":100}}`
dec := json.NewDecoder(mem.System, []byte(src))
defer dec.Free()
var p Person
// A failed decode may still have allocated some fields,
// so p is freed either way.
defer p.Free(mem.System)
dec.Next() // the opening {
if err := p.DecodeJSON(mem.System, &dec); err != nil {
println("decode failed")
return
}
println(p.Name, p.Balance.Currency, int(p.Balance.Amount))
// Alice USD 100
}
func main() {
encode()
decode()
}