blob: a49225dabfb545e95ef4f82a40787507f8cdabeb [file] [log] [blame]
khenaidooc6c7bda2020-06-17 17:20:18 -04001/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20package thrift
21
22import (
khenaidood948f772021-08-11 17:49:24 -040023 "context"
khenaidooc6c7bda2020-06-17 17:20:18 -040024 "encoding/binary"
khenaidood948f772021-08-11 17:49:24 -040025 "errors"
khenaidooc6c7bda2020-06-17 17:20:18 -040026 "fmt"
27 "io"
28 "math"
29)
30
31const (
32 COMPACT_PROTOCOL_ID = 0x082
33 COMPACT_VERSION = 1
34 COMPACT_VERSION_MASK = 0x1f
35 COMPACT_TYPE_MASK = 0x0E0
36 COMPACT_TYPE_BITS = 0x07
37 COMPACT_TYPE_SHIFT_AMOUNT = 5
38)
39
40type tCompactType byte
41
42const (
43 COMPACT_BOOLEAN_TRUE = 0x01
44 COMPACT_BOOLEAN_FALSE = 0x02
45 COMPACT_BYTE = 0x03
46 COMPACT_I16 = 0x04
47 COMPACT_I32 = 0x05
48 COMPACT_I64 = 0x06
49 COMPACT_DOUBLE = 0x07
50 COMPACT_BINARY = 0x08
51 COMPACT_LIST = 0x09
52 COMPACT_SET = 0x0A
53 COMPACT_MAP = 0x0B
54 COMPACT_STRUCT = 0x0C
55)
56
57var (
58 ttypeToCompactType map[TType]tCompactType
59)
60
61func init() {
62 ttypeToCompactType = map[TType]tCompactType{
63 STOP: STOP,
64 BOOL: COMPACT_BOOLEAN_TRUE,
65 BYTE: COMPACT_BYTE,
66 I16: COMPACT_I16,
67 I32: COMPACT_I32,
68 I64: COMPACT_I64,
69 DOUBLE: COMPACT_DOUBLE,
70 STRING: COMPACT_BINARY,
71 LIST: COMPACT_LIST,
72 SET: COMPACT_SET,
73 MAP: COMPACT_MAP,
74 STRUCT: COMPACT_STRUCT,
75 }
76}
77
khenaidood948f772021-08-11 17:49:24 -040078type TCompactProtocolFactory struct {
79 cfg *TConfiguration
80}
khenaidooc6c7bda2020-06-17 17:20:18 -040081
khenaidood948f772021-08-11 17:49:24 -040082// Deprecated: Use NewTCompactProtocolFactoryConf instead.
khenaidooc6c7bda2020-06-17 17:20:18 -040083func NewTCompactProtocolFactory() *TCompactProtocolFactory {
khenaidood948f772021-08-11 17:49:24 -040084 return NewTCompactProtocolFactoryConf(&TConfiguration{
85 noPropagation: true,
86 })
87}
88
89func NewTCompactProtocolFactoryConf(conf *TConfiguration) *TCompactProtocolFactory {
90 return &TCompactProtocolFactory{
91 cfg: conf,
92 }
khenaidooc6c7bda2020-06-17 17:20:18 -040093}
94
95func (p *TCompactProtocolFactory) GetProtocol(trans TTransport) TProtocol {
khenaidood948f772021-08-11 17:49:24 -040096 return NewTCompactProtocolConf(trans, p.cfg)
97}
98
99func (p *TCompactProtocolFactory) SetTConfiguration(conf *TConfiguration) {
100 p.cfg = conf
khenaidooc6c7bda2020-06-17 17:20:18 -0400101}
102
103type TCompactProtocol struct {
104 trans TRichTransport
105 origTransport TTransport
106
khenaidood948f772021-08-11 17:49:24 -0400107 cfg *TConfiguration
108
khenaidooc6c7bda2020-06-17 17:20:18 -0400109 // Used to keep track of the last field for the current and previous structs,
110 // so we can do the delta stuff.
111 lastField []int
112 lastFieldId int
113
114 // If we encounter a boolean field begin, save the TField here so it can
115 // have the value incorporated.
116 booleanFieldName string
117 booleanFieldId int16
118 booleanFieldPending bool
119
120 // If we read a field header, and it's a boolean field, save the boolean
121 // value here so that readBool can use it.
122 boolValue bool
123 boolValueIsNotNull bool
124 buffer [64]byte
125}
126
khenaidood948f772021-08-11 17:49:24 -0400127// Deprecated: Use NewTCompactProtocolConf instead.
khenaidooc6c7bda2020-06-17 17:20:18 -0400128func NewTCompactProtocol(trans TTransport) *TCompactProtocol {
khenaidood948f772021-08-11 17:49:24 -0400129 return NewTCompactProtocolConf(trans, &TConfiguration{
130 noPropagation: true,
131 })
132}
133
134func NewTCompactProtocolConf(trans TTransport, conf *TConfiguration) *TCompactProtocol {
135 PropagateTConfiguration(trans, conf)
136 p := &TCompactProtocol{
137 origTransport: trans,
138 cfg: conf,
139 }
khenaidooc6c7bda2020-06-17 17:20:18 -0400140 if et, ok := trans.(TRichTransport); ok {
141 p.trans = et
142 } else {
143 p.trans = NewTRichTransport(trans)
144 }
145
146 return p
khenaidooc6c7bda2020-06-17 17:20:18 -0400147}
148
149//
150// Public Writing methods.
151//
152
153// Write a message header to the wire. Compact Protocol messages contain the
154// protocol version so we can migrate forwards in the future if need be.
khenaidood948f772021-08-11 17:49:24 -0400155func (p *TCompactProtocol) WriteMessageBegin(ctx context.Context, name string, typeId TMessageType, seqid int32) error {
khenaidooc6c7bda2020-06-17 17:20:18 -0400156 err := p.writeByteDirect(COMPACT_PROTOCOL_ID)
157 if err != nil {
158 return NewTProtocolException(err)
159 }
160 err = p.writeByteDirect((COMPACT_VERSION & COMPACT_VERSION_MASK) | ((byte(typeId) << COMPACT_TYPE_SHIFT_AMOUNT) & COMPACT_TYPE_MASK))
161 if err != nil {
162 return NewTProtocolException(err)
163 }
164 _, err = p.writeVarint32(seqid)
165 if err != nil {
166 return NewTProtocolException(err)
167 }
khenaidood948f772021-08-11 17:49:24 -0400168 e := p.WriteString(ctx, name)
khenaidooc6c7bda2020-06-17 17:20:18 -0400169 return e
170
171}
172
khenaidood948f772021-08-11 17:49:24 -0400173func (p *TCompactProtocol) WriteMessageEnd(ctx context.Context) error { return nil }
khenaidooc6c7bda2020-06-17 17:20:18 -0400174
175// Write a struct begin. This doesn't actually put anything on the wire. We
176// use it as an opportunity to put special placeholder markers on the field
177// stack so we can get the field id deltas correct.
khenaidood948f772021-08-11 17:49:24 -0400178func (p *TCompactProtocol) WriteStructBegin(ctx context.Context, name string) error {
khenaidooc6c7bda2020-06-17 17:20:18 -0400179 p.lastField = append(p.lastField, p.lastFieldId)
180 p.lastFieldId = 0
181 return nil
182}
183
184// Write a struct end. This doesn't actually put anything on the wire. We use
185// this as an opportunity to pop the last field from the current struct off
186// of the field stack.
khenaidood948f772021-08-11 17:49:24 -0400187func (p *TCompactProtocol) WriteStructEnd(ctx context.Context) error {
188 if len(p.lastField) <= 0 {
189 return NewTProtocolExceptionWithType(INVALID_DATA, errors.New("WriteStructEnd called without matching WriteStructBegin call before"))
190 }
khenaidooc6c7bda2020-06-17 17:20:18 -0400191 p.lastFieldId = p.lastField[len(p.lastField)-1]
192 p.lastField = p.lastField[:len(p.lastField)-1]
193 return nil
194}
195
khenaidood948f772021-08-11 17:49:24 -0400196func (p *TCompactProtocol) WriteFieldBegin(ctx context.Context, name string, typeId TType, id int16) error {
khenaidooc6c7bda2020-06-17 17:20:18 -0400197 if typeId == BOOL {
198 // we want to possibly include the value, so we'll wait.
199 p.booleanFieldName, p.booleanFieldId, p.booleanFieldPending = name, id, true
200 return nil
201 }
khenaidood948f772021-08-11 17:49:24 -0400202 _, err := p.writeFieldBeginInternal(ctx, name, typeId, id, 0xFF)
khenaidooc6c7bda2020-06-17 17:20:18 -0400203 return NewTProtocolException(err)
204}
205
206// The workhorse of writeFieldBegin. It has the option of doing a
207// 'type override' of the type header. This is used specifically in the
208// boolean field case.
khenaidood948f772021-08-11 17:49:24 -0400209func (p *TCompactProtocol) writeFieldBeginInternal(ctx context.Context, name string, typeId TType, id int16, typeOverride byte) (int, error) {
khenaidooc6c7bda2020-06-17 17:20:18 -0400210 // short lastField = lastField_.pop();
211
212 // if there's a type override, use that.
213 var typeToWrite byte
214 if typeOverride == 0xFF {
215 typeToWrite = byte(p.getCompactType(typeId))
216 } else {
217 typeToWrite = typeOverride
218 }
219 // check if we can use delta encoding for the field id
220 fieldId := int(id)
221 written := 0
222 if fieldId > p.lastFieldId && fieldId-p.lastFieldId <= 15 {
223 // write them together
224 err := p.writeByteDirect(byte((fieldId-p.lastFieldId)<<4) | typeToWrite)
225 if err != nil {
226 return 0, err
227 }
228 } else {
229 // write them separate
230 err := p.writeByteDirect(typeToWrite)
231 if err != nil {
232 return 0, err
233 }
khenaidood948f772021-08-11 17:49:24 -0400234 err = p.WriteI16(ctx, id)
khenaidooc6c7bda2020-06-17 17:20:18 -0400235 written = 1 + 2
236 if err != nil {
237 return 0, err
238 }
239 }
240
241 p.lastFieldId = fieldId
khenaidooc6c7bda2020-06-17 17:20:18 -0400242 return written, nil
243}
244
khenaidood948f772021-08-11 17:49:24 -0400245func (p *TCompactProtocol) WriteFieldEnd(ctx context.Context) error { return nil }
khenaidooc6c7bda2020-06-17 17:20:18 -0400246
khenaidood948f772021-08-11 17:49:24 -0400247func (p *TCompactProtocol) WriteFieldStop(ctx context.Context) error {
khenaidooc6c7bda2020-06-17 17:20:18 -0400248 err := p.writeByteDirect(STOP)
249 return NewTProtocolException(err)
250}
251
khenaidood948f772021-08-11 17:49:24 -0400252func (p *TCompactProtocol) WriteMapBegin(ctx context.Context, keyType TType, valueType TType, size int) error {
khenaidooc6c7bda2020-06-17 17:20:18 -0400253 if size == 0 {
254 err := p.writeByteDirect(0)
255 return NewTProtocolException(err)
256 }
257 _, err := p.writeVarint32(int32(size))
258 if err != nil {
259 return NewTProtocolException(err)
260 }
261 err = p.writeByteDirect(byte(p.getCompactType(keyType))<<4 | byte(p.getCompactType(valueType)))
262 return NewTProtocolException(err)
263}
264
khenaidood948f772021-08-11 17:49:24 -0400265func (p *TCompactProtocol) WriteMapEnd(ctx context.Context) error { return nil }
khenaidooc6c7bda2020-06-17 17:20:18 -0400266
267// Write a list header.
khenaidood948f772021-08-11 17:49:24 -0400268func (p *TCompactProtocol) WriteListBegin(ctx context.Context, elemType TType, size int) error {
khenaidooc6c7bda2020-06-17 17:20:18 -0400269 _, err := p.writeCollectionBegin(elemType, size)
270 return NewTProtocolException(err)
271}
272
khenaidood948f772021-08-11 17:49:24 -0400273func (p *TCompactProtocol) WriteListEnd(ctx context.Context) error { return nil }
khenaidooc6c7bda2020-06-17 17:20:18 -0400274
275// Write a set header.
khenaidood948f772021-08-11 17:49:24 -0400276func (p *TCompactProtocol) WriteSetBegin(ctx context.Context, elemType TType, size int) error {
khenaidooc6c7bda2020-06-17 17:20:18 -0400277 _, err := p.writeCollectionBegin(elemType, size)
278 return NewTProtocolException(err)
279}
280
khenaidood948f772021-08-11 17:49:24 -0400281func (p *TCompactProtocol) WriteSetEnd(ctx context.Context) error { return nil }
khenaidooc6c7bda2020-06-17 17:20:18 -0400282
khenaidood948f772021-08-11 17:49:24 -0400283func (p *TCompactProtocol) WriteBool(ctx context.Context, value bool) error {
khenaidooc6c7bda2020-06-17 17:20:18 -0400284 v := byte(COMPACT_BOOLEAN_FALSE)
285 if value {
286 v = byte(COMPACT_BOOLEAN_TRUE)
287 }
288 if p.booleanFieldPending {
289 // we haven't written the field header yet
khenaidood948f772021-08-11 17:49:24 -0400290 _, err := p.writeFieldBeginInternal(ctx, p.booleanFieldName, BOOL, p.booleanFieldId, v)
khenaidooc6c7bda2020-06-17 17:20:18 -0400291 p.booleanFieldPending = false
292 return NewTProtocolException(err)
293 }
294 // we're not part of a field, so just write the value.
295 err := p.writeByteDirect(v)
296 return NewTProtocolException(err)
297}
298
299// Write a byte. Nothing to see here!
khenaidood948f772021-08-11 17:49:24 -0400300func (p *TCompactProtocol) WriteByte(ctx context.Context, value int8) error {
khenaidooc6c7bda2020-06-17 17:20:18 -0400301 err := p.writeByteDirect(byte(value))
302 return NewTProtocolException(err)
303}
304
305// Write an I16 as a zigzag varint.
khenaidood948f772021-08-11 17:49:24 -0400306func (p *TCompactProtocol) WriteI16(ctx context.Context, value int16) error {
khenaidooc6c7bda2020-06-17 17:20:18 -0400307 _, err := p.writeVarint32(p.int32ToZigzag(int32(value)))
308 return NewTProtocolException(err)
309}
310
311// Write an i32 as a zigzag varint.
khenaidood948f772021-08-11 17:49:24 -0400312func (p *TCompactProtocol) WriteI32(ctx context.Context, value int32) error {
khenaidooc6c7bda2020-06-17 17:20:18 -0400313 _, err := p.writeVarint32(p.int32ToZigzag(value))
314 return NewTProtocolException(err)
315}
316
317// Write an i64 as a zigzag varint.
khenaidood948f772021-08-11 17:49:24 -0400318func (p *TCompactProtocol) WriteI64(ctx context.Context, value int64) error {
khenaidooc6c7bda2020-06-17 17:20:18 -0400319 _, err := p.writeVarint64(p.int64ToZigzag(value))
320 return NewTProtocolException(err)
321}
322
323// Write a double to the wire as 8 bytes.
khenaidood948f772021-08-11 17:49:24 -0400324func (p *TCompactProtocol) WriteDouble(ctx context.Context, value float64) error {
khenaidooc6c7bda2020-06-17 17:20:18 -0400325 buf := p.buffer[0:8]
326 binary.LittleEndian.PutUint64(buf, math.Float64bits(value))
327 _, err := p.trans.Write(buf)
328 return NewTProtocolException(err)
329}
330
331// Write a string to the wire with a varint size preceding.
khenaidood948f772021-08-11 17:49:24 -0400332func (p *TCompactProtocol) WriteString(ctx context.Context, value string) error {
khenaidooc6c7bda2020-06-17 17:20:18 -0400333 _, e := p.writeVarint32(int32(len(value)))
334 if e != nil {
335 return NewTProtocolException(e)
336 }
337 if len(value) > 0 {
338 }
339 _, e = p.trans.WriteString(value)
340 return e
341}
342
343// Write a byte array, using a varint for the size.
khenaidood948f772021-08-11 17:49:24 -0400344func (p *TCompactProtocol) WriteBinary(ctx context.Context, bin []byte) error {
khenaidooc6c7bda2020-06-17 17:20:18 -0400345 _, e := p.writeVarint32(int32(len(bin)))
346 if e != nil {
347 return NewTProtocolException(e)
348 }
349 if len(bin) > 0 {
350 _, e = p.trans.Write(bin)
351 return NewTProtocolException(e)
352 }
353 return nil
354}
355
356//
357// Reading methods.
358//
359
360// Read a message header.
khenaidood948f772021-08-11 17:49:24 -0400361func (p *TCompactProtocol) ReadMessageBegin(ctx context.Context) (name string, typeId TMessageType, seqId int32, err error) {
362 var protocolId byte
khenaidooc6c7bda2020-06-17 17:20:18 -0400363
khenaidood948f772021-08-11 17:49:24 -0400364 _, deadlineSet := ctx.Deadline()
365 for {
366 protocolId, err = p.readByteDirect()
367 if deadlineSet && isTimeoutError(err) && ctx.Err() == nil {
368 // keep retrying I/O timeout errors since we still have
369 // time left
370 continue
371 }
372 // For anything else, don't retry
373 break
374 }
khenaidooc6c7bda2020-06-17 17:20:18 -0400375 if err != nil {
376 return
377 }
378
379 if protocolId != COMPACT_PROTOCOL_ID {
380 e := fmt.Errorf("Expected protocol id %02x but got %02x", COMPACT_PROTOCOL_ID, protocolId)
381 return "", typeId, seqId, NewTProtocolExceptionWithType(BAD_VERSION, e)
382 }
383
384 versionAndType, err := p.readByteDirect()
385 if err != nil {
386 return
387 }
388
389 version := versionAndType & COMPACT_VERSION_MASK
390 typeId = TMessageType((versionAndType >> COMPACT_TYPE_SHIFT_AMOUNT) & COMPACT_TYPE_BITS)
391 if version != COMPACT_VERSION {
392 e := fmt.Errorf("Expected version %02x but got %02x", COMPACT_VERSION, version)
393 err = NewTProtocolExceptionWithType(BAD_VERSION, e)
394 return
395 }
396 seqId, e := p.readVarint32()
397 if e != nil {
398 err = NewTProtocolException(e)
399 return
400 }
khenaidood948f772021-08-11 17:49:24 -0400401 name, err = p.ReadString(ctx)
khenaidooc6c7bda2020-06-17 17:20:18 -0400402 return
403}
404
khenaidood948f772021-08-11 17:49:24 -0400405func (p *TCompactProtocol) ReadMessageEnd(ctx context.Context) error { return nil }
khenaidooc6c7bda2020-06-17 17:20:18 -0400406
407// Read a struct begin. There's nothing on the wire for this, but it is our
408// opportunity to push a new struct begin marker onto the field stack.
khenaidood948f772021-08-11 17:49:24 -0400409func (p *TCompactProtocol) ReadStructBegin(ctx context.Context) (name string, err error) {
khenaidooc6c7bda2020-06-17 17:20:18 -0400410 p.lastField = append(p.lastField, p.lastFieldId)
411 p.lastFieldId = 0
412 return
413}
414
415// Doesn't actually consume any wire data, just removes the last field for
416// this struct from the field stack.
khenaidood948f772021-08-11 17:49:24 -0400417func (p *TCompactProtocol) ReadStructEnd(ctx context.Context) error {
khenaidooc6c7bda2020-06-17 17:20:18 -0400418 // consume the last field we read off the wire.
khenaidood948f772021-08-11 17:49:24 -0400419 if len(p.lastField) <= 0 {
420 return NewTProtocolExceptionWithType(INVALID_DATA, errors.New("ReadStructEnd called without matching ReadStructBegin call before"))
421 }
khenaidooc6c7bda2020-06-17 17:20:18 -0400422 p.lastFieldId = p.lastField[len(p.lastField)-1]
423 p.lastField = p.lastField[:len(p.lastField)-1]
424 return nil
425}
426
427// Read a field header off the wire.
khenaidood948f772021-08-11 17:49:24 -0400428func (p *TCompactProtocol) ReadFieldBegin(ctx context.Context) (name string, typeId TType, id int16, err error) {
khenaidooc6c7bda2020-06-17 17:20:18 -0400429 t, err := p.readByteDirect()
430 if err != nil {
431 return
432 }
433
434 // if it's a stop, then we can return immediately, as the struct is over.
435 if (t & 0x0f) == STOP {
436 return "", STOP, 0, nil
437 }
438
439 // mask off the 4 MSB of the type header. it could contain a field id delta.
440 modifier := int16((t & 0xf0) >> 4)
441 if modifier == 0 {
442 // not a delta. look ahead for the zigzag varint field id.
khenaidood948f772021-08-11 17:49:24 -0400443 id, err = p.ReadI16(ctx)
khenaidooc6c7bda2020-06-17 17:20:18 -0400444 if err != nil {
445 return
446 }
447 } else {
448 // has a delta. add the delta to the last read field id.
449 id = int16(p.lastFieldId) + modifier
450 }
451 typeId, e := p.getTType(tCompactType(t & 0x0f))
452 if e != nil {
453 err = NewTProtocolException(e)
454 return
455 }
456
457 // if this happens to be a boolean field, the value is encoded in the type
458 if p.isBoolType(t) {
459 // save the boolean value in a special instance variable.
460 p.boolValue = (byte(t)&0x0f == COMPACT_BOOLEAN_TRUE)
461 p.boolValueIsNotNull = true
462 }
463
464 // push the new field onto the field stack so we can keep the deltas going.
465 p.lastFieldId = int(id)
466 return
467}
468
khenaidood948f772021-08-11 17:49:24 -0400469func (p *TCompactProtocol) ReadFieldEnd(ctx context.Context) error { return nil }
khenaidooc6c7bda2020-06-17 17:20:18 -0400470
471// Read a map header off the wire. If the size is zero, skip reading the key
472// and value type. This means that 0-length maps will yield TMaps without the
473// "correct" types.
khenaidood948f772021-08-11 17:49:24 -0400474func (p *TCompactProtocol) ReadMapBegin(ctx context.Context) (keyType TType, valueType TType, size int, err error) {
khenaidooc6c7bda2020-06-17 17:20:18 -0400475 size32, e := p.readVarint32()
476 if e != nil {
477 err = NewTProtocolException(e)
478 return
479 }
480 if size32 < 0 {
481 err = invalidDataLength
482 return
483 }
484 size = int(size32)
485
486 keyAndValueType := byte(STOP)
487 if size != 0 {
488 keyAndValueType, err = p.readByteDirect()
489 if err != nil {
490 return
491 }
492 }
493 keyType, _ = p.getTType(tCompactType(keyAndValueType >> 4))
494 valueType, _ = p.getTType(tCompactType(keyAndValueType & 0xf))
495 return
496}
497
khenaidood948f772021-08-11 17:49:24 -0400498func (p *TCompactProtocol) ReadMapEnd(ctx context.Context) error { return nil }
khenaidooc6c7bda2020-06-17 17:20:18 -0400499
500// Read a list header off the wire. If the list size is 0-14, the size will
501// be packed into the element type header. If it's a longer list, the 4 MSB
502// of the element type header will be 0xF, and a varint will follow with the
503// true size.
khenaidood948f772021-08-11 17:49:24 -0400504func (p *TCompactProtocol) ReadListBegin(ctx context.Context) (elemType TType, size int, err error) {
khenaidooc6c7bda2020-06-17 17:20:18 -0400505 size_and_type, err := p.readByteDirect()
506 if err != nil {
507 return
508 }
509 size = int((size_and_type >> 4) & 0x0f)
510 if size == 15 {
511 size2, e := p.readVarint32()
512 if e != nil {
513 err = NewTProtocolException(e)
514 return
515 }
516 if size2 < 0 {
517 err = invalidDataLength
518 return
519 }
520 size = int(size2)
521 }
522 elemType, e := p.getTType(tCompactType(size_and_type))
523 if e != nil {
524 err = NewTProtocolException(e)
525 return
526 }
527 return
528}
529
khenaidood948f772021-08-11 17:49:24 -0400530func (p *TCompactProtocol) ReadListEnd(ctx context.Context) error { return nil }
khenaidooc6c7bda2020-06-17 17:20:18 -0400531
532// Read a set header off the wire. If the set size is 0-14, the size will
533// be packed into the element type header. If it's a longer set, the 4 MSB
534// of the element type header will be 0xF, and a varint will follow with the
535// true size.
khenaidood948f772021-08-11 17:49:24 -0400536func (p *TCompactProtocol) ReadSetBegin(ctx context.Context) (elemType TType, size int, err error) {
537 return p.ReadListBegin(ctx)
khenaidooc6c7bda2020-06-17 17:20:18 -0400538}
539
khenaidood948f772021-08-11 17:49:24 -0400540func (p *TCompactProtocol) ReadSetEnd(ctx context.Context) error { return nil }
khenaidooc6c7bda2020-06-17 17:20:18 -0400541
542// Read a boolean off the wire. If this is a boolean field, the value should
543// already have been read during readFieldBegin, so we'll just consume the
544// pre-stored value. Otherwise, read a byte.
khenaidood948f772021-08-11 17:49:24 -0400545func (p *TCompactProtocol) ReadBool(ctx context.Context) (value bool, err error) {
khenaidooc6c7bda2020-06-17 17:20:18 -0400546 if p.boolValueIsNotNull {
547 p.boolValueIsNotNull = false
548 return p.boolValue, nil
549 }
550 v, err := p.readByteDirect()
551 return v == COMPACT_BOOLEAN_TRUE, err
552}
553
554// Read a single byte off the wire. Nothing interesting here.
khenaidood948f772021-08-11 17:49:24 -0400555func (p *TCompactProtocol) ReadByte(ctx context.Context) (int8, error) {
khenaidooc6c7bda2020-06-17 17:20:18 -0400556 v, err := p.readByteDirect()
557 if err != nil {
558 return 0, NewTProtocolException(err)
559 }
560 return int8(v), err
561}
562
563// Read an i16 from the wire as a zigzag varint.
khenaidood948f772021-08-11 17:49:24 -0400564func (p *TCompactProtocol) ReadI16(ctx context.Context) (value int16, err error) {
565 v, err := p.ReadI32(ctx)
khenaidooc6c7bda2020-06-17 17:20:18 -0400566 return int16(v), err
567}
568
569// Read an i32 from the wire as a zigzag varint.
khenaidood948f772021-08-11 17:49:24 -0400570func (p *TCompactProtocol) ReadI32(ctx context.Context) (value int32, err error) {
khenaidooc6c7bda2020-06-17 17:20:18 -0400571 v, e := p.readVarint32()
572 if e != nil {
573 return 0, NewTProtocolException(e)
574 }
575 value = p.zigzagToInt32(v)
576 return value, nil
577}
578
579// Read an i64 from the wire as a zigzag varint.
khenaidood948f772021-08-11 17:49:24 -0400580func (p *TCompactProtocol) ReadI64(ctx context.Context) (value int64, err error) {
khenaidooc6c7bda2020-06-17 17:20:18 -0400581 v, e := p.readVarint64()
582 if e != nil {
583 return 0, NewTProtocolException(e)
584 }
585 value = p.zigzagToInt64(v)
586 return value, nil
587}
588
589// No magic here - just read a double off the wire.
khenaidood948f772021-08-11 17:49:24 -0400590func (p *TCompactProtocol) ReadDouble(ctx context.Context) (value float64, err error) {
khenaidooc6c7bda2020-06-17 17:20:18 -0400591 longBits := p.buffer[0:8]
592 _, e := io.ReadFull(p.trans, longBits)
593 if e != nil {
594 return 0.0, NewTProtocolException(e)
595 }
596 return math.Float64frombits(p.bytesToUint64(longBits)), nil
597}
598
599// Reads a []byte (via readBinary), and then UTF-8 decodes it.
khenaidood948f772021-08-11 17:49:24 -0400600func (p *TCompactProtocol) ReadString(ctx context.Context) (value string, err error) {
khenaidooc6c7bda2020-06-17 17:20:18 -0400601 length, e := p.readVarint32()
602 if e != nil {
603 return "", NewTProtocolException(e)
604 }
khenaidood948f772021-08-11 17:49:24 -0400605 err = checkSizeForProtocol(length, p.cfg)
606 if err != nil {
607 return
khenaidooc6c7bda2020-06-17 17:20:18 -0400608 }
khenaidooc6c7bda2020-06-17 17:20:18 -0400609 if length == 0 {
610 return "", nil
611 }
khenaidood948f772021-08-11 17:49:24 -0400612 if length < int32(len(p.buffer)) {
613 // Avoid allocation on small reads
614 buf := p.buffer[:length]
615 read, e := io.ReadFull(p.trans, buf)
616 return string(buf[:read]), NewTProtocolException(e)
khenaidooc6c7bda2020-06-17 17:20:18 -0400617 }
khenaidood948f772021-08-11 17:49:24 -0400618
619 buf, e := safeReadBytes(length, p.trans)
khenaidooc6c7bda2020-06-17 17:20:18 -0400620 return string(buf), NewTProtocolException(e)
621}
622
623// Read a []byte from the wire.
khenaidood948f772021-08-11 17:49:24 -0400624func (p *TCompactProtocol) ReadBinary(ctx context.Context) (value []byte, err error) {
khenaidooc6c7bda2020-06-17 17:20:18 -0400625 length, e := p.readVarint32()
626 if e != nil {
627 return nil, NewTProtocolException(e)
628 }
khenaidood948f772021-08-11 17:49:24 -0400629 err = checkSizeForProtocol(length, p.cfg)
630 if err != nil {
631 return
632 }
khenaidooc6c7bda2020-06-17 17:20:18 -0400633 if length == 0 {
634 return []byte{}, nil
635 }
khenaidooc6c7bda2020-06-17 17:20:18 -0400636
khenaidood948f772021-08-11 17:49:24 -0400637 buf, e := safeReadBytes(length, p.trans)
khenaidooc6c7bda2020-06-17 17:20:18 -0400638 return buf, NewTProtocolException(e)
639}
640
khenaidood948f772021-08-11 17:49:24 -0400641func (p *TCompactProtocol) Flush(ctx context.Context) (err error) {
642 return NewTProtocolException(p.trans.Flush(ctx))
khenaidooc6c7bda2020-06-17 17:20:18 -0400643}
644
khenaidood948f772021-08-11 17:49:24 -0400645func (p *TCompactProtocol) Skip(ctx context.Context, fieldType TType) (err error) {
646 return SkipDefaultDepth(ctx, p, fieldType)
khenaidooc6c7bda2020-06-17 17:20:18 -0400647}
648
649func (p *TCompactProtocol) Transport() TTransport {
650 return p.origTransport
651}
652
653//
654// Internal writing methods
655//
656
657// Abstract method for writing the start of lists and sets. List and sets on
658// the wire differ only by the type indicator.
659func (p *TCompactProtocol) writeCollectionBegin(elemType TType, size int) (int, error) {
660 if size <= 14 {
661 return 1, p.writeByteDirect(byte(int32(size<<4) | int32(p.getCompactType(elemType))))
662 }
663 err := p.writeByteDirect(0xf0 | byte(p.getCompactType(elemType)))
664 if err != nil {
665 return 0, err
666 }
667 m, err := p.writeVarint32(int32(size))
668 return 1 + m, err
669}
670
671// Write an i32 as a varint. Results in 1-5 bytes on the wire.
672// TODO(pomack): make a permanent buffer like writeVarint64?
673func (p *TCompactProtocol) writeVarint32(n int32) (int, error) {
674 i32buf := p.buffer[0:5]
675 idx := 0
676 for {
677 if (n & ^0x7F) == 0 {
678 i32buf[idx] = byte(n)
679 idx++
680 // p.writeByteDirect(byte(n));
681 break
682 // return;
683 } else {
684 i32buf[idx] = byte((n & 0x7F) | 0x80)
685 idx++
686 // p.writeByteDirect(byte(((n & 0x7F) | 0x80)));
687 u := uint32(n)
688 n = int32(u >> 7)
689 }
690 }
691 return p.trans.Write(i32buf[0:idx])
692}
693
694// Write an i64 as a varint. Results in 1-10 bytes on the wire.
695func (p *TCompactProtocol) writeVarint64(n int64) (int, error) {
696 varint64out := p.buffer[0:10]
697 idx := 0
698 for {
699 if (n & ^0x7F) == 0 {
700 varint64out[idx] = byte(n)
701 idx++
702 break
703 } else {
704 varint64out[idx] = byte((n & 0x7F) | 0x80)
705 idx++
706 u := uint64(n)
707 n = int64(u >> 7)
708 }
709 }
710 return p.trans.Write(varint64out[0:idx])
711}
712
713// Convert l into a zigzag long. This allows negative numbers to be
714// represented compactly as a varint.
715func (p *TCompactProtocol) int64ToZigzag(l int64) int64 {
716 return (l << 1) ^ (l >> 63)
717}
718
719// Convert l into a zigzag long. This allows negative numbers to be
720// represented compactly as a varint.
721func (p *TCompactProtocol) int32ToZigzag(n int32) int32 {
722 return (n << 1) ^ (n >> 31)
723}
724
725func (p *TCompactProtocol) fixedUint64ToBytes(n uint64, buf []byte) {
726 binary.LittleEndian.PutUint64(buf, n)
727}
728
729func (p *TCompactProtocol) fixedInt64ToBytes(n int64, buf []byte) {
730 binary.LittleEndian.PutUint64(buf, uint64(n))
731}
732
733// Writes a byte without any possibility of all that field header nonsense.
734// Used internally by other writing methods that know they need to write a byte.
735func (p *TCompactProtocol) writeByteDirect(b byte) error {
736 return p.trans.WriteByte(b)
737}
738
739// Writes a byte without any possibility of all that field header nonsense.
740func (p *TCompactProtocol) writeIntAsByteDirect(n int) (int, error) {
741 return 1, p.writeByteDirect(byte(n))
742}
743
744//
745// Internal reading methods
746//
747
748// Read an i32 from the wire as a varint. The MSB of each byte is set
749// if there is another byte to follow. This can read up to 5 bytes.
750func (p *TCompactProtocol) readVarint32() (int32, error) {
751 // if the wire contains the right stuff, this will just truncate the i64 we
752 // read and get us the right sign.
753 v, err := p.readVarint64()
754 return int32(v), err
755}
756
757// Read an i64 from the wire as a proper varint. The MSB of each byte is set
758// if there is another byte to follow. This can read up to 10 bytes.
759func (p *TCompactProtocol) readVarint64() (int64, error) {
760 shift := uint(0)
761 result := int64(0)
762 for {
763 b, err := p.readByteDirect()
764 if err != nil {
765 return 0, err
766 }
767 result |= int64(b&0x7f) << shift
768 if (b & 0x80) != 0x80 {
769 break
770 }
771 shift += 7
772 }
773 return result, nil
774}
775
776// Read a byte, unlike ReadByte that reads Thrift-byte that is i8.
777func (p *TCompactProtocol) readByteDirect() (byte, error) {
778 return p.trans.ReadByte()
779}
780
781//
782// encoding helpers
783//
784
785// Convert from zigzag int to int.
786func (p *TCompactProtocol) zigzagToInt32(n int32) int32 {
787 u := uint32(n)
788 return int32(u>>1) ^ -(n & 1)
789}
790
791// Convert from zigzag long to long.
792func (p *TCompactProtocol) zigzagToInt64(n int64) int64 {
793 u := uint64(n)
794 return int64(u>>1) ^ -(n & 1)
795}
796
797// Note that it's important that the mask bytes are long literals,
798// otherwise they'll default to ints, and when you shift an int left 56 bits,
799// you just get a messed up int.
800func (p *TCompactProtocol) bytesToInt64(b []byte) int64 {
801 return int64(binary.LittleEndian.Uint64(b))
802}
803
804// Note that it's important that the mask bytes are long literals,
805// otherwise they'll default to ints, and when you shift an int left 56 bits,
806// you just get a messed up int.
807func (p *TCompactProtocol) bytesToUint64(b []byte) uint64 {
808 return binary.LittleEndian.Uint64(b)
809}
810
811//
812// type testing and converting
813//
814
815func (p *TCompactProtocol) isBoolType(b byte) bool {
816 return (b&0x0f) == COMPACT_BOOLEAN_TRUE || (b&0x0f) == COMPACT_BOOLEAN_FALSE
817}
818
819// Given a tCompactType constant, convert it to its corresponding
820// TType value.
821func (p *TCompactProtocol) getTType(t tCompactType) (TType, error) {
822 switch byte(t) & 0x0f {
823 case STOP:
824 return STOP, nil
825 case COMPACT_BOOLEAN_FALSE, COMPACT_BOOLEAN_TRUE:
826 return BOOL, nil
827 case COMPACT_BYTE:
828 return BYTE, nil
829 case COMPACT_I16:
830 return I16, nil
831 case COMPACT_I32:
832 return I32, nil
833 case COMPACT_I64:
834 return I64, nil
835 case COMPACT_DOUBLE:
836 return DOUBLE, nil
837 case COMPACT_BINARY:
838 return STRING, nil
839 case COMPACT_LIST:
840 return LIST, nil
841 case COMPACT_SET:
842 return SET, nil
843 case COMPACT_MAP:
844 return MAP, nil
845 case COMPACT_STRUCT:
846 return STRUCT, nil
847 }
khenaidood948f772021-08-11 17:49:24 -0400848 return STOP, NewTProtocolException(fmt.Errorf("don't know what type: %v", t&0x0f))
khenaidooc6c7bda2020-06-17 17:20:18 -0400849}
850
851// Given a TType value, find the appropriate TCompactProtocol.Types constant.
852func (p *TCompactProtocol) getCompactType(t TType) tCompactType {
853 return ttypeToCompactType[t]
854}
khenaidood948f772021-08-11 17:49:24 -0400855
856func (p *TCompactProtocol) SetTConfiguration(conf *TConfiguration) {
857 PropagateTConfiguration(p.trans, conf)
858 PropagateTConfiguration(p.origTransport, conf)
859 p.cfg = conf
860}
861
862var (
863 _ TConfigurationSetter = (*TCompactProtocolFactory)(nil)
864 _ TConfigurationSetter = (*TCompactProtocol)(nil)
865)