1.7 generic array handling, updated escaping
This commit is contained in:
parent
f483844d65
commit
0a02524d86
6 changed files with 163 additions and 214 deletions
|
|
@ -5,12 +5,12 @@
|
|||
<parent>
|
||||
<groupId>nl.sander</groupId>
|
||||
<artifactId>jsonthingy-pom</artifactId>
|
||||
<version>1.6</version>
|
||||
<version>1.7</version>
|
||||
</parent>
|
||||
|
||||
<name>JsonToy-JMH</name>
|
||||
<artifactId>jsonthingy-jmhtests</artifactId>
|
||||
<version>1.6</version>
|
||||
<version>1.7</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<properties>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,79 @@
|
|||
package nl.sanderhautvast.json.jmh;
|
||||
|
||||
import org.openjdk.jmh.annotations.*;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@State(Scope.Thread)
|
||||
@BenchmarkMode(Mode.AverageTime)
|
||||
@OutputTimeUnit(TimeUnit.NANOSECONDS)
|
||||
public class ArrayReflectionBenchmarks {
|
||||
|
||||
private static final int ITERATIONS = 10;
|
||||
|
||||
public static void main(String[] args) {
|
||||
new ArrayReflectionBenchmarks().testReflectiveArray();
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void testReflectiveArray() {
|
||||
int[] r1 = {1};
|
||||
int[] r2 = {1, 2};
|
||||
int[][] table1 = {r1, r2};
|
||||
int[][] table2 = {r1, r2};
|
||||
int[][][] schema = {table1, table2};
|
||||
for (int i = 0; i < ITERATIONS; i++) {
|
||||
addArrayElements(schema);
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void testNonReflectiveArray() {
|
||||
int[] r1 = {1};
|
||||
int[] r2 = {1, 2};
|
||||
int[][] table1 = {r1, r2};
|
||||
int[][] table2 = {r1, r2};
|
||||
int[][][] schema = {table1, table2};
|
||||
for (int i = 0; i < ITERATIONS; i++) {
|
||||
addIntegerArray(schema);
|
||||
}
|
||||
}
|
||||
|
||||
private int addArrayElements(Object o) {
|
||||
int sum = 0;
|
||||
if (o.getClass().isArray()) {
|
||||
int length = Array.getLength(o);
|
||||
for (int i = 0; i < length; i++) {
|
||||
sum += addArrayElements(Array.get(o, i));
|
||||
}
|
||||
} else {
|
||||
sum += (Integer) o;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
private int addIntegerArray(int[][][] o) {
|
||||
int sum = 0;
|
||||
for (int[][] ints : o) {
|
||||
sum += addIntegerArray2(ints);
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
private int addIntegerArray2(int[][] o) {
|
||||
int sum = 0;
|
||||
for (int[] ints : o) {
|
||||
sum += addIntegerArray3(ints);
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
private int addIntegerArray3(int[] o) {
|
||||
int sum = 0;
|
||||
for (int j : o) {
|
||||
sum += j;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
}
|
||||
|
|
@ -8,14 +8,14 @@ import org.openjdk.jmh.annotations.*;
|
|||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@State(Scope.Thread)
|
||||
@BenchmarkMode(Mode.AverageTime)
|
||||
@OutputTimeUnit(TimeUnit.NANOSECONDS)
|
||||
//@State(Scope.Thread)
|
||||
//@BenchmarkMode(Mode.AverageTime)
|
||||
//@OutputTimeUnit(TimeUnit.NANOSECONDS)
|
||||
public class Benchmarks {
|
||||
|
||||
private static final int ITERATIONS = 10;
|
||||
|
||||
@Benchmark
|
||||
// @Benchmark
|
||||
public void testJson() {
|
||||
Bean1 bean1;
|
||||
Bean2 bean2;
|
||||
|
|
@ -30,7 +30,7 @@ public class Benchmarks {
|
|||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
// @Benchmark
|
||||
public void testJackson() throws JsonProcessingException {
|
||||
Bean1 bean1;
|
||||
Bean2 bean2;
|
||||
|
|
|
|||
|
|
@ -5,12 +5,12 @@
|
|||
<parent>
|
||||
<groupId>nl.sander</groupId>
|
||||
<artifactId>jsonthingy-pom</artifactId>
|
||||
<version>1.6</version>
|
||||
<version>1.7</version>
|
||||
</parent>
|
||||
|
||||
<name>JsonToy</name>
|
||||
<artifactId>jsonthingy</artifactId>
|
||||
<version>1.6</version>
|
||||
<version>1.7</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<properties>
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import org.objectweb.asm.ClassReader;
|
|||
import org.objectweb.asm.ClassWriter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
|
|
@ -20,6 +21,15 @@ public class Mapper {
|
|||
private static final Map<Class<?>, BaseMapper<?>> mappers = new ConcurrentHashMap<>();
|
||||
|
||||
private static final ByteClassLoader generatedClassesLoader = new ByteClassLoader();
|
||||
public static final char[] TAB = {'\\', 't'};
|
||||
public static final char[] DOUBLEQUOTE = {'\\', '\"'};
|
||||
public static final char[] SLASH = {'\\', '/'};
|
||||
public static final char[] RETURN = {'\\', 'r'};
|
||||
public static final char[] BACKSLASH = {'\\', '\\'};
|
||||
public static final char[] NEWLINE = {'\\', 'n'};
|
||||
public static final char[] BELL = {'\\', 'b'};
|
||||
public static final char[] FORMFEED = {'\\', 'f'};
|
||||
private static final char[][] MAP = createEscapeMap();
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -49,25 +59,7 @@ public class Mapper {
|
|||
} else {
|
||||
Class<?> type = value.getClass();
|
||||
if (type.isArray()) {
|
||||
if (value instanceof byte[]) {
|
||||
array(b, (byte[]) value);
|
||||
} else if (value instanceof int[]) {
|
||||
array(b, (int[]) value);
|
||||
} else if (value instanceof short[]) {
|
||||
array(b, (short[]) value);
|
||||
} else if (value instanceof boolean[]) {
|
||||
array(b, (boolean[]) value);
|
||||
} else if (value instanceof char[]) {
|
||||
array(b, (char[]) value);
|
||||
} else if (value instanceof long[]) {
|
||||
array(b, (long[]) value);
|
||||
} else if (value instanceof float[]) {
|
||||
array(b, (float[]) value);
|
||||
} else if (value instanceof double[]) {
|
||||
array(b, (double[]) value);
|
||||
} else {
|
||||
array(b, (Object[]) value);
|
||||
}
|
||||
array(b, value);
|
||||
} else if (value instanceof Collection) {
|
||||
list(b, (Collection) value);
|
||||
} else if (value instanceof Map) {
|
||||
|
|
@ -104,140 +96,16 @@ public class Mapper {
|
|||
}
|
||||
}
|
||||
|
||||
private static void array(StringBuilder b, Object[] array) {
|
||||
if (array.length == 0) {
|
||||
b.append("[]");
|
||||
} else {
|
||||
Object first = array[0];
|
||||
b.append("[");
|
||||
Mapper.json(b, first);
|
||||
Arrays.stream(array).skip(1)
|
||||
.forEach(element -> {
|
||||
b.append(",");
|
||||
Mapper.json(b, element);
|
||||
});
|
||||
b.append("]");
|
||||
}
|
||||
}
|
||||
|
||||
private static void array(StringBuilder b, byte[] array) {
|
||||
if (array.length == 0) {
|
||||
b.append("[]");
|
||||
} else {
|
||||
byte first = array[0];
|
||||
b.append("[");
|
||||
json(b, first);
|
||||
for (int i = 1; i < array.length; i++) {
|
||||
b.append(",");
|
||||
json(b, array[i]);
|
||||
}
|
||||
b.append("]");
|
||||
}
|
||||
}
|
||||
|
||||
private static void array(StringBuilder b, short[] array) {
|
||||
if (array.length == 0) {
|
||||
b.append("[]");
|
||||
} else {
|
||||
short first = array[0];
|
||||
b.append("[");
|
||||
json(b, first);
|
||||
for (int i = 1; i < array.length; i++) {
|
||||
b.append(",");
|
||||
json(b, array[i]);
|
||||
}
|
||||
b.append("]");
|
||||
}
|
||||
}
|
||||
|
||||
private static void array(StringBuilder b, long[] array) {
|
||||
if (array.length == 0) {
|
||||
b.append("[]");
|
||||
} else {
|
||||
long first = array[0];
|
||||
b.append("[");
|
||||
json(b, first);
|
||||
for (int i = 1; i < array.length; i++) {
|
||||
b.append(",");
|
||||
json(b, array[i]);
|
||||
}
|
||||
b.append("]");
|
||||
}
|
||||
}
|
||||
|
||||
private static void array(StringBuilder b, boolean[] array) {
|
||||
if (array.length == 0) {
|
||||
b.append("[]");
|
||||
} else {
|
||||
boolean first = array[0];
|
||||
b.append("[");
|
||||
json(b, first);
|
||||
for (int i = 1; i < array.length; i++) {
|
||||
b.append(",");
|
||||
json(b, array[i]);
|
||||
}
|
||||
b.append("]");
|
||||
}
|
||||
}
|
||||
|
||||
private static void array(StringBuilder b, double[] array) {
|
||||
if (array.length == 0) {
|
||||
b.append("[]");
|
||||
} else {
|
||||
double first = array[0];
|
||||
b.append("[");
|
||||
json(b, first);
|
||||
for (int i = 1; i < array.length; i++) {
|
||||
b.append(",");
|
||||
json(b, array[i]);
|
||||
}
|
||||
b.append("]");
|
||||
}
|
||||
}
|
||||
|
||||
private static void array(StringBuilder b, char[] array) {
|
||||
if (array.length == 0) {
|
||||
b.append("[]");
|
||||
} else {
|
||||
char first = array[0];
|
||||
b.append("[");
|
||||
json(b, first);
|
||||
for (int i = 1; i < array.length; i++) {
|
||||
b.append(",");
|
||||
json(b, array[i]);
|
||||
}
|
||||
b.append("]");
|
||||
}
|
||||
}
|
||||
|
||||
private static void array(StringBuilder b, float[] array) {
|
||||
if (array.length == 0) {
|
||||
b.append("[]");
|
||||
} else {
|
||||
float first = array[0];
|
||||
b.append("[");
|
||||
json(b, first);
|
||||
for (int i = 1; i < array.length; i++) {
|
||||
b.append(",");
|
||||
json(b, array[i]);
|
||||
}
|
||||
b.append("]");
|
||||
}
|
||||
}
|
||||
|
||||
private static void array(StringBuilder b, int[] array) {
|
||||
if (array.length == 0) {
|
||||
b.append("[]");
|
||||
} else {
|
||||
int first = array[0];
|
||||
b.append("[");
|
||||
json(b, first);
|
||||
for (int i = 1; i < array.length; i++) {
|
||||
b.append(",");
|
||||
json(b, array[i]);
|
||||
}
|
||||
b.append("]");
|
||||
//TODO make this more performant
|
||||
private static void array(StringBuilder b, Object value) {
|
||||
b.append("[");
|
||||
StringJoiner joiner = new StringJoiner(",");
|
||||
for (int i = 0; i < Array.getLength(value); i++) {
|
||||
Object arrayElement = Array.get(value, i);
|
||||
joiner.add(Mapper.json(arrayElement)); // recursie
|
||||
}
|
||||
b.append(joiner);
|
||||
b.append("]");
|
||||
}
|
||||
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
|
|
@ -342,61 +210,63 @@ public class Mapper {
|
|||
escape(b, String.valueOf(c));
|
||||
}
|
||||
|
||||
static void escape(StringBuilder b, String value) {
|
||||
int offset = b.length();
|
||||
b.append(value);
|
||||
int i = offset;
|
||||
while (i < b.length()) {
|
||||
char c = b.charAt(i);
|
||||
switch (c) {
|
||||
case '\t':
|
||||
b.replace(i, i + 1, "\\");
|
||||
b.insert(i + 1, "t");
|
||||
break;
|
||||
case '\"':
|
||||
b.replace(i, i + 1, "\\");
|
||||
b.insert(++i, "\"");
|
||||
break;
|
||||
case '/':
|
||||
b.replace(i, i + 1, "\\");
|
||||
b.insert(++i, "/");
|
||||
break;
|
||||
case '\r':
|
||||
b.replace(i, i + 1, "\\");
|
||||
b.insert(++i, "r");
|
||||
break;
|
||||
case '\n':
|
||||
b.replace(i, i + 1, "\\");
|
||||
b.insert(++i, "n");
|
||||
break;
|
||||
case '\b':
|
||||
b.replace(i, i + 1, "\\");
|
||||
b.insert(++i, "b");
|
||||
break;
|
||||
case '\f':
|
||||
b.replace(i, i + 1, "\\");
|
||||
b.insert(++i, "f");
|
||||
break;
|
||||
case '\\':
|
||||
b.replace(i, i + 1, "\\");
|
||||
b.insert(++i, "\\");
|
||||
break;
|
||||
case '\'':
|
||||
break;
|
||||
default:
|
||||
if ((c <= '\u001F') || (c >= '\u007F' && c <= '\u009F') || (c >= '\u2000' && c <= '\u20FF')) {
|
||||
String ss = Integer.toHexString(c);
|
||||
b.replace(i, i + 1, "\\");
|
||||
b.insert(++i, "u");
|
||||
for (int k = 0; k < 4 - ss.length(); k++) {
|
||||
b.insert(++i, '0');
|
||||
}
|
||||
b.insert(++i, ss.toUpperCase());
|
||||
}
|
||||
private static char[][] createEscapeMap() {
|
||||
char[][] charmap = new char[0x2100][];
|
||||
for (int i = 0; i < 0x2100; i++) {
|
||||
char c = (char) i;
|
||||
char[] replacement;
|
||||
if (c == '\t') {
|
||||
replacement = TAB;
|
||||
} else if (c == '\"') {
|
||||
replacement = DOUBLEQUOTE;
|
||||
} else if (c == '/') {
|
||||
replacement = SLASH;
|
||||
} else if (c == '\r') {
|
||||
replacement = RETURN;
|
||||
} else if (c == '\\') {
|
||||
replacement = BACKSLASH;
|
||||
} else if (c == '\n') {
|
||||
replacement = NEWLINE;
|
||||
} else if (c == '\b') {
|
||||
replacement = BELL;
|
||||
} else if (c == '\f') {
|
||||
replacement = FORMFEED;
|
||||
} else if ((c <= '\u001F') || (c >= '\u007F' && c <= '\u009F') || (c >= '\u2000')) {
|
||||
replacement = new char[6];
|
||||
replacement[0] = '\\';
|
||||
replacement[1] = 'u';
|
||||
|
||||
String hex = Integer.toHexString(c).toUpperCase();
|
||||
int hexlen = hex.length();
|
||||
for (int k = 0; k < 4 - hexlen; k++) {
|
||||
replacement[k + 2] = '0';
|
||||
}
|
||||
for (int k = 0; k < hexlen; k++) {
|
||||
replacement[6 - hexlen + k] = hex.charAt(k);
|
||||
}
|
||||
} else {
|
||||
replacement = new char[]{c};
|
||||
}
|
||||
charmap[i] = replacement;
|
||||
}
|
||||
|
||||
return charmap;
|
||||
}
|
||||
|
||||
//both methods are equally slow it seems
|
||||
//mainly because we can't do a batch copy into the stringbuilder and have to map every character individually
|
||||
static void escape(StringBuilder b, String value) {
|
||||
for (int i = 0; i < value.length(); i++) {
|
||||
int c = value.charAt(i);
|
||||
|
||||
if (c < 0x2100) {
|
||||
b.append(MAP[c]);
|
||||
} else {
|
||||
b.append((char) c);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
2
pom.xml
2
pom.xml
|
|
@ -5,7 +5,7 @@
|
|||
<name>JsonToy</name>
|
||||
<groupId>nl.sander</groupId>
|
||||
<artifactId>jsonthingy-pom</artifactId>
|
||||
<version>1.6</version>
|
||||
<version>1.7</version>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<modules>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue