Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 2 additions & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions shell/platform/darwin/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -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",
]

Expand Down
1 change: 1 addition & 0 deletions shell/platform/darwin/common/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -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",
]

Expand Down
124 changes: 36 additions & 88 deletions shell/platform/darwin/common/framework/Source/FlutterStandardCodec.mm
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand All @@ -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

Expand Down
Original file line number Diff line number Diff line change
@@ -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 <stdint.h>

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:
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't remember exactly, does the compiler warn you if a new enum is added and a case is not handled in the switch statement?

Copy link
Contributor

Choose a reason for hiding this comment

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

My point is this might cause mistakes if a new enum is added and this code is not updated. If there is no compiler warning, maybe we can add a comment, or use a different way to determine valid enums.

Copy link
Member Author

Choose a reason for hiding this comment

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

I moved the check to be right next to the enum declaration and added a comment that it needs to be updated if it changes.

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);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we have a unit test in the engine to test this? Basically making sure overriding Standard codec works?
(We have a test in framework, but it would be nice to catch potential bugs in the engine)

Copy link
Member Author

Choose a reason for hiding this comment

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

good call, done


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);
}
}
Loading