blob: 45b0ef34b5989e80555f9b00455fd197f931cced [file] [log] [blame]
Jens Geyerdc799ca2015-04-27 22:56:54 +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
20#import "TCompactProtocol.h"
21#import "TObjective-C.h"
22#import "TProtocolException.h"
23
24static const uint8_t COMPACT_PROTOCOL_ID = 0x82;
25static const uint8_t COMPACT_VERSION = 1;
26static const uint8_t COMPACT_VERSION_MASK = 0x1F; // 0001 1111
27static const uint8_t COMPACT_TYPE_MASK = 0xE0; // 1110 0000
28static const uint8_t COMPACT_TYPE_BITS = 0x07; // 0000 0111
29static const int COMPACT_TYPE_SHIFT_AMOUNT = 5;
30
31enum {
32 TCType_STOP = 0x00,
33 TCType_BOOLEAN_TRUE = 0x01,
34 TCType_BOOLEAN_FALSE = 0x02,
35 TCType_BYTE = 0x03,
36 TCType_I16 = 0x04,
37 TCType_I32 = 0x05,
38 TCType_I64 = 0x06,
39 TCType_DOUBLE = 0x07,
40 TCType_BINARY = 0x08,
41 TCType_LIST = 0x09,
42 TCType_SET = 0x0A,
43 TCType_MAP = 0x0B,
44 TCType_STRUCT = 0x0C,
45};
46
47@implementation TCompactProtocolFactory
48
49+ (TCompactProtocolFactory *) sharedFactory
50{
51 static TCompactProtocolFactory * gSharedFactory = nil;
52 if (gSharedFactory == nil) {
53 gSharedFactory = [[TCompactProtocolFactory alloc] init];
54 }
55
56 return gSharedFactory;
57}
58
59- (TCompactProtocol *) newProtocolOnTransport: (id <TTransport>) transport
60{
61 return [[TCompactProtocol alloc] initWithTransport: transport];
62}
63
64@end
65
66@implementation TCompactProtocol {
67 NSMutableArray * lastField;
68 short lastFieldId;
69 id <TTransport> mTransport;
70
71 NSString * boolFieldName;
72 NSNumber * boolFieldType;
73 NSNumber * boolFieldId;
74 NSNumber * booleanValue;
75}
76
77- (id) init
78{
79 self = [super init];
80
81 if (self != nil) {
82 lastField = [[NSMutableArray alloc] init];
83 }
84
85 return self;
86}
87
88- (id) initWithTransport: (id <TTransport>) transport
89{
90 self = [self init];
91
92 if (self != nil) {
93 mTransport = [transport retain_stub];
94 }
95
96 return self;
97}
98
99- (void) dealloc
100{
101 [lastField release_stub];
102 [mTransport release_stub];
103 [boolFieldName release_stub];
104 [boolFieldType release_stub];
105 [boolFieldId release_stub];
106 [booleanValue release_stub];
107
108 [super dealloc_stub];
109}
110
111- (id <TTransport>) transport
112{
113 return mTransport;
114}
115
116- (void) writeByteDirect: (int8_t) n
117{
118 [mTransport write: (uint8_t *)&n offset: 0 length: 1];
119}
120
121- (void)writeVarint32: (uint32_t) n
122{
123 uint8_t i32buf[5] = {0};
124 uint32_t idx = 0;
125
126 while (true) {
127 if ((n & ~0x7F) == 0) {
128 i32buf[idx++] = (uint8_t)n;
129 break;
130 } else {
131 i32buf[idx++] = (uint8_t)((n & 0x7F) | 0x80);
132 n >>= 7;
133 }
134 }
135
136 [mTransport write: i32buf offset: 0 length: idx];
137}
138
139- (void) writeMessageBeginWithName: (NSString *) name
140 type: (int) messageType
141 sequenceID: (int) sequenceID
142{
143 [self writeByteDirect: COMPACT_PROTOCOL_ID];
144 [self writeByteDirect: (uint8_t)((COMPACT_VERSION & COMPACT_VERSION_MASK) |
145 ((((uint32_t)messageType) << COMPACT_TYPE_SHIFT_AMOUNT) & COMPACT_TYPE_MASK))];
146 [self writeVarint32: (uint32_t)sequenceID];
147 [self writeString: name];
148}
149
150- (void) writeStructBeginWithName: (NSString *) name
151{
152 [lastField addObject: [NSNumber numberWithShort: lastFieldId]];
153 lastFieldId = 0;
154}
155
156- (void) writeStructEnd
157{
158 lastFieldId = [[lastField lastObject] shortValue];
159 [lastField removeLastObject];
160}
161
162- (void) writeFieldBeginWithName: (NSString *) name
163 type: (int) fieldType
164 fieldID: (int) fieldID
165{
166 if (fieldType == TType_BOOL) {
167 boolFieldName = [name copy];
168 boolFieldType = [[NSNumber numberWithInt: fieldType] retain_stub];
169 boolFieldId = [[NSNumber numberWithInt: fieldID] retain_stub];
170 } else {
171 [self writeFieldBeginInternalWithName: name
172 type: fieldType
173 fieldID: fieldID
174 typeOverride: 0xFF];
175 }
176}
177
178- (void) writeFieldBeginInternalWithName: (NSString *) name
179 type: (int) fieldType
180 fieldID: (int) fieldID
181 typeOverride: (uint8_t) typeOverride
182{
183 uint8_t typeToWrite = typeOverride == 0xFF ? [self compactTypeForTType: fieldType] : typeOverride;
184
185 // check if we can use delta encoding for the field id
186 if (fieldID > lastFieldId && fieldID - lastFieldId <= 15) {
187 // Write them together
188 [self writeByteDirect: (fieldID - lastFieldId) << 4 | typeToWrite];
189 } else {
190 // Write them separate
191 [self writeByteDirect: typeToWrite];
192 [self writeI16: fieldID];
193 }
194
195 lastFieldId = fieldID;
196}
197
198- (void) writeFieldStop
199{
200 [self writeByteDirect: TCType_STOP];
201}
202
203- (void) writeMapBeginWithKeyType: (int) keyType
204 valueType: (int) valueType
205 size: (int) size
206{
207 if (size == 0) {
208 [self writeByteDirect: 0];
209 } else {
210 [self writeVarint32: (uint32_t)size];
211 [self writeByteDirect: [self compactTypeForTType: keyType] << 4 | [self compactTypeForTType: valueType]];
212 }
213}
214
215- (void) writeListBeginWithElementType: (int) elementType
216 size: (int) size
217{
218 [self writeCollectionBeginWithElementType: elementType size: size];
219}
220
221- (void) writeSetBeginWithElementType: (int) elementType
222 size: (int) size
223{
224 [self writeCollectionBeginWithElementType: elementType size: size];
225}
226
227- (void) writeBool: (BOOL) b
228{
229 if (boolFieldId != nil && boolFieldName != nil && boolFieldType != nil) {
230 // we haven't written the field header yet
231 [self writeFieldBeginInternalWithName: boolFieldName
232 type: [boolFieldType intValue]
233 fieldID: [boolFieldId intValue]
234 typeOverride: b ? TCType_BOOLEAN_TRUE : TCType_BOOLEAN_FALSE];
235
236 [boolFieldId release_stub];
237 [boolFieldName release_stub];
238 [boolFieldType release_stub];
239
240 boolFieldId = nil;
241 boolFieldName = nil;
242 boolFieldType = nil;
243 } else {
244 // we're not part of a field, so just Write the value.
245 [self writeByteDirect: b ? TCType_BOOLEAN_TRUE : TCType_BOOLEAN_FALSE];
246 }
247}
248
249- (void) writeByte: (uint8_t) value
250{
251 [self writeByteDirect: value];
252}
253
254- (void) writeI16: (int16_t) value
255{
256 [self writeVarint32: [self i32ToZigZag: value]];
257}
258
259- (void) writeI32: (int32_t) value
260{
261 [self writeVarint32: [self i32ToZigZag: value]];
262}
263
264- (void) writeI64: (int64_t) value
265{
266 [self writeVarint64: [self i64ToZigZag: value]];
267}
268
269- (void) writeDouble: (double) value
270{
271 //Safe bit-casting double->uint64
272
273 uint64_t bits = 0;
274 memcpy(&bits, &value, 8);
275
276 bits = OSSwapHostToLittleInt64(bits);
277
278 [mTransport write: (uint8_t *)&bits offset: 0 length: 8];
279}
280
281- (void) writeString: (NSString *) value
282{
283 [self writeBinary: [value dataUsingEncoding: NSUTF8StringEncoding]];
284}
285
286- (void) writeBinary: (NSData *) data
287{
288 [self writeVarint32: (uint32_t)data.length];
289 [mTransport write: data.bytes offset: 0 length: data.length];
290}
291
292- (void) writeMessageEnd {}
293- (void) writeMapEnd {}
294- (void) writeListEnd {}
295- (void) writeSetEnd {}
296- (void) writeFieldEnd {}
297
298- (void) writeCollectionBeginWithElementType: (int) elementType
299 size: (int) size
300{
301 if (size <= 14) {
302 [self writeByteDirect: size << 4 | [self compactTypeForTType: elementType]];
303 } else {
304 [self writeByteDirect: 0xf0 | [self compactTypeForTType: elementType]];
305 [self writeVarint32: (uint32_t)size];
306 }
307}
308
309- (void) writeVarint64: (uint64_t) n
310{
311 uint8_t varint64out[10] = {0};
312 int idx = 0;
313
314 while (true) {
315 if ((n & ~0x7FL) == 0) {
316 varint64out[idx++] = (uint8_t)n;
317 break;
318 } else {
319 varint64out[idx++] = (uint8_t)((n & 0x7F) | 0x80);
320 n >>= 7;
321 }
322 }
323
324 [mTransport write: varint64out offset: 0 length: idx];
325}
326
327- (uint32_t) i32ToZigZag: (int32_t) n
328{
329 /*
330 ZigZag encoding maps signed integers to unsigned integers so that
331 numbers with a small absolute value (for instance, -1) have
332 a small varint encoded value too. It does this in a way that
333 "zig-zags" back and forth through the positive and negative integers,
334 so that -1 is encoded as 1, 1 is encoded as 2, -2 is encoded as 3, and so on
335 */
336 return (uint32_t)(n << 1) ^ (uint32_t)(n >> 31);
337}
338
339- (uint64_t) i64ToZigZag: (int64_t) n
340{
341 return (uint64_t)(n << 1) ^ (uint64_t)(n >> 63);
342}
343
344- (void) readMessageBeginReturningName: (NSString **) pname
345 type: (int *) ptype
346 sequenceID: (int *) psequenceID
347{
348 uint8_t protocolId = [self readByte];
349 if (protocolId != COMPACT_PROTOCOL_ID) {
350 @throw [TProtocolException exceptionWithName: @"TProtocolException"
351 reason: [NSString stringWithFormat: @"Expected protocol id %X but got %X", COMPACT_PROTOCOL_ID, protocolId]];
352 }
353
354 uint8_t versionAndType = [self readByte];
355 uint8_t version = versionAndType & COMPACT_VERSION_MASK;
356 if (version != COMPACT_VERSION) {
357 @throw [TProtocolException exceptionWithName: @"TProtocolException"
358 reason: [NSString stringWithFormat: @"Expected version %d but got %d", COMPACT_VERSION, version]];
359 }
360
361 int type = (versionAndType >> COMPACT_TYPE_SHIFT_AMOUNT) & COMPACT_TYPE_BITS;
362 int sequenceID = (int)[self readVarint32];
363 NSString* name = [self readString];
364
365 if (ptype != NULL) {
366 *ptype = type;
367 }
368 if (psequenceID != NULL) {
369 *psequenceID = sequenceID;
370 }
371 if (pname != NULL) {
372 *pname = name;
373 }
374}
375
376- (void) readStructBeginReturningName: (NSString **) pname
377{
378 [lastField addObject: [NSNumber numberWithShort: lastFieldId]];
379 lastFieldId = 0;
380
381 if (pname != NULL) {
382 *pname = @"";
383 }
384}
385
386- (void) readStructEnd
387{
388 lastFieldId = [[lastField lastObject] shortValue];
389 [lastField removeLastObject];
390}
391
392- (void) readFieldBeginReturningName: (NSString **) pname
393 type: (int *) pfieldType
394 fieldID: (int *) pfieldID
395{
396 uint8_t byte = [self readByte];
397 uint8_t type = byte & 0x0f;
398
399 // if it's a stop, then we can return immediately, as the struct is over.
400 if (type == TCType_STOP) {
401 if (pname != NULL) {
402 *pname = @"";
403 }
404 if (pfieldType != NULL) {
405 *pfieldType = TType_STOP;
406 }
407 if (pfieldID != NULL) {
408 *pfieldID = 0;
409 }
410 return;
411 }
412
413 short fieldId = 0;
414
415 // mask off the 4 MSB of the type header. it could contain a field id delta.
416 short modifier = (byte & 0xf0) >> 4;
417 if (modifier == 0) {
418 // not a delta. look ahead for the zigzag varint field id.
419 fieldId = [self readI16];
420 } else {
421 // has a delta. add the delta to the last Read field id.
422 fieldId = lastFieldId + modifier;
423 }
424
425 int fieldType = [self ttypeForCompactType: type];
426
427 if (pname != NULL) {
428 *pname = @"";
429 }
430 if (pfieldType != NULL) {
431 *pfieldType = fieldType;
432 }
433 if (pfieldID != NULL) {
434 *pfieldID = fieldId;
435 }
436
437 // if this happens to be a boolean field, the value is encoded in the type
438 if (type == TCType_BOOLEAN_TRUE ||
439 type == TCType_BOOLEAN_FALSE) {
440 // save the boolean value in a special instance variable.
441 booleanValue = [[NSNumber numberWithBool: type == TCType_BOOLEAN_TRUE] retain_stub];
442 }
443
444 // push the new field onto the field stack so we can keep the deltas going.
445 lastFieldId = fieldId;
446}
447
448- (void) readMapBeginReturningKeyType: (int *) pkeyType
449 valueType: (int *) pvalueType
450 size: (int *) psize
451{
452 uint8_t keyAndValueType = 0;
453 int size = (int)[self readVarint32];
454 if (size != 0) {
455 keyAndValueType = [self readByte];
456 }
457
458 int keyType = [self ttypeForCompactType: keyAndValueType >> 4];
459 int valueType = [self ttypeForCompactType: keyAndValueType & 0xf];
460
461 if (pkeyType != NULL) {
462 *pkeyType = keyType;
463 }
464 if (pvalueType != NULL) {
465 *pvalueType = valueType;
466 }
467 if (psize != NULL) {
468 *psize = size;
469 }
470}
471
472- (void) readListBeginReturningElementType: (int *) pelementType
473 size: (int *) psize
474{
475 uint8_t size_and_type = [self readByte];
476 int size = (size_and_type >> 4) & 0x0f;
477 if (size == 15) {
478 size = (int)[self readVarint32];
479 }
480
481 int elementType = [self ttypeForCompactType: size_and_type & 0x0f];
482
483 if (pelementType != NULL) {
484 *pelementType = elementType;
485 }
486 if (psize != NULL) {
487 *psize = size;
488 }
489}
490
491- (void) readSetBeginReturningElementType: (int *) pelementType
492 size: (int *) psize
493{
494 [self readListBeginReturningElementType: pelementType size: psize];
495}
496
497- (BOOL) readBool
498{
499 if (booleanValue != nil) {
500 BOOL result = [booleanValue boolValue];
501 [booleanValue release_stub];
502 booleanValue = nil;
503 return result;
504 } else {
505 return [self readByte] == TCType_BOOLEAN_TRUE;
506 }
507}
508
509- (uint8_t) readByte
510{
511 uint8_t buf = 0;
512 [mTransport readAll: &buf offset: 0 length: 1];
513 return buf;
514}
515
516- (int16_t) readI16
517{
518 return (int16_t)[self zigZagToi32: [self readVarint32]];
519}
520
521- (int32_t) readI32
522{
523 return [self zigZagToi32: [self readVarint32]];
524}
525
526- (int64_t) readI64
527{
528 return [self zigZagToi64: [self readVarint64]];
529}
530
531- (double) readDouble
532{
533 uint64_t bits = 0;
534 [mTransport readAll: (uint8_t *)&bits offset: 0 length: 8];
535 bits = OSSwapLittleToHostInt64(bits);
536
537 double result = 0;
538 memcpy(&result, &bits, 8);
539
540 return result;
541}
542
543- (NSString *) readString
544{
545 int length = (int)[self readVarint32];
546 if (length == 0) {
547 return @"";
548 }
549
550 return [[[NSString alloc] initWithData: [self readBinary: length]
551 encoding: NSUTF8StringEncoding] autorelease_stub];
552}
553
554- (NSData *) readBinary
555{
556 return [self readBinary: (int)[self readVarint32]];
557}
558
559- (NSData *) readBinary: (int) length
560{
561 if (length == 0) {
562 return [NSData data];
563 }
564
565 NSMutableData* buf = [NSMutableData dataWithLength: length];
566 [mTransport readAll: buf.mutableBytes offset: 0 length: length];
567 return buf;
568}
569
570- (void) readMessageEnd {}
571- (void) readFieldEnd {}
572- (void) readMapEnd {}
573- (void) readListEnd {}
574- (void) readSetEnd {}
575
576- (uint32_t) readVarint32
577{
578 uint32_t result = 0;
579 int shift = 0;
580
581 while (true) {
582 uint8_t byte = [self readByte];
583 result |= (uint32_t)(byte & 0x7f) << shift;
584 if (!(byte & 0x80)) {
585 break;
586 }
587
588 shift += 7;
589 }
590 return result;
591}
592
593- (uint64_t) readVarint64
594{
595 int shift = 0;
596 uint64_t result = 0;
597
598 while (true) {
599 uint8_t byte = [self readByte];
600 result |= (uint64_t)(byte & 0x7f) << shift;
601 if (!(byte & 0x80)) {
602 break;
603 }
604
605 shift += 7;
606 }
607
608 return result;
609}
610
611- (int32_t) zigZagToi32: (uint32_t) n
612{
613 return (int32_t)(n >> 1) ^ (-(int32_t)(n & 1));
614}
615
616- (int64_t) zigZagToi64: (uint64_t) n
617{
618 return (int64_t)(n >> 1) ^ (-(int64_t)(n & 1));
619}
620
621- (uint8_t) ttypeForCompactType: (uint8_t) type
622{
623 switch (type & 0x0f) {
624 case TCType_STOP:
625 return TType_STOP;
626
627 case TCType_BOOLEAN_FALSE:
628 case TCType_BOOLEAN_TRUE:
629 return TType_BOOL;
630
631 case TCType_BYTE:
632 return TType_BYTE;
633
634 case TCType_I16:
635 return TType_I16;
636
637 case TCType_I32:
638 return TType_I32;
639
640 case TCType_I64:
641 return TType_I64;
642
643 case TCType_DOUBLE:
644 return TType_DOUBLE;
645
646 case TCType_BINARY:
647 return TType_STRING;
648
649 case TCType_LIST:
650 return TType_LIST;
651
652 case TCType_SET:
653 return TType_SET;
654
655 case TCType_MAP:
656 return TType_MAP;
657
658 case TCType_STRUCT:
659 return TType_STRUCT;
660
661 default:
662 @throw [TProtocolException exceptionWithName: @"TProtocolException"
663 reason: [NSString stringWithFormat: @"Don't know what type: %d", (uint8_t)(type & 0x0F)]];
664 }
665}
666
667- (uint8_t) compactTypeForTType: (uint8_t) ttype
668{
669 static uint8_t ttypeToCompactType[] = {
670 [TType_STOP] = TCType_STOP,
671 [TType_BOOL] = TCType_BOOLEAN_FALSE,
672 [TType_BYTE] = TCType_BYTE,
673 [TType_DOUBLE] = TCType_DOUBLE,
674 [TType_I16] = TCType_I16,
675 [TType_I32] = TCType_I32,
676 [TType_I64] = TCType_I64,
677 [TType_STRING] = TCType_BINARY,
678 [TType_STRUCT] = TCType_STRUCT,
679 [TType_MAP] = TCType_MAP,
680 [TType_SET] = TCType_SET,
681 [TType_LIST] = TCType_LIST
682 };
683
684 return ttypeToCompactType[ttype];
685}
686
687@end