blob: 1f9e57ac8be0d3c29635afe6dded46b06baa029f [file] [log] [blame]
/*
* 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 "TBinaryProtocol.h"
#import "TProtocolError.h"
static SInt32 VERSION_1 = 0x80010000;
static SInt32 VERSION_MASK = 0xffff0000;
static TBinaryProtocolFactory *gSharedFactory = nil;
@implementation TBinaryProtocolFactory
+(TBinaryProtocolFactory *) sharedFactory
{
if (gSharedFactory == nil) {
gSharedFactory = [[TBinaryProtocolFactory alloc] init];
}
return gSharedFactory;
}
-(NSString *) protocolName
{
return @"binary";
}
-(TBinaryProtocol *) newProtocolOnTransport:(id <TTransport>)transport
{
return [[TBinaryProtocol alloc] initWithTransport:transport];
}
@end
@interface TBinaryProtocol ()
@property(strong, nonatomic) id <TTransport> transport;
@property(assign, nonatomic) BOOL strictRead;
@property(assign, nonatomic) BOOL strictWrite;
@property(strong, nonatomic) NSString *currentMessageName;
@property(strong, nonatomic) NSString *currentFieldName;
@end
@implementation TBinaryProtocol
-(id) initWithTransport:(id <TTransport>)aTransport
{
return [self initWithTransport:aTransport strictRead:NO strictWrite:YES];
}
-(id) initWithTransport:(id <TTransport>)transport
strictRead:(BOOL)strictRead
strictWrite:(BOOL)strictWrite
{
self = [super init];
if (self) {
_transport = transport;
_strictRead = strictRead;
_strictWrite = strictWrite;
}
return self;
}
-(id <TTransport>) transport
{
return _transport;
}
-(NSString *) readStringBody:(int)size error:(NSError **)error
{
NSMutableData *data = [NSMutableData dataWithLength:size];
if (!data) {
PROTOCOL_ERROR(nil, Unknown, @"Unable to allocate %d bytes", size);
}
if (![_transport readAll:data.mutableBytes offset:0 length:size error:error]) {
PROTOCOL_TRANSPORT_ERROR(nil, error, @"Transport read failed");
}
return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
}
-(BOOL) readMessageBeginReturningName:(NSString **)name
type:(SInt32 *)type
sequenceID:(SInt32 *)sequenceID
error:(NSError *__autoreleasing *)error
{
SInt32 size;
if (![self readI32:&size error:error]) {
return NO;
}
;
if (size < 0) {
int version = size & VERSION_MASK;
if (version != VERSION_1) {
PROTOCOL_ERROR(NO, BadVersion, @"Bad message version");
}
if (type != NULL) {
*type = size & 0x00FF;
}
NSString *messageName;
if (![self readString:&messageName error:error]) {
return NO;
}
if (name != nil) {
*name = messageName;
}
}
else {
if (_strictRead) {
PROTOCOL_ERROR(NO, InvalidData, @"Missing message version, old client?");
}
if (_messageSizeLimit > 0 && size > _messageSizeLimit) {
PROTOCOL_ERROR(NO, SizeLimit, @"Message exceeeds size limit of %d", (int)size);
}
NSString *messageName = [self readStringBody:size error:error];
if (!messageName) {
return NO;
}
if (name != NULL) {
*name = messageName;
}
UInt8 messageType;
if (![self readByte:&messageType error:error]) {
return NO;
}
if (type != NULL) {
*type = messageType;
}
}
SInt32 seqID;
if (![self readI32:&seqID error:error]) {
return NO;
}
if (sequenceID != NULL) {
*sequenceID = seqID;
}
return YES;
}
-(BOOL) readMessageEnd:(NSError *__autoreleasing *)error
{
return YES;
}
-(BOOL) readStructBeginReturningName:(NSString *__autoreleasing *)name error:(NSError *__autoreleasing *)error
{
return YES;
}
-(BOOL) readStructEnd:(NSError *__autoreleasing *)error
{
return YES;
}
-(BOOL) readFieldBeginReturningName:(NSString *__autoreleasing *)name
type:(SInt32 *)fieldType
fieldID:(SInt32 *)fieldID
error:(NSError *__autoreleasing *)error
{
if (name != nil) {
*name = nil;
}
UInt8 ft;
if (![self readByte:&ft error:error]) {
return NO;
}
if (fieldType != NULL) {
*fieldType = ft;
}
if (ft != TTypeSTOP) {
SInt16 fid;
if (![self readI16:&fid error:error]) {
return NO;
}
if (fieldID != NULL) {
*fieldID = fid;
}
}
return YES;
}
-(BOOL) readFieldEnd:(NSError *__autoreleasing *)error
{
return YES;
}
-(BOOL) readString:(NSString *__autoreleasing *)value error:(NSError *__autoreleasing *)error
{
SInt32 size;
if (![self readI32:&size error:error]) {
return NO;
}
NSString *string = [self readStringBody:size error:error];
if (!string) {
return NO;
}
*value = string;
return YES;
}
-(BOOL) readBool:(BOOL *)value error:(NSError *__autoreleasing *)error
{
UInt8 byte;
if (![self readByte:&byte error:error]) {
return NO;
}
*value = byte == 1;
return YES;
}
-(BOOL) readByte:(UInt8 *)value error:(NSError *__autoreleasing *)error
{
UInt8 buff[1];
if (![_transport readAll:buff offset:0 length:1 error:error]) {
PROTOCOL_TRANSPORT_ERROR(NO, error, @"Transport read failed");
}
*value = buff[0];
return YES;
}
-(BOOL) readI16:(SInt16 *)value error:(NSError *__autoreleasing *)error
{
UInt8 buff[2];
if (![_transport readAll:buff offset:0 length:2 error:error]) {
PROTOCOL_TRANSPORT_ERROR(NO, error, @"Transport read failed");
}
*value =
((SInt16)(buff[0] & 0xff) << 8) |
((SInt16)(buff[1] & 0xff));
return YES;
}
-(BOOL) readI32:(SInt32 *)value error:(NSError *__autoreleasing *)error
{
UInt8 i32rd[4];
if (![_transport readAll:i32rd offset:0 length:4 error:error]) {
PROTOCOL_TRANSPORT_ERROR(NO, error, @"Transport read failed");
}
*value =
((i32rd[0] & 0xff) << 24) |
((i32rd[1] & 0xff) << 16) |
((i32rd[2] & 0xff) << 8) |
((i32rd[3] & 0xff));
return YES;
}
-(BOOL) readI64:(SInt64 *)value error:(NSError *__autoreleasing *)error
{
UInt8 buff[8];
if (![_transport readAll:buff offset:0 length:8 error:error]) {
PROTOCOL_TRANSPORT_ERROR(NO, error, @"Transport read failed");
}
*value =
((SInt64)(buff[0] & 0xff) << 56) |
((SInt64)(buff[1] & 0xff) << 48) |
((SInt64)(buff[2] & 0xff) << 40) |
((SInt64)(buff[3] & 0xff) << 32) |
((SInt64)(buff[4] & 0xff) << 24) |
((SInt64)(buff[5] & 0xff) << 16) |
((SInt64)(buff[6] & 0xff) << 8) |
((SInt64)(buff[7] & 0xff));
return YES;
}
-(BOOL) readDouble:(double *)value error:(NSError *__autoreleasing *)error
{
// FIXME - will this get us into trouble on PowerPC?
return [self readI64:(SInt64 *)value error:error];
}
-(BOOL) readBinary:(NSData *__autoreleasing *)value error:(NSError *__autoreleasing *)error
{
SInt32 size;
if (![self readI32:&size error:error]) {
return NO;
}
NSMutableData *data = [NSMutableData dataWithLength:size];
if (!data) {
PROTOCOL_ERROR(NO, Unknown, @"Unable to allocate %d bytes", (int)size);
}
if (![_transport readAll:data.mutableBytes offset:0 length:size error:error]) {
PROTOCOL_TRANSPORT_ERROR(NO, error, @"Transport read failed");
}
*value = data;
return YES;
}
-(BOOL) readMapBeginReturningKeyType:(SInt32 *)keyType
valueType:(SInt32 *)valueType
size:(SInt32 *)size
error:(NSError *__autoreleasing *)error
{
UInt8 kt;
if (![self readByte:&kt error:error]) {
return NO;
}
UInt8 vt;
if (![self readByte:&vt error:error]) {
return NO;
}
SInt32 s;
if (![self readI32:&s error:error]) {
return NO;
}
if (keyType != NULL) {
*keyType = kt;
}
if (valueType != NULL) {
*valueType = vt;
}
if (size != NULL) {
*size = s;
}
return YES;
}
-(BOOL) readMapEnd:(NSError *__autoreleasing *)error
{
return YES;
}
-(BOOL) readSetBeginReturningElementType:(SInt32 *)elementType
size:(SInt32 *)size
error:(NSError *__autoreleasing *)error
{
UInt8 et;
if (![self readByte:&et error:error]) {
return NO;
}
SInt32 s;
if (![self readI32:&s error:error]) {
return NO;
}
if (elementType != NULL) {
*elementType = et;
}
if (size != NULL) {
*size = s;
}
return YES;
}
-(BOOL) readSetEnd:(NSError *__autoreleasing *)error
{
return YES;
}
-(BOOL) readListBeginReturningElementType:(SInt32 *)elementType
size:(SInt32 *)size
error:(NSError *__autoreleasing *)error
{
UInt8 et;
if (![self readByte:&et error:error]) {
return NO;
}
SInt32 s;
if (![self readI32:&s error:error]) {
return NO;
}
if (elementType != NULL) {
*elementType = et;
}
if (size != NULL) {
*size = s;
}
return YES;
}
-(BOOL) readListEnd:(NSError *__autoreleasing *)error
{
return YES;
}
-(BOOL) writeMessageBeginWithName:(NSString *)name
type:(SInt32)messageType
sequenceID:(SInt32)sequenceID
error:(NSError *__autoreleasing *)error
{
if (_strictWrite) {
int version = VERSION_1 | messageType;
if (![self writeI32:version error:error]) {
return NO;
}
if (![self writeString:name error:error]) {
return NO;
}
if (![self writeI32:sequenceID error:error]) {
return NO;
}
}
else {
if (![self writeString:name error:error]) {
return NO;
}
if (![self writeByte:messageType error:error]) {
return NO;
}
if (![self writeI32:sequenceID error:error]) {
return NO;
}
}
_currentMessageName = name;
return YES;
}
-(BOOL) writeMessageEnd:(NSError *__autoreleasing *)error
{
_currentMessageName = nil;
return YES;
}
-(BOOL) writeStructBeginWithName:(NSString *)name
error:(NSError *__autoreleasing *)error
{
return YES;
}
-(BOOL) writeStructEnd:(NSError *__autoreleasing *)error
{
return YES;
}
-(BOOL) writeFieldBeginWithName:(NSString *)name
type:(SInt32)fieldType
fieldID:(SInt32)fieldID
error:(NSError *__autoreleasing *)error
{
if (![self writeByte:fieldType error:error]) {
return NO;
}
if (![self writeI16:fieldID error:error]) {
return NO;
}
return YES;
}
-(BOOL) writeBool:(BOOL)value error:(NSError *__autoreleasing *)error
{
return [self writeByte:(value ? 1 : 0) error:error];
}
-(BOOL) writeByte:(UInt8)value error:(NSError *__autoreleasing *)error
{
if (![_transport write:&value offset:0 length:1 error:error]) {
PROTOCOL_TRANSPORT_ERROR(NO, error, @"Transport write failed");
}
return YES;
}
-(BOOL) writeI16:(short)value error:(NSError *__autoreleasing *)error
{
UInt8 buff[2];
buff[0] = 0xff & (value >> 8);
buff[1] = 0xff & value;
if (![_transport write:buff offset:0 length:2 error:error]) {
PROTOCOL_TRANSPORT_ERROR(NO, error, @"Transport write failed");
}
return YES;
}
-(BOOL) writeI32:(SInt32)value error:(NSError *__autoreleasing *)error
{
UInt8 buff[4];
buff[0] = 0xFF & (value >> 24);
buff[1] = 0xFF & (value >> 16);
buff[2] = 0xFF & (value >> 8);
buff[3] = 0xFF & value;
if (![_transport write:buff offset:0 length:4 error:error]) {
PROTOCOL_TRANSPORT_ERROR(NO, error, @"Transport write failed");
}
return YES;
}
-(BOOL) writeI64:(SInt64)value error:(NSError *__autoreleasing *)error
{
UInt8 buff[8];
buff[0] = 0xFF & (value >> 56);
buff[1] = 0xFF & (value >> 48);
buff[2] = 0xFF & (value >> 40);
buff[3] = 0xFF & (value >> 32);
buff[4] = 0xFF & (value >> 24);
buff[5] = 0xFF & (value >> 16);
buff[6] = 0xFF & (value >> 8);
buff[7] = 0xFF & value;
if (![_transport write:buff offset:0 length:8 error:error]) {
PROTOCOL_TRANSPORT_ERROR(NO, error, @"Transport write failed");
}
return YES;
}
-(BOOL) writeDouble:(double)value error:(NSError *__autoreleasing *)error
{
// FIXME - will this get us in trouble on PowerPC?
if (![self writeI64:*(SInt64 *)&value error:error]) {
return NO;
}
return YES;
}
-(BOOL) writeString:(NSString *)value error:(NSError *__autoreleasing *)error
{
if (value != nil) {
const char *utf8Bytes = [value UTF8String];
SInt32 length = (SInt32)strlen(utf8Bytes);
if (![self writeI32:length error:error]) {
return NO;
}
if (![_transport write:(UInt8 *)utf8Bytes offset:0 length:(int)length error:error]) {
PROTOCOL_TRANSPORT_ERROR(NO, error, @"Transport write failed");
}
}
else {
// instead of crashing when we get null, let's write out a zero
// length string
if (![self writeI32:0 error:error]) {
return NO;
}
}
return YES;
}
-(BOOL) writeBinary:(NSData *)data error:(NSError *__autoreleasing *)error
{
if (![self writeI32:(SInt32)data.length error:error]) {
return NO;
}
if (![_transport write:data.bytes offset:0 length:(UInt32)data.length error:error]) {
PROTOCOL_TRANSPORT_ERROR(NO, error, @"Transport write failed");
}
return YES;
}
-(BOOL) writeFieldStop:(NSError *__autoreleasing *)error
{
if (![self writeByte:TTypeSTOP error:error]) {
return NO;
}
return YES;
}
-(BOOL) writeFieldEnd:(NSError *__autoreleasing *)error
{
return YES;
}
-(BOOL) writeMapBeginWithKeyType:(SInt32)keyType
valueType:(SInt32)valueType
size:(SInt32)size
error:(NSError *__autoreleasing *)error
{
if (![self writeByte:keyType error:error]) {
return NO;
}
if (![self writeByte:valueType error:error]) {
return NO;
}
if (![self writeI32:(int)size error:error]) {
return NO;
}
return YES;
}
-(BOOL) writeMapEnd:(NSError *__autoreleasing *)error
{
return YES;
}
-(BOOL) writeSetBeginWithElementType:(SInt32)elementType
size:(SInt32)size
error:(NSError *__autoreleasing *)error
{
if (![self writeByte:elementType error:error]) {
return NO;
}
if (![self writeI32:size error:error]) {
return NO;
}
return YES;
}
-(BOOL) writeSetEnd:(NSError *__autoreleasing *)error
{
return YES;
}
-(BOOL) writeListBeginWithElementType:(SInt32)elementType
size:(SInt32)size
error:(NSError *__autoreleasing *)error
{
if (![self writeByte:elementType error:error]) {
return NO;
}
if (![self writeI32:size error:error]) {
return NO;
}
return YES;
}
-(BOOL) writeListEnd:(NSError *__autoreleasing *)error
{
return YES;
}
@end