-
Notifications
You must be signed in to change notification settings - Fork 1
/
writer.go
181 lines (170 loc) · 7.07 KB
/
writer.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
// Copyright 2012 Google, Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.
package gopacket
import ()
// SerializableLayer allows its implementations to be written out as a set of bytes,
// so those bytes may be sent on the wire or otherwise used by the caller.
// SerializableLayer is implemented by certain Layer types, and can be encoded to
// bytes using the LayerWriter object.
type SerializableLayer interface {
// SerializeTo writes this layer to a slice, growing that slice if necessary
// to make it fit the layer's data.
// Args:
// b: SerializeBuffer to write this layer on to. When called, b.Bytes()
// is the payload this layer should wrap, if any. Note that this
// layer can either prepend itself (common), append itself
// (uncommon), or both (sometimes padding or footers are required at
// the end of packet data). It's also possible (though probably very
// rarely needed) to overwrite any bytes in the current payload.
// After this call, b.Bytes() should return the byte encoding of
// this layer wrapping the original b.Bytes() payload.
// opts: options to use while writing out data.
// Returns:
// error if a problem was encountered during encoding. If an error is
// returned, the bytes in data should be considered invalidated, and
// not used.
//
// SerializeTo calls SHOULD entirely ignore LayerContents and
// LayerPayload. It just serializes based on struct fields, neither
// modifying nor using contents/payload.
SerializeTo(b *SerializeBuffer, opts SerializeOptions) error
}
// SerializeOptions provides options for behaviors that SerializableLayers may want to
// implement.
type SerializeOptions struct {
// FixLengths determines whether, during serialization, layers should fix
// the values for any length field that depends on the payload.
FixLengths bool
// ComputeChecksums determines whether, during serialization, layers
// should recompute checksums based on their payloads.
ComputeChecksums bool
}
// SerializeBuffer is a helper used by gopacket for writing out packet layers.
// SerializeBuffer starts off as an empty []byte. Subsequent calls to PrependBytes
// return byte slices before the current Bytes(), AppendBytes returns byte
// slices after.
//
// Byte slices returned by PrependBytes/AppendBytes are NOT zero'd out, so if
// you want to make sure they're all zeros, set them as such.
//
// SerializeBuffer is specifically designed to handle packet writing, where unlike
// with normal writes it's easier to start writing at the inner-most layer and
// work out, meaning that we often need to prepend bytes. This runs counter to
// typical writes to byte slices using append(), where we only write at the end
// of the buffer.
//
// As seen with the Clear method in the example above, a write buffer can be
// reused by clearing it. Note, however, that a Clear call will invalidate the
// byte slices returned by any previous Bytes() call (the same buffer is
// reused). This means that:
//
// 1) Reusing a write buffer is extremely fast and should be efficient.
// 2) If a byte slice from a previous Bytes() call will continue to be used,
// it's better to create a new SerializeBuffer.
//
// The Clear method is specifically designed to minimize memory allocations for
// similar later workloads on the SerializeBuffer. IE: if you make a set of
// Prepend/Append calls, then clear, then make the same calls with the same
// sizes, the second round (and all future similar rounds) shouldn't allocate
// any new memory.
//
// A SerializeBuffer may be used simply by declaring it (its zero value is fully
// functional). Use of NewSerializeBuffer is just an optimization.
type SerializeBuffer struct {
data []byte
start int
prepended, appended int
}
func NewSerializeBuffer(expectedPrependLength, expectedAppendLength int) *SerializeBuffer {
return &SerializeBuffer{
data: make([]byte, expectedPrependLength, expectedPrependLength+expectedAppendLength),
start: expectedPrependLength,
prepended: expectedPrependLength,
appended: expectedAppendLength,
}
}
// Bytes returns the contiguous set of bytes collected so far by Prepend/Append
// calls.
func (w *SerializeBuffer) Bytes() []byte {
return w.data[w.start:]
}
// PrependBytes returns a set of bytes which prepends the current bytes in this
// buffer. These bytes start in an indeterminate state, so they should be
// overwritten by the caller. The caller must only call PrependBytes if they
// know they're going to immediately overwrite all bytes returned.
func (w *SerializeBuffer) PrependBytes(num int) []byte {
if num < 0 {
panic("num < 0")
}
if w.start < num {
toPrepend := w.prepended
if toPrepend < num {
toPrepend = num
}
w.prepended += toPrepend
length := cap(w.data) + toPrepend
newData := make([]byte, length)
newStart := w.start + toPrepend
copy(newData[newStart:], w.data[w.start:])
w.start = newStart
w.data = newData[:toPrepend+len(w.data)]
}
w.start -= num
return w.data[w.start : w.start+num]
}
// AppendBytes returns a set of bytes which prepends the current bytes in this
// buffer. These bytes start in an indeterminate state, so they should be
// overwritten by the caller. The caller must only call AppendBytes if they
// know they're going to immediately overwrite all bytes returned.
func (w *SerializeBuffer) AppendBytes(num int) []byte {
if num < 0 {
panic("num < 0")
}
initialLength := len(w.data)
if cap(w.data)-initialLength < num {
toAppend := w.appended
if toAppend < num {
toAppend = num
}
w.appended += toAppend
newData := make([]byte, cap(w.data)+toAppend)
copy(newData[w.start:], w.data[w.start:])
w.data = newData[:initialLength]
}
// Grow the buffer. We know it'll be under capacity given above.
w.data = w.data[:initialLength+num]
return w.data[initialLength:]
}
// Clear resets the SerializeBuffer to a new, empty buffer. After a call to clear,
// the byte slice returned by any previous call to Bytes() for this buffer
// should be considered invalidated.
func (w *SerializeBuffer) Clear() {
w.start = w.prepended
w.data = w.data[:w.start]
}
// SerializeLayers clears the given write buffer, then writes all layers into it so
// they correctly wrap each other. Note that by clearing the buffer, it
// invalidates all slices previously returned by w.Bytes()
//
// Example:
// var buf SerializeBuffer
// var opts SerializeOptions
// buf.SerializeLayers([]SerializableLayer{a, b, c}, opts)
// firstPayload := buf.Bytes() // contains byte representation of a(b(c))
// buf.SerializeLayers([]SerializableLayer{d, e, f}, opts)
// secondPayload := buf.Bytes() // contains byte representation of d(e(f)).
// firstPayload is now invalidated, since the SerializeLayers call Clears buf.
func (w *SerializeBuffer) SerializeLayers(opts SerializeOptions, layers ...SerializableLayer) error {
w.Clear()
for i := len(layers) - 1; i >= 0; i-- {
layer := layers[i]
err := layer.SerializeTo(w, opts)
if err != nil {
return err
}
}
return nil
}