-
Notifications
You must be signed in to change notification settings - Fork 49
/
marshal.go
134 lines (129 loc) · 3.06 KB
/
marshal.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
128
129
130
131
132
133
134
package codec
import (
"fmt"
"github.com/polydawn/refmt/shared"
"github.com/polydawn/refmt/tok"
ipld "github.com/ipld/go-ipld-prime"
)
// Marshal provides a very general node-to-tokens marshalling feature.
// It can handle either cbor or json by being combined with a refmt TokenSink.
//
// It is valid for all the data model types except links, which are only
// supported if the nodes are typed and provide additional information
// to clarify how links should be encoded through their type info.
// (The dag-cbor and dag-json formats can be used if links are of CID
// implementation and need to be encoded in a schemafree way.)
func Marshal(n ipld.Node, sink shared.TokenSink) error {
var tk tok.Token
return marshal(n, &tk, sink)
}
func marshal(n ipld.Node, tk *tok.Token, sink shared.TokenSink) error {
switch n.Kind() {
case ipld.Kind_Invalid:
return fmt.Errorf("cannot traverse a node that is absent")
case ipld.Kind_Null:
tk.Type = tok.TNull
_, err := sink.Step(tk)
return err
case ipld.Kind_Map:
// Emit start of map.
tk.Type = tok.TMapOpen
tk.Length = int(n.Length()) // TODO: overflow check
if _, err := sink.Step(tk); err != nil {
return err
}
// Emit map contents (and recurse).
for itr := n.MapIterator(); !itr.Done(); {
k, v, err := itr.Next()
if err != nil {
return err
}
tk.Type = tok.TString
tk.Str, err = k.AsString()
if err != nil {
return err
}
if _, err := sink.Step(tk); err != nil {
return err
}
if err := marshal(v, tk, sink); err != nil {
return err
}
}
// Emit map close.
tk.Type = tok.TMapClose
_, err := sink.Step(tk)
return err
case ipld.Kind_List:
// Emit start of list.
tk.Type = tok.TArrOpen
l := n.Length()
tk.Length = int(l) // TODO: overflow check
if _, err := sink.Step(tk); err != nil {
return err
}
// Emit list contents (and recurse).
for i := int64(0); i < l; i++ {
v, err := n.LookupByIndex(i)
if err != nil {
return err
}
if err := marshal(v, tk, sink); err != nil {
return err
}
}
// Emit list close.
tk.Type = tok.TArrClose
_, err := sink.Step(tk)
return err
case ipld.Kind_Bool:
v, err := n.AsBool()
if err != nil {
return err
}
tk.Type = tok.TBool
tk.Bool = v
_, err = sink.Step(tk)
return err
case ipld.Kind_Int:
v, err := n.AsInt()
if err != nil {
return err
}
tk.Type = tok.TInt
tk.Int = int64(v)
_, err = sink.Step(tk)
return err
case ipld.Kind_Float:
v, err := n.AsFloat()
if err != nil {
return err
}
tk.Type = tok.TFloat64
tk.Float64 = v
_, err = sink.Step(tk)
return err
case ipld.Kind_String:
v, err := n.AsString()
if err != nil {
return err
}
tk.Type = tok.TString
tk.Str = v
_, err = sink.Step(tk)
return err
case ipld.Kind_Bytes:
v, err := n.AsBytes()
if err != nil {
return err
}
tk.Type = tok.TBytes
tk.Bytes = v
_, err = sink.Step(tk)
return err
case ipld.Kind_Link:
return fmt.Errorf("link emission not supported by this codec without a schema! (maybe you want dag-cbor or dag-json)")
default:
panic("unreachable")
}
}