mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Serialize and assert type information in mapbuffer
Summary: Serializes type information along with key/value in MapBuffer, asserting the data type on Java side during read. At the moment, accessing value with incorrect will result in a crash. Other changes: Adds a `getType` method to verify types before accessing them. Removes `null` as a type, as just not inserting value and checking for its existence with `hasKey` is more optimal. Changelog: [Internal] Reviewed By: mdvacca Differential Revision: D33656841 fbshipit-source-id: 23a78daa0d84704aab141088b5dfe881e9609472
This commit is contained in:
committed by
Facebook GitHub Bot
parent
d287598d23
commit
adb2167640
+95
-32
@@ -25,17 +25,32 @@ public class ReadableMapBuffer implements Iterable<ReadableMapBuffer.MapBufferEn
|
||||
ReadableMapBufferSoLoader.staticInit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Data types supported by MapBuffer. Keep in sync with definition in `MapBuffer.h`, as enum
|
||||
* serialization relies on correct order.
|
||||
*/
|
||||
public enum DataType {
|
||||
BOOL,
|
||||
INT,
|
||||
DOUBLE,
|
||||
STRING,
|
||||
MAP;
|
||||
}
|
||||
|
||||
// Value used to verify if the data is serialized with LittleEndian order.
|
||||
private static final int ALIGNMENT = 0xFE;
|
||||
|
||||
// 8 bytes = 2 (alignment) + 2 (count) + 4 (size)
|
||||
private static final int HEADER_SIZE = 8;
|
||||
|
||||
// key size = 2 bytes
|
||||
private static final int KEY_SIZE = 2;
|
||||
// 10 bytes = 2 (key) + 2 (type) + 8 (value)
|
||||
private static final int BUCKET_SIZE = 12;
|
||||
|
||||
// 10 bytes = 2 bytes key + 8 bytes value
|
||||
private static final int BUCKET_SIZE = 10;
|
||||
// 2 bytes = 2 (key)
|
||||
private static final int TYPE_OFFSET = 2;
|
||||
|
||||
// 4 bytes = 2 (key) + 2 (type)
|
||||
private static final int VALUE_OFFSET = 4;
|
||||
|
||||
private static final int INT_SIZE = 4;
|
||||
|
||||
@@ -65,17 +80,6 @@ public class ReadableMapBuffer implements Iterable<ReadableMapBuffer.MapBufferEn
|
||||
return HEADER_SIZE + BUCKET_SIZE * bucketIndex;
|
||||
}
|
||||
|
||||
private int getValueOffsetForKey(int key) {
|
||||
importByteBufferAndReadHeader();
|
||||
int bucketIndex = getBucketIndexForKey(key);
|
||||
if (bucketIndex == -1) {
|
||||
// TODO T83483191: Add tests
|
||||
throw new IllegalArgumentException("Unable to find key: " + key);
|
||||
}
|
||||
assertKeyExists(key, bucketIndex);
|
||||
return getKeyOffsetForBucketIndex(bucketIndex) + KEY_SIZE;
|
||||
}
|
||||
|
||||
// returns the relative offset of the first byte of dynamic data
|
||||
private int getOffsetForDynamicData() {
|
||||
// TODO T83483191: check if there's dynamic data?
|
||||
@@ -88,6 +92,7 @@ public class ReadableMapBuffer implements Iterable<ReadableMapBuffer.MapBufferEn
|
||||
* (log(n))
|
||||
*/
|
||||
private int getBucketIndexForKey(int key) {
|
||||
importByteBufferAndReadHeader();
|
||||
int lo = 0;
|
||||
int hi = getCount() - 1;
|
||||
while (lo <= hi) {
|
||||
@@ -104,8 +109,34 @@ public class ReadableMapBuffer implements Iterable<ReadableMapBuffer.MapBufferEn
|
||||
return -1;
|
||||
}
|
||||
|
||||
private int readUnsignedShort(int position) {
|
||||
return mBuffer.getShort(position) & 0xFFFF;
|
||||
private DataType readDataType(int bucketIndex) {
|
||||
int value = readUnsignedShort(getKeyOffsetForBucketIndex(bucketIndex) + TYPE_OFFSET);
|
||||
return DataType.values()[value];
|
||||
}
|
||||
|
||||
private int getTypedValueOffsetForKey(int key, DataType expected) {
|
||||
int bucketIndex = getBucketIndexForKey(key);
|
||||
DataType dataType = readDataType(bucketIndex);
|
||||
if (bucketIndex == -1) {
|
||||
throw new IllegalArgumentException("Key not found: " + key);
|
||||
}
|
||||
|
||||
if (dataType != expected) {
|
||||
throw new IllegalStateException(
|
||||
"Expected "
|
||||
+ expected
|
||||
+ " for key: "
|
||||
+ key
|
||||
+ " found "
|
||||
+ dataType.toString()
|
||||
+ " instead.");
|
||||
}
|
||||
|
||||
return getKeyOffsetForBucketIndex(bucketIndex) + VALUE_OFFSET;
|
||||
}
|
||||
|
||||
private int readUnsignedShort(int bufferPosition) {
|
||||
return mBuffer.getShort(bufferPosition) & 0xFFFF;
|
||||
}
|
||||
|
||||
private double readDoubleValue(int bufferPosition) {
|
||||
@@ -169,6 +200,17 @@ public class ReadableMapBuffer implements Iterable<ReadableMapBuffer.MapBufferEn
|
||||
return getBucketIndexForKey(key) != -1;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public DataType getType(int key) {
|
||||
int bucketIndex = getBucketIndexForKey(key);
|
||||
|
||||
if (bucketIndex == -1) {
|
||||
throw new IllegalArgumentException("Key not found: " + key);
|
||||
}
|
||||
|
||||
return readDataType(bucketIndex);
|
||||
}
|
||||
|
||||
/** @return amount of elements stored into the MapBuffer */
|
||||
public int getCount() {
|
||||
importByteBufferAndReadHeader();
|
||||
@@ -180,8 +222,7 @@ public class ReadableMapBuffer implements Iterable<ReadableMapBuffer.MapBufferEn
|
||||
* @return return the int associated to the Key received as a parameter.
|
||||
*/
|
||||
public int getInt(int key) {
|
||||
// TODO T83483191: extract common code of "get methods"
|
||||
return readIntValue(getValueOffsetForKey(key));
|
||||
return readIntValue(getTypedValueOffsetForKey(key, DataType.INT));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -189,7 +230,7 @@ public class ReadableMapBuffer implements Iterable<ReadableMapBuffer.MapBufferEn
|
||||
* @return return the double associated to the Key received as a parameter.
|
||||
*/
|
||||
public double getDouble(int key) {
|
||||
return readDoubleValue(getValueOffsetForKey(key));
|
||||
return readDoubleValue(getTypedValueOffsetForKey(key, DataType.DOUBLE));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -197,11 +238,11 @@ public class ReadableMapBuffer implements Iterable<ReadableMapBuffer.MapBufferEn
|
||||
* @return return the int associated to the Key received as a parameter.
|
||||
*/
|
||||
public String getString(int key) {
|
||||
return readStringValue(getValueOffsetForKey(key));
|
||||
return readStringValue(getTypedValueOffsetForKey(key, DataType.STRING));
|
||||
}
|
||||
|
||||
public boolean getBoolean(int key) {
|
||||
return readBooleanValue(getValueOffsetForKey(key));
|
||||
return readBooleanValue(getTypedValueOffsetForKey(key, DataType.BOOL));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -209,7 +250,7 @@ public class ReadableMapBuffer implements Iterable<ReadableMapBuffer.MapBufferEn
|
||||
* @return return the int associated to the Key received as a parameter.
|
||||
*/
|
||||
public ReadableMapBuffer getMapBuffer(int key) {
|
||||
return readMapBufferValue(getValueOffsetForKey(key));
|
||||
return readMapBufferValue(getTypedValueOffsetForKey(key, DataType.MAP));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -287,39 +328,61 @@ public class ReadableMapBuffer implements Iterable<ReadableMapBuffer.MapBufferEn
|
||||
mBucketOffset = position;
|
||||
}
|
||||
|
||||
private void assertType(DataType expected) {
|
||||
DataType dataType = getType();
|
||||
if (expected != dataType) {
|
||||
throw new IllegalStateException(
|
||||
"Expected "
|
||||
+ expected
|
||||
+ " for key: "
|
||||
+ getKey()
|
||||
+ " found "
|
||||
+ dataType.toString()
|
||||
+ " instead.");
|
||||
}
|
||||
}
|
||||
|
||||
/** @return a {@link short} that represents the key of this {@link MapBufferEntry}. */
|
||||
public int getKey() {
|
||||
return readUnsignedShort(mBucketOffset);
|
||||
}
|
||||
|
||||
public DataType getType() {
|
||||
return DataType.values()[readUnsignedShort(mBucketOffset + TYPE_OFFSET)];
|
||||
}
|
||||
|
||||
/** @return the double value that is stored in this {@link MapBufferEntry}. */
|
||||
public double getDouble(double defaultValue) {
|
||||
// TODO T83483191 Extend serialization of MapBuffer to add type checking
|
||||
public double getDouble() {
|
||||
// TODO T83483191 Extend serialization of MapBuffer to return null if there's no value
|
||||
// stored in this MapBufferEntry.
|
||||
return readDoubleValue(mBucketOffset + KEY_SIZE);
|
||||
assertType(DataType.DOUBLE);
|
||||
return readDoubleValue(mBucketOffset + VALUE_OFFSET);
|
||||
}
|
||||
|
||||
/** @return the int value that is stored in this {@link MapBufferEntry}. */
|
||||
public int getInt(int defaultValue) {
|
||||
return readIntValue(mBucketOffset + KEY_SIZE);
|
||||
public int getInt() {
|
||||
assertType(DataType.INT);
|
||||
return readIntValue(mBucketOffset + VALUE_OFFSET);
|
||||
}
|
||||
|
||||
/** @return the boolean value that is stored in this {@link MapBufferEntry}. */
|
||||
public boolean getBoolean(boolean defaultValue) {
|
||||
return readBooleanValue(mBucketOffset + KEY_SIZE);
|
||||
public boolean getBoolean() {
|
||||
assertType(DataType.BOOL);
|
||||
return readBooleanValue(mBucketOffset + VALUE_OFFSET);
|
||||
}
|
||||
|
||||
/** @return the String value that is stored in this {@link MapBufferEntry}. */
|
||||
public @Nullable String getString() {
|
||||
return readStringValue(mBucketOffset + KEY_SIZE);
|
||||
assertType(DataType.STRING);
|
||||
return readStringValue(mBucketOffset + VALUE_OFFSET);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the {@link ReadableMapBuffer} value that is stored in this {@link MapBufferEntry}.
|
||||
*/
|
||||
public @Nullable ReadableMapBuffer getReadableMapBuffer() {
|
||||
return readMapBufferValue(mBucketOffset + KEY_SIZE);
|
||||
assertType(DataType.MAP);
|
||||
return readMapBufferValue(mBucketOffset + VALUE_OFFSET);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,10 +149,10 @@ public class TextAttributeProps {
|
||||
ReadableMapBuffer.MapBufferEntry entry = iterator.next();
|
||||
switch (entry.getKey()) {
|
||||
case TA_KEY_FOREGROUND_COLOR:
|
||||
result.setColor(entry.getInt(0));
|
||||
result.setColor(entry.getInt());
|
||||
break;
|
||||
case TA_KEY_BACKGROUND_COLOR:
|
||||
result.setBackgroundColor(entry.getInt(0));
|
||||
result.setBackgroundColor(entry.getInt());
|
||||
break;
|
||||
case TA_KEY_OPACITY:
|
||||
break;
|
||||
@@ -160,7 +160,7 @@ public class TextAttributeProps {
|
||||
result.setFontFamily(entry.getString());
|
||||
break;
|
||||
case TA_KEY_FONT_SIZE:
|
||||
result.setFontSize((float) entry.getDouble(UNSET));
|
||||
result.setFontSize((float) entry.getDouble());
|
||||
break;
|
||||
case TA_KEY_FONT_SIZE_MULTIPLIER:
|
||||
break;
|
||||
@@ -174,13 +174,13 @@ public class TextAttributeProps {
|
||||
result.setFontVariant(entry.getReadableMapBuffer());
|
||||
break;
|
||||
case TA_KEY_ALLOW_FONT_SCALING:
|
||||
result.setAllowFontScaling(entry.getBoolean(true));
|
||||
result.setAllowFontScaling(entry.getBoolean());
|
||||
break;
|
||||
case TA_KEY_LETTER_SPACING:
|
||||
result.setLetterSpacing((float) entry.getDouble(Float.NaN));
|
||||
result.setLetterSpacing((float) entry.getDouble());
|
||||
break;
|
||||
case TA_KEY_LINE_HEIGHT:
|
||||
result.setLineHeight((float) entry.getDouble(UNSET));
|
||||
result.setLineHeight((float) entry.getDouble());
|
||||
break;
|
||||
case TA_KEY_ALIGNMENT:
|
||||
break;
|
||||
@@ -194,10 +194,10 @@ public class TextAttributeProps {
|
||||
case TA_KEY_TEXT_DECORATION_STYLE:
|
||||
break;
|
||||
case TA_KEY_TEXT_SHADOW_RAIDUS:
|
||||
result.setTextShadowRadius(entry.getInt(1));
|
||||
result.setTextShadowRadius(entry.getInt());
|
||||
break;
|
||||
case TA_KEY_TEXT_SHADOW_COLOR:
|
||||
result.setTextShadowColor(entry.getInt(DEFAULT_TEXT_SHADOW_COLOR));
|
||||
result.setTextShadowColor(entry.getInt());
|
||||
break;
|
||||
case TA_KEY_IS_HIGHLIGHTED:
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user