forked from landonia/gobert
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtype.go
349 lines (310 loc) · 10.3 KB
/
type.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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
package bert
import (
"io"
"io/ioutil"
)
const (
VersionTag = 131
DistributionHeaderTag = 68
CompressedTag = 80
SmallIntTag = 97
IntTag = 98
SmallBignumTag = 110
LargeBignumTag = 111
FloatTag = 99
NewFloatTag = 70
AtomCacheRefTag = 82
AtomTag = 100
SmallAtomTag = 115
AtomUtf8Tag = 118
SmallAtomUtf8Tag = 119
SmallTupleTag = 104
LargeTupleTag = 105
NilTag = 106
StringTag = 107
ListTag = 108
BinTag = 109
MapTag = 116
PidTag = 103
PortTag = 102
FunTag = 117
ReferenceTag = 101
NewReferenceTag = 114
NewFunTag = 112
ExportTag = 113
)
type Atom string
const (
BertAtom = Atom("bert")
NilAtom = Atom("nil")
TrueAtom = Atom("true")
FalseAtom = Atom("false")
)
const (
MinorVersion0 = 0
MinorVersion1 = 1
)
type Term interface{}
type Request struct {
Kind Atom
Module Atom
Function Atom
Arguments []Term
}
const (
// NewCacheEntry is used to determine if an entry is a new cache entry
NewCacheEntry byte = 8
// SegmentIndex can be used to extract the segment index
SegmentIndex byte = 7
// LongAtoms is used to determine if 2 byte atoms are used
LongAtoms byte = 1
)
// As of erts version 5.7.2 the old atom cache protocol was dropped and a new one was introduced.
// This atom cache protocol introduced the distribution header. Nodes with erts versions earlier than
// 5.7.2 can still communicate with new nodes, but no distribution header and no atom cache will be used.
//
// The distribution header currently only contains an atom cache reference section, but could in the future
// contain more information. The distribution header precedes one or more Erlang terms on the external format.
// For more information see the documentation of the protocol between connected nodes in the distribution
// protocol documentation.
//
// ATOM_CACHE_REF entries with corresponding AtomCacheReferenceIndex in terms encoded on the external format
// following a distribution header refers to the atom cache references made in the distribution header.
// The range is 0 <= AtomCacheReferenceIndex < 255, i.e., at most 255 different atom cache references
// from the following terms can be made.
type DistributionHeader struct {
// bucket holds all the available atoms that have been set
// bucket[AtomCacheReferenceIndex][SegmentIndex][InternalSegmentIndex]
bucket [255][8][256]*Atom
// cache holds the current lookup into the bucket for the specific atom cache reference
// cache[AtomCacheReferenceIndex]
cache [255]*cacheIndex
// Flags for an even AtomCacheReferenceIndex are located in the least significant half byte and flags for an
// odd AtomCacheReferenceIndex are located in the most significant half byte.
//
// The flag field of an atom cache reference has the following format:
// 1 bit 3 bits
// NewCacheEntryFlag SegmentIndex
//
// The most significant bit is the NewCacheEntryFlag. If set, the corresponding cache reference is new.
// The three least significant bits are the SegmentIndex of the corresponding atom cache entry.
// An atom cache consists of 8 segments each of size 256, i.e., an atom cache can contain 2048 entries.
flags []byte
}
// cacheIndex holds the current cache position for an atom
type cacheIndex struct {
// SegmentIndex of the current atom cache
segmentIndex uint8
// InternalIndex of the current atom cache
internalIndex uint8
}
// GetAtom will return the atom that exists for the atomCacheReferenceIndex
func (dh DistributionHeader) GetAtom(atomCacheReferenceIndex uint8) (*Atom, error) {
// Get the atom cache index position
atomCache := dh.cache[atomCacheReferenceIndex]
// Look up the segment and internal index from the latest flags
if atomCache != nil {
atom := dh.bucket[atomCacheReferenceIndex][atomCache.segmentIndex][atomCache.internalIndex]
if atom == nil {
return nil, ErrMissingAtom
}
return atom, nil
}
return nil, ErrMissingAtom
}
// UpdateFlags will update the flags for the cache
func (dh DistributionHeader) Update(r io.Reader) error {
noAtomRefs, err := read1(r)
if err != nil {
return err
}
// If NumberOfAtomCacheRefs is 0, Flags and AtomCacheRefs are omitted
if noAtomRefs != 0 {
// Flags consists of NumberOfAtomCacheRefs/2+1 bytes
flags, err := ioutil.ReadAll(io.LimitReader(r, int64((noAtomRefs/2)+1)))
if err != nil {
return err
}
// Are these long atoms? Check the last half byte least significant bit
atomLen := 1
if dh.flags[len(dh.flags)-1]&LongAtoms == LongAtoms {
atomLen = 2
}
// The flag information is stored within the
for i, even := 0, true; i < noAtomRefs; i, even = i+1, !even {
// Get the cache item
cacheItem := dh.cache[i]
if cacheItem == nil {
cacheItem = &cacheIndex{}
dh.cache[i] = cacheItem
}
// We need the cache entry and segment index
newCacheEntry := (flags[i/2] & NewCacheEntry) == NewCacheEntry
cacheItem.segmentIndex = flags[i/2] & SegmentIndex
if !even {
newCacheEntry = (flags[i/2] >> 4 & NewCacheEntry) == NewCacheEntry
cacheItem.segmentIndex = flags[i/2] >> 4 & SegmentIndex
}
// We have the information to extract this atom
internalSegmentIndex, err := read1(r)
if err != nil {
return err
}
cacheItem.internalIndex = uint8(internalSegmentIndex)
// Extract the atom info for this is a new entry
if newCacheEntry {
// The length of the atom (can be 1 or 2 bytes)
alen := 0
if atomLen == 2 {
alen, err = read2(r)
} else {
alen, err = read1(r)
}
if err != nil {
return err
}
// Get the atom
atomBytes, err := ioutil.ReadAll(io.LimitReader(r, int64(alen)))
if err != nil {
return err
}
// Store the atom in the bucket using the index position
atom := Atom(atomBytes)
dh.bucket[i][cacheItem.segmentIndex][cacheItem.internalIndex] = &atom
}
}
}
return nil
}
// Reference wraps the REFERENCE_EXT tag type (101)
//
// Encode a reference object (an object generated with make_ref/0).
//
// The Node term is an encoded atom, i.e. ATOM_EXT, SMALL_ATOM_EXT or ATOM_CACHE_REF.
//
// The ID field contains a big-endian unsigned integer, but should be regarded as uninterpreted data
// since this field is node specific. Creation is a byte containing a node serial number that makes it
// possible to separate old (crashed) nodes from a new one.
//
// In ID, only 18 bits are significant; the rest should be 0. In Creation, only 2 bits are significant;
// the rest should be 0. See NEW_REFERENCE_EXT.
type Reference struct {
Node Atom
ID uint32
Creation uint8
}
// NewReference wraps the NEW_REFERENCE_EXT tag type (114)
//
// Node and Creation are as in REFERENCE_EXT.
//
// ID contains a sequence of big-endian unsigned integers (4 bytes each, so N' is a multiple of 4),
// but should be regarded as uninterpreted data.
//
// N' = 4 * Len.
//
// In the first word (four bytes) of ID, only 18 bits are significant, the rest should be 0.
//
// In Creation, only 2 bits are significant, the rest should be 0.
//
// NEW_REFERENCE_EXT was introduced with distribution version 4. In version 4, N' should be at most 12
type NewReference struct {
Node Atom
Creation uint8
ID []uint32
}
// Port wraps the PORT_EXT tag type (102)
//
// Encode a port object (obtained form open_port/2). The ID is a node specific identifier for a local port.
// Port operations are not allowed across node boundaries. The Creation works just like in REFERENCE_EXT.
type Port struct {
Node Atom
ID uint32
Creation uint8
}
// Pid wraps the PID_EXT tag type (103)
//
// Encode a process identifier object (obtained from spawn/3 or friends). The ID and Creation fields
// works just like in REFERENCE_EXT, while the Serial field is used to improve safety. In ID,
// only 15 bits are significant; the rest should be 0.
type Pid struct {
Node Atom
ID uint32
Serial uint32
Creation uint8
}
// Func wraps the FUN_EXT tag type (117)
//
// Pid
// is a process identifier as in PID_EXT. It represents the process in which the fun was created.
//
// Module
// is an encoded as an atom, using ATOM_EXT, SMALL_ATOM_EXT or ATOM_CACHE_REF. This is the module that the fun is implemented in.
//
// Index
// is an integer encoded using SMALL_INTEGER_EXT or INTEGER_EXT. It is typically a small index into the module's fun table.
//
// Uniq
// is an integer encoded using SMALL_INTEGER_EXT or INTEGER_EXT. Uniq is the hash value of the parse for the fun.
//
// Free vars
// is NumFree number of terms, each one encoded according to its type.
type Func struct {
Pid Pid
Module Atom
Index uint32
Uniq uint32
FreeVars []Term
}
// NewFunc wraps the NEW_FUN_EXT tag type (112)
// This is the new encoding of internal funs: fun F/A and fun(Arg1,..) -> ... end.
//
// Size
// is the total number of bytes, including the Size field.
//
// Arity
// is the arity of the function implementing the fun.
//
// Uniq
// is the 16 bytes MD5 of the significant parts of the Beam file.
//
// Index
// is an index number. Each fun within a module has an unique index. Index is stored in big-endian byte order.
//
// NumFree
// is the number of free variables.
//
// Module
// is an encoded as an atom, using ATOM_EXT, SMALL_ATOM_EXT or ATOM_CACHE_REF. This is the module that the fun is implemented in.
//
// OldIndex
// is an integer encoded using SMALL_INTEGER_EXT or INTEGER_EXT. It is typically a small index into the module's fun table.
//
// OldUniq
// is an integer encoded using SMALL_INTEGER_EXT or INTEGER_EXT. Uniq is the hash value of the parse tree for the fun.
//
// Pid
// is a process identifier as in PID_EXT. It represents the process in which the fun was created.
//
// Free vars
// is NumFree number of terms, each one encoded according to its type.
type NewFunc struct {
Arity uint8
Uniq []byte
Index uint32
Module Atom
OldIndex uint32
OldUnique uint32
Pid Pid
FreeVars []Term
}
// Export wraps the EXPORT_EXT tag type (113)
// This term is the encoding for external funs: fun M:F/A.
//
// Module and Function are atoms (encoded using ATOM_EXT, SMALL_ATOM_EXT or ATOM_CACHE_REF).
//
// Arity is an integer encoded using SMALL_INTEGER_EXT.
type Export struct {
Module Atom
Function Atom
Arity uint8
}