Skip to content

Commit

Permalink
PERF : Compress Array String element size
Browse files Browse the repository at this point in the history
  • Loading branch information
[email protected] authored and NSV committed Oct 22, 2018
1 parent aada4f0 commit 84c7801
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 61 deletions.
39 changes: 26 additions & 13 deletions ponysdk/src/main/java/com/ponysdk/core/model/ArrayValueModel.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,27 +25,36 @@

public enum ArrayValueModel {

NULL(ValueTypeModel.NULL_SIZE),
BOOLEAN_FALSE(ArrayValueModel.BOOLEAN_SIZE),
BOOLEAN_TRUE(ArrayValueModel.BOOLEAN_SIZE),
BYTE(ValueTypeModel.BYTE_SIZE),
SHORT(ValueTypeModel.SHORT_SIZE),
INTEGER(ValueTypeModel.INTEGER_SIZE),
LONG(ValueTypeModel.LONG_SIZE),
DOUBLE(ValueTypeModel.DOUBLE_SIZE),
FLOAT(ValueTypeModel.FLOAT_SIZE),
STRING_ASCII(ArrayValueModel.STRING_MIN_SIZE),
STRING_UTF8(ArrayValueModel.STRING_MIN_SIZE);
NULL(ValueTypeModel.NULL_SIZE, false),
BOOLEAN_FALSE(ArrayValueModel.BOOLEAN_SIZE, false),
BOOLEAN_TRUE(ArrayValueModel.BOOLEAN_SIZE, false),
BYTE(ValueTypeModel.BYTE_SIZE, false),
SHORT(ValueTypeModel.SHORT_SIZE, false),
INTEGER(ValueTypeModel.INTEGER_SIZE, false),
LONG(ValueTypeModel.LONG_SIZE, false),
DOUBLE(ValueTypeModel.DOUBLE_SIZE, false),
FLOAT(ValueTypeModel.FLOAT_SIZE, false),

STRING_ASCII_UINT8_LENGTH(ArrayValueModel.STRING_UINT8_LENGTH_MIN_SIZE, true),
STRING_ASCII_UINT16_LENGTH(ArrayValueModel.STRING_UINT16_LENGTH_SIZE, true),

STRING_UTF8_UINT8_LENGTH(ArrayValueModel.STRING_UINT8_LENGTH_MIN_SIZE, true),
STRING_UTF8_UINT16_LENGTH(ArrayValueModel.STRING_UINT16_LENGTH_SIZE, true),
STRING_UTF8_INT32_LENGTH(ArrayValueModel.STRING_INT32_LENGTH_MIN_SIZE, true);

private static final int BOOLEAN_SIZE = 0;
private static final int STRING_MIN_SIZE = 2;
private static final int STRING_UINT8_LENGTH_MIN_SIZE = 1;
private static final int STRING_UINT16_LENGTH_SIZE = 2;
private static final int STRING_INT32_LENGTH_MIN_SIZE = 4;

private final int minSize;
private final boolean dynamicSize;

private static final ArrayValueModel[] VALUES = ArrayValueModel.values();

private ArrayValueModel(final int minSize) {
private ArrayValueModel(final int minSize, final boolean dynamicSize) {
this.minSize = minSize;
this.dynamicSize = dynamicSize;
}

public int getMinSize() {
Expand All @@ -56,6 +65,10 @@ public byte getValue() {
return (byte) ordinal();
}

public boolean isDynamicSize() {
return dynamicSize;
}

public static ArrayValueModel fromRawValue(final int rawValue) {
return VALUES[rawValue];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -252,16 +252,7 @@ private void putArrayElement(final Object o) throws IOException {
} else if (o instanceof Integer) {
putCompressedInt((int) o);
} else if (o instanceof String) {
final String s = (String) o;
final byte[] bytes = s.getBytes(UTF8_CHARSET);
final int length = bytes.length;
if (length > MAX_UNSIGNED_SHORT_VALUE) {
throw new IllegalArgumentException("String array element too big (" + s.length() + " > " + MAX_UNSIGNED_BYTE_VALUE
+ "), use a Json Object instead : " + s.substring(0, 100) + "...");
}
put(length == s.length() ? ArrayValueModel.STRING_ASCII.getValue() : ArrayValueModel.STRING_UTF8.getValue());
putUnsignedShort(length);
put(bytes);
putArrayStringElement(o);
} else if (o instanceof Byte) {
put(ArrayValueModel.BYTE.getValue());
put((byte) o);
Expand All @@ -281,6 +272,29 @@ private void putArrayElement(final Object o) throws IOException {
}
}

private void putArrayStringElement(final Object o) throws IOException {
final String s = (String) o;
final byte[] bytes = s.getBytes(UTF8_CHARSET);
final int length = bytes.length;

if (length <= MAX_UNSIGNED_BYTE_VALUE) {
put(length == s.length() ? ArrayValueModel.STRING_ASCII_UINT8_LENGTH.getValue()
: ArrayValueModel.STRING_UTF8_UINT8_LENGTH.getValue());
putUnsignedByte((short) length);

} else if (length <= MAX_UNSIGNED_SHORT_VALUE) {
put(length == s.length() ? ArrayValueModel.STRING_ASCII_UINT16_LENGTH.getValue()
: ArrayValueModel.STRING_UTF8_UINT16_LENGTH.getValue());
putUnsignedShort(length);

} else {
put(ArrayValueModel.STRING_UTF8_INT32_LENGTH.getValue());
putInt(length);
}

put(bytes);
}

private void write(final ServerToClientModel model, final String value) throws IOException {
putModelKey(model);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -271,18 +271,12 @@ private int getArray(final Object[] array) {
for (int i = 0; i < array.length; i++) {
final ArrayValueModel arrayValueModel = ArrayValueModel.fromRawValue(getByte());
size += arrayValueModel.getMinSize();
if (arrayValueModel == ArrayValueModel.NULL) {
if (arrayValueModel.isDynamicSize()) {
array[i] = getDynamicSizeArrayElement(arrayValueModel);
} else if (arrayValueModel == ArrayValueModel.NULL) {
array[i] = null;
} else if (arrayValueModel == ArrayValueModel.INTEGER) {
array[i] = getInt();
} else if (arrayValueModel == ArrayValueModel.STRING_ASCII) {
final int messageSize = getUnsignedShort();
size += messageSize;
array[i] = decodeStringAscii(messageSize);
} else if (arrayValueModel == ArrayValueModel.STRING_UTF8) {
final int messageSize = getUnsignedShort();
size += messageSize;
array[i] = decodeStringUTF8(messageSize);
} else if (arrayValueModel == ArrayValueModel.SHORT) {
array[i] = getShort();
} else if (arrayValueModel == ArrayValueModel.BOOLEAN_FALSE) {
Expand All @@ -304,6 +298,22 @@ private int getArray(final Object[] array) {
return size;
}

private Object getDynamicSizeArrayElement(final ArrayValueModel arrayValueModel) {
final int msgSize = getArrayElementDynamicSize(arrayValueModel.getMinSize());
boolean ascii;
if (arrayValueModel == ArrayValueModel.STRING_ASCII_UINT8_LENGTH
|| arrayValueModel == ArrayValueModel.STRING_ASCII_UINT16_LENGTH) {
ascii = true;
} else if (arrayValueModel == ArrayValueModel.STRING_UTF8_UINT8_LENGTH
|| arrayValueModel == ArrayValueModel.STRING_UTF8_UINT16_LENGTH
|| arrayValueModel == ArrayValueModel.STRING_UTF8_INT32_LENGTH) {
ascii = false;
} else {
throw new IllegalArgumentException("Unsupported ArrayValueModel " + arrayValueModel);
}
return ascii ? decodeStringAscii(msgSize) : decodeStringUTF8(msgSize);
}

private String decodeStringAscii(final int size) {
checkRemainingBytes(size);
final String result = fromCharCode(buffer.subarray(position, position + size));
Expand Down Expand Up @@ -405,21 +415,27 @@ private final ServerToClientModel shiftBinaryModel() {
private void shiftArray(final int arrayLength) {
for (int i = 0; i < arrayLength; i++) {
final ArrayValueModel arrayValueModel = ArrayValueModel.fromRawValue(getByte());
if (arrayValueModel == ArrayValueModel.STRING_ASCII || arrayValueModel == ArrayValueModel.STRING_UTF8) {
final int msgLength = getUnsignedShort();
if (arrayValueModel.isDynamicSize()) {
final int msgLength = getArrayElementDynamicSize(arrayValueModel.getMinSize());
position += msgLength;
} else if (arrayValueModel == ArrayValueModel.NULL || arrayValueModel == ArrayValueModel.INTEGER
|| arrayValueModel == ArrayValueModel.SHORT || arrayValueModel == ArrayValueModel.BOOLEAN_FALSE
|| arrayValueModel == ArrayValueModel.BOOLEAN_TRUE || arrayValueModel == ArrayValueModel.DOUBLE
|| arrayValueModel == ArrayValueModel.LONG || arrayValueModel == ArrayValueModel.FLOAT
|| arrayValueModel == ArrayValueModel.BYTE) {
position += arrayValueModel.getMinSize();
} else {
throw new IllegalArgumentException("Unsupported ArrayValueModel " + arrayValueModel);
position += arrayValueModel.getMinSize();
}
}
}

private int getArrayElementDynamicSize(final int lengthOfSize) {
if (lengthOfSize == 1) {
return getUnsignedByte();
} else if (lengthOfSize == 2) {
return getUnsignedShort();
} else if (lengthOfSize == 4) {
return getInt();
} else {
throw new IllegalArgumentException("Invalid Array element lengthOfSize " + lengthOfSize);
}
}

/**
* Get the model key
*/
Expand Down
40 changes: 20 additions & 20 deletions ponysdk/src/main/java/com/ponysdk/driver/PonySDKWebDriver.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.ToIntFunction;

import javax.json.Json;
import javax.json.JsonArrayBuilder;
Expand Down Expand Up @@ -344,17 +345,10 @@ private Object readValue(final ByteBuffer b, final int minSize, final Function<B
return function.apply(b);
}

private Object getStringAscii(final ByteBuffer b) {
return getString(b, StandardCharsets.ISO_8859_1);
}

private Object getStringUTF8(final ByteBuffer b) {
return getString(b, StandardCharsets.UTF_8);
}

private Object getString(final ByteBuffer b) {
private Object getString(final ByteBuffer b, final ToIntFunction<ByteBuffer> readStringLength) {
final byte charsetStringType = b.get();
return getString(b, charsetStringType == CharsetModel.ASCII.getValue() ? StandardCharsets.ISO_8859_1 : StandardCharsets.UTF_8);
return getString(b, charsetStringType == CharsetModel.ASCII.getValue() ? StandardCharsets.ISO_8859_1 : StandardCharsets.UTF_8,
readStringLength);
}

private Object getJsonObject(final ByteBuffer b) {
Expand All @@ -371,8 +365,8 @@ private Object getJsonObject(final ByteBuffer b) {
}
}

private Object getString(final ByteBuffer b, final Charset charset) {
final int strLength = readUnsignedShort(b);
private Object getString(final ByteBuffer b, final Charset charset, final ToIntFunction<ByteBuffer> readStringLength) {
final int strLength = readStringLength.applyAsInt(b);
length += strLength;
return getString(charset, b, strLength);
}
Expand Down Expand Up @@ -408,10 +402,16 @@ private Object readArrayElementValue(final ByteBuffer b, final ArrayValueModel m
return b.getDouble();
case FLOAT:
return b.getFloat();
case STRING_ASCII:
return getStringAscii(b);
case STRING_UTF8:
return getStringUTF8(b);
case STRING_ASCII_UINT8_LENGTH:
return getString(b, StandardCharsets.ISO_8859_1, PonySDKWebDriver::readUnsignedByte);
case STRING_ASCII_UINT16_LENGTH:
return getString(b, StandardCharsets.ISO_8859_1, PonySDKWebDriver::readUnsignedShort);
case STRING_UTF8_UINT8_LENGTH:
return getString(b, StandardCharsets.UTF_8, PonySDKWebDriver::readUnsignedByte);
case STRING_UTF8_UINT16_LENGTH:
return getString(b, StandardCharsets.UTF_8, PonySDKWebDriver::readUnsignedShort);
case STRING_UTF8_INT32_LENGTH:
return getString(b, StandardCharsets.UTF_8, ByteBuffer::getInt);
default:
throw new IllegalArgumentException("ArrayValueModel " + model + " is not supported");
}
Expand All @@ -436,9 +436,9 @@ private Object readModelValue(final ByteBuffer b, final ValueTypeModel type) {
case FLOAT:
return readValue(b, 4, ByteBuffer::getFloat);
case STRING_ASCII:
return readValue(b, 2, this::getStringAscii);
return readValue(b, 2, buff -> getString(buff, StandardCharsets.ISO_8859_1, PonySDKWebDriver::readUnsignedShort));
case STRING:
return readValue(b, 3, this::getString);
return readValue(b, 2, buff -> getString(buff, PonySDKWebDriver::readUnsignedShort));
case JSON_OBJECT:
return readValue(b, 5, this::getJsonObject);
case ARRAY:
Expand Down Expand Up @@ -567,8 +567,8 @@ private ArrayValueModel readArrayValueModel(final ByteBuffer buffer) {
return ArrayValueModel.fromRawValue(buffer.get());
}

private static short readUnsignedByte(final ByteBuffer buffer) {
return (short) (buffer.get() & 0xFF);
private static int readUnsignedByte(final ByteBuffer buffer) {
return buffer.get() & 0xFF;
}

private static int readUnsignedShort(final ByteBuffer buffer) {
Expand Down

0 comments on commit 84c7801

Please sign in to comment.