blob: fc0fd0ca167073957df2d0a3b5787218ee76cfdf [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "flutter/shell/platform/darwin/common/framework/Source/FlutterStandardCodecHelper.h"
#import "flutter/shell/platform/darwin/common/framework/Source/FlutterStandardCodec_Internal.h"
FLUTTER_ASSERT_ARC
#pragma mark - Codec for basic message channel
@implementation FlutterStandardMessageCodec {
FlutterStandardReaderWriter* _readerWriter;
}
+ (instancetype)sharedInstance {
static id _sharedInstance = nil;
if (!_sharedInstance) {
FlutterStandardReaderWriter* readerWriter = [[FlutterStandardReaderWriter alloc] init];
_sharedInstance = [[FlutterStandardMessageCodec alloc] initWithReaderWriter:readerWriter];
}
return _sharedInstance;
}
+ (instancetype)codecWithReaderWriter:(FlutterStandardReaderWriter*)readerWriter {
return [[FlutterStandardMessageCodec alloc] initWithReaderWriter:readerWriter];
}
- (instancetype)initWithReaderWriter:(FlutterStandardReaderWriter*)readerWriter {
self = [super init];
NSAssert(self, @"Super init cannot be nil");
_readerWriter = readerWriter;
return self;
}
- (NSData*)encode:(id)message {
if (message == nil) {
return nil;
}
NSMutableData* data = [NSMutableData dataWithCapacity:32];
FlutterStandardWriter* writer = [_readerWriter writerWithData:data];
[writer writeValue:message];
return data;
}
- (id)decode:(NSData*)message {
if ([message length] == 0) {
return nil;
}
FlutterStandardReader* reader = [_readerWriter readerWithData:message];
id value = [reader readValue];
NSAssert(![reader hasMore], @"Corrupted standard message");
return value;
}
@end
#pragma mark - Codec for method channel
@implementation FlutterStandardMethodCodec {
FlutterStandardReaderWriter* _readerWriter;
}
+ (instancetype)sharedInstance {
static id _sharedInstance = nil;
if (!_sharedInstance) {
FlutterStandardReaderWriter* readerWriter = [[FlutterStandardReaderWriter alloc] init];
_sharedInstance = [[FlutterStandardMethodCodec alloc] initWithReaderWriter:readerWriter];
}
return _sharedInstance;
}
+ (instancetype)codecWithReaderWriter:(FlutterStandardReaderWriter*)readerWriter {
return [[FlutterStandardMethodCodec alloc] initWithReaderWriter:readerWriter];
}
- (instancetype)initWithReaderWriter:(FlutterStandardReaderWriter*)readerWriter {
self = [super init];
NSAssert(self, @"Super init cannot be nil");
_readerWriter = readerWriter;
return self;
}
- (NSData*)encodeMethodCall:(FlutterMethodCall*)call {
NSMutableData* data = [NSMutableData dataWithCapacity:32];
FlutterStandardWriter* writer = [_readerWriter writerWithData:data];
[writer writeValue:call.method];
[writer writeValue:call.arguments];
return data;
}
- (NSData*)encodeSuccessEnvelope:(id)result {
NSMutableData* data = [NSMutableData dataWithCapacity:32];
FlutterStandardWriter* writer = [_readerWriter writerWithData:data];
[writer writeByte:0];
[writer writeValue:result];
return data;
}
- (NSData*)encodeErrorEnvelope:(FlutterError*)error {
NSMutableData* data = [NSMutableData dataWithCapacity:32];
FlutterStandardWriter* writer = [_readerWriter writerWithData:data];
[writer writeByte:1];
[writer writeValue:error.code];
[writer writeValue:error.message];
[writer writeValue:error.details];
return data;
}
- (FlutterMethodCall*)decodeMethodCall:(NSData*)message {
FlutterStandardReader* reader = [_readerWriter readerWithData:message];
id value1 = [reader readValue];
id value2 = [reader readValue];
NSAssert(![reader hasMore], @"Corrupted standard method call");
NSAssert([value1 isKindOfClass:[NSString class]], @"Corrupted standard method call");
return [FlutterMethodCall methodCallWithMethodName:value1 arguments:value2];
}
- (id)decodeEnvelope:(NSData*)envelope {
FlutterStandardReader* reader = [_readerWriter readerWithData:envelope];
UInt8 flag = [reader readByte];
NSAssert(flag <= 1, @"Corrupted standard envelope");
id result;
switch (flag) {
case 0: {
result = [reader readValue];
NSAssert(![reader hasMore], @"Corrupted standard envelope");
} break;
case 1: {
id code = [reader readValue];
id message = [reader readValue];
id details = [reader readValue];
NSAssert(![reader hasMore], @"Corrupted standard envelope");
NSAssert([code isKindOfClass:[NSString class]], @"Invalid standard envelope");
NSAssert(message == nil || [message isKindOfClass:[NSString class]],
@"Invalid standard envelope");
result = [FlutterError errorWithCode:code message:message details:details];
} break;
}
return result;
}
@end
using namespace flutter;
#pragma mark - Standard serializable types
@implementation FlutterStandardTypedData
+ (instancetype)typedDataWithBytes:(NSData*)data {
return [FlutterStandardTypedData typedDataWithData:data type:FlutterStandardDataTypeUInt8];
}
+ (instancetype)typedDataWithInt32:(NSData*)data {
return [FlutterStandardTypedData typedDataWithData:data type:FlutterStandardDataTypeInt32];
}
+ (instancetype)typedDataWithInt64:(NSData*)data {
return [FlutterStandardTypedData typedDataWithData:data type:FlutterStandardDataTypeInt64];
}
+ (instancetype)typedDataWithFloat32:(NSData*)data {
return [FlutterStandardTypedData typedDataWithData:data type:FlutterStandardDataTypeFloat32];
}
+ (instancetype)typedDataWithFloat64:(NSData*)data {
return [FlutterStandardTypedData typedDataWithData:data type:FlutterStandardDataTypeFloat64];
}
+ (instancetype)typedDataWithData:(NSData*)data type:(FlutterStandardDataType)type {
return [[FlutterStandardTypedData alloc] initWithData:data type:type];
}
- (instancetype)initWithData:(NSData*)data type:(FlutterStandardDataType)type {
UInt8 elementSize = elementSizeForFlutterStandardDataType(type);
NSAssert(data, @"Data cannot be nil");
NSAssert(data.length % elementSize == 0, @"Data must contain integral number of elements");
self = [super init];
NSAssert(self, @"Super init cannot be nil");
_data = [data copy];
_type = type;
_elementSize = elementSize;
_elementCount = data.length / elementSize;
return self;
}
- (BOOL)isEqual:(id)object {
if (self == object) {
return YES;
}
if (![object isKindOfClass:[FlutterStandardTypedData class]]) {
return NO;
}
FlutterStandardTypedData* other = (FlutterStandardTypedData*)object;
return self.type == other.type && self.elementCount == other.elementCount &&
[self.data isEqual:other.data];
}
- (NSUInteger)hash {
return [self.data hash] ^ self.type;
}
@end
#pragma mark - Writer and reader of standard codec
@implementation FlutterStandardWriter {
NSMutableData* _data;
}
- (instancetype)initWithData:(NSMutableData*)data {
self = [super init];
NSAssert(self, @"Super init cannot be nil");
_data = data;
return self;
}
- (void)writeByte:(UInt8)value {
FlutterStandardCodecHelperWriteByte((__bridge CFMutableDataRef)_data, value);
}
- (void)writeBytes:(const void*)bytes length:(NSUInteger)length {
FlutterStandardCodecHelperWriteBytes((__bridge CFMutableDataRef)_data, bytes, length);
}
- (void)writeData:(NSData*)data {
FlutterStandardCodecHelperWriteData((__bridge CFMutableDataRef)_data, (__bridge CFDataRef)data);
}
- (void)writeSize:(UInt32)size {
FlutterStandardCodecHelperWriteSize((__bridge CFMutableDataRef)_data, size);
}
- (void)writeAlignment:(UInt8)alignment {
FlutterStandardCodecHelperWriteAlignment((__bridge CFMutableDataRef)_data, alignment);
}
- (void)writeUTF8:(NSString*)value {
FlutterStandardCodecHelperWriteUTF8((__bridge CFMutableDataRef)_data,
(__bridge CFStringRef)value);
}
static FlutterStandardCodecObjcType GetWriteType(id value) {
if (value == nil || (__bridge CFNullRef)value == kCFNull) {
return FlutterStandardCodecObjcTypeNil;
} else if ([value isKindOfClass:[NSNumber class]]) {
return FlutterStandardCodecObjcTypeNSNumber;
} else if ([value isKindOfClass:[NSString class]]) {
return FlutterStandardCodecObjcTypeNSString;
} else if ([value isKindOfClass:[FlutterStandardTypedData class]]) {
return FlutterStandardCodecObjcTypeFlutterStandardTypedData;
} else if ([value isKindOfClass:[NSData class]]) {
return FlutterStandardCodecObjcTypeNSData;
} else if ([value isKindOfClass:[NSArray class]]) {
return FlutterStandardCodecObjcTypeNSArray;
} else if ([value isKindOfClass:[NSDictionary class]]) {
return FlutterStandardCodecObjcTypeNSDictionary;
} else {
return FlutterStandardCodecObjcTypeUnknown;
}
}
struct WriteKeyValuesInfo {
CFTypeRef writer;
CFMutableDataRef data;
};
static void WriteKeyValues(CFTypeRef key, CFTypeRef value, void* context) {
WriteKeyValuesInfo* info = (WriteKeyValuesInfo*)context;
FastWriteValueOfType(info->writer, info->data, key);
FastWriteValueOfType(info->writer, info->data, value);
}
// Recurses into WriteValueOfType directly if it is writing a known type,
// otherwise recurses with objc_msgSend.
static void FastWriteValueOfType(CFTypeRef writer, CFMutableDataRef data, CFTypeRef value) {
FlutterStandardCodecObjcType type = GetWriteType((__bridge id)value);
if (type != FlutterStandardCodecObjcTypeUnknown) {
WriteValueOfType(writer, data, type, value);
} else {
[(__bridge FlutterStandardWriter*)writer writeValue:(__bridge id)value];
}
}
static void WriteValueOfType(CFTypeRef writer,
CFMutableDataRef data,
FlutterStandardCodecObjcType type,
CFTypeRef value) {
switch (type) {
case FlutterStandardCodecObjcTypeNil:
FlutterStandardCodecHelperWriteByte(data, FlutterStandardFieldNil);
break;
case FlutterStandardCodecObjcTypeNSNumber: {
CFNumberRef number = (CFNumberRef)value;
BOOL success = FlutterStandardCodecHelperWriteNumber(data, number);
if (!success) {
NSLog(@"Unsupported value: %@ of number type %ld", value, CFNumberGetType(number));
NSCAssert(NO, @"Unsupported value for standard codec");
}
break;
}
case FlutterStandardCodecObjcTypeNSString: {
CFStringRef string = (CFStringRef)value;
FlutterStandardCodecHelperWriteByte(data, FlutterStandardFieldString);
FlutterStandardCodecHelperWriteUTF8(data, string);
break;
}
case FlutterStandardCodecObjcTypeFlutterStandardTypedData: {
FlutterStandardTypedData* typedData = (__bridge FlutterStandardTypedData*)value;
FlutterStandardCodecHelperWriteByte(data, FlutterStandardFieldForDataType(typedData.type));
FlutterStandardCodecHelperWriteSize(data, typedData.elementCount);
FlutterStandardCodecHelperWriteAlignment(data, typedData.elementSize);
FlutterStandardCodecHelperWriteData(data, (__bridge CFDataRef)typedData.data);
break;
}
case FlutterStandardCodecObjcTypeNSData:
WriteValueOfType(writer, data, FlutterStandardCodecObjcTypeFlutterStandardTypedData,
(__bridge CFTypeRef)
[FlutterStandardTypedData typedDataWithBytes:(__bridge NSData*)value]);
break;
case FlutterStandardCodecObjcTypeNSArray: {
CFArrayRef array = (CFArrayRef)value;
FlutterStandardCodecHelperWriteByte(data, FlutterStandardFieldList);
CFIndex count = CFArrayGetCount(array);
FlutterStandardCodecHelperWriteSize(data, count);
for (CFIndex i = 0; i < count; ++i) {
FastWriteValueOfType(writer, data, CFArrayGetValueAtIndex(array, i));
}
break;
}
case FlutterStandardCodecObjcTypeNSDictionary: {
CFDictionaryRef dict = (CFDictionaryRef)value;
FlutterStandardCodecHelperWriteByte(data, FlutterStandardFieldMap);
CFIndex count = CFDictionaryGetCount(dict);
FlutterStandardCodecHelperWriteSize(data, count);
WriteKeyValuesInfo info = {
.writer = writer,
.data = data,
};
CFDictionaryApplyFunction(dict, WriteKeyValues, (void*)&info);
break;
}
case FlutterStandardCodecObjcTypeUnknown: {
id objc_value = (__bridge id)value;
NSLog(@"Unsupported value: %@ of type %@", objc_value, [objc_value class]);
NSCAssert(NO, @"Unsupported value for standard codec");
break;
}
}
}
- (void)writeValue:(id)value {
FlutterStandardCodecObjcType type = GetWriteType(value);
WriteValueOfType((__bridge CFTypeRef)self, (__bridge CFMutableDataRef)self->_data, type,
(__bridge CFTypeRef)value);
}
@end
@implementation FlutterStandardReader {
NSData* _data;
NSRange _range;
}
- (instancetype)initWithData:(NSData*)data {
self = [super init];
NSAssert(self, @"Super init cannot be nil");
_data = [data copy];
_range = NSMakeRange(0, 0);
return self;
}
- (BOOL)hasMore {
return _range.location < _data.length;
}
- (void)readBytes:(void*)destination length:(NSUInteger)length {
FlutterStandardCodecHelperReadBytes(&_range.location, length, destination,
(__bridge CFDataRef)_data);
}
- (UInt8)readByte {
return FlutterStandardCodecHelperReadByte(&_range.location, (__bridge CFDataRef)_data);
}
- (UInt32)readSize {
return FlutterStandardCodecHelperReadSize(&_range.location, (__bridge CFDataRef)_data);
}
- (NSData*)readData:(NSUInteger)length {
_range.length = length;
NSData* data = [_data subdataWithRange:_range];
_range.location += _range.length;
return data;
}
- (NSString*)readUTF8 {
return (__bridge NSString*)FlutterStandardCodecHelperReadUTF8(&_range.location,
(__bridge CFDataRef)_data);
}
- (void)readAlignment:(UInt8)alignment {
FlutterStandardCodecHelperReadAlignment(&_range.location, alignment);
}
- (nullable id)readValue {
return (__bridge id)ReadValue((__bridge CFTypeRef)self);
}
static CFTypeRef ReadValue(CFTypeRef user_data) {
FlutterStandardReader* reader = (__bridge FlutterStandardReader*)user_data;
uint8_t type = FlutterStandardCodecHelperReadByte(&reader->_range.location,
(__bridge CFDataRef)reader->_data);
return (__bridge CFTypeRef)[reader readValueOfType:type];
}
static CFTypeRef ReadTypedDataOfType(FlutterStandardField field, CFTypeRef user_data) {
FlutterStandardReader* reader = (__bridge FlutterStandardReader*)user_data;
unsigned long* location = &reader->_range.location;
CFDataRef data = (__bridge CFDataRef)reader->_data;
FlutterStandardDataType type = FlutterStandardDataTypeForField(field);
UInt64 elementCount = FlutterStandardCodecHelperReadSize(location, data);
UInt64 elementSize = elementSizeForFlutterStandardDataType(type);
FlutterStandardCodecHelperReadAlignment(location, elementSize);
UInt64 length = elementCount * elementSize;
NSRange range = NSMakeRange(*location, length);
// Note: subdataWithRange performs better than CFDataCreate and
// CFDataCreateBytesNoCopy crashes.
NSData* bytes = [(__bridge NSData*)data subdataWithRange:range];
*location += length;
return (__bridge CFTypeRef)[FlutterStandardTypedData typedDataWithData:bytes type:type];
}
- (nullable id)readValueOfType:(UInt8)type {
return (__bridge id)FlutterStandardCodecHelperReadValueOfType(
&_range.location, (__bridge CFDataRef)_data, type, ReadValue, ReadTypedDataOfType,
(__bridge CFTypeRef)self);
}
@end
@implementation FlutterStandardReaderWriter
- (FlutterStandardWriter*)writerWithData:(NSMutableData*)data {
return [[FlutterStandardWriter alloc] initWithData:data];
}
- (FlutterStandardReader*)readerWithData:(NSData*)data {
return [[FlutterStandardReader alloc] initWithData:data];
}
@end