blob: 18915fee8ad9204835b3f41cb6d39094f77c119a [file] [log] [blame]
Jens Geyer0e87c462013-06-18 22:25:07 +02001/*
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 (
John Boiles57852792018-01-05 14:37:05 -080023 "context"
Jens Geyer0e87c462013-06-18 22:25:07 +020024 "encoding/binary"
Yuxuan 'fishy' Wang64c2a4b2020-10-10 18:39:32 -070025 "errors"
Jens Geyer0e87c462013-06-18 22:25:07 +020026 "fmt"
27 "io"
28 "math"
Jens Geyer0e87c462013-06-18 22:25:07 +020029)
30
31const (
32 COMPACT_PROTOCOL_ID = 0x082
33 COMPACT_VERSION = 1
34 COMPACT_VERSION_MASK = 0x1f
35 COMPACT_TYPE_MASK = 0x0E0
Jens Geyera86886e2014-09-17 22:25:48 +020036 COMPACT_TYPE_BITS = 0x07
Jens Geyer0e87c462013-06-18 22:25:07 +020037 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
Yuxuan 'fishy' Wang19c13b42022-10-12 14:13:15 -070055 COMPACT_UUID = 0x0D
Jens Geyer0e87c462013-06-18 22:25:07 +020056)
57
58var (
59 ttypeToCompactType map[TType]tCompactType
60)
61
62func init() {
63 ttypeToCompactType = map[TType]tCompactType{
64 STOP: STOP,
65 BOOL: COMPACT_BOOLEAN_TRUE,
66 BYTE: COMPACT_BYTE,
67 I16: COMPACT_I16,
68 I32: COMPACT_I32,
69 I64: COMPACT_I64,
70 DOUBLE: COMPACT_DOUBLE,
71 STRING: COMPACT_BINARY,
72 LIST: COMPACT_LIST,
73 SET: COMPACT_SET,
74 MAP: COMPACT_MAP,
75 STRUCT: COMPACT_STRUCT,
Yuxuan 'fishy' Wang19c13b42022-10-12 14:13:15 -070076 UUID: COMPACT_UUID,
Jens Geyer0e87c462013-06-18 22:25:07 +020077 }
78}
79
Yuxuan 'fishy' Wangc4d1c0d2020-12-16 17:10:48 -080080type TCompactProtocolFactory struct {
81 cfg *TConfiguration
82}
Jens Geyer0e87c462013-06-18 22:25:07 +020083
Yuxuan 'fishy' Wangc4d1c0d2020-12-16 17:10:48 -080084// Deprecated: Use NewTCompactProtocolFactoryConf instead.
Jens Geyer0e87c462013-06-18 22:25:07 +020085func NewTCompactProtocolFactory() *TCompactProtocolFactory {
Yuxuan 'fishy' Wangc4d1c0d2020-12-16 17:10:48 -080086 return NewTCompactProtocolFactoryConf(&TConfiguration{
87 noPropagation: true,
88 })
89}
90
91func NewTCompactProtocolFactoryConf(conf *TConfiguration) *TCompactProtocolFactory {
92 return &TCompactProtocolFactory{
93 cfg: conf,
94 }
Jens Geyer0e87c462013-06-18 22:25:07 +020095}
96
97func (p *TCompactProtocolFactory) GetProtocol(trans TTransport) TProtocol {
Yuxuan 'fishy' Wangc4d1c0d2020-12-16 17:10:48 -080098 return NewTCompactProtocolConf(trans, p.cfg)
99}
100
101func (p *TCompactProtocolFactory) SetTConfiguration(conf *TConfiguration) {
102 p.cfg = conf
Jens Geyer0e87c462013-06-18 22:25:07 +0200103}
104
105type TCompactProtocol struct {
Jens Geyer09972502014-05-02 01:30:13 +0200106 trans TRichTransport
107 origTransport TTransport
Jens Geyer0e87c462013-06-18 22:25:07 +0200108
Yuxuan 'fishy' Wangc4d1c0d2020-12-16 17:10:48 -0800109 cfg *TConfiguration
110
Jens Geyer0e87c462013-06-18 22:25:07 +0200111 // Used to keep track of the last field for the current and previous structs,
112 // so we can do the delta stuff.
113 lastField []int
114 lastFieldId int
115
116 // If we encounter a boolean field begin, save the TField here so it can
117 // have the value incorporated.
Jens Geyer09972502014-05-02 01:30:13 +0200118 booleanFieldName string
119 booleanFieldId int16
120 booleanFieldPending bool
Jens Geyer0e87c462013-06-18 22:25:07 +0200121
122 // If we read a field header, and it's a boolean field, save the boolean
123 // value here so that readBool can use it.
124 boolValue bool
125 boolValueIsNotNull bool
Jens Geyer09972502014-05-02 01:30:13 +0200126 buffer [64]byte
Jens Geyer0e87c462013-06-18 22:25:07 +0200127}
128
Yuxuan 'fishy' Wangc4d1c0d2020-12-16 17:10:48 -0800129// Deprecated: Use NewTCompactProtocolConf instead.
Jens Geyer0e87c462013-06-18 22:25:07 +0200130func NewTCompactProtocol(trans TTransport) *TCompactProtocol {
Yuxuan 'fishy' Wangc4d1c0d2020-12-16 17:10:48 -0800131 return NewTCompactProtocolConf(trans, &TConfiguration{
132 noPropagation: true,
133 })
134}
135
136func NewTCompactProtocolConf(trans TTransport, conf *TConfiguration) *TCompactProtocol {
137 PropagateTConfiguration(trans, conf)
138 p := &TCompactProtocol{
139 origTransport: trans,
140 cfg: conf,
141 }
Jens Geyer09972502014-05-02 01:30:13 +0200142 if et, ok := trans.(TRichTransport); ok {
143 p.trans = et
144 } else {
145 p.trans = NewTRichTransport(trans)
146 }
147
148 return p
Jens Geyer0e87c462013-06-18 22:25:07 +0200149}
150
151//
152// Public Writing methods.
153//
154
155// Write a message header to the wire. Compact Protocol messages contain the
156// protocol version so we can migrate forwards in the future if need be.
Yuxuan 'fishy' Wange79f7642020-06-12 22:22:35 -0700157func (p *TCompactProtocol) WriteMessageBegin(ctx context.Context, name string, typeId TMessageType, seqid int32) error {
Jens Geyer09972502014-05-02 01:30:13 +0200158 err := p.writeByteDirect(COMPACT_PROTOCOL_ID)
Jens Geyer0e87c462013-06-18 22:25:07 +0200159 if err != nil {
160 return NewTProtocolException(err)
161 }
Jens Geyer09972502014-05-02 01:30:13 +0200162 err = p.writeByteDirect((COMPACT_VERSION & COMPACT_VERSION_MASK) | ((byte(typeId) << COMPACT_TYPE_SHIFT_AMOUNT) & COMPACT_TYPE_MASK))
Jens Geyer0e87c462013-06-18 22:25:07 +0200163 if err != nil {
164 return NewTProtocolException(err)
165 }
166 _, err = p.writeVarint32(seqid)
167 if err != nil {
168 return NewTProtocolException(err)
169 }
Yuxuan 'fishy' Wange79f7642020-06-12 22:22:35 -0700170 e := p.WriteString(ctx, name)
Jens Geyer0e87c462013-06-18 22:25:07 +0200171 return e
172
173}
174
Yuxuan 'fishy' Wange79f7642020-06-12 22:22:35 -0700175func (p *TCompactProtocol) WriteMessageEnd(ctx context.Context) error { return nil }
Jens Geyer0e87c462013-06-18 22:25:07 +0200176
177// Write a struct begin. This doesn't actually put anything on the wire. We
178// use it as an opportunity to put special placeholder markers on the field
179// stack so we can get the field id deltas correct.
Yuxuan 'fishy' Wange79f7642020-06-12 22:22:35 -0700180func (p *TCompactProtocol) WriteStructBegin(ctx context.Context, name string) error {
Jens Geyer0e87c462013-06-18 22:25:07 +0200181 p.lastField = append(p.lastField, p.lastFieldId)
182 p.lastFieldId = 0
183 return nil
184}
185
186// Write a struct end. This doesn't actually put anything on the wire. We use
187// this as an opportunity to pop the last field from the current struct off
188// of the field stack.
Yuxuan 'fishy' Wange79f7642020-06-12 22:22:35 -0700189func (p *TCompactProtocol) WriteStructEnd(ctx context.Context) error {
Yuxuan 'fishy' Wang64c2a4b2020-10-10 18:39:32 -0700190 if len(p.lastField) <= 0 {
191 return NewTProtocolExceptionWithType(INVALID_DATA, errors.New("WriteStructEnd called without matching WriteStructBegin call before"))
192 }
Jens Geyer0e87c462013-06-18 22:25:07 +0200193 p.lastFieldId = p.lastField[len(p.lastField)-1]
194 p.lastField = p.lastField[:len(p.lastField)-1]
195 return nil
196}
197
Yuxuan 'fishy' Wange79f7642020-06-12 22:22:35 -0700198func (p *TCompactProtocol) WriteFieldBegin(ctx context.Context, name string, typeId TType, id int16) error {
Jens Geyer0e87c462013-06-18 22:25:07 +0200199 if typeId == BOOL {
200 // we want to possibly include the value, so we'll wait.
Jens Geyer09972502014-05-02 01:30:13 +0200201 p.booleanFieldName, p.booleanFieldId, p.booleanFieldPending = name, id, true
Jens Geyer0e87c462013-06-18 22:25:07 +0200202 return nil
203 }
Yuxuan 'fishy' Wange79f7642020-06-12 22:22:35 -0700204 _, err := p.writeFieldBeginInternal(ctx, name, typeId, id, 0xFF)
Jens Geyer0e87c462013-06-18 22:25:07 +0200205 return NewTProtocolException(err)
206}
207
208// The workhorse of writeFieldBegin. It has the option of doing a
209// 'type override' of the type header. This is used specifically in the
210// boolean field case.
Yuxuan 'fishy' Wange79f7642020-06-12 22:22:35 -0700211func (p *TCompactProtocol) writeFieldBeginInternal(ctx context.Context, name string, typeId TType, id int16, typeOverride byte) (int, error) {
Jens Geyer0e87c462013-06-18 22:25:07 +0200212 // short lastField = lastField_.pop();
213
214 // if there's a type override, use that.
215 var typeToWrite byte
216 if typeOverride == 0xFF {
217 typeToWrite = byte(p.getCompactType(typeId))
218 } else {
219 typeToWrite = typeOverride
220 }
221 // check if we can use delta encoding for the field id
222 fieldId := int(id)
223 written := 0
224 if fieldId > p.lastFieldId && fieldId-p.lastFieldId <= 15 {
225 // write them together
Jens Geyer09972502014-05-02 01:30:13 +0200226 err := p.writeByteDirect(byte((fieldId-p.lastFieldId)<<4) | typeToWrite)
Jens Geyer0e87c462013-06-18 22:25:07 +0200227 if err != nil {
Jens Geyer09972502014-05-02 01:30:13 +0200228 return 0, err
Jens Geyer0e87c462013-06-18 22:25:07 +0200229 }
230 } else {
231 // write them separate
Jens Geyer09972502014-05-02 01:30:13 +0200232 err := p.writeByteDirect(typeToWrite)
Jens Geyer0e87c462013-06-18 22:25:07 +0200233 if err != nil {
Jens Geyer09972502014-05-02 01:30:13 +0200234 return 0, err
Jens Geyer0e87c462013-06-18 22:25:07 +0200235 }
Yuxuan 'fishy' Wange79f7642020-06-12 22:22:35 -0700236 err = p.WriteI16(ctx, id)
Jens Geyer09972502014-05-02 01:30:13 +0200237 written = 1 + 2
Jens Geyer0e87c462013-06-18 22:25:07 +0200238 if err != nil {
Jens Geyer09972502014-05-02 01:30:13 +0200239 return 0, err
Jens Geyer0e87c462013-06-18 22:25:07 +0200240 }
241 }
242
243 p.lastFieldId = fieldId
Jens Geyer0e87c462013-06-18 22:25:07 +0200244 return written, nil
245}
246
Yuxuan 'fishy' Wange79f7642020-06-12 22:22:35 -0700247func (p *TCompactProtocol) WriteFieldEnd(ctx context.Context) error { return nil }
Jens Geyer0e87c462013-06-18 22:25:07 +0200248
Yuxuan 'fishy' Wange79f7642020-06-12 22:22:35 -0700249func (p *TCompactProtocol) WriteFieldStop(ctx context.Context) error {
Jens Geyer09972502014-05-02 01:30:13 +0200250 err := p.writeByteDirect(STOP)
Jens Geyer0e87c462013-06-18 22:25:07 +0200251 return NewTProtocolException(err)
252}
253
Yuxuan 'fishy' Wange79f7642020-06-12 22:22:35 -0700254func (p *TCompactProtocol) WriteMapBegin(ctx context.Context, keyType TType, valueType TType, size int) error {
Jens Geyer0e87c462013-06-18 22:25:07 +0200255 if size == 0 {
Jens Geyer09972502014-05-02 01:30:13 +0200256 err := p.writeByteDirect(0)
Jens Geyer0e87c462013-06-18 22:25:07 +0200257 return NewTProtocolException(err)
258 }
259 _, err := p.writeVarint32(int32(size))
260 if err != nil {
261 return NewTProtocolException(err)
262 }
Jens Geyer09972502014-05-02 01:30:13 +0200263 err = p.writeByteDirect(byte(p.getCompactType(keyType))<<4 | byte(p.getCompactType(valueType)))
Jens Geyer0e87c462013-06-18 22:25:07 +0200264 return NewTProtocolException(err)
265}
266
Yuxuan 'fishy' Wange79f7642020-06-12 22:22:35 -0700267func (p *TCompactProtocol) WriteMapEnd(ctx context.Context) error { return nil }
Jens Geyer0e87c462013-06-18 22:25:07 +0200268
269// Write a list header.
Yuxuan 'fishy' Wange79f7642020-06-12 22:22:35 -0700270func (p *TCompactProtocol) WriteListBegin(ctx context.Context, elemType TType, size int) error {
Jens Geyer0e87c462013-06-18 22:25:07 +0200271 _, err := p.writeCollectionBegin(elemType, size)
272 return NewTProtocolException(err)
273}
274
Yuxuan 'fishy' Wange79f7642020-06-12 22:22:35 -0700275func (p *TCompactProtocol) WriteListEnd(ctx context.Context) error { return nil }
Jens Geyer0e87c462013-06-18 22:25:07 +0200276
277// Write a set header.
Yuxuan 'fishy' Wange79f7642020-06-12 22:22:35 -0700278func (p *TCompactProtocol) WriteSetBegin(ctx context.Context, elemType TType, size int) error {
Jens Geyer0e87c462013-06-18 22:25:07 +0200279 _, err := p.writeCollectionBegin(elemType, size)
280 return NewTProtocolException(err)
281}
282
Yuxuan 'fishy' Wange79f7642020-06-12 22:22:35 -0700283func (p *TCompactProtocol) WriteSetEnd(ctx context.Context) error { return nil }
Jens Geyer0e87c462013-06-18 22:25:07 +0200284
Yuxuan 'fishy' Wange79f7642020-06-12 22:22:35 -0700285func (p *TCompactProtocol) WriteBool(ctx context.Context, value bool) error {
Jens Geyer0e87c462013-06-18 22:25:07 +0200286 v := byte(COMPACT_BOOLEAN_FALSE)
287 if value {
288 v = byte(COMPACT_BOOLEAN_TRUE)
289 }
Jens Geyer09972502014-05-02 01:30:13 +0200290 if p.booleanFieldPending {
Jens Geyer0e87c462013-06-18 22:25:07 +0200291 // we haven't written the field header yet
Yuxuan 'fishy' Wange79f7642020-06-12 22:22:35 -0700292 _, err := p.writeFieldBeginInternal(ctx, p.booleanFieldName, BOOL, p.booleanFieldId, v)
Jens Geyer09972502014-05-02 01:30:13 +0200293 p.booleanFieldPending = false
Jens Geyer0e87c462013-06-18 22:25:07 +0200294 return NewTProtocolException(err)
295 }
296 // we're not part of a field, so just write the value.
Jens Geyer09972502014-05-02 01:30:13 +0200297 err := p.writeByteDirect(v)
Jens Geyer0e87c462013-06-18 22:25:07 +0200298 return NewTProtocolException(err)
299}
300
301// Write a byte. Nothing to see here!
Yuxuan 'fishy' Wange79f7642020-06-12 22:22:35 -0700302func (p *TCompactProtocol) WriteByte(ctx context.Context, value int8) error {
Jens Geyer5bc8b5a2015-09-05 12:50:24 +0200303 err := p.writeByteDirect(byte(value))
Jens Geyer0e87c462013-06-18 22:25:07 +0200304 return NewTProtocolException(err)
305}
306
307// Write an I16 as a zigzag varint.
Yuxuan 'fishy' Wange79f7642020-06-12 22:22:35 -0700308func (p *TCompactProtocol) WriteI16(ctx context.Context, value int16) error {
Jens Geyer0e87c462013-06-18 22:25:07 +0200309 _, err := p.writeVarint32(p.int32ToZigzag(int32(value)))
310 return NewTProtocolException(err)
311}
312
313// Write an i32 as a zigzag varint.
Yuxuan 'fishy' Wange79f7642020-06-12 22:22:35 -0700314func (p *TCompactProtocol) WriteI32(ctx context.Context, value int32) error {
Jens Geyer0e87c462013-06-18 22:25:07 +0200315 _, err := p.writeVarint32(p.int32ToZigzag(value))
316 return NewTProtocolException(err)
317}
318
319// Write an i64 as a zigzag varint.
Yuxuan 'fishy' Wange79f7642020-06-12 22:22:35 -0700320func (p *TCompactProtocol) WriteI64(ctx context.Context, value int64) error {
Jens Geyer0e87c462013-06-18 22:25:07 +0200321 _, err := p.writeVarint64(p.int64ToZigzag(value))
322 return NewTProtocolException(err)
323}
324
325// Write a double to the wire as 8 bytes.
Yuxuan 'fishy' Wange79f7642020-06-12 22:22:35 -0700326func (p *TCompactProtocol) WriteDouble(ctx context.Context, value float64) error {
Jens Geyer09972502014-05-02 01:30:13 +0200327 buf := p.buffer[0:8]
Jens Geyer0e87c462013-06-18 22:25:07 +0200328 binary.LittleEndian.PutUint64(buf, math.Float64bits(value))
329 _, err := p.trans.Write(buf)
330 return NewTProtocolException(err)
331}
332
Konrad Grochowski3b5dacb2014-11-24 10:55:31 +0100333// Write a string to the wire with a varint size preceding.
Yuxuan 'fishy' Wange79f7642020-06-12 22:22:35 -0700334func (p *TCompactProtocol) WriteString(ctx context.Context, value string) error {
Jens Geyer09972502014-05-02 01:30:13 +0200335 _, e := p.writeVarint32(int32(len(value)))
336 if e != nil {
337 return NewTProtocolException(e)
338 }
Yuxuan 'fishy' Wang17373a32021-08-26 11:04:27 -0700339 if len(value) == 0 {
340 return nil
Jens Geyer09972502014-05-02 01:30:13 +0200341 }
342 _, e = p.trans.WriteString(value)
343 return e
Jens Geyer0e87c462013-06-18 22:25:07 +0200344}
345
346// Write a byte array, using a varint for the size.
Yuxuan 'fishy' Wange79f7642020-06-12 22:22:35 -0700347func (p *TCompactProtocol) WriteBinary(ctx context.Context, bin []byte) error {
Jens Geyer0e87c462013-06-18 22:25:07 +0200348 _, e := p.writeVarint32(int32(len(bin)))
349 if e != nil {
350 return NewTProtocolException(e)
351 }
352 if len(bin) > 0 {
353 _, e = p.trans.Write(bin)
354 return NewTProtocolException(e)
355 }
356 return nil
357}
358
Yuxuan 'fishy' Wang19c13b42022-10-12 14:13:15 -0700359// Write a Tuuid to the wire as 16 bytes.
360func (p *TCompactProtocol) WriteUUID(ctx context.Context, value Tuuid) error {
361 _, err := p.trans.Write(value[:])
362 return NewTProtocolException(err)
363}
364
Jens Geyer0e87c462013-06-18 22:25:07 +0200365//
366// Reading methods.
367//
368
369// Read a message header.
Yuxuan 'fishy' Wange79f7642020-06-12 22:22:35 -0700370func (p *TCompactProtocol) ReadMessageBegin(ctx context.Context) (name string, typeId TMessageType, seqId int32, err error) {
371 var protocolId byte
Jens Geyer3f2e7102015-06-26 21:54:35 +0200372
Yuxuan 'fishy' Wange79f7642020-06-12 22:22:35 -0700373 _, deadlineSet := ctx.Deadline()
374 for {
375 protocolId, err = p.readByteDirect()
376 if deadlineSet && isTimeoutError(err) && ctx.Err() == nil {
377 // keep retrying I/O timeout errors since we still have
378 // time left
379 continue
380 }
381 // For anything else, don't retry
382 break
383 }
Jens Geyer3f2e7102015-06-26 21:54:35 +0200384 if err != nil {
385 return
386 }
387
Jens Geyer0e87c462013-06-18 22:25:07 +0200388 if protocolId != COMPACT_PROTOCOL_ID {
389 e := fmt.Errorf("Expected protocol id %02x but got %02x", COMPACT_PROTOCOL_ID, protocolId)
390 return "", typeId, seqId, NewTProtocolExceptionWithType(BAD_VERSION, e)
391 }
Jens Geyer3f2e7102015-06-26 21:54:35 +0200392
Jens Geyer5bc8b5a2015-09-05 12:50:24 +0200393 versionAndType, err := p.readByteDirect()
Jens Geyer0e87c462013-06-18 22:25:07 +0200394 if err != nil {
395 return
396 }
Jens Geyer3f2e7102015-06-26 21:54:35 +0200397
398 version := versionAndType & COMPACT_VERSION_MASK
399 typeId = TMessageType((versionAndType >> COMPACT_TYPE_SHIFT_AMOUNT) & COMPACT_TYPE_BITS)
Jens Geyer0e87c462013-06-18 22:25:07 +0200400 if version != COMPACT_VERSION {
401 e := fmt.Errorf("Expected version %02x but got %02x", COMPACT_VERSION, version)
402 err = NewTProtocolExceptionWithType(BAD_VERSION, e)
403 return
404 }
405 seqId, e := p.readVarint32()
406 if e != nil {
407 err = NewTProtocolException(e)
408 return
409 }
Yuxuan 'fishy' Wange79f7642020-06-12 22:22:35 -0700410 name, err = p.ReadString(ctx)
Jens Geyer0e87c462013-06-18 22:25:07 +0200411 return
412}
413
Yuxuan 'fishy' Wange79f7642020-06-12 22:22:35 -0700414func (p *TCompactProtocol) ReadMessageEnd(ctx context.Context) error { return nil }
Jens Geyer0e87c462013-06-18 22:25:07 +0200415
416// Read a struct begin. There's nothing on the wire for this, but it is our
417// opportunity to push a new struct begin marker onto the field stack.
Yuxuan 'fishy' Wange79f7642020-06-12 22:22:35 -0700418func (p *TCompactProtocol) ReadStructBegin(ctx context.Context) (name string, err error) {
Jens Geyer0e87c462013-06-18 22:25:07 +0200419 p.lastField = append(p.lastField, p.lastFieldId)
420 p.lastFieldId = 0
421 return
422}
423
424// Doesn't actually consume any wire data, just removes the last field for
425// this struct from the field stack.
Yuxuan 'fishy' Wange79f7642020-06-12 22:22:35 -0700426func (p *TCompactProtocol) ReadStructEnd(ctx context.Context) error {
Jens Geyer0e87c462013-06-18 22:25:07 +0200427 // consume the last field we read off the wire.
Yuxuan 'fishy' Wang64c2a4b2020-10-10 18:39:32 -0700428 if len(p.lastField) <= 0 {
429 return NewTProtocolExceptionWithType(INVALID_DATA, errors.New("ReadStructEnd called without matching ReadStructBegin call before"))
430 }
Jens Geyer0e87c462013-06-18 22:25:07 +0200431 p.lastFieldId = p.lastField[len(p.lastField)-1]
Jens Geyerf322d912013-11-28 21:15:17 +0100432 p.lastField = p.lastField[:len(p.lastField)-1]
Jens Geyer0e87c462013-06-18 22:25:07 +0200433 return nil
434}
435
436// Read a field header off the wire.
Yuxuan 'fishy' Wange79f7642020-06-12 22:22:35 -0700437func (p *TCompactProtocol) ReadFieldBegin(ctx context.Context) (name string, typeId TType, id int16, err error) {
Jens Geyer5bc8b5a2015-09-05 12:50:24 +0200438 t, err := p.readByteDirect()
Jens Geyer0e87c462013-06-18 22:25:07 +0200439 if err != nil {
440 return
441 }
442
443 // if it's a stop, then we can return immediately, as the struct is over.
444 if (t & 0x0f) == STOP {
Jens Geyer09972502014-05-02 01:30:13 +0200445 return "", STOP, 0, nil
Jens Geyer0e87c462013-06-18 22:25:07 +0200446 }
447
448 // mask off the 4 MSB of the type header. it could contain a field id delta.
449 modifier := int16((t & 0xf0) >> 4)
450 if modifier == 0 {
451 // not a delta. look ahead for the zigzag varint field id.
Yuxuan 'fishy' Wange79f7642020-06-12 22:22:35 -0700452 id, err = p.ReadI16(ctx)
Jens Geyer0e87c462013-06-18 22:25:07 +0200453 if err != nil {
454 return
455 }
456 } else {
457 // has a delta. add the delta to the last read field id.
458 id = int16(p.lastFieldId) + modifier
459 }
460 typeId, e := p.getTType(tCompactType(t & 0x0f))
461 if e != nil {
462 err = NewTProtocolException(e)
463 return
464 }
465
466 // if this happens to be a boolean field, the value is encoded in the type
467 if p.isBoolType(t) {
468 // save the boolean value in a special instance variable.
469 p.boolValue = (byte(t)&0x0f == COMPACT_BOOLEAN_TRUE)
470 p.boolValueIsNotNull = true
471 }
472
473 // push the new field onto the field stack so we can keep the deltas going.
474 p.lastFieldId = int(id)
475 return
476}
477
Yuxuan 'fishy' Wange79f7642020-06-12 22:22:35 -0700478func (p *TCompactProtocol) ReadFieldEnd(ctx context.Context) error { return nil }
Jens Geyer0e87c462013-06-18 22:25:07 +0200479
480// Read a map header off the wire. If the size is zero, skip reading the key
481// and value type. This means that 0-length maps will yield TMaps without the
482// "correct" types.
Yuxuan 'fishy' Wange79f7642020-06-12 22:22:35 -0700483func (p *TCompactProtocol) ReadMapBegin(ctx context.Context) (keyType TType, valueType TType, size int, err error) {
Jens Geyer0e87c462013-06-18 22:25:07 +0200484 size32, e := p.readVarint32()
Jens Geyer0e87c462013-06-18 22:25:07 +0200485 if e != nil {
486 err = NewTProtocolException(e)
487 return
488 }
Yuxuan 'fishy' Wang6583f4e2021-03-25 17:00:31 -0700489 err = checkSizeForProtocol(size32, p.cfg)
490 if err != nil {
Jens Geyer91cfb992014-05-17 01:07:28 +0200491 return
492 }
493 size = int(size32)
494
Jens Geyer0e87c462013-06-18 22:25:07 +0200495 keyAndValueType := byte(STOP)
496 if size != 0 {
Jens Geyer5bc8b5a2015-09-05 12:50:24 +0200497 keyAndValueType, err = p.readByteDirect()
Jens Geyer0e87c462013-06-18 22:25:07 +0200498 if err != nil {
499 return
500 }
501 }
502 keyType, _ = p.getTType(tCompactType(keyAndValueType >> 4))
503 valueType, _ = p.getTType(tCompactType(keyAndValueType & 0xf))
504 return
505}
506
Yuxuan 'fishy' Wange79f7642020-06-12 22:22:35 -0700507func (p *TCompactProtocol) ReadMapEnd(ctx context.Context) error { return nil }
Jens Geyer0e87c462013-06-18 22:25:07 +0200508
509// Read a list header off the wire. If the list size is 0-14, the size will
510// be packed into the element type header. If it's a longer list, the 4 MSB
511// of the element type header will be 0xF, and a varint will follow with the
512// true size.
Yuxuan 'fishy' Wange79f7642020-06-12 22:22:35 -0700513func (p *TCompactProtocol) ReadListBegin(ctx context.Context) (elemType TType, size int, err error) {
Jens Geyer5bc8b5a2015-09-05 12:50:24 +0200514 size_and_type, err := p.readByteDirect()
Jens Geyer0e87c462013-06-18 22:25:07 +0200515 if err != nil {
516 return
517 }
518 size = int((size_and_type >> 4) & 0x0f)
519 if size == 15 {
520 size2, e := p.readVarint32()
521 if e != nil {
522 err = NewTProtocolException(e)
523 return
524 }
525 size = int(size2)
526 }
Yuxuan 'fishy' Wang71ba05b2022-05-10 13:18:51 -0700527 err = checkSizeForProtocol(int32(size), p.cfg)
Yuxuan 'fishy' Wang6583f4e2021-03-25 17:00:31 -0700528 if err != nil {
529 return
530 }
Jens Geyer0e87c462013-06-18 22:25:07 +0200531 elemType, e := p.getTType(tCompactType(size_and_type))
532 if e != nil {
533 err = NewTProtocolException(e)
534 return
535 }
536 return
537}
538
Yuxuan 'fishy' Wange79f7642020-06-12 22:22:35 -0700539func (p *TCompactProtocol) ReadListEnd(ctx context.Context) error { return nil }
Jens Geyer0e87c462013-06-18 22:25:07 +0200540
541// Read a set header off the wire. If the set size is 0-14, the size will
542// be packed into the element type header. If it's a longer set, the 4 MSB
543// of the element type header will be 0xF, and a varint will follow with the
544// true size.
Yuxuan 'fishy' Wange79f7642020-06-12 22:22:35 -0700545func (p *TCompactProtocol) ReadSetBegin(ctx context.Context) (elemType TType, size int, err error) {
546 return p.ReadListBegin(ctx)
Jens Geyer0e87c462013-06-18 22:25:07 +0200547}
548
Yuxuan 'fishy' Wange79f7642020-06-12 22:22:35 -0700549func (p *TCompactProtocol) ReadSetEnd(ctx context.Context) error { return nil }
Jens Geyer0e87c462013-06-18 22:25:07 +0200550
551// Read a boolean off the wire. If this is a boolean field, the value should
552// already have been read during readFieldBegin, so we'll just consume the
553// pre-stored value. Otherwise, read a byte.
Yuxuan 'fishy' Wange79f7642020-06-12 22:22:35 -0700554func (p *TCompactProtocol) ReadBool(ctx context.Context) (value bool, err error) {
Jens Geyer0e87c462013-06-18 22:25:07 +0200555 if p.boolValueIsNotNull {
556 p.boolValueIsNotNull = false
557 return p.boolValue, nil
558 }
Jens Geyer5bc8b5a2015-09-05 12:50:24 +0200559 v, err := p.readByteDirect()
Jens Geyer0e87c462013-06-18 22:25:07 +0200560 return v == COMPACT_BOOLEAN_TRUE, err
561}
562
563// Read a single byte off the wire. Nothing interesting here.
Yuxuan 'fishy' Wange79f7642020-06-12 22:22:35 -0700564func (p *TCompactProtocol) ReadByte(ctx context.Context) (int8, error) {
Jens Geyer5bc8b5a2015-09-05 12:50:24 +0200565 v, err := p.readByteDirect()
Jens Geyer09972502014-05-02 01:30:13 +0200566 if err != nil {
567 return 0, NewTProtocolException(err)
Jens Geyer0e87c462013-06-18 22:25:07 +0200568 }
Jens Geyer5bc8b5a2015-09-05 12:50:24 +0200569 return int8(v), err
Jens Geyer0e87c462013-06-18 22:25:07 +0200570}
571
572// Read an i16 from the wire as a zigzag varint.
Yuxuan 'fishy' Wange79f7642020-06-12 22:22:35 -0700573func (p *TCompactProtocol) ReadI16(ctx context.Context) (value int16, err error) {
574 v, err := p.ReadI32(ctx)
Jens Geyer0e87c462013-06-18 22:25:07 +0200575 return int16(v), err
576}
577
578// Read an i32 from the wire as a zigzag varint.
Yuxuan 'fishy' Wange79f7642020-06-12 22:22:35 -0700579func (p *TCompactProtocol) ReadI32(ctx context.Context) (value int32, err error) {
Jens Geyer0e87c462013-06-18 22:25:07 +0200580 v, e := p.readVarint32()
581 if e != nil {
582 return 0, NewTProtocolException(e)
583 }
584 value = p.zigzagToInt32(v)
585 return value, nil
586}
587
588// Read an i64 from the wire as a zigzag varint.
Yuxuan 'fishy' Wange79f7642020-06-12 22:22:35 -0700589func (p *TCompactProtocol) ReadI64(ctx context.Context) (value int64, err error) {
Jens Geyer0e87c462013-06-18 22:25:07 +0200590 v, e := p.readVarint64()
591 if e != nil {
592 return 0, NewTProtocolException(e)
593 }
594 value = p.zigzagToInt64(v)
595 return value, nil
596}
597
598// No magic here - just read a double off the wire.
Yuxuan 'fishy' Wange79f7642020-06-12 22:22:35 -0700599func (p *TCompactProtocol) ReadDouble(ctx context.Context) (value float64, err error) {
Jens Geyer09972502014-05-02 01:30:13 +0200600 longBits := p.buffer[0:8]
Jens Geyer0e87c462013-06-18 22:25:07 +0200601 _, e := io.ReadFull(p.trans, longBits)
602 if e != nil {
603 return 0.0, NewTProtocolException(e)
604 }
605 return math.Float64frombits(p.bytesToUint64(longBits)), nil
606}
607
608// Reads a []byte (via readBinary), and then UTF-8 decodes it.
Yuxuan 'fishy' Wange79f7642020-06-12 22:22:35 -0700609func (p *TCompactProtocol) ReadString(ctx context.Context) (value string, err error) {
Jens Geyer09972502014-05-02 01:30:13 +0200610 length, e := p.readVarint32()
611 if e != nil {
612 return "", NewTProtocolException(e)
613 }
Yuxuan 'fishy' Wangc4d1c0d2020-12-16 17:10:48 -0800614 err = checkSizeForProtocol(length, p.cfg)
615 if err != nil {
616 return
Jens Geyer91cfb992014-05-17 01:07:28 +0200617 }
Jens Geyer09972502014-05-02 01:30:13 +0200618 if length == 0 {
619 return "", nil
620 }
Yuxuan 'fishy' Wang37c2ceb2020-12-10 14:42:37 -0800621 if length < int32(len(p.buffer)) {
622 // Avoid allocation on small reads
623 buf := p.buffer[:length]
624 read, e := io.ReadFull(p.trans, buf)
625 return string(buf[:read]), NewTProtocolException(e)
Jens Geyer09972502014-05-02 01:30:13 +0200626 }
Yuxuan 'fishy' Wang37c2ceb2020-12-10 14:42:37 -0800627
628 buf, e := safeReadBytes(length, p.trans)
Jens Geyer09972502014-05-02 01:30:13 +0200629 return string(buf), NewTProtocolException(e)
Jens Geyer0e87c462013-06-18 22:25:07 +0200630}
631
632// Read a []byte from the wire.
Yuxuan 'fishy' Wange79f7642020-06-12 22:22:35 -0700633func (p *TCompactProtocol) ReadBinary(ctx context.Context) (value []byte, err error) {
Jens Geyer0e87c462013-06-18 22:25:07 +0200634 length, e := p.readVarint32()
635 if e != nil {
Jens Geyer09972502014-05-02 01:30:13 +0200636 return nil, NewTProtocolException(e)
Jens Geyer0e87c462013-06-18 22:25:07 +0200637 }
Yuxuan 'fishy' Wangc4d1c0d2020-12-16 17:10:48 -0800638 err = checkSizeForProtocol(length, p.cfg)
639 if err != nil {
640 return
641 }
Jens Geyer0e87c462013-06-18 22:25:07 +0200642 if length == 0 {
Jens Geyer91cfb992014-05-17 01:07:28 +0200643 return []byte{}, nil
644 }
Jens Geyer0e87c462013-06-18 22:25:07 +0200645
Yuxuan 'fishy' Wang37c2ceb2020-12-10 14:42:37 -0800646 buf, e := safeReadBytes(length, p.trans)
Jens Geyer0e87c462013-06-18 22:25:07 +0200647 return buf, NewTProtocolException(e)
648}
649
Yuxuan 'fishy' Wang19c13b42022-10-12 14:13:15 -0700650// Read fixed 16 bytes as UUID.
651func (p *TCompactProtocol) ReadUUID(ctx context.Context) (value Tuuid, err error) {
652 buf := p.buffer[0:16]
653 _, e := io.ReadFull(p.trans, buf)
654 if e == nil {
655 copy(value[:], buf)
656 }
657 return value, NewTProtocolException(e)
658}
659
John Boiles57852792018-01-05 14:37:05 -0800660func (p *TCompactProtocol) Flush(ctx context.Context) (err error) {
661 return NewTProtocolException(p.trans.Flush(ctx))
Jens Geyer0e87c462013-06-18 22:25:07 +0200662}
663
Yuxuan 'fishy' Wange79f7642020-06-12 22:22:35 -0700664func (p *TCompactProtocol) Skip(ctx context.Context, fieldType TType) (err error) {
665 return SkipDefaultDepth(ctx, p, fieldType)
Jens Geyer0e87c462013-06-18 22:25:07 +0200666}
667
668func (p *TCompactProtocol) Transport() TTransport {
Jens Geyer09972502014-05-02 01:30:13 +0200669 return p.origTransport
Jens Geyer0e87c462013-06-18 22:25:07 +0200670}
671
672//
673// Internal writing methods
674//
675
676// Abstract method for writing the start of lists and sets. List and sets on
677// the wire differ only by the type indicator.
678func (p *TCompactProtocol) writeCollectionBegin(elemType TType, size int) (int, error) {
679 if size <= 14 {
Jens Geyer09972502014-05-02 01:30:13 +0200680 return 1, p.writeByteDirect(byte(int32(size<<4) | int32(p.getCompactType(elemType))))
Jens Geyer0e87c462013-06-18 22:25:07 +0200681 }
Jens Geyer09972502014-05-02 01:30:13 +0200682 err := p.writeByteDirect(0xf0 | byte(p.getCompactType(elemType)))
Jens Geyer0e87c462013-06-18 22:25:07 +0200683 if err != nil {
Jens Geyer09972502014-05-02 01:30:13 +0200684 return 0, err
Jens Geyer0e87c462013-06-18 22:25:07 +0200685 }
686 m, err := p.writeVarint32(int32(size))
Jens Geyer09972502014-05-02 01:30:13 +0200687 return 1 + m, err
Jens Geyer0e87c462013-06-18 22:25:07 +0200688}
689
690// Write an i32 as a varint. Results in 1-5 bytes on the wire.
691// TODO(pomack): make a permanent buffer like writeVarint64?
692func (p *TCompactProtocol) writeVarint32(n int32) (int, error) {
Jens Geyer09972502014-05-02 01:30:13 +0200693 i32buf := p.buffer[0:5]
Jens Geyer0e87c462013-06-18 22:25:07 +0200694 idx := 0
695 for {
696 if (n & ^0x7F) == 0 {
697 i32buf[idx] = byte(n)
698 idx++
699 // p.writeByteDirect(byte(n));
700 break
701 // return;
702 } else {
703 i32buf[idx] = byte((n & 0x7F) | 0x80)
704 idx++
705 // p.writeByteDirect(byte(((n & 0x7F) | 0x80)));
706 u := uint32(n)
707 n = int32(u >> 7)
708 }
709 }
710 return p.trans.Write(i32buf[0:idx])
711}
712
713// Write an i64 as a varint. Results in 1-10 bytes on the wire.
714func (p *TCompactProtocol) writeVarint64(n int64) (int, error) {
Jens Geyer09972502014-05-02 01:30:13 +0200715 varint64out := p.buffer[0:10]
Jens Geyer0e87c462013-06-18 22:25:07 +0200716 idx := 0
717 for {
718 if (n & ^0x7F) == 0 {
719 varint64out[idx] = byte(n)
720 idx++
721 break
722 } else {
723 varint64out[idx] = byte((n & 0x7F) | 0x80)
724 idx++
725 u := uint64(n)
726 n = int64(u >> 7)
727 }
728 }
729 return p.trans.Write(varint64out[0:idx])
730}
731
732// Convert l into a zigzag long. This allows negative numbers to be
733// represented compactly as a varint.
734func (p *TCompactProtocol) int64ToZigzag(l int64) int64 {
735 return (l << 1) ^ (l >> 63)
736}
737
738// Convert l into a zigzag long. This allows negative numbers to be
739// represented compactly as a varint.
740func (p *TCompactProtocol) int32ToZigzag(n int32) int32 {
741 return (n << 1) ^ (n >> 31)
742}
743
Konrad Grochowski3b5dacb2014-11-24 10:55:31 +0100744// Writes a byte without any possibility of all that field header nonsense.
Jens Geyer0e87c462013-06-18 22:25:07 +0200745// Used internally by other writing methods that know they need to write a byte.
Jens Geyer09972502014-05-02 01:30:13 +0200746func (p *TCompactProtocol) writeByteDirect(b byte) error {
747 return p.trans.WriteByte(b)
Jens Geyer0e87c462013-06-18 22:25:07 +0200748}
749
Jens Geyer0e87c462013-06-18 22:25:07 +0200750//
751// Internal reading methods
752//
753
754// Read an i32 from the wire as a varint. The MSB of each byte is set
755// if there is another byte to follow. This can read up to 5 bytes.
756func (p *TCompactProtocol) readVarint32() (int32, error) {
757 // if the wire contains the right stuff, this will just truncate the i64 we
758 // read and get us the right sign.
759 v, err := p.readVarint64()
760 return int32(v), err
761}
762
763// Read an i64 from the wire as a proper varint. The MSB of each byte is set
764// if there is another byte to follow. This can read up to 10 bytes.
765func (p *TCompactProtocol) readVarint64() (int64, error) {
766 shift := uint(0)
767 result := int64(0)
768 for {
Jens Geyer5bc8b5a2015-09-05 12:50:24 +0200769 b, err := p.readByteDirect()
Jens Geyer0e87c462013-06-18 22:25:07 +0200770 if err != nil {
771 return 0, err
772 }
773 result |= int64(b&0x7f) << shift
774 if (b & 0x80) != 0x80 {
775 break
776 }
777 shift += 7
778 }
779 return result, nil
780}
781
Jens Geyer5bc8b5a2015-09-05 12:50:24 +0200782// Read a byte, unlike ReadByte that reads Thrift-byte that is i8.
783func (p *TCompactProtocol) readByteDirect() (byte, error) {
784 return p.trans.ReadByte()
785}
786
Jens Geyer0e87c462013-06-18 22:25:07 +0200787//
788// encoding helpers
789//
790
791// Convert from zigzag int to int.
792func (p *TCompactProtocol) zigzagToInt32(n int32) int32 {
793 u := uint32(n)
794 return int32(u>>1) ^ -(n & 1)
795}
796
797// Convert from zigzag long to long.
798func (p *TCompactProtocol) zigzagToInt64(n int64) int64 {
799 u := uint64(n)
800 return int64(u>>1) ^ -(n & 1)
801}
802
803// Note that it's important that the mask bytes are long literals,
804// otherwise they'll default to ints, and when you shift an int left 56 bits,
805// you just get a messed up int.
Jens Geyer0e87c462013-06-18 22:25:07 +0200806func (p *TCompactProtocol) bytesToUint64(b []byte) uint64 {
807 return binary.LittleEndian.Uint64(b)
808}
809
810//
811// type testing and converting
812//
813
814func (p *TCompactProtocol) isBoolType(b byte) bool {
815 return (b&0x0f) == COMPACT_BOOLEAN_TRUE || (b&0x0f) == COMPACT_BOOLEAN_FALSE
816}
817
818// Given a tCompactType constant, convert it to its corresponding
819// TType value.
820func (p *TCompactProtocol) getTType(t tCompactType) (TType, error) {
821 switch byte(t) & 0x0f {
822 case STOP:
823 return STOP, nil
Jens Geyer9957d302013-11-04 22:18:40 +0100824 case COMPACT_BOOLEAN_FALSE, COMPACT_BOOLEAN_TRUE:
Jens Geyer0e87c462013-06-18 22:25:07 +0200825 return BOOL, nil
826 case COMPACT_BYTE:
827 return BYTE, nil
828 case COMPACT_I16:
829 return I16, nil
830 case COMPACT_I32:
831 return I32, nil
832 case COMPACT_I64:
833 return I64, nil
834 case COMPACT_DOUBLE:
835 return DOUBLE, nil
836 case COMPACT_BINARY:
837 return STRING, nil
838 case COMPACT_LIST:
839 return LIST, nil
840 case COMPACT_SET:
841 return SET, nil
842 case COMPACT_MAP:
843 return MAP, nil
844 case COMPACT_STRUCT:
845 return STRUCT, nil
Yuxuan 'fishy' Wang19c13b42022-10-12 14:13:15 -0700846 case COMPACT_UUID:
847 return UUID, nil
Jens Geyer0e87c462013-06-18 22:25:07 +0200848 }
Yuxuan 'fishy' Wangd8312302020-12-22 09:53:58 -0800849 return STOP, NewTProtocolException(fmt.Errorf("don't know what type: %v", t&0x0f))
Jens Geyer0e87c462013-06-18 22:25:07 +0200850}
851
852// Given a TType value, find the appropriate TCompactProtocol.Types constant.
853func (p *TCompactProtocol) getCompactType(t TType) tCompactType {
854 return ttypeToCompactType[t]
855}
Yuxuan 'fishy' Wangc4d1c0d2020-12-16 17:10:48 -0800856
857func (p *TCompactProtocol) SetTConfiguration(conf *TConfiguration) {
858 PropagateTConfiguration(p.trans, conf)
859 PropagateTConfiguration(p.origTransport, conf)
860 p.cfg = conf
861}
862
863var (
864 _ TConfigurationSetter = (*TCompactProtocolFactory)(nil)
865 _ TConfigurationSetter = (*TCompactProtocol)(nil)
866)