From 54407e18d13fbf5e88d4f07b6ba8b146bb003e68 Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Wed, 14 Dec 2022 13:21:32 -0800 Subject: [PATCH 1/5] Sped up reading with FlutterStandardCodec. --- ci/licenses_golden/licenses_flutter | 2 + shell/platform/darwin/BUILD.gn | 1 + shell/platform/darwin/common/BUILD.gn | 1 + .../framework/Source/FlutterStandardCodec.mm | 124 ++++-------- .../Source/FlutterStandardCodecHelper.c | 189 ++++++++++++++++++ .../Source/FlutterStandardCodecHelper.h | 62 ++++++ .../Source/FlutterStandardCodec_Internal.h | 25 +-- 7 files changed, 296 insertions(+), 108 deletions(-) create mode 100644 shell/platform/darwin/common/framework/Source/FlutterStandardCodecHelper.c create mode 100644 shell/platform/darwin/common/framework/Source/FlutterStandardCodecHelper.h diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index b91f747c8f3fb..4ef894b1f0a2c 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -4872,6 +4872,8 @@ FILE: ../../../flutter/shell/platform/darwin/common/framework/Source/FlutterChan FILE: ../../../flutter/shell/platform/darwin/common/framework/Source/FlutterChannelsTest.m FILE: ../../../flutter/shell/platform/darwin/common/framework/Source/FlutterCodecs.mm FILE: ../../../flutter/shell/platform/darwin/common/framework/Source/FlutterStandardCodec.mm +FILE: ../../../flutter/shell/platform/darwin/common/framework/Source/FlutterStandardCodecHelper.c +FILE: ../../../flutter/shell/platform/darwin/common/framework/Source/FlutterStandardCodecHelper.h FILE: ../../../flutter/shell/platform/darwin/common/framework/Source/FlutterStandardCodec_Internal.h FILE: ../../../flutter/shell/platform/darwin/graphics/FlutterDarwinContextMetalImpeller.h FILE: ../../../flutter/shell/platform/darwin/graphics/FlutterDarwinContextMetalImpeller.mm diff --git a/shell/platform/darwin/BUILD.gn b/shell/platform/darwin/BUILD.gn index f11a8b7aa1cb1..766261c9bab1e 100644 --- a/shell/platform/darwin/BUILD.gn +++ b/shell/platform/darwin/BUILD.gn @@ -47,6 +47,7 @@ source_set("flutter_channels_arc") { "common/framework/Source/FlutterChannels.mm", "common/framework/Source/FlutterCodecs.mm", "common/framework/Source/FlutterStandardCodec.mm", + "common/framework/Source/FlutterStandardCodecHelper.c", "common/framework/Source/FlutterStandardCodec_Internal.h", ] diff --git a/shell/platform/darwin/common/BUILD.gn b/shell/platform/darwin/common/BUILD.gn index 72e3a807f1d85..59599e0f4cb0c 100644 --- a/shell/platform/darwin/common/BUILD.gn +++ b/shell/platform/darwin/common/BUILD.gn @@ -37,6 +37,7 @@ source_set("framework_shared") { "framework/Source/FlutterChannels.mm", "framework/Source/FlutterCodecs.mm", "framework/Source/FlutterStandardCodec.mm", + "framework/Source/FlutterStandardCodecHelper.c", "framework/Source/FlutterStandardCodec_Internal.h", ] diff --git a/shell/platform/darwin/common/framework/Source/FlutterStandardCodec.mm b/shell/platform/darwin/common/framework/Source/FlutterStandardCodec.mm index 62e85e66a2440..373c73eba60fa 100644 --- a/shell/platform/darwin/common/framework/Source/FlutterStandardCodec.mm +++ b/shell/platform/darwin/common/framework/Source/FlutterStandardCodec.mm @@ -2,6 +2,7 @@ // 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 @@ -338,30 +339,16 @@ - (BOOL)hasMore { } - (void)readBytes:(void*)destination length:(NSUInteger)length { - _range.length = length; - [_data getBytes:destination range:_range]; - _range.location += _range.length; + FlutterStandardCodecHelperReadBytes(&_range.location, length, destination, + (__bridge CFDataRef)_data); } - (UInt8)readByte { - UInt8 value; - [self readBytes:&value length:1]; - return value; + return FlutterStandardCodecHelperReadByte(&_range.location, (__bridge CFDataRef)_data); } - (UInt32)readSize { - UInt8 byte = [self readByte]; - if (byte < 254) { - return (UInt32)byte; - } else if (byte == 254) { - UInt16 value; - [self readBytes:&value length:2]; - return value; - } else { - UInt32 value; - [self readBytes:&value length:4]; - return value; - } + return FlutterStandardCodecHelperReadSize(&_range.location, (__bridge CFDataRef)_data); } - (NSData*)readData:(NSUInteger)length { @@ -372,86 +359,47 @@ - (NSData*)readData:(NSUInteger)length { } - (NSString*)readUTF8 { - NSData* bytes = [self readData:[self readSize]]; - return [[NSString alloc] initWithData:bytes encoding:NSUTF8StringEncoding]; + return (__bridge NSString*)FlutterStandardCodecHelperReadUTF8(&_range.location, + (__bridge CFDataRef)_data); } - (void)readAlignment:(UInt8)alignment { - UInt8 mod = _range.location % alignment; - if (mod) { - _range.location += (alignment - mod); - } + FlutterStandardCodecHelperReadAlignment(&_range.location, alignment); } -- (FlutterStandardTypedData*)readTypedDataOfType:(FlutterStandardDataType)type { - UInt32 elementCount = [self readSize]; - UInt8 elementSize = elementSizeForFlutterStandardDataType(type); - [self readAlignment:elementSize]; - NSData* data = [self readData:elementCount * elementSize]; - return [FlutterStandardTypedData typedDataWithData:data type:type]; +- (nullable id)readValue { + return (__bridge id)ReadValue((__bridge CFTypeRef)self); } -- (nullable id)readValue { - return [self readValueOfType:[self readByte]]; +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 { - FlutterStandardField field = (FlutterStandardField)type; - switch (field) { - case FlutterStandardFieldNil: - return nil; - case FlutterStandardFieldTrue: - return @YES; - case FlutterStandardFieldFalse: - return @NO; - case FlutterStandardFieldInt32: { - SInt32 value; - [self readBytes:&value length:4]; - return @(value); - } - case FlutterStandardFieldInt64: { - SInt64 value; - [self readBytes:&value length:8]; - return @(value); - } - case FlutterStandardFieldFloat64: { - Float64 value; - [self readAlignment:8]; - [self readBytes:&value length:8]; - return [NSNumber numberWithDouble:value]; - } - case FlutterStandardFieldIntHex: - case FlutterStandardFieldString: - return [self readUTF8]; - case FlutterStandardFieldUInt8Data: - case FlutterStandardFieldInt32Data: - case FlutterStandardFieldInt64Data: - case FlutterStandardFieldFloat32Data: - case FlutterStandardFieldFloat64Data: - return [self readTypedDataOfType:FlutterStandardDataTypeForField(field)]; - case FlutterStandardFieldList: { - UInt32 length = [self readSize]; - NSMutableArray* array = [NSMutableArray arrayWithCapacity:length]; - for (UInt32 i = 0; i < length; i++) { - id value = [self readValue]; - [array addObject:(value == nil ? [NSNull null] : value)]; - } - return array; - } - case FlutterStandardFieldMap: { - UInt32 size = [self readSize]; - NSMutableDictionary* dict = [NSMutableDictionary dictionaryWithCapacity:size]; - 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)]; - } - return dict; - } - default: - NSAssert(NO, @"Corrupted standard message"); - } + return (__bridge id)FlutterStandardCodecHelperReadValueOfType( + &_range.location, (__bridge CFDataRef)_data, type, ReadValue, ReadTypedDataOfType, + (__bridge CFTypeRef)self); } @end diff --git a/shell/platform/darwin/common/framework/Source/FlutterStandardCodecHelper.c b/shell/platform/darwin/common/framework/Source/FlutterStandardCodecHelper.c new file mode 100644 index 0000000000000..48605d11cb295 --- /dev/null +++ b/shell/platform/darwin/common/framework/Source/FlutterStandardCodecHelper.c @@ -0,0 +1,189 @@ +// 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. + +#include "flutter/shell/platform/darwin/common/framework/Source/FlutterStandardCodecHelper.h" +#include + +void FlutterStandardCodecHelperReadAlignment(unsigned long* location, + uint8_t alignment) { + uint8_t mod = *location % alignment; + if (mod) { + *location += (alignment - mod); + } +} + +static uint8_t PeekByte(unsigned long location, CFDataRef data) { + uint8_t result; + CFRange range = CFRangeMake(location, 1); + CFDataGetBytes(data, range, &result); + return result; +} + +static bool IsStandardType(uint8_t type) { + switch (type) { + case FlutterStandardFieldNil: + case FlutterStandardFieldTrue: + case FlutterStandardFieldFalse: + case FlutterStandardFieldInt32: + case FlutterStandardFieldInt64: + case FlutterStandardFieldIntHex: + case FlutterStandardFieldFloat64: + case FlutterStandardFieldString: + case FlutterStandardFieldUInt8Data: + case FlutterStandardFieldInt32Data: + case FlutterStandardFieldInt64Data: + case FlutterStandardFieldFloat64Data: + case FlutterStandardFieldList: + case FlutterStandardFieldMap: + case FlutterStandardFieldFloat32Data: + return true; + default: + return false; + } +} + +void FlutterStandardCodecHelperReadBytes(unsigned long* location, + unsigned long length, + void* destination, + CFDataRef data) { + CFRange range = CFRangeMake(*location, length); + CFDataGetBytes(data, range, destination); + *location += length; +} + +uint8_t FlutterStandardCodecHelperReadByte(unsigned long* location, + CFDataRef data) { + uint8_t value; + FlutterStandardCodecHelperReadBytes(location, 1, &value, data); + return value; +} + +uint32_t FlutterStandardCodecHelperReadSize(unsigned long* location, + CFDataRef data) { + uint8_t byte = FlutterStandardCodecHelperReadByte(location, data); + if (byte < 254) { + return (uint32_t)byte; + } else if (byte == 254) { + UInt16 value; + FlutterStandardCodecHelperReadBytes(location, 2, &value, data); + return value; + } else { + UInt32 value; + FlutterStandardCodecHelperReadBytes(location, 4, &value, data); + return value; + } +} + +static CFDataRef ReadDataNoCopy(unsigned long* location, + unsigned long length, + CFDataRef data) { + CFDataRef result = CFDataCreateWithBytesNoCopy( + kCFAllocatorDefault, CFDataGetBytePtr(data) + *location, length, + kCFAllocatorNull); + *location += length; + return CFAutorelease(result); +} + +CFStringRef FlutterStandardCodecHelperReadUTF8(unsigned long* location, + CFDataRef data) { + uint32_t size = FlutterStandardCodecHelperReadSize(location, data); + CFDataRef bytes = ReadDataNoCopy(location, size, data); + CFStringRef result = CFStringCreateFromExternalRepresentation( + kCFAllocatorDefault, bytes, kCFStringEncodingUTF8); + return CFAutorelease(result); +} + +// Peeks ahead to see if we are reading a standard type. If so, recurse +// directly to FlutterStandardCodecHelperReadValueOfType, otherwise recurse to +// objc. +static inline CFTypeRef FastReadValue( + unsigned long* location, + CFDataRef data, + CFTypeRef (*ReadValue)(CFTypeRef), + CFTypeRef (*ReadTypedDataOfType)(FlutterStandardField, CFTypeRef), + CFTypeRef user_data) { + uint8_t type = PeekByte(*location, data); + if (IsStandardType(type)) { + *location += 1; + return FlutterStandardCodecHelperReadValueOfType( + location, data, type, ReadValue, ReadTypedDataOfType, user_data); + } else { + return ReadValue(user_data); + } +} + +CFTypeRef FlutterStandardCodecHelperReadValueOfType( + unsigned long* location, + CFDataRef data, + uint8_t type, + CFTypeRef (*ReadValue)(CFTypeRef), + CFTypeRef (*ReadTypedDataOfType)(FlutterStandardField, CFTypeRef), + CFTypeRef user_data) { + FlutterStandardField field = (FlutterStandardField)type; + switch (field) { + case FlutterStandardFieldNil: + return nil; + case FlutterStandardFieldTrue: + return kCFBooleanTrue; + case FlutterStandardFieldFalse: + return kCFBooleanFalse; + case FlutterStandardFieldInt32: { + int32_t value; + FlutterStandardCodecHelperReadBytes(location, 4, &value, data); + return CFAutorelease( + CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &value)); + } + case FlutterStandardFieldInt64: { + int64_t value; + FlutterStandardCodecHelperReadBytes(location, 8, &value, data); + return CFAutorelease( + CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &value)); + } + case FlutterStandardFieldFloat64: { + Float64 value; + FlutterStandardCodecHelperReadAlignment(location, 8); + FlutterStandardCodecHelperReadBytes(location, 8, &value, data); + return CFAutorelease( + CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &value)); + } + case FlutterStandardFieldIntHex: + case FlutterStandardFieldString: + return FlutterStandardCodecHelperReadUTF8(location, data); + case FlutterStandardFieldUInt8Data: + case FlutterStandardFieldInt32Data: + case FlutterStandardFieldInt64Data: + case FlutterStandardFieldFloat32Data: + case FlutterStandardFieldFloat64Data: + return ReadTypedDataOfType(field, user_data); + case FlutterStandardFieldList: { + UInt32 length = FlutterStandardCodecHelperReadSize(location, data); + CFMutableArrayRef array = CFArrayCreateMutable( + kCFAllocatorDefault, length, &kCFTypeArrayCallBacks); + for (UInt32 i = 0; i < length; i++) { + CFTypeRef value = FastReadValue(location, data, ReadValue, + ReadTypedDataOfType, user_data); + CFArrayAppendValue(array, (value == nil ? kCFNull : value)); + } + return CFAutorelease(array); + } + case FlutterStandardFieldMap: { + UInt32 size = FlutterStandardCodecHelperReadSize(location, data); + CFMutableDictionaryRef dict = CFDictionaryCreateMutable( + kCFAllocatorDefault, size, &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + for (UInt32 i = 0; i < size; i++) { + CFTypeRef key = FastReadValue(location, data, ReadValue, + ReadTypedDataOfType, user_data); + CFTypeRef val = FastReadValue(location, data, ReadValue, + ReadTypedDataOfType, user_data); + CFDictionaryAddValue(dict, (key == nil ? kCFNull : key), + (val == nil ? kCFNull : val)); + } + return CFAutorelease(dict); + } + default: + // Malformed message. + assert(false); + } +} diff --git a/shell/platform/darwin/common/framework/Source/FlutterStandardCodecHelper.h b/shell/platform/darwin/common/framework/Source/FlutterStandardCodecHelper.h new file mode 100644 index 0000000000000..55f9114056c8c --- /dev/null +++ b/shell/platform/darwin/common/framework/Source/FlutterStandardCodecHelper.h @@ -0,0 +1,62 @@ +// 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. + +#ifndef SHELL_PLATFORM_DARWIN_COMMON_FRAMEWORK_SOURCE_FLUTTERSTANDARDCODECHELPER_H_ +#define SHELL_PLATFORM_DARWIN_COMMON_FRAMEWORK_SOURCE_FLUTTERSTANDARDCODECHELPER_H_ + +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +typedef enum { + FlutterStandardFieldNil, + FlutterStandardFieldTrue, + FlutterStandardFieldFalse, + FlutterStandardFieldInt32, + FlutterStandardFieldInt64, + FlutterStandardFieldIntHex, + FlutterStandardFieldFloat64, + FlutterStandardFieldString, + FlutterStandardFieldUInt8Data, + FlutterStandardFieldInt32Data, + FlutterStandardFieldInt64Data, + FlutterStandardFieldFloat64Data, + FlutterStandardFieldList, + FlutterStandardFieldMap, + FlutterStandardFieldFloat32Data, +} FlutterStandardField; + +void FlutterStandardCodecHelperReadAlignment(unsigned long* location, + uint8_t alignment); + +void FlutterStandardCodecHelperReadBytes(unsigned long* location, + unsigned long length, + void* destination, + CFDataRef data); + +uint8_t FlutterStandardCodecHelperReadByte(unsigned long* location, + CFDataRef data); + +uint32_t FlutterStandardCodecHelperReadSize(unsigned long* location, + CFDataRef data); + +CFStringRef FlutterStandardCodecHelperReadUTF8(unsigned long* location, + CFDataRef data); + +CFTypeRef FlutterStandardCodecHelperReadValueOfType( + unsigned long* location, + CFDataRef data, + uint8_t type, + CFTypeRef (*ReadValue)(CFTypeRef), + CFTypeRef (*ReadTypedDataOfType)(FlutterStandardField, CFTypeRef), + CFTypeRef user_data); + +#if defined(__cplusplus) +} +#endif + +#endif // SHELL_PLATFORM_DARWIN_COMMON_FRAMEWORK_SOURCE_FLUTTERSTANDARDCODECHELPER_H_ diff --git a/shell/platform/darwin/common/framework/Source/FlutterStandardCodec_Internal.h b/shell/platform/darwin/common/framework/Source/FlutterStandardCodec_Internal.h index 88d7f277476ea..81ba3e21e2a7a 100644 --- a/shell/platform/darwin/common/framework/Source/FlutterStandardCodec_Internal.h +++ b/shell/platform/darwin/common/framework/Source/FlutterStandardCodec_Internal.h @@ -6,27 +6,11 @@ #define SHELL_PLATFORM_DARWIN_COMMON_FRAMEWORK_SOURCE_FLUTTERSTANDARDCODECINTERNAL_H_ #import "flutter/shell/platform/darwin/common/framework/Headers/FlutterCodecs.h" - -typedef NS_ENUM(NSInteger, FlutterStandardField) { - FlutterStandardFieldNil, - FlutterStandardFieldTrue, - FlutterStandardFieldFalse, - FlutterStandardFieldInt32, - FlutterStandardFieldInt64, - FlutterStandardFieldIntHex, - FlutterStandardFieldFloat64, - FlutterStandardFieldString, - FlutterStandardFieldUInt8Data, - FlutterStandardFieldInt32Data, - FlutterStandardFieldInt64Data, - FlutterStandardFieldFloat64Data, - FlutterStandardFieldList, - FlutterStandardFieldMap, - FlutterStandardFieldFloat32Data, -}; +#import "flutter/shell/platform/darwin/common/framework/Source/FlutterStandardCodecHelper.h" namespace flutter { -FlutterStandardField FlutterStandardFieldForDataType(FlutterStandardDataType type) { +FlutterStandardField FlutterStandardFieldForDataType( + FlutterStandardDataType type) { switch (type) { case FlutterStandardDataTypeUInt8: return FlutterStandardFieldUInt8Data; @@ -40,7 +24,8 @@ FlutterStandardField FlutterStandardFieldForDataType(FlutterStandardDataType typ return FlutterStandardFieldFloat64Data; } } -FlutterStandardDataType FlutterStandardDataTypeForField(FlutterStandardField field) { +FlutterStandardDataType FlutterStandardDataTypeForField( + FlutterStandardField field) { switch (field) { case FlutterStandardFieldUInt8Data: return FlutterStandardDataTypeUInt8; From e47a555230e0a24d176e5aa4433b78eda2b7f50d Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Thu, 15 Dec 2022 14:47:03 -0800 Subject: [PATCH 2/5] added missing license diff --- ci/licenses_golden/licenses_flutter | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 4ef894b1f0a2c..fb068a796a759 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -2417,6 +2417,8 @@ ORIGIN: ../../../flutter/shell/platform/darwin/common/framework/Source/FlutterCh ORIGIN: ../../../flutter/shell/platform/darwin/common/framework/Source/FlutterChannelsTest.m + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/darwin/common/framework/Source/FlutterCodecs.mm + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/darwin/common/framework/Source/FlutterStandardCodec.mm + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/platform/darwin/common/framework/Source/FlutterStandardCodecHelper.c + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/platform/darwin/common/framework/Source/FlutterStandardCodecHelper.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/darwin/common/framework/Source/FlutterStandardCodec_Internal.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/darwin/graphics/FlutterDarwinContextMetalImpeller.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/darwin/graphics/FlutterDarwinContextMetalImpeller.mm + ../../../flutter/LICENSE From cf38db3593ce7be5f44b2d265c71fe78d1bc8eed Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Thu, 15 Dec 2022 15:02:20 -0800 Subject: [PATCH 3/5] put the IsStandardType in the header so it's closer to where it should change --- .../Source/FlutterStandardCodecHelper.c | 25 +------------------ .../Source/FlutterStandardCodecHelper.h | 7 ++++++ 2 files changed, 8 insertions(+), 24 deletions(-) diff --git a/shell/platform/darwin/common/framework/Source/FlutterStandardCodecHelper.c b/shell/platform/darwin/common/framework/Source/FlutterStandardCodecHelper.c index 48605d11cb295..2fbe4277648b0 100644 --- a/shell/platform/darwin/common/framework/Source/FlutterStandardCodecHelper.c +++ b/shell/platform/darwin/common/framework/Source/FlutterStandardCodecHelper.c @@ -20,29 +20,6 @@ static uint8_t PeekByte(unsigned long location, CFDataRef data) { return result; } -static bool IsStandardType(uint8_t type) { - switch (type) { - case FlutterStandardFieldNil: - case FlutterStandardFieldTrue: - case FlutterStandardFieldFalse: - case FlutterStandardFieldInt32: - case FlutterStandardFieldInt64: - case FlutterStandardFieldIntHex: - case FlutterStandardFieldFloat64: - case FlutterStandardFieldString: - case FlutterStandardFieldUInt8Data: - case FlutterStandardFieldInt32Data: - case FlutterStandardFieldInt64Data: - case FlutterStandardFieldFloat64Data: - case FlutterStandardFieldList: - case FlutterStandardFieldMap: - case FlutterStandardFieldFloat32Data: - return true; - default: - return false; - } -} - void FlutterStandardCodecHelperReadBytes(unsigned long* location, unsigned long length, void* destination, @@ -104,7 +81,7 @@ static inline CFTypeRef FastReadValue( CFTypeRef (*ReadTypedDataOfType)(FlutterStandardField, CFTypeRef), CFTypeRef user_data) { uint8_t type = PeekByte(*location, data); - if (IsStandardType(type)) { + if (FlutterStandardFieldIsStandardType(type)) { *location += 1; return FlutterStandardCodecHelperReadValueOfType( location, data, type, ReadValue, ReadTypedDataOfType, user_data); diff --git a/shell/platform/darwin/common/framework/Source/FlutterStandardCodecHelper.h b/shell/platform/darwin/common/framework/Source/FlutterStandardCodecHelper.h index 55f9114056c8c..5c06d6091145b 100644 --- a/shell/platform/darwin/common/framework/Source/FlutterStandardCodecHelper.h +++ b/shell/platform/darwin/common/framework/Source/FlutterStandardCodecHelper.h @@ -6,12 +6,14 @@ #define SHELL_PLATFORM_DARWIN_COMMON_FRAMEWORK_SOURCE_FLUTTERSTANDARDCODECHELPER_H_ #include +#include #include #if defined(__cplusplus) extern "C" { #endif +// Note: Update FlutterStandardFieldIsStandardType if this changes. typedef enum { FlutterStandardFieldNil, FlutterStandardFieldTrue, @@ -30,6 +32,11 @@ typedef enum { FlutterStandardFieldFloat32Data, } FlutterStandardField; +static inline bool FlutterStandardFieldIsStandardType(uint8_t field) { + return field <= FlutterStandardFieldFloat32Data && + field >= FlutterStandardFieldNil; +} + void FlutterStandardCodecHelperReadAlignment(unsigned long* location, uint8_t alignment); From dad86234b06e46dc047ed9128adb9298030fd136 Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Thu, 15 Dec 2022 15:14:27 -0800 Subject: [PATCH 4/5] added unittest for subclassing codecs --- .../Source/flutter_standard_codec_unittest.mm | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/shell/platform/darwin/common/framework/Source/flutter_standard_codec_unittest.mm b/shell/platform/darwin/common/framework/Source/flutter_standard_codec_unittest.mm index 946580eb3bf83..9900b836293bb 100644 --- a/shell/platform/darwin/common/framework/Source/flutter_standard_codec_unittest.mm +++ b/shell/platform/darwin/common/framework/Source/flutter_standard_codec_unittest.mm @@ -6,6 +6,85 @@ #include "gtest/gtest.h" +FLUTTER_ASSERT_NOT_ARC + +@interface Pair : NSObject +@property(atomic, readonly, strong, nullable) NSObject* left; +@property(atomic, readonly, strong, nullable) NSObject* right; +- (instancetype)initWithLeft:(NSObject*)first right:(NSObject*)right; +@end + +@implementation Pair +- (instancetype)initWithLeft:(NSObject*)left right:(NSObject*)right { + self = [super init]; + _left = left; + _right = right; + return self; +} +@end + +const UInt8 DATE = 128; +const UInt8 PAIR = 129; + +@interface ExtendedWriter : FlutterStandardWriter +- (void)writeValue:(id)value; +@end + +@implementation ExtendedWriter +- (void)writeValue:(id)value { + if ([value isKindOfClass:[NSDate class]]) { + [self writeByte:DATE]; + NSDate* date = value; + NSTimeInterval time = date.timeIntervalSince1970; + SInt64 ms = (SInt64)(time * 1000.0); + [self writeBytes:&ms length:8]; + } else if ([value isKindOfClass:[Pair class]]) { + Pair* pair = value; + [self writeByte:PAIR]; + [self writeValue:pair.left]; + [self writeValue:pair.right]; + } else { + [super writeValue:value]; + } +} +@end + +@interface ExtendedReader : FlutterStandardReader +- (id)readValueOfType:(UInt8)type; +@end + +@implementation ExtendedReader +- (id)readValueOfType:(UInt8)type { + switch (type) { + case DATE: { + SInt64 value; + [self readBytes:&value length:8]; + NSTimeInterval time = [NSNumber numberWithLong:value].doubleValue / 1000.0; + return [NSDate dateWithTimeIntervalSince1970:time]; + } + case PAIR: { + return [[Pair alloc] initWithLeft:[self readValue] right:[self readValue]]; + } + default: + return [super readValueOfType:type]; + } +} +@end + +@interface ExtendedReaderWriter : FlutterStandardReaderWriter +- (FlutterStandardWriter*)writerWithData:(NSMutableData*)data; +- (FlutterStandardReader*)readerWithData:(NSData*)data; +@end + +@implementation ExtendedReaderWriter +- (FlutterStandardWriter*)writerWithData:(NSMutableData*)data { + return [[ExtendedWriter alloc] initWithData:data]; +} +- (FlutterStandardReader*)readerWithData:(NSData*)data { + return [[ExtendedReader alloc] initWithData:data]; +} +@end + static void CheckEncodeDecode(id value, NSData* expectedEncoding) { FlutterStandardMessageCodec* codec = [FlutterStandardMessageCodec sharedInstance]; NSData* encoded = [codec encode:value]; @@ -252,3 +331,16 @@ static void CheckEncodeDecode(id value) { id decoded = [codec decodeEnvelope:encoded]; ASSERT_TRUE([decoded isEqual:error]); } + +TEST(FlutterStandardCodec, HandlesSubclasses) { + ExtendedReaderWriter* extendedReaderWriter = [[ExtendedReaderWriter alloc] init]; + FlutterStandardMessageCodec* codec = + [FlutterStandardMessageCodec codecWithReaderWriter:extendedReaderWriter]; + Pair* pair = [[Pair alloc] initWithLeft:@1 right:@2]; + NSData* encoded = [codec encode:pair]; + Pair* decoded = [codec decode:encoded]; + ASSERT_TRUE([pair.left isEqual:decoded.left]); + ASSERT_TRUE([pair.right isEqual:decoded.right]); + [pair release]; + [extendedReaderWriter release]; +} From 175c167c7b9a0f56866d466658552ed5e30d0d92 Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Thu, 15 Dec 2022 16:52:17 -0800 Subject: [PATCH 5/5] fixed lints --- .../Source/flutter_standard_codec_unittest.mm | 36 +++++++++++-------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/shell/platform/darwin/common/framework/Source/flutter_standard_codec_unittest.mm b/shell/platform/darwin/common/framework/Source/flutter_standard_codec_unittest.mm index 9900b836293bb..e0a2a2ca211e6 100644 --- a/shell/platform/darwin/common/framework/Source/flutter_standard_codec_unittest.mm +++ b/shell/platform/darwin/common/framework/Source/flutter_standard_codec_unittest.mm @@ -17,14 +17,22 @@ - (instancetype)initWithLeft:(NSObject*)first right:(NSObject*)right; @implementation Pair - (instancetype)initWithLeft:(NSObject*)left right:(NSObject*)right { self = [super init]; - _left = left; - _right = right; + if (self) { + _left = [left retain]; + _right = [right retain]; + } return self; } + +- (void)dealloc { + [_left release]; + [_right release]; + [super dealloc]; +} @end -const UInt8 DATE = 128; -const UInt8 PAIR = 129; +static const UInt8 kDATE = 128; +static const UInt8 kPAIR = 129; @interface ExtendedWriter : FlutterStandardWriter - (void)writeValue:(id)value; @@ -33,14 +41,14 @@ - (void)writeValue:(id)value; @implementation ExtendedWriter - (void)writeValue:(id)value { if ([value isKindOfClass:[NSDate class]]) { - [self writeByte:DATE]; + [self writeByte:kDATE]; NSDate* date = value; NSTimeInterval time = date.timeIntervalSince1970; SInt64 ms = (SInt64)(time * 1000.0); [self writeBytes:&ms length:8]; } else if ([value isKindOfClass:[Pair class]]) { Pair* pair = value; - [self writeByte:PAIR]; + [self writeByte:kPAIR]; [self writeValue:pair.left]; [self writeValue:pair.right]; } else { @@ -56,14 +64,14 @@ - (id)readValueOfType:(UInt8)type; @implementation ExtendedReader - (id)readValueOfType:(UInt8)type { switch (type) { - case DATE: { + case kDATE: { SInt64 value; [self readBytes:&value length:8]; NSTimeInterval time = [NSNumber numberWithLong:value].doubleValue / 1000.0; return [NSDate dateWithTimeIntervalSince1970:time]; } - case PAIR: { - return [[Pair alloc] initWithLeft:[self readValue] right:[self readValue]]; + case kPAIR: { + return [[[Pair alloc] initWithLeft:[self readValue] right:[self readValue]] autorelease]; } default: return [super readValueOfType:type]; @@ -78,10 +86,10 @@ - (FlutterStandardReader*)readerWithData:(NSData*)data; @implementation ExtendedReaderWriter - (FlutterStandardWriter*)writerWithData:(NSMutableData*)data { - return [[ExtendedWriter alloc] initWithData:data]; + return [[[ExtendedWriter alloc] initWithData:data] autorelease]; } - (FlutterStandardReader*)readerWithData:(NSData*)data { - return [[ExtendedReader alloc] initWithData:data]; + return [[[ExtendedReader alloc] initWithData:data] autorelease]; } @end @@ -333,14 +341,12 @@ static void CheckEncodeDecode(id value) { } TEST(FlutterStandardCodec, HandlesSubclasses) { - ExtendedReaderWriter* extendedReaderWriter = [[ExtendedReaderWriter alloc] init]; + ExtendedReaderWriter* extendedReaderWriter = [[[ExtendedReaderWriter alloc] init] autorelease]; FlutterStandardMessageCodec* codec = [FlutterStandardMessageCodec codecWithReaderWriter:extendedReaderWriter]; - Pair* pair = [[Pair alloc] initWithLeft:@1 right:@2]; + Pair* pair = [[[Pair alloc] initWithLeft:@1 right:@2] autorelease]; NSData* encoded = [codec encode:pair]; Pair* decoded = [codec decode:encoded]; ASSERT_TRUE([pair.left isEqual:decoded.left]); ASSERT_TRUE([pair.right isEqual:decoded.right]); - [pair release]; - [extendedReaderWriter release]; }