-
Notifications
You must be signed in to change notification settings - Fork 114
/
payload.go
195 lines (161 loc) · 5.88 KB
/
payload.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
package statement
import (
"fmt"
"sync"
"github.com/iotaledger/goshimmer/packages/tangle/payload"
"github.com/iotaledger/hive.go/cerrors"
"github.com/iotaledger/hive.go/marshalutil"
"github.com/iotaledger/hive.go/stringify"
"golang.org/x/xerrors"
)
const (
// ObjectName defines the name of the Statement object.
ObjectName = "Statement"
)
// StatementType represents the payload Type of a Statement.
var StatementType payload.Type
func init() {
// Type defines the type of the statement payload.
StatementType = payload.NewType(3, ObjectName, func(data []byte) (payload payload.Payload, err error) {
payload, _, err = FromBytes(data)
return
})
}
// Statement defines a Statement payload.
type Statement struct {
ConflictsCount uint32
Conflicts Conflicts
TimestampsCount uint32
Timestamps Timestamps
bytes []byte
bytesMutex sync.RWMutex
}
// New creates a new Statement payload.
func New(conflicts Conflicts, timestamps Timestamps) *Statement {
return &Statement{
ConflictsCount: uint32(len(conflicts)),
Conflicts: conflicts,
TimestampsCount: uint32(len(timestamps)),
Timestamps: timestamps,
}
}
// FromBytes unmarshals a Statement Payload from a sequence of bytes.
func FromBytes(bytes []byte) (statement *Statement, consumedBytes int, err error) {
marshalUtil := marshalutil.New(bytes)
if statement, err = Parse(marshalUtil); err != nil {
err = xerrors.Errorf("failed to parse Statement Payload from MarshalUtil: %w", err)
return
}
consumedBytes = marshalUtil.ReadOffset()
// store bytes, so we don't have to marshal manually
statement.bytes = bytes[:consumedBytes]
return
}
// Parse unmarshals a statement using the given marshalUtil (for easier marshaling/unmarshaling).
func Parse(marshalUtil *marshalutil.MarshalUtil) (statement *Statement, err error) {
readStartOffset := marshalUtil.ReadOffset()
// read information that are required to identify the payload from the outside
statement = &Statement{}
payloadSize, err := marshalUtil.ReadUint32()
if err != nil {
err = xerrors.Errorf("failed to parse payload size of statement payload: %w", err)
return
}
// a payloadSize of 0 indicates the payload is omitted and the payload is nil
if payloadSize == 0 {
return
}
payloadType, err := payload.TypeFromMarshalUtil(marshalUtil)
if err != nil {
err = xerrors.Errorf("failed to parse payload type of statement payload: %w", err)
return
}
if payloadType != StatementType {
err = xerrors.Errorf("payload type '%s' does not match expected '%s': %w", payloadType, StatementType, cerrors.ErrParseBytesFailed)
return
}
// parse conflicts
if statement.ConflictsCount, err = marshalUtil.ReadUint32(); err != nil {
err = xerrors.Errorf("failed to parse conflicts len of statement payload: %w", err)
return
}
parsedBytes := marshalUtil.ReadOffset() - 8 //skip the payload size and type
if uint32(parsedBytes)+(statement.ConflictsCount*ConflictLength) > payloadSize {
err = fmt.Errorf("failed to parse statement payload: number of conflicts overflowing: %w", err)
return
}
if statement.Conflicts, err = ConflictsFromMarshalUtil(marshalUtil, statement.ConflictsCount); err != nil {
err = xerrors.Errorf("failed to parse conflicts from statement payload: %w", err)
return
}
// parse timestamps
if statement.TimestampsCount, err = marshalUtil.ReadUint32(); err != nil {
err = xerrors.Errorf("failed to parse timestamps len of statement payload: %w", err)
return
}
parsedBytes = marshalUtil.ReadOffset() - 8 //skip the payload size and type
if uint32(parsedBytes)+statement.TimestampsCount*TimestampLength > payloadSize {
err = fmt.Errorf("failed to parse statement payload: number of timestamps overflowing: %w", err)
return
}
if statement.Timestamps, err = TimestampsFromMarshalUtil(marshalUtil, statement.TimestampsCount); err != nil {
err = xerrors.Errorf("failed to parse timestamps from statement payload: %w", err)
return
}
// return the number of bytes we processed
parsedBytes = marshalUtil.ReadOffset() - readStartOffset
if parsedBytes != int(payloadSize)+4 { //skip the payload size
err = xerrors.Errorf("parsed bytes (%d) did not match expected size (%d): %w", parsedBytes, payloadSize, cerrors.ErrParseBytesFailed)
return
}
return
}
// Bytes returns the statement payload bytes.
func (s *Statement) Bytes() (bytes []byte) {
// acquire lock for reading bytes
s.bytesMutex.RLock()
// return if bytes have been determined already
if bytes = s.bytes; bytes != nil {
s.bytesMutex.RUnlock()
return
}
// switch to write lock
s.bytesMutex.RUnlock()
s.bytesMutex.Lock()
defer s.bytesMutex.Unlock()
// return if bytes have been determined in the mean time
if bytes = s.bytes; bytes != nil {
return
}
payloadBytes := marshalutil.New().
WriteUint32(s.ConflictsCount).
Write(s.Conflicts).
WriteUint32(s.TimestampsCount).
Write(s.Timestamps).
Bytes()
payloadBytesLength := len(payloadBytes)
// add uint32 for length and type
return marshalutil.New(2*marshalutil.Uint32Size + payloadBytesLength).
WriteUint32(payload.TypeLength + uint32(payloadBytesLength)).
Write(StatementType).
WriteBytes(payloadBytes).
Bytes()
}
func (s *Statement) String() string {
return stringify.Struct("Payload",
stringify.StructField("conflictsLen", s.ConflictsCount),
stringify.StructField("conflicts", s.Conflicts),
stringify.StructField("timestampsLen", s.TimestampsCount),
stringify.StructField("timestamps", s.Timestamps),
)
}
// region Payload implementation ///////////////////////////////////////////////////////////////////////////////////////
// Type returns the type of the statement payload.
func (*Statement) Type() payload.Type {
return StatementType
}
// Marshal marshals the statement payload into bytes.
func (s *Statement) Marshal() (bytes []byte, err error) {
return s.Bytes(), nil
}
// // endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////