Add Float32List support to StandardMessageCodec (flutter/engine#26386)

This commit is contained in:
Jeff Ward 2021-05-27 17:49:02 -04:00 committed by GitHub
parent 78254e537a
commit 2438363a2e
10 changed files with 115 additions and 5 deletions

View File

@ -33,7 +33,7 @@ import java.util.Map.Entry;
* <li>BigIntegers (see below)
* <li>Floats, Doubles
* <li>Strings
* <li>byte[], int[], long[], double[]
* <li>byte[], int[], long[], float[], double[]
* <li>Lists of supported values
* <li>Maps with supported keys and values
* </ul>
@ -49,6 +49,7 @@ import java.util.Map.Entry;
* <li>byte[]: Uint8List
* <li>int[]: Int32List
* <li>long[]: Int64List
* <li>float[]: Float32List
* <li>double[]: Float64List
* <li>List: List
* <li>Map: Map
@ -104,6 +105,7 @@ public class StandardMessageCodec implements MessageCodec<Object> {
private static final byte DOUBLE_ARRAY = 11;
private static final byte LIST = 12;
private static final byte MAP = 13;
private static final byte FLOAT_ARRAY = 14;
/**
* Writes an int representing a size to the specified stream. Uses an expanding code of 1 to 5
@ -173,6 +175,11 @@ public class StandardMessageCodec implements MessageCodec<Object> {
}
}
/** Writes the specified double as 4 bytes to the specified stream */
protected static final void writeFloat(ByteArrayOutputStream stream, float value) {
writeInt(stream, Float.floatToIntBits(value));
}
/** Writes the specified double as 8 bytes to the specified stream. */
protected static final void writeDouble(ByteArrayOutputStream stream, double value) {
writeLong(stream, Double.doubleToLongBits(value));
@ -272,6 +279,14 @@ public class StandardMessageCodec implements MessageCodec<Object> {
writeValue(stream, entry.getKey());
writeValue(stream, entry.getValue());
}
} else if (value instanceof float[]) {
stream.write(FLOAT_ARRAY);
final float[] array = (float[]) value;
writeSize(stream, array.length);
writeAlignment(stream, 4);
for (final float f : array) {
writeFloat(stream, f);
}
} else {
throw new IllegalArgumentException("Unsupported value: " + value);
}
@ -412,6 +427,16 @@ public class StandardMessageCodec implements MessageCodec<Object> {
result = map;
break;
}
case FLOAT_ARRAY:
{
final int length = readSize(buffer);
final float[] array = new float[length];
readAlignment(buffer, 4);
buffer.asFloatBuffer().get(array);
result = array;
buffer.position(buffer.position() + 4 * length);
break;
}
default:
throw new IllegalArgumentException("Message corrupted");
}

View File

@ -1,5 +1,6 @@
package io.flutter.plugin.common;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import java.nio.ByteBuffer;
@ -28,6 +29,7 @@ public class StandardMessageCodecTest {
private static final byte DOUBLE_ARRAY = 11;
private static final byte LIST = 12;
private static final byte MAP = 13;
private static final byte FLOAT_ARRAY = 14;
@Test
public void itEncodesNullLiterals() {
@ -93,4 +95,17 @@ public class StandardMessageCodecTest {
expected.flip();
assertEquals(expected, message);
}
@Test
public void itEncodesFloatArrays() {
StandardMessageCodec codec = new StandardMessageCodec();
float[] expectedValues = new float[] {1.0f, 2.2f, 5.3f};
ByteBuffer message = codec.encodeMessage(expectedValues);
message.flip();
float[] values = (float[]) codec.decodeMessage(message);
assertArrayEquals(expectedValues, values, 0.01f);
}
}

View File

@ -217,6 +217,9 @@ TEST(EncodableValueTest, Comparison) {
EncodableValue(
EncodableMap{{EncodableValue(), EncodableValue(1.0)},
{EncodableValue("key"), EncodableValue("value")}}),
// FloatList
EncodableValue(std::vector<float>{0, 1}),
EncodableValue(std::vector<float>{0, 100}),
};
for (size_t i = 0; i < values.size(); ++i) {

View File

@ -110,6 +110,7 @@ using EncodableValueVariant = std::variant<std::monostate,
std::vector<double>,
EncodableList,
EncodableMap,
std::vector<float>,
CustomEncodableValue>;
} // namespace internal
@ -158,6 +159,7 @@ using EncodableValueVariant = std::variant<std::monostate,
// std::vector<double> -> Float64List
// EncodableList -> List
// EncodableMap -> Map
// std::vector<float> -> Float32List
class EncodableValue : public internal::EncodableValueVariant {
public:
// Rely on std::variant for most of the constructors/operators.

View File

@ -41,6 +41,7 @@ enum class EncodedType {
kFloat64List,
kList,
kMap,
kFloat32List,
};
// Returns the encoded type that should be written when serializing |value|.
@ -70,6 +71,8 @@ EncodedType EncodedTypeForValue(const EncodableValue& value) {
return EncodedType::kList;
case 11:
return EncodedType::kMap;
case 12:
return EncodedType::kFloat32List;
}
assert(false);
return EncodedType::kNull;
@ -150,7 +153,11 @@ void StandardCodecSerializer::WriteValue(const EncodableValue& value,
}
break;
}
case 12:
case 12: {
WriteVector(std::get<std::vector<float>>(value), stream);
break;
}
case 13:
std::cerr
<< "Unhandled custom type in StandardCodecSerializer::WriteValue. "
<< "Custom types require codec extensions." << std::endl;
@ -210,6 +217,9 @@ EncodableValue StandardCodecSerializer::ReadValueOfType(
}
return EncodableValue(map_value);
}
case EncodedType::kFloat32List: {
return ReadVector<float>(stream);
}
}
std::cerr << "Unknown type in StandardCodecSerializer::ReadValueOfType: "
<< static_cast<int>(type) << std::endl;

