Repository: thrift Updated Branches: refs/heads/master fa0a125d2 -> dc799ca07
Compact Protocol in Cocoa Client: Cocoa Patch: creker <[email protected]> This closes #442 Project: http://git-wip-us.apache.org/repos/asf/thrift/repo Commit: http://git-wip-us.apache.org/repos/asf/thrift/commit/dc799ca0 Tree: http://git-wip-us.apache.org/repos/asf/thrift/tree/dc799ca0 Diff: http://git-wip-us.apache.org/repos/asf/thrift/diff/dc799ca0 Branch: refs/heads/master Commit: dc799ca078627a8e400cfcdbb965acf6abf86eef Parents: fa0a125 Author: Jens Geyer <[email protected]> Authored: Mon Apr 27 22:56:54 2015 +0200 Committer: Jens Geyer <[email protected]> Committed: Mon Apr 27 23:02:05 2015 +0200 ---------------------------------------------------------------------- lib/cocoa/src/protocol/TCompactProtocol.h | 36 ++ lib/cocoa/src/protocol/TCompactProtocol.m | 687 +++++++++++++++++++++++++ 2 files changed, 723 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/thrift/blob/dc799ca0/lib/cocoa/src/protocol/TCompactProtocol.h ---------------------------------------------------------------------- diff --git a/lib/cocoa/src/protocol/TCompactProtocol.h b/lib/cocoa/src/protocol/TCompactProtocol.h new file mode 100644 index 0000000..3c9195c --- /dev/null +++ b/lib/cocoa/src/protocol/TCompactProtocol.h @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#import "TProtocol.h" +#import "TTransport.h" +#import "TProtocolFactory.h" + +@interface TCompactProtocol : NSObject <TProtocol> + +- (id) initWithTransport: (id <TTransport>) transport; + +@end + +@interface TCompactProtocolFactory : NSObject <TProtocolFactory> + ++ (TCompactProtocolFactory *) sharedFactory; + +- (TCompactProtocol *) newProtocolOnTransport: (id <TTransport>) transport; + +@end http://git-wip-us.apache.org/repos/asf/thrift/blob/dc799ca0/lib/cocoa/src/protocol/TCompactProtocol.m ---------------------------------------------------------------------- diff --git a/lib/cocoa/src/protocol/TCompactProtocol.m b/lib/cocoa/src/protocol/TCompactProtocol.m new file mode 100644 index 0000000..45b0ef3 --- /dev/null +++ b/lib/cocoa/src/protocol/TCompactProtocol.m @@ -0,0 +1,687 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#import "TCompactProtocol.h" +#import "TObjective-C.h" +#import "TProtocolException.h" + +static const uint8_t COMPACT_PROTOCOL_ID = 0x82; +static const uint8_t COMPACT_VERSION = 1; +static const uint8_t COMPACT_VERSION_MASK = 0x1F; // 0001 1111 +static const uint8_t COMPACT_TYPE_MASK = 0xE0; // 1110 0000 +static const uint8_t COMPACT_TYPE_BITS = 0x07; // 0000 0111 +static const int COMPACT_TYPE_SHIFT_AMOUNT = 5; + +enum { + TCType_STOP = 0x00, + TCType_BOOLEAN_TRUE = 0x01, + TCType_BOOLEAN_FALSE = 0x02, + TCType_BYTE = 0x03, + TCType_I16 = 0x04, + TCType_I32 = 0x05, + TCType_I64 = 0x06, + TCType_DOUBLE = 0x07, + TCType_BINARY = 0x08, + TCType_LIST = 0x09, + TCType_SET = 0x0A, + TCType_MAP = 0x0B, + TCType_STRUCT = 0x0C, +}; + +@implementation TCompactProtocolFactory + ++ (TCompactProtocolFactory *) sharedFactory +{ + static TCompactProtocolFactory * gSharedFactory = nil; + if (gSharedFactory == nil) { + gSharedFactory = [[TCompactProtocolFactory alloc] init]; + } + + return gSharedFactory; +} + +- (TCompactProtocol *) newProtocolOnTransport: (id <TTransport>) transport +{ + return [[TCompactProtocol alloc] initWithTransport: transport]; +} + +@end + +@implementation TCompactProtocol { + NSMutableArray * lastField; + short lastFieldId; + id <TTransport> mTransport; + + NSString * boolFieldName; + NSNumber * boolFieldType; + NSNumber * boolFieldId; + NSNumber * booleanValue; +} + +- (id) init +{ + self = [super init]; + + if (self != nil) { + lastField = [[NSMutableArray alloc] init]; + } + + return self; +} + +- (id) initWithTransport: (id <TTransport>) transport +{ + self = [self init]; + + if (self != nil) { + mTransport = [transport retain_stub]; + } + + return self; +} + +- (void) dealloc +{ + [lastField release_stub]; + [mTransport release_stub]; + [boolFieldName release_stub]; + [boolFieldType release_stub]; + [boolFieldId release_stub]; + [booleanValue release_stub]; + + [super dealloc_stub]; +} + +- (id <TTransport>) transport +{ + return mTransport; +} + +- (void) writeByteDirect: (int8_t) n +{ + [mTransport write: (uint8_t *)&n offset: 0 length: 1]; +} + +- (void)writeVarint32: (uint32_t) n +{ + uint8_t i32buf[5] = {0}; + uint32_t idx = 0; + + while (true) { + if ((n & ~0x7F) == 0) { + i32buf[idx++] = (uint8_t)n; + break; + } else { + i32buf[idx++] = (uint8_t)((n & 0x7F) | 0x80); + n >>= 7; + } + } + + [mTransport write: i32buf offset: 0 length: idx]; +} + +- (void) writeMessageBeginWithName: (NSString *) name + type: (int) messageType + sequenceID: (int) sequenceID +{ + [self writeByteDirect: COMPACT_PROTOCOL_ID]; + [self writeByteDirect: (uint8_t)((COMPACT_VERSION & COMPACT_VERSION_MASK) | + ((((uint32_t)messageType) << COMPACT_TYPE_SHIFT_AMOUNT) & COMPACT_TYPE_MASK))]; + [self writeVarint32: (uint32_t)sequenceID]; + [self writeString: name]; +} + +- (void) writeStructBeginWithName: (NSString *) name +{ + [lastField addObject: [NSNumber numberWithShort: lastFieldId]]; + lastFieldId = 0; +} + +- (void) writeStructEnd +{ + lastFieldId = [[lastField lastObject] shortValue]; + [lastField removeLastObject]; +} + +- (void) writeFieldBeginWithName: (NSString *) name + type: (int) fieldType + fieldID: (int) fieldID +{ + if (fieldType == TType_BOOL) { + boolFieldName = [name copy]; + boolFieldType = [[NSNumber numberWithInt: fieldType] retain_stub]; + boolFieldId = [[NSNumber numberWithInt: fieldID] retain_stub]; + } else { + [self writeFieldBeginInternalWithName: name + type: fieldType + fieldID: fieldID + typeOverride: 0xFF]; + } +} + +- (void) writeFieldBeginInternalWithName: (NSString *) name + type: (int) fieldType + fieldID: (int) fieldID + typeOverride: (uint8_t) typeOverride +{ + uint8_t typeToWrite = typeOverride == 0xFF ? [self compactTypeForTType: fieldType] : typeOverride; + + // check if we can use delta encoding for the field id + if (fieldID > lastFieldId && fieldID - lastFieldId <= 15) { + // Write them together + [self writeByteDirect: (fieldID - lastFieldId) << 4 | typeToWrite]; + } else { + // Write them separate + [self writeByteDirect: typeToWrite]; + [self writeI16: fieldID]; + } + + lastFieldId = fieldID; +} + +- (void) writeFieldStop +{ + [self writeByteDirect: TCType_STOP]; +} + +- (void) writeMapBeginWithKeyType: (int) keyType + valueType: (int) valueType + size: (int) size +{ + if (size == 0) { + [self writeByteDirect: 0]; + } else { + [self writeVarint32: (uint32_t)size]; + [self writeByteDirect: [self compactTypeForTType: keyType] << 4 | [self compactTypeForTType: valueType]]; + } +} + +- (void) writeListBeginWithElementType: (int) elementType + size: (int) size +{ + [self writeCollectionBeginWithElementType: elementType size: size]; +} + +- (void) writeSetBeginWithElementType: (int) elementType + size: (int) size +{ + [self writeCollectionBeginWithElementType: elementType size: size]; +} + +- (void) writeBool: (BOOL) b +{ + if (boolFieldId != nil && boolFieldName != nil && boolFieldType != nil) { + // we haven't written the field header yet + [self writeFieldBeginInternalWithName: boolFieldName + type: [boolFieldType intValue] + fieldID: [boolFieldId intValue] + typeOverride: b ? TCType_BOOLEAN_TRUE : TCType_BOOLEAN_FALSE]; + + [boolFieldId release_stub]; + [boolFieldName release_stub]; + [boolFieldType release_stub]; + + boolFieldId = nil; + boolFieldName = nil; + boolFieldType = nil; + } else { + // we're not part of a field, so just Write the value. + [self writeByteDirect: b ? TCType_BOOLEAN_TRUE : TCType_BOOLEAN_FALSE]; + } +} + +- (void) writeByte: (uint8_t) value +{ + [self writeByteDirect: value]; +} + +- (void) writeI16: (int16_t) value +{ + [self writeVarint32: [self i32ToZigZag: value]]; +} + +- (void) writeI32: (int32_t) value +{ + [self writeVarint32: [self i32ToZigZag: value]]; +} + +- (void) writeI64: (int64_t) value +{ + [self writeVarint64: [self i64ToZigZag: value]]; +} + +- (void) writeDouble: (double) value +{ + //Safe bit-casting double->uint64 + + uint64_t bits = 0; + memcpy(&bits, &value, 8); + + bits = OSSwapHostToLittleInt64(bits); + + [mTransport write: (uint8_t *)&bits offset: 0 length: 8]; +} + +- (void) writeString: (NSString *) value +{ + [self writeBinary: [value dataUsingEncoding: NSUTF8StringEncoding]]; +} + +- (void) writeBinary: (NSData *) data +{ + [self writeVarint32: (uint32_t)data.length]; + [mTransport write: data.bytes offset: 0 length: data.length]; +} + +- (void) writeMessageEnd {} +- (void) writeMapEnd {} +- (void) writeListEnd {} +- (void) writeSetEnd {} +- (void) writeFieldEnd {} + +- (void) writeCollectionBeginWithElementType: (int) elementType + size: (int) size +{ + if (size <= 14) { + [self writeByteDirect: size << 4 | [self compactTypeForTType: elementType]]; + } else { + [self writeByteDirect: 0xf0 | [self compactTypeForTType: elementType]]; + [self writeVarint32: (uint32_t)size]; + } +} + +- (void) writeVarint64: (uint64_t) n +{ + uint8_t varint64out[10] = {0}; + int idx = 0; + + while (true) { + if ((n & ~0x7FL) == 0) { + varint64out[idx++] = (uint8_t)n; + break; + } else { + varint64out[idx++] = (uint8_t)((n & 0x7F) | 0x80); + n >>= 7; + } + } + + [mTransport write: varint64out offset: 0 length: idx]; +} + +- (uint32_t) i32ToZigZag: (int32_t) n +{ + /* + ZigZag encoding maps signed integers to unsigned integers so that + numbers with a small absolute value (for instance, -1) have + a small varint encoded value too. It does this in a way that + "zig-zags" back and forth through the positive and negative integers, + so that -1 is encoded as 1, 1 is encoded as 2, -2 is encoded as 3, and so on + */ + return (uint32_t)(n << 1) ^ (uint32_t)(n >> 31); +} + +- (uint64_t) i64ToZigZag: (int64_t) n +{ + return (uint64_t)(n << 1) ^ (uint64_t)(n >> 63); +} + +- (void) readMessageBeginReturningName: (NSString **) pname + type: (int *) ptype + sequenceID: (int *) psequenceID +{ + uint8_t protocolId = [self readByte]; + if (protocolId != COMPACT_PROTOCOL_ID) { + @throw [TProtocolException exceptionWithName: @"TProtocolException" + reason: [NSString stringWithFormat: @"Expected protocol id %X but got %X", COMPACT_PROTOCOL_ID, protocolId]]; + } + + uint8_t versionAndType = [self readByte]; + uint8_t version = versionAndType & COMPACT_VERSION_MASK; + if (version != COMPACT_VERSION) { + @throw [TProtocolException exceptionWithName: @"TProtocolException" + reason: [NSString stringWithFormat: @"Expected version %d but got %d", COMPACT_VERSION, version]]; + } + + int type = (versionAndType >> COMPACT_TYPE_SHIFT_AMOUNT) & COMPACT_TYPE_BITS; + int sequenceID = (int)[self readVarint32]; + NSString* name = [self readString]; + + if (ptype != NULL) { + *ptype = type; + } + if (psequenceID != NULL) { + *psequenceID = sequenceID; + } + if (pname != NULL) { + *pname = name; + } +} + +- (void) readStructBeginReturningName: (NSString **) pname +{ + [lastField addObject: [NSNumber numberWithShort: lastFieldId]]; + lastFieldId = 0; + + if (pname != NULL) { + *pname = @""; + } +} + +- (void) readStructEnd +{ + lastFieldId = [[lastField lastObject] shortValue]; + [lastField removeLastObject]; +} + +- (void) readFieldBeginReturningName: (NSString **) pname + type: (int *) pfieldType + fieldID: (int *) pfieldID +{ + uint8_t byte = [self readByte]; + uint8_t type = byte & 0x0f; + + // if it's a stop, then we can return immediately, as the struct is over. + if (type == TCType_STOP) { + if (pname != NULL) { + *pname = @""; + } + if (pfieldType != NULL) { + *pfieldType = TType_STOP; + } + if (pfieldID != NULL) { + *pfieldID = 0; + } + return; + } + + short fieldId = 0; + + // mask off the 4 MSB of the type header. it could contain a field id delta. + short modifier = (byte & 0xf0) >> 4; + if (modifier == 0) { + // not a delta. look ahead for the zigzag varint field id. + fieldId = [self readI16]; + } else { + // has a delta. add the delta to the last Read field id. + fieldId = lastFieldId + modifier; + } + + int fieldType = [self ttypeForCompactType: type]; + + if (pname != NULL) { + *pname = @""; + } + if (pfieldType != NULL) { + *pfieldType = fieldType; + } + if (pfieldID != NULL) { + *pfieldID = fieldId; + } + + // if this happens to be a boolean field, the value is encoded in the type + if (type == TCType_BOOLEAN_TRUE || + type == TCType_BOOLEAN_FALSE) { + // save the boolean value in a special instance variable. + booleanValue = [[NSNumber numberWithBool: type == TCType_BOOLEAN_TRUE] retain_stub]; + } + + // push the new field onto the field stack so we can keep the deltas going. + lastFieldId = fieldId; +} + +- (void) readMapBeginReturningKeyType: (int *) pkeyType + valueType: (int *) pvalueType + size: (int *) psize +{ + uint8_t keyAndValueType = 0; + int size = (int)[self readVarint32]; + if (size != 0) { + keyAndValueType = [self readByte]; + } + + int keyType = [self ttypeForCompactType: keyAndValueType >> 4]; + int valueType = [self ttypeForCompactType: keyAndValueType & 0xf]; + + if (pkeyType != NULL) { + *pkeyType = keyType; + } + if (pvalueType != NULL) { + *pvalueType = valueType; + } + if (psize != NULL) { + *psize = size; + } +} + +- (void) readListBeginReturningElementType: (int *) pelementType + size: (int *) psize +{ + uint8_t size_and_type = [self readByte]; + int size = (size_and_type >> 4) & 0x0f; + if (size == 15) { + size = (int)[self readVarint32]; + } + + int elementType = [self ttypeForCompactType: size_and_type & 0x0f]; + + if (pelementType != NULL) { + *pelementType = elementType; + } + if (psize != NULL) { + *psize = size; + } +} + +- (void) readSetBeginReturningElementType: (int *) pelementType + size: (int *) psize +{ + [self readListBeginReturningElementType: pelementType size: psize]; +} + +- (BOOL) readBool +{ + if (booleanValue != nil) { + BOOL result = [booleanValue boolValue]; + [booleanValue release_stub]; + booleanValue = nil; + return result; + } else { + return [self readByte] == TCType_BOOLEAN_TRUE; + } +} + +- (uint8_t) readByte +{ + uint8_t buf = 0; + [mTransport readAll: &buf offset: 0 length: 1]; + return buf; +} + +- (int16_t) readI16 +{ + return (int16_t)[self zigZagToi32: [self readVarint32]]; +} + +- (int32_t) readI32 +{ + return [self zigZagToi32: [self readVarint32]]; +} + +- (int64_t) readI64 +{ + return [self zigZagToi64: [self readVarint64]]; +} + +- (double) readDouble +{ + uint64_t bits = 0; + [mTransport readAll: (uint8_t *)&bits offset: 0 length: 8]; + bits = OSSwapLittleToHostInt64(bits); + + double result = 0; + memcpy(&result, &bits, 8); + + return result; +} + +- (NSString *) readString +{ + int length = (int)[self readVarint32]; + if (length == 0) { + return @""; + } + + return [[[NSString alloc] initWithData: [self readBinary: length] + encoding: NSUTF8StringEncoding] autorelease_stub]; +} + +- (NSData *) readBinary +{ + return [self readBinary: (int)[self readVarint32]]; +} + +- (NSData *) readBinary: (int) length +{ + if (length == 0) { + return [NSData data]; + } + + NSMutableData* buf = [NSMutableData dataWithLength: length]; + [mTransport readAll: buf.mutableBytes offset: 0 length: length]; + return buf; +} + +- (void) readMessageEnd {} +- (void) readFieldEnd {} +- (void) readMapEnd {} +- (void) readListEnd {} +- (void) readSetEnd {} + +- (uint32_t) readVarint32 +{ + uint32_t result = 0; + int shift = 0; + + while (true) { + uint8_t byte = [self readByte]; + result |= (uint32_t)(byte & 0x7f) << shift; + if (!(byte & 0x80)) { + break; + } + + shift += 7; + } + return result; +} + +- (uint64_t) readVarint64 +{ + int shift = 0; + uint64_t result = 0; + + while (true) { + uint8_t byte = [self readByte]; + result |= (uint64_t)(byte & 0x7f) << shift; + if (!(byte & 0x80)) { + break; + } + + shift += 7; + } + + return result; +} + +- (int32_t) zigZagToi32: (uint32_t) n +{ + return (int32_t)(n >> 1) ^ (-(int32_t)(n & 1)); +} + +- (int64_t) zigZagToi64: (uint64_t) n +{ + return (int64_t)(n >> 1) ^ (-(int64_t)(n & 1)); +} + +- (uint8_t) ttypeForCompactType: (uint8_t) type +{ + switch (type & 0x0f) { + case TCType_STOP: + return TType_STOP; + + case TCType_BOOLEAN_FALSE: + case TCType_BOOLEAN_TRUE: + return TType_BOOL; + + case TCType_BYTE: + return TType_BYTE; + + case TCType_I16: + return TType_I16; + + case TCType_I32: + return TType_I32; + + case TCType_I64: + return TType_I64; + + case TCType_DOUBLE: + return TType_DOUBLE; + + case TCType_BINARY: + return TType_STRING; + + case TCType_LIST: + return TType_LIST; + + case TCType_SET: + return TType_SET; + + case TCType_MAP: + return TType_MAP; + + case TCType_STRUCT: + return TType_STRUCT; + + default: + @throw [TProtocolException exceptionWithName: @"TProtocolException" + reason: [NSString stringWithFormat: @"Don't know what type: %d", (uint8_t)(type & 0x0F)]]; + } +} + +- (uint8_t) compactTypeForTType: (uint8_t) ttype +{ + static uint8_t ttypeToCompactType[] = { + [TType_STOP] = TCType_STOP, + [TType_BOOL] = TCType_BOOLEAN_FALSE, + [TType_BYTE] = TCType_BYTE, + [TType_DOUBLE] = TCType_DOUBLE, + [TType_I16] = TCType_I16, + [TType_I32] = TCType_I32, + [TType_I64] = TCType_I64, + [TType_STRING] = TCType_BINARY, + [TType_STRUCT] = TCType_STRUCT, + [TType_MAP] = TCType_MAP, + [TType_SET] = TCType_SET, + [TType_LIST] = TCType_LIST + }; + + return ttypeToCompactType[ttype]; +} + +@end
