Repository navigation
Expand file tree
/
Copy pathencode.go
More file actions
583 lines (501 loc) · 18.3 KB
/
Copy pathencode.go
File metadata and controls
583 lines (501 loc) · 18.3 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
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
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
package protocol
import (
"bytes"
"encoding/binary"
"errors"
)
var errInvalidJSON = errors.New("invalid JSON data")
// PushEncoder encodes Push and its parts to bytes.
//
// Parts of a Push are encoded separately from the Push envelope, so that a
// payload encoded once may be reused for all subscribers of a channel.
//
// Methods which accept a variadic reuse argument may write the result into the
// provided buffer if it's large enough, avoiding an allocation. The returned
// slice may or may not alias the given buffer, so always use the returned value
// and don't touch the buffer until the result is not needed anymore.
type PushEncoder interface {
Encode(*Push) ([]byte, error)
EncodeMessage(*Message, ...[]byte) ([]byte, error)
EncodePublication(*Publication, ...[]byte) ([]byte, error)
EncodeJoin(*Join, ...[]byte) ([]byte, error)
EncodeLeave(*Leave, ...[]byte) ([]byte, error)
EncodeUnsubscribe(*Unsubscribe, ...[]byte) ([]byte, error)
EncodeSubscribe(*Subscribe, ...[]byte) ([]byte, error)
EncodeConnect(*Connect, ...[]byte) ([]byte, error)
EncodeDisconnect(*Disconnect, ...[]byte) ([]byte, error)
EncodeRefresh(*Refresh, ...[]byte) ([]byte, error)
}
var _ PushEncoder = (*JSONPushEncoder)(nil)
var _ PushEncoder = (*ProtobufPushEncoder)(nil)
// JSONPushEncoder is a PushEncoder which encodes to JSON.
type JSONPushEncoder struct {
}
// NewJSONPushEncoder creates a new JSONPushEncoder. It's safe to use the
// returned encoder concurrently, see also DefaultJsonPushEncoder.
func NewJSONPushEncoder() *JSONPushEncoder {
return &JSONPushEncoder{}
}
// Encode Push to bytes.
func (e *JSONPushEncoder) Encode(message *Push) ([]byte, error) {
// Payloads are given by the application and may be anything. The rest
// of a message is written by generated code, so payloads which are
// valid JSON values are all it takes for the message to be valid.
if !message.validRaw() {
return nil, errInvalidJSON
}
return encodeJSON(message), nil
}
// EncodePublication to bytes.
func (e *JSONPushEncoder) EncodePublication(message *Publication, reuse ...[]byte) ([]byte, error) {
return encodeJSON(message, reuse...), nil
}
// EncodeMessage to bytes.
func (e *JSONPushEncoder) EncodeMessage(message *Message, reuse ...[]byte) ([]byte, error) {
return encodeJSON(message, reuse...), nil
}
// EncodeJoin to bytes.
func (e *JSONPushEncoder) EncodeJoin(message *Join, reuse ...[]byte) ([]byte, error) {
return encodeJSON(message, reuse...), nil
}
// EncodeLeave to bytes.
func (e *JSONPushEncoder) EncodeLeave(message *Leave, reuse ...[]byte) ([]byte, error) {
return encodeJSON(message, reuse...), nil
}
// EncodeUnsubscribe to bytes.
func (e *JSONPushEncoder) EncodeUnsubscribe(message *Unsubscribe, reuse ...[]byte) ([]byte, error) {
return encodeJSON(message, reuse...), nil
}
// EncodeSubscribe to bytes.
func (e *JSONPushEncoder) EncodeSubscribe(message *Subscribe, reuse ...[]byte) ([]byte, error) {
return encodeJSON(message, reuse...), nil
}
// EncodeConnect to bytes.
func (e *JSONPushEncoder) EncodeConnect(message *Connect, reuse ...[]byte) ([]byte, error) {
return encodeJSON(message, reuse...), nil
}
// EncodeDisconnect to bytes.
func (e *JSONPushEncoder) EncodeDisconnect(message *Disconnect, reuse ...[]byte) ([]byte, error) {
return encodeJSON(message, reuse...), nil
}
// EncodeRefresh to bytes.
func (e *JSONPushEncoder) EncodeRefresh(message *Refresh, reuse ...[]byte) ([]byte, error) {
return encodeJSON(message, reuse...), nil
}
// ProtobufPushEncoder is a PushEncoder which encodes to Protobuf.
type ProtobufPushEncoder struct {
}
// NewProtobufPushEncoder creates a new ProtobufPushEncoder. It's safe to use the
// returned encoder concurrently, see also DefaultProtobufPushEncoder.
func NewProtobufPushEncoder() *ProtobufPushEncoder {
return &ProtobufPushEncoder{}
}
// Encode Push to bytes.
func (e *ProtobufPushEncoder) Encode(message *Push) ([]byte, error) {
return message.MarshalCF()
}
// EncodePublication to bytes.
func (e *ProtobufPushEncoder) EncodePublication(message *Publication, reuse ...[]byte) ([]byte, error) {
if len(reuse) == 1 {
size := message.SizeCF()
if cap(reuse[0]) >= size {
n, err := message.MarshalToSizedBufferCF(reuse[0][:size])
if err != nil {
return nil, err
}
return reuse[0][:n], nil
}
}
return message.MarshalCF()
}
// EncodeMessage to bytes.
func (e *ProtobufPushEncoder) EncodeMessage(message *Message, reuse ...[]byte) ([]byte, error) {
if len(reuse) == 1 {
size := message.SizeCF()
if cap(reuse[0]) >= size {
n, err := message.MarshalToSizedBufferCF(reuse[0][:size])
if err != nil {
return nil, err
}
return reuse[0][:n], nil
}
}
return message.MarshalCF()
}
// EncodeJoin to bytes.
func (e *ProtobufPushEncoder) EncodeJoin(message *Join, reuse ...[]byte) ([]byte, error) {
if len(reuse) == 1 {
size := message.SizeCF()
if cap(reuse[0]) >= size {
n, err := message.MarshalToSizedBufferCF(reuse[0][:size])
if err != nil {
return nil, err
}
return reuse[0][:n], nil
}
}
return message.MarshalCF()
}
// EncodeLeave to bytes.
func (e *ProtobufPushEncoder) EncodeLeave(message *Leave, reuse ...[]byte) ([]byte, error) {
if len(reuse) == 1 {
size := message.SizeCF()
if cap(reuse[0]) >= size {
n, err := message.MarshalToSizedBufferCF(reuse[0][:size])
if err != nil {
return nil, err
}
return reuse[0][:n], nil
}
}
return message.MarshalCF()
}
// EncodeUnsubscribe to bytes.
func (e *ProtobufPushEncoder) EncodeUnsubscribe(message *Unsubscribe, reuse ...[]byte) ([]byte, error) {
if len(reuse) == 1 {
size := message.SizeCF()
if cap(reuse[0]) >= size {
n, err := message.MarshalToSizedBufferCF(reuse[0][:size])
if err != nil {
return nil, err
}
return reuse[0][:n], nil
}
}
return message.MarshalCF()
}
// EncodeSubscribe to bytes.
func (e *ProtobufPushEncoder) EncodeSubscribe(message *Subscribe, reuse ...[]byte) ([]byte, error) {
if len(reuse) == 1 {
size := message.SizeCF()
if cap(reuse[0]) >= size {
n, err := message.MarshalToSizedBufferCF(reuse[0][:size])
if err != nil {
return nil, err
}
return reuse[0][:n], nil
}
}
return message.MarshalCF()
}
// EncodeConnect to bytes.
func (e *ProtobufPushEncoder) EncodeConnect(message *Connect, reuse ...[]byte) ([]byte, error) {
if len(reuse) == 1 {
size := message.SizeCF()
if cap(reuse[0]) >= size {
n, err := message.MarshalToSizedBufferCF(reuse[0][:size])
if err != nil {
return nil, err
}
return reuse[0][:n], nil
}
}
return message.MarshalCF()
}
// EncodeDisconnect to bytes.
func (e *ProtobufPushEncoder) EncodeDisconnect(message *Disconnect, reuse ...[]byte) ([]byte, error) {
if len(reuse) == 1 {
size := message.SizeCF()
if cap(reuse[0]) >= size {
n, err := message.MarshalToSizedBufferCF(reuse[0][:size])
if err != nil {
return nil, err
}
return reuse[0][:n], nil
}
}
return message.MarshalCF()
}
// EncodeRefresh to bytes.
func (e *ProtobufPushEncoder) EncodeRefresh(message *Refresh, reuse ...[]byte) ([]byte, error) {
if len(reuse) == 1 {
size := message.SizeCF()
if cap(reuse[0]) >= size {
n, err := message.MarshalToSizedBufferCF(reuse[0][:size])
if err != nil {
return nil, err
}
return reuse[0][:n], nil
}
}
return message.MarshalCF()
}
// ReplyEncoder encodes Reply to bytes. Use GetReplyEncoder to get an
// implementation for a concrete protocol Type.
type ReplyEncoder interface {
Encode(*Reply) ([]byte, error)
}
// JSONReplyEncoder is a ReplyEncoder which encodes to JSON.
type JSONReplyEncoder struct{}
// NewJSONReplyEncoder creates a new JSONReplyEncoder. It's safe to use the
// returned encoder concurrently, see also DefaultJsonReplyEncoder.
func NewJSONReplyEncoder() *JSONReplyEncoder {
return &JSONReplyEncoder{}
}
// Encode Reply to bytes.
func (e *JSONReplyEncoder) Encode(r *Reply) ([]byte, error) {
// See JSONPushEncoder.Encode.
if !r.validRaw() {
return nil, errInvalidJSON
}
return encodeJSON(r), nil
}
// ProtobufReplyEncoder is a ReplyEncoder which encodes to Protobuf.
type ProtobufReplyEncoder struct{}
// NewProtobufReplyEncoder creates a new ProtobufReplyEncoder. It's safe to use
// the returned encoder concurrently, see also DefaultProtobufReplyEncoder.
func NewProtobufReplyEncoder() *ProtobufReplyEncoder {
return &ProtobufReplyEncoder{}
}
// Encode Reply to bytes.
func (e *ProtobufReplyEncoder) Encode(r *Reply) ([]byte, error) {
return r.MarshalCF()
}
// DataEncoder concatenates already encoded messages into a single transport
// frame, applying the framing of a concrete protocol Type: messages are
// separated by a `\n` delimiter in JSON and prefixed with their length in
// Protobuf.
//
// A DataEncoder is not safe for concurrent use. Use GetDataEncoder and
// PutDataEncoder to take one from a pool and return it back when done.
type DataEncoder interface {
// Reset prepares the encoder to build a new frame, dropping everything
// encoded so far.
Reset()
// Encode appends an already encoded message to the frame.
Encode([]byte) error
// Finish returns a copy of the frame built so far.
Finish() []byte
// FinishNoCopy returns the frame built so far without copying it. The
// returned slice is only valid until the next Reset or Encode call, so use
// it only if the data is written to a connection before that happens.
FinishNoCopy() []byte
}
// JSONDataEncoder is a DataEncoder which separates messages by a `\n` delimiter.
type JSONDataEncoder struct {
count int
buffer bytes.Buffer
}
// NewJSONDataEncoder creates a new JSONDataEncoder.
func NewJSONDataEncoder() *JSONDataEncoder {
return &JSONDataEncoder{}
}
// Reset prepares the encoder to build a new frame.
func (e *JSONDataEncoder) Reset() {
e.count = 0
e.buffer.Reset()
}
// Encode appends an already encoded message to the frame, separating it from
// the previous one with a `\n` delimiter.
func (e *JSONDataEncoder) Encode(data []byte) error {
if e.count > 0 {
e.buffer.WriteString("\n")
}
e.buffer.Write(data)
e.count++
return nil
}
// Finish returns a copy of the frame built so far.
func (e *JSONDataEncoder) Finish() []byte {
data := e.buffer.Bytes()
dataCopy := make([]byte, len(data))
copy(dataCopy, data)
return dataCopy
}
// FinishNoCopy returns the frame built so far without copying it, see the
// DataEncoder interface for the lifetime of the returned slice.
func (e *JSONDataEncoder) FinishNoCopy() []byte {
return e.buffer.Bytes()
}
// ProtobufDataEncoder is a DataEncoder which prefixes each message with its
// length encoded as a varint.
type ProtobufDataEncoder struct {
buffer bytes.Buffer
}
// NewProtobufDataEncoder creates a new ProtobufDataEncoder.
func NewProtobufDataEncoder() *ProtobufDataEncoder {
return &ProtobufDataEncoder{}
}
// Encode appends an already encoded message to the frame, prefixing it with its
// length encoded as a varint.
func (e *ProtobufDataEncoder) Encode(data []byte) error {
var bs [binary.MaxVarintLen64]byte
n := binary.PutUvarint(bs[:], uint64(len(data)))
e.buffer.Write(bs[:n])
e.buffer.Write(data)
return nil
}
// Reset prepares the encoder to build a new frame.
func (e *ProtobufDataEncoder) Reset() {
e.buffer.Reset()
}
// Finish returns a copy of the frame built so far.
func (e *ProtobufDataEncoder) Finish() []byte {
data := e.buffer.Bytes()
dataCopy := make([]byte, len(data))
copy(dataCopy, data)
return dataCopy
}
// FinishNoCopy returns the frame built so far without copying it, see the
// DataEncoder interface for the lifetime of the returned slice.
func (e *ProtobufDataEncoder) FinishNoCopy() []byte {
return e.buffer.Bytes()
}
// ResultEncoder encodes command results to bytes. Results are encoded separately
// from the Reply envelope, so that a result may be encoded once and then reused
// for many connections. Use GetResultEncoder to get an implementation for a
// concrete protocol Type.
type ResultEncoder interface {
EncodeConnectResult(*ConnectResult) ([]byte, error)
EncodeRefreshResult(*RefreshResult) ([]byte, error)
EncodeSubscribeResult(*SubscribeResult) ([]byte, error)
EncodeSubRefreshResult(*SubRefreshResult) ([]byte, error)
EncodeUnsubscribeResult(*UnsubscribeResult) ([]byte, error)
EncodePublishResult(*PublishResult) ([]byte, error)
EncodePresenceResult(*PresenceResult) ([]byte, error)
EncodePresenceStatsResult(*PresenceStatsResult) ([]byte, error)
EncodeHistoryResult(*HistoryResult) ([]byte, error)
EncodePingResult(*PingResult) ([]byte, error)
EncodeRPCResult(*RPCResult) ([]byte, error)
}
// JSONResultEncoder is a ResultEncoder which encodes to JSON.
type JSONResultEncoder struct{}
// NewJSONResultEncoder creates a new JSONResultEncoder. It's safe to use the
// returned encoder concurrently.
func NewJSONResultEncoder() *JSONResultEncoder {
return &JSONResultEncoder{}
}
// EncodeConnectResult encodes ConnectResult to bytes.
func (e *JSONResultEncoder) EncodeConnectResult(res *ConnectResult) ([]byte, error) {
return encodeJSON(res), nil
}
// EncodeRefreshResult encodes RefreshResult to bytes.
func (e *JSONResultEncoder) EncodeRefreshResult(res *RefreshResult) ([]byte, error) {
return encodeJSON(res), nil
}
// EncodeSubscribeResult encodes SubscribeResult to bytes.
func (e *JSONResultEncoder) EncodeSubscribeResult(res *SubscribeResult) ([]byte, error) {
return encodeJSON(res), nil
}
// EncodeSubRefreshResult encodes SubRefreshResult to bytes.
func (e *JSONResultEncoder) EncodeSubRefreshResult(res *SubRefreshResult) ([]byte, error) {
return encodeJSON(res), nil
}
// EncodeUnsubscribeResult encodes UnsubscribeResult to bytes.
func (e *JSONResultEncoder) EncodeUnsubscribeResult(res *UnsubscribeResult) ([]byte, error) {
return encodeJSON(res), nil
}
// EncodePublishResult encodes PublishResult to bytes.
func (e *JSONResultEncoder) EncodePublishResult(res *PublishResult) ([]byte, error) {
return encodeJSON(res), nil
}
// EncodePresenceResult encodes PresenceResult to bytes.
func (e *JSONResultEncoder) EncodePresenceResult(res *PresenceResult) ([]byte, error) {
return encodeJSON(res), nil
}
// EncodePresenceStatsResult encodes PresenceStatsResult to bytes.
func (e *JSONResultEncoder) EncodePresenceStatsResult(res *PresenceStatsResult) ([]byte, error) {
return encodeJSON(res), nil
}
// EncodeHistoryResult encodes HistoryResult to bytes.
func (e *JSONResultEncoder) EncodeHistoryResult(res *HistoryResult) ([]byte, error) {
return encodeJSON(res), nil
}
// EncodePingResult encodes PingResult to bytes.
func (e *JSONResultEncoder) EncodePingResult(res *PingResult) ([]byte, error) {
return encodeJSON(res), nil
}
// EncodeRPCResult encodes RPCResult to bytes.
func (e *JSONResultEncoder) EncodeRPCResult(res *RPCResult) ([]byte, error) {
return encodeJSON(res), nil
}
// ProtobufResultEncoder is a ResultEncoder which encodes to Protobuf.
type ProtobufResultEncoder struct{}
// NewProtobufResultEncoder creates a new ProtobufResultEncoder. It's safe to use
// the returned encoder concurrently.
func NewProtobufResultEncoder() *ProtobufResultEncoder {
return &ProtobufResultEncoder{}
}
// EncodeConnectResult encodes ConnectResult to bytes.
func (e *ProtobufResultEncoder) EncodeConnectResult(res *ConnectResult) ([]byte, error) {
return res.MarshalCF()
}
// EncodeRefreshResult encodes RefreshResult to bytes.
func (e *ProtobufResultEncoder) EncodeRefreshResult(res *RefreshResult) ([]byte, error) {
return res.MarshalCF()
}
// EncodeSubscribeResult encodes SubscribeResult to bytes.
func (e *ProtobufResultEncoder) EncodeSubscribeResult(res *SubscribeResult) ([]byte, error) {
return res.MarshalCF()
}
// EncodeSubRefreshResult encodes SubRefreshResult to bytes.
func (e *ProtobufResultEncoder) EncodeSubRefreshResult(res *SubRefreshResult) ([]byte, error) {
return res.MarshalCF()
}
// EncodeUnsubscribeResult encodes UnsubscribeResult to bytes.
func (e *ProtobufResultEncoder) EncodeUnsubscribeResult(res *UnsubscribeResult) ([]byte, error) {
return res.MarshalCF()
}
// EncodePublishResult encodes PublishResult to bytes.
func (e *ProtobufResultEncoder) EncodePublishResult(res *PublishResult) ([]byte, error) {
return res.MarshalCF()
}
// EncodePresenceResult encodes PresenceResult to bytes.
func (e *ProtobufResultEncoder) EncodePresenceResult(res *PresenceResult) ([]byte, error) {
return res.MarshalCF()
}
// EncodePresenceStatsResult encodes PresenceStatsResult to bytes.
func (e *ProtobufResultEncoder) EncodePresenceStatsResult(res *PresenceStatsResult) ([]byte, error) {
return res.MarshalCF()
}
// EncodeHistoryResult encodes HistoryResult to bytes.
func (e *ProtobufResultEncoder) EncodeHistoryResult(res *HistoryResult) ([]byte, error) {
return res.MarshalCF()
}
// EncodePingResult encodes PingResult to bytes.
func (e *ProtobufResultEncoder) EncodePingResult(res *PingResult) ([]byte, error) {
return res.MarshalCF()
}
// EncodeRPCResult encodes RPCResult to bytes.
func (e *ProtobufResultEncoder) EncodeRPCResult(res *RPCResult) ([]byte, error) {
return res.MarshalCF()
}
// CommandEncoder encodes Command to bytes. It's the client-side counterpart of
// CommandDecoder.
type CommandEncoder interface {
Encode(cmd *Command) ([]byte, error)
}
// JSONCommandEncoder is a CommandEncoder which encodes to JSON.
type JSONCommandEncoder struct {
}
// NewJSONCommandEncoder creates a new JSONCommandEncoder. It's safe to use the
// returned encoder concurrently.
func NewJSONCommandEncoder() *JSONCommandEncoder {
return &JSONCommandEncoder{}
}
// Encode Command to bytes. The result contains no framing: to send several
// commands in one frame join them with a `\n` delimiter, see JSONDataEncoder.
func (e *JSONCommandEncoder) Encode(cmd *Command) ([]byte, error) {
return encodeJSON(cmd), nil
}
// ProtobufCommandEncoder is a CommandEncoder which encodes to Protobuf.
type ProtobufCommandEncoder struct {
}
// NewProtobufCommandEncoder creates a new ProtobufCommandEncoder. It's safe to
// use the returned encoder concurrently.
func NewProtobufCommandEncoder() *ProtobufCommandEncoder {
return &ProtobufCommandEncoder{}
}
// Encode Command to bytes prefixed with the command length encoded as a varint,
// so that encoded commands may be sent one after another in a single frame.
func (e *ProtobufCommandEncoder) Encode(cmd *Command) ([]byte, error) {
size := cmd.SizeCF()
var prefix [binary.MaxVarintLen64]byte
prefixLen := binary.PutUvarint(prefix[:], uint64(size)) //nolint:gosec // G115: a size is never negative.
out := make([]byte, prefixLen+size)
copy(out, prefix[:prefixLen])
if _, err := cmd.MarshalToSizedBufferCF(out[prefixLen:]); err != nil {
return nil, err
}
return out, nil
}