View File

@ -175,6 +175,13 @@ TEST(StandardMessageCodec, CanEncodeAndDecodeInt64Array) {
CheckEncodeDecode(value, bytes);
}
TEST(StandradMessageCodec, CanEncodeAndDecodeFloat32Array) {
std::vector<uint8_t> bytes = {0x0e, 0x02, 0x00, 0x00, 0xd8, 0x0f,
0x49, 0x40, 0x00, 0x00, 0x7a, 0x44};
EncodableValue value(std::vector<float>{3.1415920257568359375f, 1000.0f});
CheckEncodeDecode(value, bytes);
}
TEST(StandardMessageCodec, CanEncodeAndDecodeFloat64Array) {
std::vector<uint8_t> bytes = {0x0b, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x18, 0x2d, 0x44, 0x54, 0xfb, 0x21, 0x09, 0x40,

View File

@ -282,6 +282,7 @@ typedef NS_ENUM(NSInteger, FlutterStandardDataType) {
FlutterStandardDataTypeUInt8,
FlutterStandardDataTypeInt32,
FlutterStandardDataTypeInt64,
FlutterStandardDataTypeFloat32,
FlutterStandardDataTypeFloat64,
};
@ -319,6 +320,14 @@ FLUTTER_DARWIN_EXPORT
*/
+ (instancetype)typedDataWithInt64:(NSData*)data;
/**
* Creates a `FlutterStandardTypedData` which interprets the specified data
* as 32-bit floats.
*
* @param data the byte data. The length must be divisible by 8.
*/
+ (instancetype)typedDataWithFloat32:(NSData*)data;
/**
* Creates a `FlutterStandardTypedData` which interprets the specified data
* as 64-bit floats.

View File

@ -162,6 +162,10 @@ using namespace flutter;
return [FlutterStandardTypedData typedDataWithData:data type:FlutterStandardDataTypeInt64];
}
+ (instancetype)typedDataWithFloat32:(NSData*)data {
return [FlutterStandardTypedData typedDataWithData:data type:FlutterStandardDataTypeFloat32];
}
+ (instancetype)typedDataWithFloat64:(NSData*)data {
return [FlutterStandardTypedData typedDataWithData:data type:FlutterStandardDataTypeFloat64];
}
@ -443,6 +447,7 @@ using namespace flutter;
case FlutterStandardFieldUInt8Data:
case FlutterStandardFieldInt32Data:
case FlutterStandardFieldInt64Data:
case FlutterStandardFieldFloat32Data:
case FlutterStandardFieldFloat64Data:
return [self readTypedDataOfType:FlutterStandardDataTypeForField(field)];
case FlutterStandardFieldList: {

View File

@ -16,22 +16,47 @@ typedef NS_ENUM(NSInteger, FlutterStandardField) {
FlutterStandardFieldIntHex,
FlutterStandardFieldFloat64,
FlutterStandardFieldString,
// The following must match the corresponding order from `FlutterStandardDataType`.
FlutterStandardFieldUInt8Data,
FlutterStandardFieldInt32Data,
FlutterStandardFieldInt64Data,
FlutterStandardFieldFloat64Data,
FlutterStandardFieldList,
FlutterStandardFieldMap,
FlutterStandardFieldFloat32Data,
};
namespace flutter {
FlutterStandardField FlutterStandardFieldForDataType(FlutterStandardDataType type) {
return (FlutterStandardField)(type + FlutterStandardFieldUInt8Data);
switch (type) {
case FlutterStandardDataTypeUInt8:
return FlutterStandardFieldUInt8Data;
case FlutterStandardDataTypeInt32:
return FlutterStandardFieldInt32Data;
case FlutterStandardDataTypeInt64:
return FlutterStandardFieldInt64Data;
case FlutterStandardDataTypeFloat32:
return FlutterStandardFieldFloat32Data;
case FlutterStandardDataTypeFloat64:
return FlutterStandardFieldFloat64Data;
}
}
FlutterStandardDataType FlutterStandardDataTypeForField(FlutterStandardField field) {
return (FlutterStandardDataType)(field - FlutterStandardFieldUInt8Data);
switch (field) {
case FlutterStandardFieldUInt8Data:
return FlutterStandardDataTypeUInt8;
case FlutterStandardFieldInt32Data:
return FlutterStandardDataTypeInt32;
case FlutterStandardFieldInt64Data:
return FlutterStandardDataTypeInt64;
case FlutterStandardFieldFloat32Data:
return FlutterStandardDataTypeFloat32;
case FlutterStandardFieldFloat64Data:
return FlutterStandardDataTypeFloat64;
default:
return FlutterStandardDataTypeUInt8;
}
}
UInt8 elementSizeForFlutterStandardDataType(FlutterStandardDataType type) {
switch (type) {
case FlutterStandardDataTypeUInt8:
@ -40,6 +65,8 @@ UInt8 elementSizeForFlutterStandardDataType(FlutterStandardDataType type) {
return 4;
case FlutterStandardDataTypeInt64:
return 8;
case FlutterStandardDataTypeFloat32:
return 4;
case FlutterStandardDataTypeFloat64:
return 8;
}

View File

@ -176,6 +176,13 @@ TEST(FlutterStandardCodec, CanEncodeAndDecodeInt64Array) {
checkEncodeDecode(value);
}
TEST(FlutterStandardCodec, CanEncodeAndDecodeFloat32Array) {
uint8_t bytes[8] = {0xd8, 0x0f, 0x49, 0x40, 0x00, 0x00, 0x7a, 0x44};
NSData* data = [NSData dataWithBytes:bytes length:8];
FlutterStandardTypedData* value = [FlutterStandardTypedData typedDataWithFloat32:data];
checkEncodeDecode(value);
}
TEST(FlutterStandardCodec, CanEncodeAndDecodeFloat64Array) {
uint8_t bytes[16] = {0xBA, 0x5E, 0xBA, 0x11, 0xff, 0xff, 0xff, 0xff,
0xBA, 0x5E, 0xBA, 0x11, 0xff, 0xff, 0xff, 0xff};