From 678f4e7d40c19277555908cd1c908a52f1c1e75a Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Thu, 6 May 2021 14:46:01 -0700 Subject: [PATCH 1/4] Sped up the StandardMessageCodec for iOS, tiny tweaks about 5% gain. --- .../framework/Source/FlutterStandardCodec.mm | 148 +++++++++++------- 1 file changed, 92 insertions(+), 56 deletions(-) diff --git a/shell/platform/darwin/common/framework/Source/FlutterStandardCodec.mm b/shell/platform/darwin/common/framework/Source/FlutterStandardCodec.mm index 9019f5bff953a..c4f173f11d069 100644 --- a/shell/platform/darwin/common/framework/Source/FlutterStandardCodec.mm +++ b/shell/platform/darwin/common/framework/Source/FlutterStandardCodec.mm @@ -6,6 +6,15 @@ #pragma mark - Codec for basic message channel +static const UInt8 s_zeroBuffer[8] = {0, 0, 0, 0, 0, 0, 0, 0}; +static const Class s_nsNumber = [NSNumber class]; +static const id s_nsNull = [NSNull null]; +static const Class s_nsString = [NSString class]; +static const Class s_nsData = [NSData class]; +static const Class s_nsArray = [NSArray class]; +static const Class s_nsDictionary = [NSDictionary class]; +static const Class s_flutterStandardTypedData = [FlutterStandardTypedData class]; + @implementation FlutterStandardMessageCodec { FlutterStandardReaderWriter* _readerWriter; } @@ -221,115 +230,142 @@ - (void)dealloc { [super dealloc]; } -- (void)writeByte:(UInt8)value { - [_data appendBytes:&value length:1]; +static void WriteByte(CFMutableDataRef data, UInt8 value) { + CFDataAppendBytes(data, &value, 1); } -- (void)writeBytes:(const void*)bytes length:(NSUInteger)length { - [_data appendBytes:bytes length:length]; +static void WriteBytes(CFMutableDataRef data, const void* bytes, NSUInteger length) { + CFDataAppendBytes(data, (const UInt8*)bytes, length); } -- (void)writeData:(NSData*)data { - [_data appendData:data]; +static void WriteData(CFMutableDataRef destination, NSData* source) { + CFDataAppendBytes(destination, (const UInt8*)source.bytes, source.length); } -- (void)writeSize:(UInt32)size { +static void WriteSize(CFMutableDataRef data, UInt32 size) { if (size < 254) { - [self writeByte:(UInt8)size]; + WriteByte(data, (UInt8)size); } else if (size <= 0xffff) { - [self writeByte:254]; + WriteByte(data, 254); UInt16 value = (UInt16)size; - [self writeBytes:&value length:2]; + WriteBytes(data, &value, 2); } else { - [self writeByte:255]; - [self writeBytes:&size length:4]; + WriteByte(data, 255); + WriteBytes(data, &size, 4); } } -- (void)writeAlignment:(UInt8)alignment { - UInt8 mod = _data.length % alignment; +static void WriteAlignment(CFMutableDataRef data, UInt8 alignment) { + assert(alignment <= 8); + UInt8 mod = CFDataGetLength(data) % alignment; if (mod) { - for (int i = 0; i < (alignment - mod); i++) { - [self writeByte:0]; - } + WriteBytes(data, s_zeroBuffer, alignment - mod); } } -- (void)writeUTF8:(NSString*)value { +static void WriteUTF8(CFMutableDataRef data, NSString* value) { UInt32 length = [value lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; - [self writeSize:length]; - [self writeBytes:value.UTF8String length:length]; + WriteSize(data, length); + WriteBytes(data, value.UTF8String, length); } -- (void)writeValue:(id)value { - if (value == nil || value == [NSNull null]) { - [self writeByte:FlutterStandardFieldNil]; - } else if ([value isKindOfClass:[NSNumber class]]) { +static void WriteValue(CFMutableDataRef data, id value) { + if (value == nil || value == s_nsNull) { + WriteByte(data, FlutterStandardFieldNil); + } else if ([value isKindOfClass:s_nsNumber]) { CFNumberRef number = (CFNumberRef)value; BOOL success = NO; if (CFGetTypeID(number) == CFBooleanGetTypeID()) { BOOL b = CFBooleanGetValue((CFBooleanRef)number); - [self writeByte:(b ? FlutterStandardFieldTrue : FlutterStandardFieldFalse)]; + WriteByte(data, (b ? FlutterStandardFieldTrue : FlutterStandardFieldFalse)); success = YES; } else if (CFNumberIsFloatType(number)) { Float64 f; success = CFNumberGetValue(number, kCFNumberFloat64Type, &f); if (success) { - [self writeByte:FlutterStandardFieldFloat64]; - [self writeAlignment:8]; - [self writeBytes:(UInt8*)&f length:8]; + WriteByte(data, FlutterStandardFieldFloat64); + WriteAlignment(data, 8); + WriteBytes(data, (UInt8*)&f, 8); } } else if (CFNumberGetByteSize(number) <= 4) { SInt32 n; success = CFNumberGetValue(number, kCFNumberSInt32Type, &n); if (success) { - [self writeByte:FlutterStandardFieldInt32]; - [self writeBytes:(UInt8*)&n length:4]; + WriteByte(data, FlutterStandardFieldInt32); + WriteBytes(data, (UInt8*)&n, 4); } } else if (CFNumberGetByteSize(number) <= 8) { SInt64 n; success = CFNumberGetValue(number, kCFNumberSInt64Type, &n); if (success) { - [self writeByte:FlutterStandardFieldInt64]; - [self writeBytes:(UInt8*)&n length:8]; + WriteByte(data, FlutterStandardFieldInt64); + WriteBytes(data, (UInt8*)&n, 8); } } if (!success) { NSLog(@"Unsupported value: %@ of number type %ld", value, CFNumberGetType(number)); - NSAssert(NO, @"Unsupported value for standard codec"); + assert(NO); // Unsupported value for standard codec. } - } else if ([value isKindOfClass:[NSString class]]) { + } else if ([value isKindOfClass:s_nsString]) { NSString* string = value; - [self writeByte:FlutterStandardFieldString]; - [self writeUTF8:string]; - } else if ([value isKindOfClass:[FlutterStandardTypedData class]]) { + WriteByte(data, FlutterStandardFieldString); + WriteUTF8(data, string); + } else if ([value isKindOfClass:s_flutterStandardTypedData]) { FlutterStandardTypedData* typedData = value; - [self writeByte:FlutterStandardFieldForDataType(typedData.type)]; - [self writeSize:typedData.elementCount]; - [self writeAlignment:typedData.elementSize]; - [self writeData:typedData.data]; - } else if ([value isKindOfClass:[NSData class]]) { - [self writeValue:[FlutterStandardTypedData typedDataWithBytes:value]]; - } else if ([value isKindOfClass:[NSArray class]]) { + WriteByte(data, FlutterStandardFieldForDataType(typedData.type)); + WriteSize(data, typedData.elementCount); + WriteAlignment(data, typedData.elementSize); + WriteData(data, typedData.data); + } else if ([value isKindOfClass:s_nsData]) { + WriteValue(data, [FlutterStandardTypedData typedDataWithBytes:value]); + } else if ([value isKindOfClass:s_nsArray]) { NSArray* array = value; - [self writeByte:FlutterStandardFieldList]; - [self writeSize:array.count]; + WriteByte(data, FlutterStandardFieldList); + WriteSize(data, array.count); for (id object in array) { - [self writeValue:object]; + WriteValue(data, object); } - } else if ([value isKindOfClass:[NSDictionary class]]) { + } else if ([value isKindOfClass:s_nsDictionary]) { NSDictionary* dict = value; - [self writeByte:FlutterStandardFieldMap]; - [self writeSize:dict.count]; + WriteByte(data, FlutterStandardFieldMap); + WriteSize(data, dict.count); for (id key in dict) { - [self writeValue:key]; - [self writeValue:[dict objectForKey:key]]; + WriteValue(data, key); + WriteValue(data, [dict objectForKey:key]); } } else { NSLog(@"Unsupported value: %@ of type %@", value, [value class]); - NSAssert(NO, @"Unsupported value for standard codec"); + assert(NO); // Unsupported value for standard codec. } } + +- (void)writeByte:(UInt8)value { + WriteByte((CFMutableDataRef)_data, value); +} + +- (void)writeBytes:(const void*)bytes length:(NSUInteger)length { + WriteBytes((CFMutableDataRef)_data, bytes, length); +} + +- (void)writeData:(NSData*)data { + WriteData((CFMutableDataRef)_data, data); +} + +- (void)writeSize:(UInt32)size { + WriteSize((CFMutableDataRef)_data, size); +} + +- (void)writeAlignment:(UInt8)alignment { + WriteAlignment((CFMutableDataRef)_data, alignment); +} + +- (void)writeUTF8:(NSString*)value { + WriteUTF8((CFMutableDataRef)_data, value); +} + +- (void)writeValue:(id)value { + WriteValue((CFMutableDataRef)_data, value); +} @end @implementation FlutterStandardReader { @@ -450,7 +486,7 @@ - (nullable id)readValueOfType:(UInt8)type { NSMutableArray* array = [NSMutableArray arrayWithCapacity:length]; for (UInt32 i = 0; i < length; i++) { id value = [self readValue]; - [array addObject:(value == nil ? [NSNull null] : value)]; + [array addObject:(value == nil ? s_nsNull : value)]; } return array; } @@ -460,8 +496,8 @@ - (nullable id)readValueOfType:(UInt8)type { for (UInt32 i = 0; i < size; i++) { id key = [self readValue]; id val = [self readValue]; - [dict setObject:(val == nil ? [NSNull null] : val) - forKey:(key == nil ? [NSNull null] : key)]; + [dict setObject:(val == nil ? s_nsNull : val) + forKey:(key == nil ? s_nsNull : key)]; } return dict; } From 763f5630f7a6076a3e3c7ca78f9b4c4919477d9e Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Fri, 7 May 2021 13:32:00 -0700 Subject: [PATCH 2/4] added __bridge and renamed constants --- .../framework/Source/FlutterStandardCodec.mm | 51 +++++++++---------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/shell/platform/darwin/common/framework/Source/FlutterStandardCodec.mm b/shell/platform/darwin/common/framework/Source/FlutterStandardCodec.mm index c4f173f11d069..d70cebcecac0a 100644 --- a/shell/platform/darwin/common/framework/Source/FlutterStandardCodec.mm +++ b/shell/platform/darwin/common/framework/Source/FlutterStandardCodec.mm @@ -6,14 +6,14 @@ #pragma mark - Codec for basic message channel -static const UInt8 s_zeroBuffer[8] = {0, 0, 0, 0, 0, 0, 0, 0}; -static const Class s_nsNumber = [NSNumber class]; -static const id s_nsNull = [NSNull null]; -static const Class s_nsString = [NSString class]; -static const Class s_nsData = [NSData class]; -static const Class s_nsArray = [NSArray class]; -static const Class s_nsDictionary = [NSDictionary class]; -static const Class s_flutterStandardTypedData = [FlutterStandardTypedData class]; +static const UInt8 kZeroBuffer[8] = {0, 0, 0, 0, 0, 0, 0, 0}; +static const Class kNSNumberClass = [NSNumber class]; +static const id kNSNull = [NSNull null]; +static const Class kNSStringClass = [NSString class]; +static const Class kNSDataClass = [NSData class]; +static const Class kNSArrayClass = [NSArray class]; +static const Class kNSDictionaryClass = [NSDictionary class]; +static const Class kFlutterStandardTypedDataClass = [FlutterStandardTypedData class]; @implementation FlutterStandardMessageCodec { FlutterStandardReaderWriter* _readerWriter; @@ -259,7 +259,7 @@ static void WriteAlignment(CFMutableDataRef data, UInt8 alignment) { assert(alignment <= 8); UInt8 mod = CFDataGetLength(data) % alignment; if (mod) { - WriteBytes(data, s_zeroBuffer, alignment - mod); + WriteBytes(data, kZeroBuffer, alignment - mod); } } @@ -270,9 +270,9 @@ static void WriteUTF8(CFMutableDataRef data, NSString* value) { } static void WriteValue(CFMutableDataRef data, id value) { - if (value == nil || value == s_nsNull) { + if (value == nil || value == kNSNull) { WriteByte(data, FlutterStandardFieldNil); - } else if ([value isKindOfClass:s_nsNumber]) { + } else if ([value isKindOfClass:kNSNumberClass]) { CFNumberRef number = (CFNumberRef)value; BOOL success = NO; if (CFGetTypeID(number) == CFBooleanGetTypeID()) { @@ -306,26 +306,26 @@ static void WriteValue(CFMutableDataRef data, id value) { NSLog(@"Unsupported value: %@ of number type %ld", value, CFNumberGetType(number)); assert(NO); // Unsupported value for standard codec. } - } else if ([value isKindOfClass:s_nsString]) { + } else if ([value isKindOfClass:kNSStringClass]) { NSString* string = value; WriteByte(data, FlutterStandardFieldString); WriteUTF8(data, string); - } else if ([value isKindOfClass:s_flutterStandardTypedData]) { + } else if ([value isKindOfClass:kFlutterStandardTypedDataClass]) { FlutterStandardTypedData* typedData = value; WriteByte(data, FlutterStandardFieldForDataType(typedData.type)); WriteSize(data, typedData.elementCount); WriteAlignment(data, typedData.elementSize); WriteData(data, typedData.data); - } else if ([value isKindOfClass:s_nsData]) { + } else if ([value isKindOfClass:kNSDataClass]) { WriteValue(data, [FlutterStandardTypedData typedDataWithBytes:value]); - } else if ([value isKindOfClass:s_nsArray]) { + } else if ([value isKindOfClass:kNSArrayClass]) { NSArray* array = value; WriteByte(data, FlutterStandardFieldList); WriteSize(data, array.count); for (id object in array) { WriteValue(data, object); } - } else if ([value isKindOfClass:s_nsDictionary]) { + } else if ([value isKindOfClass:kNSDictionaryClass]) { NSDictionary* dict = value; WriteByte(data, FlutterStandardFieldMap); WriteSize(data, dict.count); @@ -340,31 +340,31 @@ static void WriteValue(CFMutableDataRef data, id value) { } - (void)writeByte:(UInt8)value { - WriteByte((CFMutableDataRef)_data, value); + WriteByte((__bridge CFMutableDataRef)_data, value); } - (void)writeBytes:(const void*)bytes length:(NSUInteger)length { - WriteBytes((CFMutableDataRef)_data, bytes, length); + WriteBytes((__bridge CFMutableDataRef)_data, bytes, length); } - (void)writeData:(NSData*)data { - WriteData((CFMutableDataRef)_data, data); + WriteData((__bridge CFMutableDataRef)_data, data); } - (void)writeSize:(UInt32)size { - WriteSize((CFMutableDataRef)_data, size); + WriteSize((__bridge CFMutableDataRef)_data, size); } - (void)writeAlignment:(UInt8)alignment { - WriteAlignment((CFMutableDataRef)_data, alignment); + WriteAlignment((__bridge CFMutableDataRef)_data, alignment); } - (void)writeUTF8:(NSString*)value { - WriteUTF8((CFMutableDataRef)_data, value); + WriteUTF8((__bridge CFMutableDataRef)_data, value); } - (void)writeValue:(id)value { - WriteValue((CFMutableDataRef)_data, value); + WriteValue((__bridge CFMutableDataRef)_data, value); } @end @@ -486,7 +486,7 @@ - (nullable id)readValueOfType:(UInt8)type { NSMutableArray* array = [NSMutableArray arrayWithCapacity:length]; for (UInt32 i = 0; i < length; i++) { id value = [self readValue]; - [array addObject:(value == nil ? s_nsNull : value)]; + [array addObject:(value == nil ? kNSNull : value)]; } return array; } @@ -496,8 +496,7 @@ - (nullable id)readValueOfType:(UInt8)type { for (UInt32 i = 0; i < size; i++) { id key = [self readValue]; id val = [self readValue]; - [dict setObject:(val == nil ? s_nsNull : val) - forKey:(key == nil ? s_nsNull : key)]; + [dict setObject:(val == nil ? kNSNull : val) forKey:(key == nil ? kNSNull : key)]; } return dict; } From 8f93af8fe071ce7c9582f762343d6f8dc8d17eeb Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Fri, 7 May 2021 13:35:14 -0700 Subject: [PATCH 3/4] switched to nscassert --- .../darwin/common/framework/Source/FlutterStandardCodec.mm | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/shell/platform/darwin/common/framework/Source/FlutterStandardCodec.mm b/shell/platform/darwin/common/framework/Source/FlutterStandardCodec.mm index d70cebcecac0a..3a4e56c41533a 100644 --- a/shell/platform/darwin/common/framework/Source/FlutterStandardCodec.mm +++ b/shell/platform/darwin/common/framework/Source/FlutterStandardCodec.mm @@ -256,7 +256,7 @@ static void WriteSize(CFMutableDataRef data, UInt32 size) { } static void WriteAlignment(CFMutableDataRef data, UInt8 alignment) { - assert(alignment <= 8); + NSCAssert(alignment <= 8, @"Alignment larger than kZeroBuffer."); UInt8 mod = CFDataGetLength(data) % alignment; if (mod) { WriteBytes(data, kZeroBuffer, alignment - mod); @@ -304,7 +304,7 @@ static void WriteValue(CFMutableDataRef data, id value) { } if (!success) { NSLog(@"Unsupported value: %@ of number type %ld", value, CFNumberGetType(number)); - assert(NO); // Unsupported value for standard codec. + NSCAssert(NO, @"Unsupported value for standard codec."); } } else if ([value isKindOfClass:kNSStringClass]) { NSString* string = value; @@ -335,7 +335,7 @@ static void WriteValue(CFMutableDataRef data, id value) { } } else { NSLog(@"Unsupported value: %@ of type %@", value, [value class]); - assert(NO); // Unsupported value for standard codec. + NSCAssert(NO, @"Unsupported value for standard codec."); } } From 18d4b10ef2eee88ce9cc41feaef2a23be2654353 Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Fri, 7 May 2021 14:26:47 -0700 Subject: [PATCH 4/4] added comment --- .../darwin/common/framework/Source/FlutterStandardCodec.mm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/shell/platform/darwin/common/framework/Source/FlutterStandardCodec.mm b/shell/platform/darwin/common/framework/Source/FlutterStandardCodec.mm index 3a4e56c41533a..67e8972f32c74 100644 --- a/shell/platform/darwin/common/framework/Source/FlutterStandardCodec.mm +++ b/shell/platform/darwin/common/framework/Source/FlutterStandardCodec.mm @@ -7,6 +7,8 @@ #pragma mark - Codec for basic message channel static const UInt8 kZeroBuffer[8] = {0, 0, 0, 0, 0, 0, 0, 0}; +// Classes are cached in static variables to avoid the extra method calls in a +// highly traffic'd recursive function. static const Class kNSNumberClass = [NSNumber class]; static const id kNSNull = [NSNull null]; static const Class kNSStringClass = [NSString class];