Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sped up the objc standard message codec #25998

Merged
merged 4 commits into from
May 12, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
149 changes: 93 additions & 56 deletions shell/platform/darwin/common/framework/Source/FlutterStandardCodec.mm
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,17 @@

#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];
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;
}
Expand Down Expand Up @@ -221,115 +232,142 @@ - (void)dealloc {
[super dealloc];
}

- (void)writeByte:(UInt8)value {
[_data appendBytes:&value length:1];
static void WriteByte(CFMutableDataRef data, UInt8 value) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this have a FLT prefix?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not required because these are static functions, their visibility is limited to this compilation unit so they can't collide.

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) {
NSCAssert(alignment <= 8, @"Alignment larger than kZeroBuffer.");
UInt8 mod = CFDataGetLength(data) % alignment;
if (mod) {
for (int i = 0; i < (alignment - mod); i++) {
[self writeByte:0];
}
WriteBytes(data, kZeroBuffer, 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 == kNSNull) {
WriteByte(data, FlutterStandardFieldNil);
} else if ([value isKindOfClass:kNSNumberClass]) {
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");
NSCAssert(NO, @"Unsupported value for standard codec.");
}
} else if ([value isKindOfClass:[NSString class]]) {
} else if ([value isKindOfClass:kNSStringClass]) {
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:kFlutterStandardTypedDataClass]) {
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:kNSDataClass]) {
WriteValue(data, [FlutterStandardTypedData typedDataWithBytes:value]);
} else if ([value isKindOfClass:kNSArrayClass]) {
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:kNSDictionaryClass]) {
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");
NSCAssert(NO, @"Unsupported value for standard codec.");
}
}

- (void)writeByte:(UInt8)value {
WriteByte((__bridge CFMutableDataRef)_data, value);
}

- (void)writeBytes:(const void*)bytes length:(NSUInteger)length {
WriteBytes((__bridge CFMutableDataRef)_data, bytes, length);
}

- (void)writeData:(NSData*)data {
WriteData((__bridge CFMutableDataRef)_data, data);
}

- (void)writeSize:(UInt32)size {
WriteSize((__bridge CFMutableDataRef)_data, size);
}

- (void)writeAlignment:(UInt8)alignment {
WriteAlignment((__bridge CFMutableDataRef)_data, alignment);
}

- (void)writeUTF8:(NSString*)value {
WriteUTF8((__bridge CFMutableDataRef)_data, value);
}

- (void)writeValue:(id)value {
WriteValue((__bridge CFMutableDataRef)_data, value);
}
@end

@implementation FlutterStandardReader {
Expand Down Expand Up @@ -450,7 +488,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 ? kNSNull : value)];
}
return array;
}
Expand All @@ -460,8 +498,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 ? [NSNull null] : val)
forKey:(key == nil ? [NSNull null] : key)];
[dict setObject:(val == nil ? kNSNull : val) forKey:(key == nil ? kNSNull : key)];
}
return dict;
}
Expand Down