blob: 38b9127f41f0da40a643cf421f7c8a9e1a1e7d0d [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.
#include "flutter/shell/platform/darwin/common/framework/Source/FlutterStandardCodecHelper.h"
#include <stdint.h>
#include <vector>
#include "flutter/fml/logging.h"
// The google-runtime-int lint suggests uint64_t in place of unsigned long,
// however these functions are frequently used with NSUInteger, which is
// defined as an unsigned long.
//
// NOLINTBEGIN(google-runtime-int)
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;
}
void FlutterStandardCodecHelperReadBytes(unsigned long* location,
unsigned long length,
void* destination,
CFDataRef data) {
CFRange range = CFRangeMake(*location, length);
CFDataGetBytes(data, range, static_cast<UInt8*>(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 static_cast<CFDataRef>(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 static_cast<CFStringRef>(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 (FlutterStandardFieldIsStandardType(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.
FML_DCHECK(false);
}
}
void FlutterStandardCodecHelperWriteByte(CFMutableDataRef data, uint8_t value) {
CFDataAppendBytes(data, &value, 1);
}
void FlutterStandardCodecHelperWriteBytes(CFMutableDataRef data,
const void* bytes,
unsigned long length) {
CFDataAppendBytes(data, static_cast<const UInt8*>(bytes), length);
}
void FlutterStandardCodecHelperWriteSize(CFMutableDataRef data, uint32_t size) {
if (size < 254) {
FlutterStandardCodecHelperWriteByte(data, size);
} else if (size <= 0xffff) {
FlutterStandardCodecHelperWriteByte(data, 254);
UInt16 value = (UInt16)size;
FlutterStandardCodecHelperWriteBytes(data, &value, 2);
} else {
FlutterStandardCodecHelperWriteByte(data, 255);
FlutterStandardCodecHelperWriteBytes(data, &size, 4);
}
}
void FlutterStandardCodecHelperWriteAlignment(CFMutableDataRef data,
uint8_t alignment) {
uint8_t mod = CFDataGetLength(data) % alignment;
if (mod) {
for (int i = 0; i < (alignment - mod); i++) {
FlutterStandardCodecHelperWriteByte(data, 0);
}
}
}
void FlutterStandardCodecHelperWriteUTF8(CFMutableDataRef data,
CFStringRef value) {
const char* utf8 = CFStringGetCStringPtr(value, kCFStringEncodingUTF8);
if (utf8) {
size_t length = strlen(utf8);
FlutterStandardCodecHelperWriteSize(data, length);
FlutterStandardCodecHelperWriteBytes(data, utf8, length);
} else {
CFIndex length = CFStringGetLength(value);
CFIndex used_length = 0;
// UTF16 length times 3 will fit all UTF8.
CFIndex buffer_length = length * 3;
std::vector<UInt8> buffer;
buffer.reserve(buffer_length);
CFStringGetBytes(value, CFRangeMake(0, length), kCFStringEncodingUTF8, 0,
false, buffer.data(), buffer_length, &used_length);
FlutterStandardCodecHelperWriteSize(data, used_length);
FlutterStandardCodecHelperWriteBytes(data, buffer.data(), used_length);
}
}
void FlutterStandardCodecHelperWriteData(CFMutableDataRef data,
CFDataRef value) {
const UInt8* bytes = CFDataGetBytePtr(value);
CFIndex length = CFDataGetLength(value);
FlutterStandardCodecHelperWriteBytes(data, bytes, length);
}
bool FlutterStandardCodecHelperWriteNumber(CFMutableDataRef data,
CFNumberRef number) {
bool success = false;
if (CFGetTypeID(number) == CFBooleanGetTypeID()) {
bool b = CFBooleanGetValue((CFBooleanRef)number);
FlutterStandardCodecHelperWriteByte(
data, (b ? FlutterStandardFieldTrue : FlutterStandardFieldFalse));
success = true;
} else if (CFNumberIsFloatType(number)) {
Float64 f;
success = CFNumberGetValue(number, kCFNumberFloat64Type, &f);
if (success) {
FlutterStandardCodecHelperWriteByte(data, FlutterStandardFieldFloat64);
FlutterStandardCodecHelperWriteAlignment(data, 8);
FlutterStandardCodecHelperWriteBytes(data, &f, 8);
}
} else if (CFNumberGetByteSize(number) <= 4) {
SInt32 n;
success = CFNumberGetValue(number, kCFNumberSInt32Type, &n);
if (success) {
FlutterStandardCodecHelperWriteByte(data, FlutterStandardFieldInt32);
FlutterStandardCodecHelperWriteBytes(data, &n, 4);
}
} else if (CFNumberGetByteSize(number) <= 8) {
SInt64 n;
success = CFNumberGetValue(number, kCFNumberSInt64Type, &n);
if (success) {
FlutterStandardCodecHelperWriteByte(data, FlutterStandardFieldInt64);
FlutterStandardCodecHelperWriteBytes(data, &n, 8);
}
}
return success;
}
// NOLINTEND(google-runtime-int)