Speed up SkipValue (#3365)
* add simd-benchmark * checkstyle * add simdjson benchmark * fix checkstyle * fix benchmark * fix benchmark * fix benchmark * fast skip * bug fix * fieldClassSerializable for Record * fast skip * check style * simplify skipNumber * update benchmark * update benchmark * update benchmark * optimize skipValue * optimize skipValue * optimize skipValue
This commit is contained in:
parent
1e9204fa00
commit
c17a8d4a0d
@ -0,0 +1,26 @@
|
||||
package com.alibaba.fastjson2.benchmark.simdjson;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.benchmark.eishay.EishayParseBinary;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.openjdk.jmh.infra.Blackhole;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public class GithubEvents {
|
||||
static final String file = "data/simd-json/github_events.json";
|
||||
static byte[] bytes;
|
||||
static {
|
||||
try (InputStream is = EishayParseBinary.class.getClassLoader().getResourceAsStream(file)) {
|
||||
String str = IOUtils.toString(is, "UTF-8");
|
||||
bytes = str.getBytes();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void fastjson2_parse(Blackhole bh) {
|
||||
bh.consume(JSON.parseArray(bytes));
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
benchmark/src/main/resources/data/simd-json/twitter.json
Normal file
1
benchmark/src/main/resources/data/simd-json/twitter.json
Normal file
File diff suppressed because one or more lines are too long
@ -0,0 +1,22 @@
|
||||
package com.alibaba.fastjson2.benchmark.simdjson;
|
||||
|
||||
import static com.alibaba.fastjson2.benchmark.JMH.BH;
|
||||
|
||||
public class GithubEventsTest {
|
||||
private static final GithubEvents benchmark = new GithubEvents();
|
||||
|
||||
public static void fastjson2_parse() {
|
||||
for (int j = 0; j < 5; j++) {
|
||||
long start = System.currentTimeMillis();
|
||||
for (int i = 0; i < 1000 * 10; ++i) {
|
||||
benchmark.fastjson2_parse(BH);
|
||||
}
|
||||
long millis = System.currentTimeMillis() - start;
|
||||
System.out.println("GithubEvents-fastjson2_parse : " + millis);
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
fastjson2_parse();
|
||||
}
|
||||
}
|
215
benchmark_21/pom.xml
Normal file
215
benchmark_21/pom.xml
Normal file
@ -0,0 +1,215 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.alibaba.fastjson2</groupId>
|
||||
<artifactId>fastjson2-parent</artifactId>
|
||||
<version>2.0.57-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>fastjson2-benchmark_21</artifactId>
|
||||
<name>fastjson2-benchmark_21</name>
|
||||
<description>JMH Benchmark 21 for Fastjson2</description>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<properties>
|
||||
<maven.deploy.skip>true</maven.deploy.skip>
|
||||
<maven.test.skip>true</maven.test.skip>
|
||||
<uberjar.name>fastjson2-benchmarks</uberjar.name>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
<maven.compiler.target>${maven.compiler.source}</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.fastjson2</groupId>
|
||||
<artifactId>fastjson2-extension</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
<version>${fastjson1x.version}</version>
|
||||
</dependency>
|
||||
<!--
|
||||
<dependency>
|
||||
<groupId>com.cainiao.ai</groupId>
|
||||
<artifactId>seq-csv</artifactId>
|
||||
<version>0.1.3</version>
|
||||
</dependency>
|
||||
-->
|
||||
<dependency>
|
||||
<groupId>com.caucho</groupId>
|
||||
<artifactId>hessian</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.dslplatform</groupId>
|
||||
<artifactId>dsl-json</artifactId>
|
||||
<version>2.0.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.esotericsoftware</groupId>
|
||||
<artifactId>kryo</artifactId>
|
||||
<version>5.6.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.erosb</groupId>
|
||||
<artifactId>everit-json-schema</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.protobuf</groupId>
|
||||
<artifactId>protobuf-java</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.jayway.jsonpath</groupId>
|
||||
<artifactId>json-path</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.networknt</groupId>
|
||||
<artifactId>json-schema-validator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.univocity</groupId>
|
||||
<artifactId>univocity-parsers</artifactId>
|
||||
<version>2.9.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.github.wycst</groupId>
|
||||
<artifactId>wast</artifactId>
|
||||
<version>0.0.25</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.annotation</groupId>
|
||||
<artifactId>javax.annotation-api</artifactId>
|
||||
<version>1.3.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.sourceforge.javacsv</groupId>
|
||||
<artifactId>javacsv</artifactId>
|
||||
<version>2.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.arrow</groupId>
|
||||
<artifactId>arrow-vector</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.fury</groupId>
|
||||
<artifactId>fury-core</artifactId>
|
||||
<version>0.9.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.msgpack</groupId>
|
||||
<artifactId>jackson-dataformat-msgpack</artifactId>
|
||||
<version>0.9.9</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.msgpack</groupId>
|
||||
<artifactId>msgpack-core</artifactId>
|
||||
<version>0.9.8</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openjdk.jmh</groupId>
|
||||
<artifactId>jmh-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.ow2.asm</groupId>
|
||||
<artifactId>asm</artifactId>
|
||||
<version>9.7.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.simdjson</groupId>
|
||||
<artifactId>simdjson-java</artifactId>
|
||||
<version>0.3.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openjdk.jmh</groupId>
|
||||
<artifactId>jmh-generator-annprocess</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>compile</id>
|
||||
<phase>compile</phase>
|
||||
<goals>
|
||||
<goal>compile</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>testCompile</id>
|
||||
<phase>test-compile</phase>
|
||||
<goals>
|
||||
<goal>testCompile</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<finalName>${uberjar.name}</finalName>
|
||||
<transformers>
|
||||
<transformer
|
||||
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||
<mainClass>org.openjdk.jmh.Main</mainClass>
|
||||
</transformer>
|
||||
<transformer
|
||||
implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
|
||||
</transformers>
|
||||
<filters>
|
||||
<filter>
|
||||
<!--
|
||||
Shading signed JARs will fail without this.
|
||||
http://stackoverflow.com/questions/999489/invalid-signature-file-when-attempting-to-run-a-jar
|
||||
-->
|
||||
<artifact>*:*</artifact>
|
||||
<excludes>
|
||||
<exclude>META-INF/*.SF</exclude>
|
||||
<exclude>META-INF/*.DSA</exclude>
|
||||
<exclude>META-INF/*.RSA</exclude>
|
||||
</excludes>
|
||||
</filter>
|
||||
</filters>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
@ -0,0 +1,79 @@
|
||||
package com.alibaba.fastjson2.benchmark.simdjson;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import org.openjdk.jmh.annotations.Benchmark;
|
||||
import org.openjdk.jmh.annotations.BenchmarkMode;
|
||||
import org.openjdk.jmh.annotations.Level;
|
||||
import org.openjdk.jmh.annotations.Mode;
|
||||
import org.openjdk.jmh.annotations.OutputTimeUnit;
|
||||
import org.openjdk.jmh.annotations.Param;
|
||||
import org.openjdk.jmh.annotations.Scope;
|
||||
import org.openjdk.jmh.annotations.Setup;
|
||||
import org.openjdk.jmh.annotations.State;
|
||||
import org.openjdk.jmh.runner.Runner;
|
||||
import org.openjdk.jmh.runner.RunnerException;
|
||||
import org.openjdk.jmh.runner.options.Options;
|
||||
import org.openjdk.jmh.runner.options.OptionsBuilder;
|
||||
import org.simdjson.JsonValue;
|
||||
import org.simdjson.SimdJsonParser;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static com.alibaba.fastjson2.benchmark.simdjson.SimdJsonPaddingUtil.padded;
|
||||
|
||||
@State(Scope.Benchmark)
|
||||
@BenchmarkMode(Mode.Throughput)
|
||||
@OutputTimeUnit(TimeUnit.SECONDS)
|
||||
public class ParseBenchmark {
|
||||
@Param({"/data/simd-json/twitter.json", "/data/simd-json/gsoc-2018.json", "/data/simd-json/github_events.json"})
|
||||
String fileName;
|
||||
|
||||
private final SimdJsonParser simdJsonParser = new SimdJsonParser();
|
||||
|
||||
private byte[] buffer;
|
||||
private byte[] bufferPadded;
|
||||
|
||||
@Setup(Level.Trial)
|
||||
public void setup() throws IOException {
|
||||
try (InputStream is = ParseBenchmark.class.getResourceAsStream(fileName)) {
|
||||
byte[] buf = new byte[1024 * 1024 * 64];
|
||||
int count = is.read(buf);
|
||||
buffer = Arrays.copyOf(buf, count);
|
||||
bufferPadded = padded(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public JsonValue simdjson() {
|
||||
return simdJsonParser.parse(buffer, buffer.length);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public Object fastjson() {
|
||||
return JSON.parse(buffer);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public Object wast() {
|
||||
return io.github.wycst.wast.json.JSON.parse(buffer);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public JsonValue simdjsonPadded() {
|
||||
return simdJsonParser.parse(bufferPadded, buffer.length);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws RunnerException {
|
||||
Options options = new OptionsBuilder()
|
||||
.include(ParseBenchmark.class.getName())
|
||||
.jvmArgsAppend("--add-opens=java.base/java.time=ALL-UNNAMED", "--add-modules=jdk.incubator.vector")
|
||||
.mode(Mode.Throughput)
|
||||
.timeUnit(TimeUnit.MILLISECONDS)
|
||||
.forks(1)
|
||||
.build();
|
||||
new Runner(options).run();
|
||||
}
|
||||
}
|
@ -0,0 +1,142 @@
|
||||
package com.alibaba.fastjson2.benchmark.simdjson;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.openjdk.jmh.annotations.Benchmark;
|
||||
import org.openjdk.jmh.annotations.BenchmarkMode;
|
||||
import org.openjdk.jmh.annotations.Level;
|
||||
import org.openjdk.jmh.annotations.Mode;
|
||||
import org.openjdk.jmh.annotations.OutputTimeUnit;
|
||||
import org.openjdk.jmh.annotations.Scope;
|
||||
import org.openjdk.jmh.annotations.Setup;
|
||||
import org.openjdk.jmh.annotations.State;
|
||||
import org.openjdk.jmh.runner.Runner;
|
||||
import org.openjdk.jmh.runner.RunnerException;
|
||||
import org.openjdk.jmh.runner.options.Options;
|
||||
import org.openjdk.jmh.runner.options.OptionsBuilder;
|
||||
import org.simdjson.SimdJsonParser;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static com.alibaba.fastjson2.benchmark.simdjson.SimdJsonPaddingUtil.padded;
|
||||
|
||||
@State(Scope.Benchmark)
|
||||
@BenchmarkMode(Mode.Throughput)
|
||||
@OutputTimeUnit(TimeUnit.SECONDS)
|
||||
public class SchemaBasedParseAndSelectBenchmark {
|
||||
private final SimdJsonParser simdJsonParser;
|
||||
private final ObjectMapper objectMapper = new ObjectMapper()
|
||||
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
|
||||
public SchemaBasedParseAndSelectBenchmark() {
|
||||
SimdJsonParser p = null;
|
||||
try {
|
||||
p = new SimdJsonParser();
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
simdJsonParser = p;
|
||||
}
|
||||
|
||||
private byte[] buffer;
|
||||
private byte[] bufferPadded;
|
||||
|
||||
@Setup(Level.Trial)
|
||||
public void setup() throws IOException {
|
||||
try (InputStream is = ParseBenchmark.class.getResourceAsStream("/data/simd-json/twitter.json")) {
|
||||
buffer = is.readAllBytes();
|
||||
bufferPadded = padded(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public int countUniqueUsersWithDefaultProfile_simdjson() {
|
||||
Set<String> defaultUsers = new HashSet<>();
|
||||
SimdJsonTwitter twitter = simdJsonParser.parse(buffer, buffer.length, SimdJsonTwitter.class);
|
||||
for (SimdJsonStatus status : twitter.statuses()) {
|
||||
SimdJsonUser user = status.user();
|
||||
if (user.default_profile()) {
|
||||
defaultUsers.add(user.screen_name());
|
||||
}
|
||||
}
|
||||
return defaultUsers.size();
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public int countUniqueUsersWithDefaultProfile_simdjsonPadded() {
|
||||
Set<String> defaultUsers = new HashSet<>();
|
||||
SimdJsonTwitter twitter = simdJsonParser.parse(bufferPadded, buffer.length, SimdJsonTwitter.class);
|
||||
for (SimdJsonStatus status : twitter.statuses()) {
|
||||
SimdJsonUser user = status.user();
|
||||
if (user.default_profile()) {
|
||||
defaultUsers.add(user.screen_name());
|
||||
}
|
||||
}
|
||||
return defaultUsers.size();
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public int countUniqueUsersWithDefaultProfile_jackson() throws IOException {
|
||||
Set<String> defaultUsers = new HashSet<>();
|
||||
SimdJsonTwitter twitter = objectMapper.readValue(buffer, SimdJsonTwitter.class);
|
||||
for (SimdJsonStatus status : twitter.statuses()) {
|
||||
SimdJsonUser user = status.user();
|
||||
if (user.default_profile()) {
|
||||
defaultUsers.add(user.screen_name());
|
||||
}
|
||||
}
|
||||
return defaultUsers.size();
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public int countUniqueUsersWithDefaultProfile_fastjson() {
|
||||
Set<String> defaultUsers = new HashSet<>();
|
||||
SimdJsonTwitter twitter = JSON.parseObject(buffer, SimdJsonTwitter.class);
|
||||
for (SimdJsonStatus status : twitter.statuses()) {
|
||||
SimdJsonUser user = status.user();
|
||||
if (user.default_profile()) {
|
||||
defaultUsers.add(user.screen_name());
|
||||
}
|
||||
}
|
||||
return defaultUsers.size();
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public int countUniqueUsersWithDefaultProfile_wast() {
|
||||
Set<String> defaultUsers = new HashSet<>();
|
||||
SimdJsonTwitter twitter = io.github.wycst.wast.json.JSON.parseObject(buffer, SimdJsonTwitter.class);
|
||||
for (SimdJsonStatus status : twitter.statuses()) {
|
||||
SimdJsonUser user = status.user();
|
||||
if (user.default_profile()) {
|
||||
defaultUsers.add(user.screen_name());
|
||||
}
|
||||
}
|
||||
return defaultUsers.size();
|
||||
}
|
||||
|
||||
public record SimdJsonUser(boolean default_profile, String screen_name) {
|
||||
}
|
||||
|
||||
public record SimdJsonStatus(SimdJsonUser user) {
|
||||
}
|
||||
|
||||
public record SimdJsonTwitter(List<SimdJsonStatus> statuses) {
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws RunnerException {
|
||||
Options options = new OptionsBuilder()
|
||||
.include(SchemaBasedParseAndSelectBenchmark.class.getName())
|
||||
.jvmArgsAppend("--add-opens=java.base/java.time=ALL-UNNAMED", "--add-modules=jdk.incubator.vector")
|
||||
.mode(Mode.Throughput)
|
||||
.timeUnit(TimeUnit.MILLISECONDS)
|
||||
.forks(1)
|
||||
.build();
|
||||
new Runner(options).run();
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package com.alibaba.fastjson2.benchmark.simdjson;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
class SimdJsonPaddingUtil {
|
||||
static byte[] padded(byte[] src) {
|
||||
byte[] bufferPadded = new byte[src.length + 64];
|
||||
System.arraycopy(src, 0, bufferPadded, 0, src.length);
|
||||
return bufferPadded;
|
||||
}
|
||||
|
||||
static byte[] padWithSpaces(String str) {
|
||||
byte[] strBytes = str.getBytes(UTF_8);
|
||||
byte[] padded = new byte[strBytes.length + 64];
|
||||
Arrays.fill(padded, (byte) ' ');
|
||||
System.arraycopy(strBytes, 0, padded, 0, strBytes.length);
|
||||
return padded;
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package com.alibaba.fastjson2.benchmark.simdjson;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.openjdk.jmh.infra.Blackhole;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
|
||||
public class Twitter {
|
||||
static final String file = "data/simd-json/twitter.json";
|
||||
static byte[] bytes;
|
||||
static {
|
||||
try (InputStream is = Twitter.class.getClassLoader().getResourceAsStream(file)) {
|
||||
String str = IOUtils.toString(is, "UTF-8");
|
||||
bytes = str.getBytes();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void fastjson2_parse(Blackhole bh) {
|
||||
bh.consume(JSON.parseObject(bytes, SimdJsonTwitter.class));
|
||||
}
|
||||
|
||||
public void wast_parse(Blackhole bh) {
|
||||
bh.consume(io.github.wycst.wast.json.JSON.parseObject(bytes, SimdJsonTwitter.class));
|
||||
}
|
||||
|
||||
public record SimdJsonUser(boolean default_profile, String screen_name) {
|
||||
}
|
||||
|
||||
public record SimdJsonStatus(SimdJsonUser user) {
|
||||
}
|
||||
|
||||
public record SimdJsonTwitter(List<SimdJsonStatus> statuses) {
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,23 @@
|
||||
package com.alibaba.fastjson2.benchmark;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URL;
|
||||
|
||||
public class Gsoc2018Test {
|
||||
@Test
|
||||
public void test() throws Exception {
|
||||
String[] paths = new String[] {
|
||||
"data/simd-json/twitter.json", "data/simd-json/gsoc-2018.json", "data/simd-json/github_events.json"
|
||||
};
|
||||
for (String path : paths) {
|
||||
URL resource = Thread.currentThread().getContextClassLoader().getResource(path);
|
||||
File file = new File(resource.getFile());
|
||||
byte[] str = FileUtils.readFileToByteArray(file);
|
||||
JSON.parse(str);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package com.alibaba.fastjson2.benchmark;
|
||||
|
||||
import org.openjdk.jmh.infra.Blackhole;
|
||||
|
||||
public class JMH {
|
||||
public static final Blackhole BH = new Blackhole("Today's password is swordfish. I understand instantiating Blackholes directly is dangerous.");
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package com.alibaba.fastjson2.benchmark;
|
||||
|
||||
import com.alibaba.fastjson2.benchmark.simdjson.Twitter;
|
||||
|
||||
import static com.alibaba.fastjson2.benchmark.JMH.BH;
|
||||
|
||||
public class TwitterTest {
|
||||
private static final Twitter benchmark = new Twitter();
|
||||
|
||||
public static void fastjson2_parse() {
|
||||
for (int j = 0; j < 5; j++) {
|
||||
long start = System.currentTimeMillis();
|
||||
for (int i = 0; i < 1000; ++i) {
|
||||
benchmark.fastjson2_parse(BH);
|
||||
}
|
||||
long millis = System.currentTimeMillis() - start;
|
||||
System.out.println("Twitter-fastjson2_parse : " + millis);
|
||||
// zulu21.32.17 247 234
|
||||
}
|
||||
}
|
||||
|
||||
public static void wast_parse() {
|
||||
for (int j = 0; j < 5; j++) {
|
||||
long start = System.currentTimeMillis();
|
||||
for (int i = 0; i < 1000; ++i) {
|
||||
benchmark.wast_parse(BH);
|
||||
}
|
||||
long millis = System.currentTimeMillis() - start;
|
||||
System.out.println("Twitter-wast_parse : " + millis);
|
||||
// zulu21.32.17 211
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
fastjson2_parse();
|
||||
// wast_parse();
|
||||
}
|
||||
}
|
@ -446,6 +446,10 @@ public abstract class JSONReader
|
||||
return (context.features & MASK_IGNORE_NONE_SERIALIZABLE) != 0;
|
||||
}
|
||||
|
||||
public boolean hasAutoTypeBeforeHandler() {
|
||||
return context.autoTypeBeforeHandler != null;
|
||||
}
|
||||
|
||||
public ObjectReader checkAutoType(Class expectClass, long expectClassHash, long features) {
|
||||
return null;
|
||||
}
|
||||
@ -4324,7 +4328,9 @@ public abstract class JSONReader
|
||||
protected static final long MASK_SUPPORT_AUTO_TYPE = 1L << 5;
|
||||
protected static final long MASK_SUPPORT_SMART_MATCH = 1L << 6;
|
||||
protected static final long MASK_TRIM_STRING = 1L << 14;
|
||||
protected static final long MASK_ALLOW_UN_QUOTED_FIELD_NAMES = 1L << 17;
|
||||
protected static final long MASK_EMPTY_STRING_AS_NULL = 1L << 27;
|
||||
protected static final long MASK_DISABLE_SINGLE_QUOTE = 1L << 31L;
|
||||
protected static final long MASK_DISABLE_REFERENCE_DETECT = 1L << 33;
|
||||
|
||||
public enum Feature {
|
||||
@ -4352,7 +4358,7 @@ public abstract class JSONReader
|
||||
TrimString(MASK_TRIM_STRING),
|
||||
ErrorOnNotSupportAutoType(1 << 15),
|
||||
DuplicateKeyValueAsArray(1 << 16),
|
||||
AllowUnQuotedFieldNames(1 << 17),
|
||||
AllowUnQuotedFieldNames(MASK_ALLOW_UN_QUOTED_FIELD_NAMES),
|
||||
NonStringKeyAsString(1 << 18),
|
||||
/**
|
||||
* @since 2.0.13
|
||||
@ -4449,7 +4455,7 @@ public abstract class JSONReader
|
||||
* Feature that disables the support for single quote.
|
||||
* @since 2.0.53
|
||||
*/
|
||||
DisableSingleQuote(1L << 31L),
|
||||
DisableSingleQuote(MASK_DISABLE_SINGLE_QUOTE),
|
||||
|
||||
/**
|
||||
* @since 2.0.53
|
||||
@ -4518,6 +4524,21 @@ public abstract class JSONReader
|
||||
this.ch = (char) savePoint.current;
|
||||
}
|
||||
|
||||
final boolean checkNameBegin(int quote) {
|
||||
long features = context.features;
|
||||
if (quote == '\'' && ((features & MASK_DISABLE_SINGLE_QUOTE) != 0)) {
|
||||
throw notSupportName();
|
||||
}
|
||||
if (quote != '"' && quote != '\'') {
|
||||
if ((features & MASK_ALLOW_UN_QUOTED_FIELD_NAMES) != 0) {
|
||||
readFieldNameHashCodeUnquote();
|
||||
return true;
|
||||
}
|
||||
throw notSupportName();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
final JSONException notSupportName() {
|
||||
return new JSONException(info("not support unquoted name"));
|
||||
}
|
||||
@ -4534,6 +4555,10 @@ public abstract class JSONReader
|
||||
return new JSONException(info(message), cause);
|
||||
}
|
||||
|
||||
final JSONException error() {
|
||||
throw new JSONValidException("error, offset " + offset + ", char " + (char) ch);
|
||||
}
|
||||
|
||||
final JSONException error(int offset, int ch) {
|
||||
throw new JSONValidException("error, offset " + offset + ", char " + (char) ch);
|
||||
}
|
||||
|
@ -17,8 +17,6 @@ import static com.alibaba.fastjson2.util.JDKUtils.*;
|
||||
final class JSONReaderASCII
|
||||
extends JSONReaderUTF8 {
|
||||
final String str;
|
||||
static final int ESCAPE_INDEX_NOT_SET = -2;
|
||||
private int nextEscapeIndex = ESCAPE_INDEX_NOT_SET;
|
||||
|
||||
JSONReaderASCII(Context ctx, String str, byte[] bytes, int offset, int length) {
|
||||
super(ctx, bytes, offset, length);
|
||||
@ -1359,7 +1357,7 @@ final class JSONReaderASCII
|
||||
if (index == -1) {
|
||||
throw error("invalid escape character EOI");
|
||||
}
|
||||
int slashIndex = indexOfSlash(bytes, offset, end);
|
||||
int slashIndex = indexOfSlash(this, bytes, offset, end);
|
||||
if (slashIndex == -1 || slashIndex > index) {
|
||||
offset = index + 1;
|
||||
} else {
|
||||
@ -1400,14 +1398,6 @@ final class JSONReaderASCII
|
||||
return readStringNotMatch();
|
||||
}
|
||||
|
||||
private int indexOfSlash(byte[] bytes, int offset, int end) {
|
||||
int slashIndex = nextEscapeIndex;
|
||||
if (slashIndex == ESCAPE_INDEX_NOT_SET || (slashIndex != -1 && slashIndex < offset)) {
|
||||
nextEscapeIndex = slashIndex = IOUtils.indexOfSlash(bytes, offset, end);
|
||||
}
|
||||
return slashIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void readString0() {
|
||||
final byte[] bytes = this.bytes;
|
||||
|
@ -2353,56 +2353,6 @@ final class JSONReaderUTF16
|
||||
return getFieldName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean skipName() {
|
||||
char quote = ch;
|
||||
if (quote == '\'' && ((context.features & Feature.DisableSingleQuote.mask) != 0)) {
|
||||
throw notSupportName();
|
||||
}
|
||||
|
||||
if (quote != '"' && quote != '\'') {
|
||||
if ((context.features & Feature.AllowUnQuotedFieldNames.mask) != 0) {
|
||||
readFieldNameHashCodeUnquote();
|
||||
return true;
|
||||
}
|
||||
throw notSupportName();
|
||||
}
|
||||
|
||||
int offset = this.offset;
|
||||
final char[] chars = this.chars;
|
||||
for (; ; ) {
|
||||
char ch = chars[offset++];
|
||||
if (ch == '\\') {
|
||||
ch = chars[offset];
|
||||
offset += (ch == 'u' ? 5 : (ch == 'x' ? 3 : 1));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch == quote) {
|
||||
ch = offset == end ? EOI : chars[offset++];
|
||||
|
||||
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
|
||||
ch = offset == end ? EOI : chars[offset++];
|
||||
}
|
||||
if (ch != ':') {
|
||||
throw syntaxError(ch);
|
||||
}
|
||||
|
||||
ch = offset == end ? EOI : chars[offset++];
|
||||
|
||||
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
|
||||
ch = offset == end ? EOI : chars[offset++];
|
||||
}
|
||||
|
||||
this.offset = offset;
|
||||
this.ch = ch;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int readInt32Value() {
|
||||
char ch = this.ch;
|
||||
@ -3163,33 +3113,54 @@ final class JSONReaderUTF16
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void skipValue() {
|
||||
final char[] chars = this.chars;
|
||||
char ch = this.ch;
|
||||
int offset = this.offset, end = this.end;
|
||||
comma = false;
|
||||
public final boolean skipName() {
|
||||
this.offset = skipName(this, chars, offset, end);
|
||||
return true;
|
||||
}
|
||||
|
||||
switch_:
|
||||
switch (ch) {
|
||||
case '-':
|
||||
case '+':
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
case '.':
|
||||
boolean sign = ch == '-' || ch == '+';
|
||||
if (sign) {
|
||||
private static int skipName(JSONReaderUTF16 jsonReader, char[] chars, int offset, int end) {
|
||||
char quote = jsonReader.ch;
|
||||
if (jsonReader.checkNameBegin(quote)) {
|
||||
return jsonReader.offset;
|
||||
}
|
||||
|
||||
for (; ; ) {
|
||||
char ch = chars[offset++];
|
||||
if (ch == '\\') {
|
||||
ch = chars[offset];
|
||||
offset += (ch == 'u' ? 5 : (ch == 'x' ? 3 : 1));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch == quote) {
|
||||
ch = offset == end ? EOI : chars[offset++];
|
||||
|
||||
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
|
||||
ch = offset == end ? EOI : chars[offset++];
|
||||
}
|
||||
if (ch != ':') {
|
||||
throw syntaxError(ch);
|
||||
}
|
||||
|
||||
ch = offset == end ? EOI : chars[offset++];
|
||||
|
||||
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
|
||||
ch = offset == end ? EOI : chars[offset++];
|
||||
}
|
||||
|
||||
jsonReader.ch = ch;
|
||||
return offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int skipNumber(JSONReaderUTF16 jsonReader, char[] bytes, int offset, int end) {
|
||||
int ch = jsonReader.ch;
|
||||
if (ch == '-' || ch == '+') {
|
||||
if (offset < end) {
|
||||
ch = chars[offset++];
|
||||
ch = bytes[offset++];
|
||||
} else {
|
||||
throw numberError(offset, ch);
|
||||
throw jsonReader.error();
|
||||
}
|
||||
}
|
||||
boolean dot = ch == '.';
|
||||
@ -3197,23 +3168,20 @@ final class JSONReaderUTF16
|
||||
if (!dot && (ch >= '0' && ch <= '9')) {
|
||||
num = true;
|
||||
do {
|
||||
ch = offset == end ? EOI : chars[offset++];
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
} while (ch >= '0' && ch <= '9');
|
||||
}
|
||||
|
||||
if (num && (ch == 'L' || ch == 'F' || ch == 'D' || ch == 'B' || ch == 'S')) {
|
||||
ch = chars[offset++];
|
||||
}
|
||||
|
||||
if (num && (ch == 'L' | ch == 'F' | ch == 'D' | ch == 'B' | ch == 'S')) {
|
||||
ch = bytes[offset++];
|
||||
} else {
|
||||
boolean small = false;
|
||||
if (ch == '.') {
|
||||
small = true;
|
||||
ch = offset == end ? EOI : chars[offset++];
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
|
||||
if (ch >= '0' && ch <= '9') {
|
||||
do {
|
||||
ch = offset == end ? EOI : chars[offset++];
|
||||
} while (ch >= '0' && ch <= '9');
|
||||
while (ch >= '0' && ch <= '9') {
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
}
|
||||
}
|
||||
|
||||
@ -3222,13 +3190,13 @@ final class JSONReaderUTF16
|
||||
}
|
||||
|
||||
if (ch == 'e' || ch == 'E') {
|
||||
ch = chars[offset++];
|
||||
ch = bytes[offset++];
|
||||
|
||||
boolean eSign = false;
|
||||
if (ch == '+' || ch == '-') {
|
||||
eSign = true;
|
||||
if (offset < end) {
|
||||
ch = chars[offset++];
|
||||
ch = bytes[offset++];
|
||||
} else {
|
||||
throw numberError(offset, ch);
|
||||
}
|
||||
@ -3236,51 +3204,45 @@ final class JSONReaderUTF16
|
||||
|
||||
if (ch >= '0' && ch <= '9') {
|
||||
do {
|
||||
ch = offset == end ? EOI : chars[offset++];
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
} while (ch >= '0' && ch <= '9');
|
||||
} else if (eSign) {
|
||||
throw numberError(offset, ch);
|
||||
}
|
||||
}
|
||||
|
||||
if (ch == 'L' || ch == 'F' || ch == 'D' || ch == 'B' || ch == 'S') {
|
||||
ch = offset == end ? EOI : chars[offset++];
|
||||
if (ch == 'F' || ch == 'D') {
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
if (offset + 3 > end) {
|
||||
throw error(offset, ch);
|
||||
}
|
||||
if (chars[offset] != 'r' || chars[offset + 1] != 'u' || chars[offset + 2] != 'e') {
|
||||
throw error(offset, ch);
|
||||
|
||||
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
}
|
||||
offset += 3;
|
||||
ch = offset == end ? EOI : chars[offset++];
|
||||
break;
|
||||
case 'f':
|
||||
if (offset + 4 > end) {
|
||||
throw error(offset, ch);
|
||||
|
||||
boolean comma = false;
|
||||
if (ch == ',') {
|
||||
comma = true;
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
}
|
||||
if (chars[offset] != 'a' || chars[offset + 1] != 'l' || chars[offset + 2] != 's' || chars[offset + 3] != 'e') {
|
||||
throw error(offset, ch);
|
||||
|
||||
if ((ch == '}' || ch == ']' || ch == EOI)) {
|
||||
throw jsonReader.error(offset, ch);
|
||||
}
|
||||
offset += 4;
|
||||
ch = offset == end ? EOI : chars[offset++];
|
||||
break;
|
||||
case 'n':
|
||||
if (offset + 3 > end) {
|
||||
throw error(offset, ch);
|
||||
} else if (ch != '}' && ch != ']' && ch != EOI) {
|
||||
throw jsonReader.error(offset, ch);
|
||||
}
|
||||
if (chars[offset] != 'u' || chars[offset + 1] != 'l' || chars[offset + 2] != 'l') {
|
||||
throw error(offset, ch);
|
||||
|
||||
jsonReader.comma = comma;
|
||||
jsonReader.ch = (char) ch;
|
||||
return offset;
|
||||
}
|
||||
offset += 3;
|
||||
ch = offset == end ? EOI : chars[offset++];
|
||||
break;
|
||||
case '"':
|
||||
case '\'': {
|
||||
char quote = ch;
|
||||
ch = chars[offset++];
|
||||
|
||||
private static int skipString(JSONReaderUTF16 jsonReader, char[] chars, int offset, int end) {
|
||||
char quote = jsonReader.ch;
|
||||
char ch = offset == end ? EOI : chars[offset++];
|
||||
for (; ; ) {
|
||||
if (ch == '\\') {
|
||||
ch = chars[offset++];
|
||||
@ -3289,7 +3251,7 @@ final class JSONReaderUTF16
|
||||
} else if (ch == 'x') {
|
||||
offset += 2;
|
||||
} else if (ch != '\\' && ch != '"') {
|
||||
char1(ch);
|
||||
jsonReader.char1(ch);
|
||||
}
|
||||
ch = chars[offset++];
|
||||
continue;
|
||||
@ -3302,68 +3264,289 @@ final class JSONReaderUTF16
|
||||
|
||||
ch = chars[offset++];
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
if (ch == '[') {
|
||||
next();
|
||||
for (int i = 0; ; ++i) {
|
||||
if (this.ch == ']') {
|
||||
comma = false;
|
||||
offset = this.offset;
|
||||
ch = offset == end ? EOI : chars[offset++];
|
||||
break switch_;
|
||||
}
|
||||
if (i != 0 && !comma) {
|
||||
throw valueError();
|
||||
}
|
||||
comma = false;
|
||||
skipValue();
|
||||
}
|
||||
} else if (ch == '{') {
|
||||
next();
|
||||
for (; ; ) {
|
||||
if (this.ch == '}') {
|
||||
comma = false;
|
||||
offset = this.offset;
|
||||
ch = offset == end ? EOI : chars[offset++];
|
||||
break switch_;
|
||||
}
|
||||
skipName();
|
||||
skipValue();
|
||||
}
|
||||
} else if (ch == 'S' && nextIfSet()) {
|
||||
skipValue();
|
||||
} else {
|
||||
throw error(offset, ch);
|
||||
}
|
||||
ch = this.ch;
|
||||
offset = this.offset;
|
||||
break;
|
||||
}
|
||||
|
||||
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
|
||||
ch = offset == end ? EOI : chars[offset++];
|
||||
}
|
||||
|
||||
boolean comma = false;
|
||||
if (ch == ',') {
|
||||
comma = true;
|
||||
ch = offset == end ? EOI : chars[offset++];
|
||||
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
|
||||
ch = offset == end ? EOI : chars[offset++];
|
||||
}
|
||||
|
||||
if ((ch == '}' || ch == ']' || ch == EOI)) {
|
||||
throw jsonReader.error(offset, ch);
|
||||
}
|
||||
} else if (ch != '}' && ch != ']' && ch != EOI) {
|
||||
throw jsonReader.error(offset, ch);
|
||||
}
|
||||
|
||||
jsonReader.comma = comma;
|
||||
jsonReader.ch = (char) ch;
|
||||
return offset;
|
||||
}
|
||||
|
||||
private static int skipStringEscaped(JSONReaderUTF16 jsonReader, char[] bytes, int offset, int quote) {
|
||||
int ch = bytes[offset++];
|
||||
for (; ; ) {
|
||||
if (ch == '\\') {
|
||||
ch = bytes[offset++];
|
||||
if (ch == 'u') {
|
||||
offset += 4;
|
||||
} else if (ch == 'x') {
|
||||
offset += 2;
|
||||
} else if (ch != '\\' && ch != '"') {
|
||||
jsonReader.char1(ch);
|
||||
}
|
||||
ch = bytes[offset++];
|
||||
continue;
|
||||
}
|
||||
if (ch == quote) {
|
||||
return offset;
|
||||
}
|
||||
|
||||
ch = bytes[offset++];
|
||||
}
|
||||
}
|
||||
|
||||
private static int skipObject(JSONReaderUTF16 jsonReader, char[] bytes, int offset, int end) {
|
||||
offset = next(jsonReader, bytes, offset, end);
|
||||
for (int i = 0; ; ++i) {
|
||||
if (jsonReader.ch == '}') {
|
||||
break;
|
||||
}
|
||||
if (i != 0 && !jsonReader.comma) {
|
||||
throw jsonReader.valueError();
|
||||
}
|
||||
offset = skipName(jsonReader, bytes, offset, end);
|
||||
offset = skipValue(jsonReader, bytes, offset, end);
|
||||
}
|
||||
|
||||
int ch = offset == end ? EOI : bytes[offset++];
|
||||
|
||||
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
}
|
||||
|
||||
boolean comma = false;
|
||||
if (ch == ',') {
|
||||
comma = true;
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
}
|
||||
|
||||
if ((ch == '}' || ch == ']' || ch == EOI)) {
|
||||
throw jsonReader.error(offset, ch);
|
||||
}
|
||||
} else if (ch != '}' && ch != ']' && ch != EOI) {
|
||||
throw jsonReader.error(offset, ch);
|
||||
}
|
||||
|
||||
jsonReader.comma = comma;
|
||||
jsonReader.ch = (char) ch;
|
||||
return offset;
|
||||
}
|
||||
|
||||
public static int next(JSONReaderUTF16 jsonReader, char[] bytes, int offset, int end) {
|
||||
int ch = offset >= end ? EOI : bytes[offset++];
|
||||
while (ch == '\0' || (ch <= ' ' && ((1L << ch) & SPACE) != 0)) {
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
}
|
||||
|
||||
jsonReader.ch = (char) ch;
|
||||
if (ch == '/') {
|
||||
jsonReader.offset = offset;
|
||||
jsonReader.skipComment();
|
||||
return jsonReader.offset;
|
||||
} else {
|
||||
return offset;
|
||||
}
|
||||
}
|
||||
|
||||
private static int skipArray(JSONReaderUTF16 jsonReader, char[] bytes, int offset, int end) {
|
||||
offset = next(jsonReader, bytes, offset, end);
|
||||
for (int i = 0; ; ++i) {
|
||||
if (jsonReader.ch == ']') {
|
||||
break;
|
||||
}
|
||||
if (i != 0 && !jsonReader.comma) {
|
||||
throw jsonReader.valueError();
|
||||
}
|
||||
offset = skipValue(jsonReader, bytes, offset, end);
|
||||
}
|
||||
|
||||
int ch = offset == end ? EOI : bytes[offset++];
|
||||
|
||||
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
}
|
||||
|
||||
boolean comma = false;
|
||||
if (ch == ',') {
|
||||
comma = true;
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
}
|
||||
}
|
||||
|
||||
if (!comma && ch != '}' && ch != ']' && ch != EOI) {
|
||||
throw error(offset, ch);
|
||||
throw jsonReader.error(offset, ch);
|
||||
}
|
||||
|
||||
if (comma && (ch == '}' || ch == ']' || ch == EOI)) {
|
||||
throw error(offset, ch);
|
||||
throw jsonReader.error(offset, ch);
|
||||
}
|
||||
|
||||
this.ch = ch;
|
||||
this.offset = offset;
|
||||
jsonReader.comma = comma;
|
||||
jsonReader.ch = (char) ch;
|
||||
return offset;
|
||||
}
|
||||
|
||||
private static int skipFalse(JSONReaderUTF16 jsonReader, char[] bytes, int offset, int end) {
|
||||
if (offset + 4 > end || IOUtils.notALSE(bytes, offset)) {
|
||||
throw jsonReader.error();
|
||||
}
|
||||
offset += 4;
|
||||
int ch = offset == end ? EOI : bytes[offset++];
|
||||
|
||||
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
}
|
||||
|
||||
boolean comma = false;
|
||||
if (ch == ',') {
|
||||
comma = true;
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
}
|
||||
|
||||
if ((ch == '}' || ch == ']' || ch == EOI)) {
|
||||
throw jsonReader.error(offset, ch);
|
||||
}
|
||||
} else if (ch != '}' && ch != ']' && ch != EOI) {
|
||||
throw jsonReader.error(offset, ch);
|
||||
}
|
||||
|
||||
jsonReader.comma = comma;
|
||||
jsonReader.ch = (char) ch;
|
||||
return offset;
|
||||
}
|
||||
|
||||
private static int skipTrue(JSONReaderUTF16 jsonReader, char[] bytes, int offset, int end) {
|
||||
if (offset + 3 > end || IOUtils.notTRUE(bytes, offset - 1)) {
|
||||
throw jsonReader.error();
|
||||
}
|
||||
offset += 3;
|
||||
int ch = offset == end ? EOI : bytes[offset++];
|
||||
|
||||
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
}
|
||||
|
||||
boolean comma = false;
|
||||
if (ch == ',') {
|
||||
comma = true;
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
}
|
||||
|
||||
if ((ch == '}' || ch == ']' || ch == EOI)) {
|
||||
throw jsonReader.error(offset, ch);
|
||||
}
|
||||
} else if (ch != '}' && ch != ']' && ch != EOI) {
|
||||
throw jsonReader.error(offset, ch);
|
||||
}
|
||||
|
||||
jsonReader.comma = comma;
|
||||
jsonReader.ch = (char) ch;
|
||||
return offset;
|
||||
}
|
||||
|
||||
private static int skipNull(JSONReaderUTF16 jsonReader, char[] bytes, int offset, int end) {
|
||||
if (offset + 3 > end || IOUtils.notNULL(bytes, offset - 1)) {
|
||||
throw jsonReader.error();
|
||||
}
|
||||
offset += 3;
|
||||
int ch = offset == end ? EOI : bytes[offset++];
|
||||
|
||||
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
}
|
||||
|
||||
boolean comma = false;
|
||||
if (ch == ',') {
|
||||
comma = true;
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
}
|
||||
|
||||
if ((ch == '}' || ch == ']' || ch == EOI)) {
|
||||
throw jsonReader.error(offset, ch);
|
||||
}
|
||||
} else if (ch != '}' && ch != ']' && ch != EOI) {
|
||||
throw jsonReader.error(offset, ch);
|
||||
}
|
||||
|
||||
jsonReader.comma = comma;
|
||||
jsonReader.ch = (char) ch;
|
||||
return offset;
|
||||
}
|
||||
|
||||
private static int skipSet(JSONReaderUTF16 jsonReader, char[] bytes, int offset, int end) {
|
||||
if (nextIfSet(jsonReader, bytes, offset, end)) {
|
||||
return skipArray(jsonReader, bytes, jsonReader.offset, end);
|
||||
} else {
|
||||
throw jsonReader.error();
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean nextIfSet(JSONReaderUTF16 jsonReader, char[] chars, int offset, int end) {
|
||||
if (offset + 1 < end && chars[offset] == 'e' && chars[offset + 1] == 't') {
|
||||
offset += 2;
|
||||
char ch = offset == end ? EOI : chars[offset++];
|
||||
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
|
||||
ch = offset == end ? EOI : chars[offset++];
|
||||
}
|
||||
jsonReader.offset = offset;
|
||||
jsonReader.ch = ch;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static int skipValue(JSONReaderUTF16 jsonReader, char[] bytes, int offset, int end) {
|
||||
switch (jsonReader.ch) {
|
||||
case 't':
|
||||
return skipTrue(jsonReader, bytes, offset, end);
|
||||
case 'f':
|
||||
return skipFalse(jsonReader, bytes, offset, end);
|
||||
case 'n':
|
||||
return skipNull(jsonReader, bytes, offset, end);
|
||||
case '"':
|
||||
case '\'':
|
||||
return skipString(jsonReader, bytes, offset, end);
|
||||
case '{':
|
||||
return skipObject(jsonReader, bytes, offset, end);
|
||||
case '[':
|
||||
return skipArray(jsonReader, bytes, offset, end);
|
||||
case 'S':
|
||||
return skipSet(jsonReader, bytes, offset, end);
|
||||
default:
|
||||
return skipNumber(jsonReader, bytes, offset, end);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void skipValue() {
|
||||
this.offset = skipValue(this, chars, offset, end);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -22,6 +22,8 @@ import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
class JSONReaderUTF8
|
||||
extends JSONReader {
|
||||
static final int REF = BIG_ENDIAN ? 0x24726566 : 0x66657224;
|
||||
static final int ESCAPE_INDEX_NOT_SET = -2;
|
||||
protected int nextEscapeIndex = ESCAPE_INDEX_NOT_SET;
|
||||
|
||||
protected final byte[] bytes;
|
||||
protected final int length;
|
||||
@ -119,6 +121,14 @@ class JSONReaderUTF8
|
||||
next();
|
||||
}
|
||||
|
||||
static int indexOfSlash(JSONReaderUTF8 jsonReader, byte[] bytes, int offset, int end) {
|
||||
int slashIndex = jsonReader.nextEscapeIndex;
|
||||
if (slashIndex == ESCAPE_INDEX_NOT_SET || (slashIndex != -1 && slashIndex < offset)) {
|
||||
jsonReader.nextEscapeIndex = slashIndex = IOUtils.indexOfSlash(bytes, offset, end);
|
||||
}
|
||||
return slashIndex;
|
||||
}
|
||||
|
||||
private void char_utf8(int ch, int offset) {
|
||||
final byte[] bytes = this.bytes;
|
||||
switch ((ch & 0xFF) >> 4) {
|
||||
@ -4246,30 +4256,30 @@ class JSONReaderUTF8
|
||||
|
||||
@Override
|
||||
public final boolean skipName() {
|
||||
char quote = ch;
|
||||
if (quote == '\'' && ((context.features & Feature.DisableSingleQuote.mask) != 0)) {
|
||||
throw notSupportName();
|
||||
}
|
||||
if (quote != '"' && quote != '\'') {
|
||||
if ((context.features & Feature.AllowUnQuotedFieldNames.mask) != 0) {
|
||||
readFieldNameHashCodeUnquote();
|
||||
this.offset = skipName(this, bytes, offset, end);
|
||||
return true;
|
||||
}
|
||||
throw notSupportName();
|
||||
|
||||
private static int skipName(JSONReaderUTF8 jsonReader, byte[] bytes, int offset, int end) {
|
||||
int quote = jsonReader.ch;
|
||||
if (jsonReader.checkNameBegin(quote)) {
|
||||
return jsonReader.offset;
|
||||
}
|
||||
|
||||
int offset = this.offset;
|
||||
final byte[] bytes = this.bytes;
|
||||
for (; ; ) {
|
||||
int ch = bytes[offset++];
|
||||
if (ch == '\\') {
|
||||
ch = bytes[offset];
|
||||
offset += (ch == 'u' ? 5 : (ch == 'x' ? 3 : 1));
|
||||
continue;
|
||||
int index = IOUtils.indexOfQuote(bytes, quote, offset, end);
|
||||
if (index == -1) {
|
||||
throw jsonReader.error("invalid escape character EOI");
|
||||
}
|
||||
|
||||
if (ch == quote) {
|
||||
int ch;
|
||||
int slashIndex = indexOfSlash(jsonReader, bytes, offset, end);
|
||||
if (slashIndex == -1 || slashIndex > index) {
|
||||
offset = index + 1;
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
} else {
|
||||
offset = skipStringEscaped(jsonReader, bytes, slashIndex, quote);
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
}
|
||||
|
||||
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
@ -4284,43 +4294,17 @@ class JSONReaderUTF8
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
}
|
||||
|
||||
this.offset = offset;
|
||||
this.ch = (char) ch;
|
||||
break;
|
||||
}
|
||||
jsonReader.ch = (char) ch;
|
||||
return offset;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void skipValue() {
|
||||
final byte[] bytes = this.bytes;
|
||||
int ch = this.ch;
|
||||
int offset = this.offset, end = this.end;
|
||||
comma = false;
|
||||
|
||||
switch_:
|
||||
switch (ch) {
|
||||
case '-':
|
||||
case '+':
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
case '.':
|
||||
boolean sign = ch == '-' || ch == '+';
|
||||
if (sign) {
|
||||
private static int skipNumber(JSONReaderUTF8 jsonReader, byte[] bytes, int offset, int end) {
|
||||
int ch = jsonReader.ch;
|
||||
if (ch == '-' || ch == '+') {
|
||||
if (offset < end) {
|
||||
ch = bytes[offset++];
|
||||
} else {
|
||||
throw numberError(offset, ch);
|
||||
throw jsonReader.error();
|
||||
}
|
||||
}
|
||||
boolean dot = ch == '.';
|
||||
@ -4332,19 +4316,16 @@ class JSONReaderUTF8
|
||||
} while (ch >= '0' && ch <= '9');
|
||||
}
|
||||
|
||||
if (num && (ch == 'L' || ch == 'F' || ch == 'D' || ch == 'B' || ch == 'S')) {
|
||||
if (num && (ch == 'L' | ch == 'F' | ch == 'D' | ch == 'B' | ch == 'S')) {
|
||||
ch = bytes[offset++];
|
||||
}
|
||||
|
||||
} else {
|
||||
boolean small = false;
|
||||
if (ch == '.') {
|
||||
small = true;
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
|
||||
if (ch >= '0' && ch <= '9') {
|
||||
do {
|
||||
while (ch >= '0' && ch <= '9') {
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
} while (ch >= '0' && ch <= '9');
|
||||
}
|
||||
}
|
||||
|
||||
@ -4374,44 +4355,77 @@ class JSONReaderUTF8
|
||||
}
|
||||
}
|
||||
|
||||
if (ch == 'L' || ch == 'F' || ch == 'D' || ch == 'B' || ch == 'S') {
|
||||
if (ch == 'F' || ch == 'D') {
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
if (offset + 3 > end) {
|
||||
throw error(offset, ch);
|
||||
}
|
||||
if (bytes[offset] != 'r' || bytes[offset + 1] != 'u' || bytes[offset + 2] != 'e') {
|
||||
throw error(offset, ch);
|
||||
}
|
||||
offset += 3;
|
||||
|
||||
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
break;
|
||||
case 'f':
|
||||
if (offset + 4 > end) {
|
||||
throw error(offset, ch);
|
||||
}
|
||||
if (bytes[offset] != 'a' || bytes[offset + 1] != 'l' || bytes[offset + 2] != 's' || bytes[offset + 3] != 'e') {
|
||||
throw error(offset, ch);
|
||||
}
|
||||
offset += 4;
|
||||
|
||||
boolean comma = false;
|
||||
if (ch == ',') {
|
||||
comma = true;
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
break;
|
||||
case 'n':
|
||||
if (offset + 3 > end) {
|
||||
throw error(offset, ch);
|
||||
}
|
||||
if (bytes[offset] != 'u' || bytes[offset + 1] != 'l' || bytes[offset + 2] != 'l') {
|
||||
throw error(offset, ch);
|
||||
}
|
||||
offset += 3;
|
||||
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
break;
|
||||
case '"':
|
||||
case '\'': {
|
||||
}
|
||||
|
||||
if ((ch == '}' || ch == ']' || ch == EOI)) {
|
||||
throw jsonReader.error(offset, ch);
|
||||
}
|
||||
} else if (ch != '}' && ch != ']' && ch != EOI) {
|
||||
throw jsonReader.error(offset, ch);
|
||||
}
|
||||
|
||||
jsonReader.comma = comma;
|
||||
jsonReader.ch = (char) ch;
|
||||
return offset;
|
||||
}
|
||||
|
||||
private static int skipString(JSONReaderUTF8 jsonReader, byte[] bytes, int offset, int end) {
|
||||
int ch = jsonReader.ch;
|
||||
int quote = ch;
|
||||
ch = bytes[offset++];
|
||||
int index = IOUtils.indexOfQuote(bytes, quote, offset, end);
|
||||
if (index == -1) {
|
||||
throw jsonReader.error("invalid escape character EOI");
|
||||
}
|
||||
int slashIndex = indexOfSlash(jsonReader, bytes, offset, end);
|
||||
if (slashIndex == -1 || slashIndex > index) {
|
||||
offset = index + 1;
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
} else {
|
||||
offset = skipStringEscaped(jsonReader, bytes, slashIndex, quote);
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
}
|
||||
|
||||
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
}
|
||||
|
||||
boolean comma = false;
|
||||
if (ch == ',') {
|
||||
comma = true;
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
}
|
||||
|
||||
if ((ch == '}' || ch == ']' || ch == EOI)) {
|
||||
throw jsonReader.error(offset, ch);
|
||||
}
|
||||
} else if (ch != '}' && ch != ']' && ch != EOI) {
|
||||
throw jsonReader.error(offset, ch);
|
||||
}
|
||||
|
||||
jsonReader.comma = comma;
|
||||
jsonReader.ch = (char) ch;
|
||||
return offset;
|
||||
}
|
||||
|
||||
private static int skipStringEscaped(JSONReaderUTF8 jsonReader, byte[] bytes, int offset, int quote) {
|
||||
int ch = bytes[offset++];
|
||||
for (; ; ) {
|
||||
if (ch == '\\') {
|
||||
ch = bytes[offset++];
|
||||
@ -4420,62 +4434,98 @@ class JSONReaderUTF8
|
||||
} else if (ch == 'x') {
|
||||
offset += 2;
|
||||
} else if (ch != '\\' && ch != '"') {
|
||||
char1(ch);
|
||||
jsonReader.char1(ch);
|
||||
}
|
||||
ch = bytes[offset++];
|
||||
continue;
|
||||
}
|
||||
if (ch == quote) {
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
break;
|
||||
return offset;
|
||||
}
|
||||
|
||||
ch = bytes[offset++];
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
if (ch == '[') {
|
||||
next();
|
||||
|
||||
private static int skipObject(JSONReaderUTF8 jsonReader, byte[] bytes, int offset, int end) {
|
||||
offset = next(jsonReader, bytes, offset, end);
|
||||
for (int i = 0; ; ++i) {
|
||||
if (this.ch == ']') {
|
||||
comma = false;
|
||||
offset = this.offset;
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
break switch_;
|
||||
}
|
||||
if (i != 0 && !comma) {
|
||||
throw valueError();
|
||||
}
|
||||
comma = false;
|
||||
skipValue();
|
||||
}
|
||||
} else if (ch == '{') {
|
||||
next();
|
||||
for (; ; ) {
|
||||
if (this.ch == '}') {
|
||||
comma = false;
|
||||
offset = this.offset;
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
break switch_;
|
||||
}
|
||||
skipName();
|
||||
skipValue();
|
||||
}
|
||||
} else if (ch == 'S' && nextIfSet()) {
|
||||
skipValue();
|
||||
} else {
|
||||
throw error(offset, ch);
|
||||
}
|
||||
ch = this.ch;
|
||||
offset = this.offset;
|
||||
if (jsonReader.ch == '}') {
|
||||
break;
|
||||
}
|
||||
if (i != 0 && !jsonReader.comma) {
|
||||
throw jsonReader.valueError();
|
||||
}
|
||||
offset = skipName(jsonReader, bytes, offset, end);
|
||||
offset = skipValue(jsonReader, bytes, offset, end);
|
||||
}
|
||||
|
||||
int ch = offset == end ? EOI : bytes[offset++];
|
||||
|
||||
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
}
|
||||
|
||||
boolean comma = false;
|
||||
if (ch == ',') {
|
||||
comma = true;
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
}
|
||||
|
||||
if ((ch == '}' || ch == ']' || ch == EOI)) {
|
||||
throw jsonReader.error(offset, ch);
|
||||
}
|
||||
} else if (ch != '}' && ch != ']' && ch != EOI) {
|
||||
throw jsonReader.error(offset, ch);
|
||||
}
|
||||
|
||||
jsonReader.comma = comma;
|
||||
jsonReader.ch = (char) ch;
|
||||
return offset;
|
||||
}
|
||||
|
||||
public static int next(JSONReaderUTF8 jsonReader, byte[] bytes, int offset, int end) {
|
||||
int ch = offset >= end ? EOI : bytes[offset++];
|
||||
while (ch == '\0' || (ch <= ' ' && ((1L << ch) & SPACE) != 0)) {
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
}
|
||||
|
||||
if (ch < 0) {
|
||||
jsonReader.char_utf8(ch, offset);
|
||||
return jsonReader.offset;
|
||||
}
|
||||
|
||||
jsonReader.ch = (char) ch;
|
||||
if (ch == '/') {
|
||||
jsonReader.offset = offset;
|
||||
jsonReader.skipComment();
|
||||
return jsonReader.offset;
|
||||
} else {
|
||||
return offset;
|
||||
}
|
||||
}
|
||||
|
||||
private static int skipArray(JSONReaderUTF8 jsonReader, byte[] bytes, int offset, int end) {
|
||||
offset = next(jsonReader, bytes, offset, end);
|
||||
for (int i = 0; ; ++i) {
|
||||
if (jsonReader.ch == ']') {
|
||||
break;
|
||||
}
|
||||
if (i != 0 && !jsonReader.comma) {
|
||||
throw jsonReader.valueError();
|
||||
}
|
||||
offset = skipValue(jsonReader, bytes, offset, end);
|
||||
}
|
||||
|
||||
int ch = offset == end ? EOI : bytes[offset++];
|
||||
|
||||
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
}
|
||||
|
||||
boolean comma = false;
|
||||
if (ch == ',') {
|
||||
comma = true;
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
@ -4485,15 +4535,158 @@ class JSONReaderUTF8
|
||||
}
|
||||
|
||||
if (!comma && ch != '}' && ch != ']' && ch != EOI) {
|
||||
throw error(offset, ch);
|
||||
throw jsonReader.error(offset, ch);
|
||||
}
|
||||
|
||||
if (comma && (ch == '}' || ch == ']' || ch == EOI)) {
|
||||
throw error(offset, ch);
|
||||
throw jsonReader.error(offset, ch);
|
||||
}
|
||||
|
||||
this.ch = (char) ch;
|
||||
this.offset = offset;
|
||||
jsonReader.comma = comma;
|
||||
jsonReader.ch = (char) ch;
|
||||
return offset;
|
||||
}
|
||||
|
||||
private static int skipFalse(JSONReaderUTF8 jsonReader, byte[] bytes, int offset, int end) {
|
||||
if (offset + 4 > end || IOUtils.notALSE(bytes, offset)) {
|
||||
throw jsonReader.error();
|
||||
}
|
||||
offset += 4;
|
||||
int ch = offset == end ? EOI : bytes[offset++];
|
||||
|
||||
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
}
|
||||
|
||||
boolean comma = false;
|
||||
if (ch == ',') {
|
||||
comma = true;
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
}
|
||||
|
||||
if ((ch == '}' || ch == ']' || ch == EOI)) {
|
||||
throw jsonReader.error(offset, ch);
|
||||
}
|
||||
} else if (ch != '}' && ch != ']' && ch != EOI) {
|
||||
throw jsonReader.error(offset, ch);
|
||||
}
|
||||
|
||||
jsonReader.comma = comma;
|
||||
jsonReader.ch = (char) ch;
|
||||
return offset;
|
||||
}
|
||||
|
||||
private static int skipTrue(JSONReaderUTF8 jsonReader, byte[] bytes, int offset, int end) {
|
||||
if (offset + 3 > end || IOUtils.notTRUE(bytes, offset - 1)) {
|
||||
throw jsonReader.error();
|
||||
}
|
||||
offset += 3;
|
||||
int ch = offset == end ? EOI : bytes[offset++];
|
||||
|
||||
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
}
|
||||
|
||||
boolean comma = false;
|
||||
if (ch == ',') {
|
||||
comma = true;
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
}
|
||||
|
||||
if ((ch == '}' || ch == ']' || ch == EOI)) {
|
||||
throw jsonReader.error(offset, ch);
|
||||
}
|
||||
} else if (ch != '}' && ch != ']' && ch != EOI) {
|
||||
throw jsonReader.error(offset, ch);
|
||||
}
|
||||
|
||||
jsonReader.comma = comma;
|
||||
jsonReader.ch = (char) ch;
|
||||
return offset;
|
||||
}
|
||||
|
||||
private static int skipNull(JSONReaderUTF8 jsonReader, byte[] bytes, int offset, int end) {
|
||||
if (offset + 3 > end || IOUtils.notNULL(bytes, offset - 1)) {
|
||||
throw jsonReader.error();
|
||||
}
|
||||
offset += 3;
|
||||
int ch = offset == end ? EOI : bytes[offset++];
|
||||
|
||||
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
}
|
||||
|
||||
boolean comma = false;
|
||||
if (ch == ',') {
|
||||
comma = true;
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
}
|
||||
|
||||
if ((ch == '}' || ch == ']' || ch == EOI)) {
|
||||
throw jsonReader.error(offset, ch);
|
||||
}
|
||||
} else if (ch != '}' && ch != ']' && ch != EOI) {
|
||||
throw jsonReader.error(offset, ch);
|
||||
}
|
||||
|
||||
jsonReader.comma = comma;
|
||||
jsonReader.ch = (char) ch;
|
||||
return offset;
|
||||
}
|
||||
|
||||
private static int skipSet(JSONReaderUTF8 jsonReader, byte[] bytes, int offset, int end) {
|
||||
if (nextIfSet(jsonReader, bytes, offset, end)) {
|
||||
return skipArray(jsonReader, bytes, jsonReader.offset, end);
|
||||
} else {
|
||||
throw jsonReader.error();
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean nextIfSet(JSONReaderUTF8 jsonReader, byte[] chars, int offset, int end) {
|
||||
if (offset + 1 < end && chars[offset] == 'e' && chars[offset + 1] == 't') {
|
||||
offset += 2;
|
||||
int ch = offset == end ? EOI : chars[offset++];
|
||||
while (ch <= ' ' && ((1L << ch) & SPACE) != 0) {
|
||||
ch = offset == end ? EOI : chars[offset++];
|
||||
}
|
||||
jsonReader.offset = offset;
|
||||
jsonReader.ch = (char) ch;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static int skipValue(JSONReaderUTF8 jsonReader, byte[] bytes, int offset, int end) {
|
||||
switch (jsonReader.ch) {
|
||||
case 't':
|
||||
return skipTrue(jsonReader, bytes, offset, end);
|
||||
case 'f':
|
||||
return skipFalse(jsonReader, bytes, offset, end);
|
||||
case 'n':
|
||||
return skipNull(jsonReader, bytes, offset, end);
|
||||
case '"':
|
||||
case '\'':
|
||||
return skipString(jsonReader, bytes, offset, end);
|
||||
case '{':
|
||||
return skipObject(jsonReader, bytes, offset, end);
|
||||
case '[':
|
||||
return skipArray(jsonReader, bytes, offset, end);
|
||||
case 'S':
|
||||
return skipSet(jsonReader, bytes, offset, end);
|
||||
default:
|
||||
return skipNumber(jsonReader, bytes, offset, end);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void skipValue() {
|
||||
this.offset = skipValue(this, bytes, offset, end);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -5740,9 +5933,10 @@ class JSONReaderUTF8
|
||||
}
|
||||
if (ch < 0) {
|
||||
char_utf8(ch, offset);
|
||||
}
|
||||
} else {
|
||||
this.offset = offset;
|
||||
this.ch = (char) ch;
|
||||
}
|
||||
return OffsetDateTime.of(
|
||||
yy + ((int) ymd & 0xFF),
|
||||
(int) (ymd >> 24) & 0xFF,
|
||||
|
@ -58,6 +58,7 @@ public class ASMUtils {
|
||||
public static final String TYPE_OBJECT_READER_10 = ObjectReader10.class.getName().replace('.', '/');
|
||||
public static final String TYPE_OBJECT_READER_11 = ObjectReader11.class.getName().replace('.', '/');
|
||||
public static final String TYPE_OBJECT_READER_12 = ObjectReader12.class.getName().replace('.', '/');
|
||||
public static final String TYPE_OBJECT_READER_NONE_DEFAULT_CONSTRUCTOR = ObjectReaderNoneDefaultConstructor.class.getName().replace('.', '/');
|
||||
public static final String TYPE_BYTE_ARRAY_VALUE_CONSUMER = ByteArrayValueConsumer.class.getName().replace('.', '/');
|
||||
public static final String TYPE_CHAR_ARRAY_VALUE_CONSUMER = CharArrayValueConsumer.class.getName().replace('.', '/');
|
||||
public static final String TYPE_TYPE_UTILS = TypeUtils.class.getName().replace('.', '/');
|
||||
|
@ -77,6 +77,8 @@ class Frame {
|
||||
|
||||
// Constants to manipulate the DIM field of an abstract type.
|
||||
|
||||
private static final int ARRAY_OF = +1 << DIM_SHIFT;
|
||||
|
||||
/**
|
||||
* The constant to be added to an abstract type to get one with one less array dimension.
|
||||
*/
|
||||
@ -877,6 +879,7 @@ class Frame {
|
||||
case Opcodes.IASTORE:
|
||||
case Opcodes.BASTORE:
|
||||
case Opcodes.CASTORE:
|
||||
case Opcodes.AASTORE:
|
||||
pop(3);
|
||||
break;
|
||||
case Opcodes.POP:
|
||||
@ -980,6 +983,15 @@ class Frame {
|
||||
push(LONG);
|
||||
push(TOP);
|
||||
break;
|
||||
case Opcodes.I2F:
|
||||
pop(1);
|
||||
push(FLOAT);
|
||||
break;
|
||||
case Opcodes.I2D:
|
||||
pop(1);
|
||||
push(DOUBLE);
|
||||
push(TOP);
|
||||
break;
|
||||
case Opcodes.F2I:
|
||||
case Opcodes.ARRAYLENGTH:
|
||||
case Opcodes.INSTANCEOF:
|
||||
@ -1022,6 +1034,15 @@ class Frame {
|
||||
case Opcodes.NEW:
|
||||
push(UNINITIALIZED_KIND | symbolTable.addUninitializedType(argSymbol.value, arg));
|
||||
break;
|
||||
case Opcodes.ANEWARRAY:
|
||||
String arrayElementType = argSymbol.value;
|
||||
pop();
|
||||
if (arrayElementType.charAt(0) == '[') {
|
||||
push(symbolTable, '[' + arrayElementType);
|
||||
} else {
|
||||
push(ARRAY_OF | REFERENCE_KIND | symbolTable.addType(arrayElementType));
|
||||
}
|
||||
break;
|
||||
case Opcodes.CHECKCAST:
|
||||
String castType = argSymbol.value;
|
||||
pop();
|
||||
|
@ -293,6 +293,14 @@ public final class MethodWriter {
|
||||
visitInsn(Opcodes.I2L);
|
||||
}
|
||||
|
||||
public void i2f() {
|
||||
visitInsn(Opcodes.I2F);
|
||||
}
|
||||
|
||||
public void i2d() {
|
||||
visitInsn(Opcodes.I2D);
|
||||
}
|
||||
|
||||
public void lxor() {
|
||||
visitInsn(Opcodes.LXOR);
|
||||
}
|
||||
@ -305,6 +313,10 @@ public final class MethodWriter {
|
||||
visitInsn(Opcodes.AALOAD);
|
||||
}
|
||||
|
||||
public void aastore() {
|
||||
visitInsn(Opcodes.AASTORE);
|
||||
}
|
||||
|
||||
private void visitInsn(final int opcode) {
|
||||
lastBytecodeOffset = code.length;
|
||||
// Add the instruction to the bytecode of the method.
|
||||
@ -455,6 +467,10 @@ public final class MethodWriter {
|
||||
visitTypeInsn(Opcodes.NEW, type);
|
||||
}
|
||||
|
||||
public void anewArray(final String type) {
|
||||
visitTypeInsn(Opcodes.ANEWARRAY, type);
|
||||
}
|
||||
|
||||
public void instanceOf(final String type) {
|
||||
visitTypeInsn(Opcodes.INSTANCEOF, type);
|
||||
}
|
||||
|
@ -91,6 +91,7 @@ public interface Opcodes {
|
||||
int DSTORE = 57; // -
|
||||
int ASTORE = 58; // -
|
||||
int IASTORE = 79; // visitInsn
|
||||
int AASTORE = 83; // -
|
||||
int BASTORE = 84; // -
|
||||
int CASTORE = 85; // -
|
||||
int POP = 87; // visitInsn
|
||||
@ -126,6 +127,8 @@ public interface Opcodes {
|
||||
int LXOR = 131; // -
|
||||
int IINC = 132; // visitIincInsn
|
||||
int I2L = 133; // visitInsn
|
||||
int I2F = 134; // -
|
||||
int I2D = 135; // -
|
||||
int L2I = 136; //
|
||||
int F2I = 139; // -
|
||||
int F2L = 140; // -
|
||||
@ -173,6 +176,8 @@ public interface Opcodes {
|
||||
int INVOKESTATIC = 184; // -
|
||||
int INVOKEINTERFACE = 185; // -
|
||||
int NEW = 187; // visitTypeInsn
|
||||
int NEWARRAY = 188; // -
|
||||
int ANEWARRAY = 189; // -
|
||||
int ARRAYLENGTH = 190; // visitInsn
|
||||
int ATHROW = 191; // -
|
||||
int CHECKCAST = 192; // visitTypeInsn
|
||||
|
@ -70,8 +70,10 @@ public abstract class FieldReader<T>
|
||||
this.fieldName = fieldName;
|
||||
this.fieldType = fieldType;
|
||||
this.fieldClass = fieldClass;
|
||||
this.fieldClassSerializable = fieldClass != null && (Serializable.class.isAssignableFrom(fieldClass)
|
||||
|| Modifier.isInterface(fieldClass.getModifiers()));
|
||||
this.fieldClassSerializable = fieldClass != null
|
||||
&& (Serializable.class.isAssignableFrom(fieldClass)
|
||||
|| Modifier.isInterface(fieldClass.getModifiers())
|
||||
|| BeanUtils.isRecord(fieldClass));
|
||||
this.features = features;
|
||||
this.fieldNameHash = Fnv.hashCode64(fieldName);
|
||||
this.fieldNameHashLCase = Fnv.hashCode64LCase(fieldName);
|
||||
|
@ -0,0 +1,37 @@
|
||||
package com.alibaba.fastjson2.reader;
|
||||
|
||||
import com.alibaba.fastjson2.schema.JSONSchema;
|
||||
import com.alibaba.fastjson2.util.Fnv;
|
||||
|
||||
import java.lang.reflect.Parameter;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Locale;
|
||||
|
||||
public class FieldReaderListParam
|
||||
extends FieldReaderList {
|
||||
final Parameter parameter;
|
||||
final String paramName;
|
||||
final long paramNameHash;
|
||||
|
||||
public FieldReaderListParam(
|
||||
String fieldName,
|
||||
Type fieldType,
|
||||
String paramName,
|
||||
Parameter parameter,
|
||||
Class fieldClass,
|
||||
Type itemType,
|
||||
Class itemClass,
|
||||
int ordinal,
|
||||
long features,
|
||||
String format,
|
||||
Locale locale,
|
||||
Object defaultValue,
|
||||
JSONSchema schema
|
||||
) {
|
||||
super(fieldName, fieldType, fieldClass, itemType, itemClass, ordinal, features, format, locale, defaultValue, schema, null, null, null);
|
||||
|
||||
this.paramName = paramName;
|
||||
this.paramNameHash = Fnv.hashCode64(paramName);
|
||||
this.parameter = parameter;
|
||||
}
|
||||
}
|
@ -445,6 +445,21 @@ public class ObjectReaderAdapter<T>
|
||||
return fieldReader;
|
||||
}
|
||||
|
||||
protected final void readFieldValue(long hashCode, JSONReader jsonReader, long features, Map<Long, Object> map) {
|
||||
FieldReader fieldReader = getFieldReader(hashCode);
|
||||
if (fieldReader == null
|
||||
&& jsonReader.isSupportSmartMatch(this.features | features)) {
|
||||
long hashCodeL = jsonReader.getNameHashCodeLCase();
|
||||
fieldReader = getFieldReaderLCase(hashCodeL);
|
||||
}
|
||||
|
||||
if (fieldReader != null) {
|
||||
map.put(hashCode, fieldReader.readFieldValue(jsonReader));
|
||||
} else {
|
||||
jsonReader.skipValue();
|
||||
}
|
||||
}
|
||||
|
||||
protected final void readFieldValue(long hashCode, JSONReader jsonReader, long features, Object object) {
|
||||
FieldReader fieldReader = getFieldReader(hashCode);
|
||||
if (fieldReader == null
|
||||
|
@ -10,6 +10,7 @@ import com.alibaba.fastjson2.util.TypeUtils;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@ -96,6 +97,26 @@ public abstract class ObjectReaderBean<T>
|
||||
processExtra(jsonReader, object, 0);
|
||||
}
|
||||
|
||||
protected void processExtra(JSONReader jsonReader, Map<Long, Object> map, long features) {
|
||||
if ((jsonReader.features(this.features | features) & JSONReader.Feature.SupportSmartMatch.mask) != 0) {
|
||||
String fieldName = jsonReader.getFieldName();
|
||||
if (fieldName.startsWith("is")) {
|
||||
String fieldName1 = fieldName.substring(2);
|
||||
long hashCode64LCase = Fnv.hashCode64LCase(fieldName1);
|
||||
FieldReader fieldReader = getFieldReaderLCase(hashCode64LCase);
|
||||
if (fieldReader != null) {
|
||||
Class fieldClass = fieldReader.fieldClass;
|
||||
if (fieldClass == Boolean.class || fieldClass == boolean.class) {
|
||||
map.put(fieldReader.fieldNameHash, fieldReader.readFieldValue(jsonReader));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
jsonReader.skipValue();
|
||||
}
|
||||
|
||||
protected void processExtra(JSONReader jsonReader, Object object, long features) {
|
||||
if ((jsonReader.features(this.features | features) & JSONReader.Feature.SupportSmartMatch.mask) != 0) {
|
||||
String fieldName = jsonReader.getFieldName();
|
||||
|
@ -1273,18 +1273,14 @@ public class ObjectReaderCreator {
|
||||
creatorConstructor.getParameters(),
|
||||
parameterNames
|
||||
);
|
||||
return new ObjectReaderNoneDefaultConstructor(
|
||||
return createNoneDefaultConstructorObjectReader(
|
||||
objectClass,
|
||||
beanInfo.typeKey,
|
||||
beanInfo.typeName,
|
||||
beanInfo.readerFeatures,
|
||||
beanInfo,
|
||||
constructorFunction,
|
||||
alternateConstructors,
|
||||
parameterNames,
|
||||
paramFieldReaders,
|
||||
fieldReaderArray,
|
||||
null,
|
||||
null
|
||||
fieldReaderArray
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1344,6 +1340,30 @@ public class ObjectReaderCreator {
|
||||
return objectReader;
|
||||
}
|
||||
|
||||
protected <T> ObjectReaderNoneDefaultConstructor createNoneDefaultConstructorObjectReader(
|
||||
Class<T> objectClass,
|
||||
BeanInfo beanInfo,
|
||||
Function<Map<Long, Object>, T> constructorFunction,
|
||||
List<Constructor> alternateConstructors,
|
||||
String[] parameterNames,
|
||||
FieldReader[] paramFieldReaders,
|
||||
FieldReader[] fieldReaderArray
|
||||
) {
|
||||
return new ObjectReaderNoneDefaultConstructor(
|
||||
objectClass,
|
||||
beanInfo.typeKey,
|
||||
beanInfo.typeName,
|
||||
beanInfo.readerFeatures,
|
||||
constructorFunction,
|
||||
alternateConstructors,
|
||||
parameterNames,
|
||||
paramFieldReaders,
|
||||
fieldReaderArray,
|
||||
null,
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
public <T> FieldReader[] createFieldReaders(Class<T> objectClass) {
|
||||
return createFieldReaders(
|
||||
objectClass,
|
||||
@ -2199,6 +2219,33 @@ public class ObjectReaderCreator {
|
||||
fieldClassResolved = fieldClass;
|
||||
}
|
||||
|
||||
Type itemType = null;
|
||||
Class itemClass = null;
|
||||
if (fieldTypeResolved instanceof ParameterizedType) {
|
||||
ParameterizedType parameterizedType = (ParameterizedType) fieldTypeResolved;
|
||||
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
|
||||
if (actualTypeArguments.length == 1) {
|
||||
itemType = actualTypeArguments[0];
|
||||
itemClass = TypeUtils.getClass(itemClass);
|
||||
}
|
||||
}
|
||||
if (fieldClassResolved != null && Collection.class.isAssignableFrom(fieldClassResolved) && itemType != null) {
|
||||
return new FieldReaderListParam(
|
||||
fieldName,
|
||||
fieldTypeResolved,
|
||||
paramName,
|
||||
parameter,
|
||||
fieldClassResolved,
|
||||
itemType,
|
||||
itemClass,
|
||||
ordinal,
|
||||
features,
|
||||
format,
|
||||
locale,
|
||||
defaultValue,
|
||||
schema);
|
||||
}
|
||||
|
||||
return new FieldReaderObjectParam(
|
||||
fieldName,
|
||||
fieldTypeResolved,
|
||||
|
@ -23,7 +23,6 @@ import java.util.function.*;
|
||||
|
||||
import static com.alibaba.fastjson2.internal.CodeGenUtils.fieldReader;
|
||||
import static com.alibaba.fastjson2.internal.asm.ASMUtils.*;
|
||||
import static com.alibaba.fastjson2.internal.asm.Opcodes.*;
|
||||
import static com.alibaba.fastjson2.reader.ObjectReader.HASH_TYPE;
|
||||
import static com.alibaba.fastjson2.util.JDKUtils.*;
|
||||
|
||||
@ -46,10 +45,12 @@ public class ObjectReaderCreatorASM
|
||||
static final String METHOD_DESC_READ_FIELD_VALUE = "(" + DESC_JSON_READER + "Ljava/lang/Object;)V";
|
||||
static final String GET_FIELD_READER_UL = "(J" + DESC_JSON_READER + "J)" + DESC_FIELD_READER;
|
||||
static final String READ_FIELD_READER_UL = "(J" + DESC_JSON_READER + "JLjava/lang/Object;)V";
|
||||
static final String READ_FIELD_READER_MAP = "(J" + DESC_JSON_READER + "JLjava/util/Map;)V";
|
||||
static final String METHOD_DESC_ADD_RESOLVE_TASK = "(" + DESC_JSON_READER + "Ljava/lang/Object;Ljava/lang/String;)V";
|
||||
static final String METHOD_DESC_ADD_RESOLVE_TASK_2 = "(" + DESC_JSON_READER + "Ljava/util/List;ILjava/lang/String;)V";
|
||||
static final String METHOD_DESC_CHECK_ARRAY_AUTO_TYPE = "(" + DESC_JSON_READER + ")" + DESC_OBJECT_READER;
|
||||
static final String METHOD_DESC_PROCESS_EXTRA = "(" + DESC_JSON_READER + "Ljava/lang/Object;J)V";
|
||||
static final String METHOD_DESC_PROCESS_EXTRA_2 = "(" + DESC_JSON_READER + "Ljava/util/Map;J)V";
|
||||
|
||||
static final String METHOD_DESC_JSON_READER_CHECK_ARRAY_AUTO_TYPE = "(" + DESC_JSON_READER + "J)" + DESC_OBJECT_READER;
|
||||
static final String METHOD_DESC_READ_ARRAY_MAPPING_JSONB_OBJECT0 = "(" + DESC_JSON_READER + "Ljava/lang/Object;I)V";
|
||||
@ -337,6 +338,150 @@ public class ObjectReaderCreatorASM
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <T> ObjectReaderNoneDefaultConstructor createNoneDefaultConstructorObjectReader(
|
||||
Class<T> objectClass,
|
||||
BeanInfo beanInfo,
|
||||
Function<Map<Long, Object>, T> constructorFunction,
|
||||
List<Constructor> alternateConstructors,
|
||||
String[] parameterNames,
|
||||
FieldReader[] paramFieldReaders,
|
||||
FieldReader[] fieldReaderArray
|
||||
) {
|
||||
ObjectReaderNoneDefaultConstructor objectReaderAdapter = new ObjectReaderNoneDefaultConstructor(
|
||||
objectClass,
|
||||
beanInfo.typeKey,
|
||||
beanInfo.typeName,
|
||||
beanInfo.readerFeatures,
|
||||
constructorFunction,
|
||||
alternateConstructors,
|
||||
parameterNames,
|
||||
paramFieldReaders,
|
||||
fieldReaderArray,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
boolean match = true;
|
||||
|
||||
if (beanInfo.autoTypeBeforeHandler != null
|
||||
|| fieldReaderArray.length != 0
|
||||
|| !(constructorFunction instanceof ConstructorFunction)
|
||||
|| !alternateConstructors.isEmpty()
|
||||
|| classLoader.isExternalClass(objectClass)
|
||||
|| (beanInfo.readerFeatures & JSONReader.Feature.SupportAutoType.mask) != 0
|
||||
) {
|
||||
match = false;
|
||||
}
|
||||
|
||||
if (match) {
|
||||
for (FieldReader fieldReader : paramFieldReaders) {
|
||||
if (fieldReader.getInitReader() != null) {
|
||||
match = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (fieldReader.defaultValue != null || fieldReader.schema != null) {
|
||||
match = false;
|
||||
break;
|
||||
}
|
||||
|
||||
Class fieldClass = fieldReader.fieldClass;
|
||||
if (fieldClass != null && (!Modifier.isPublic(fieldClass.getModifiers()) || classLoader.isExternalClass(fieldClass))) {
|
||||
match = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (fieldReader instanceof FieldReaderMapField
|
||||
&& ((FieldReaderMapField<?>) fieldReader).arrayToMapKey != null) {
|
||||
match = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (fieldReader instanceof FieldReaderMapMethod
|
||||
&& ((FieldReaderMapMethod<?>) fieldReader).arrayToMapKey != null) {
|
||||
match = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!match) {
|
||||
return objectReaderAdapter;
|
||||
}
|
||||
|
||||
boolean externalClass = objectClass != null && classLoader.isExternalClass(objectClass);
|
||||
ClassWriter cw = new ClassWriter(
|
||||
(e) -> objectClass.getName().equals(e) ? objectClass : null
|
||||
);
|
||||
|
||||
beanInfo.readerFeatures |= FieldInfo.DISABLE_REFERENCE_DETECT;
|
||||
ObjectWriteContext context = new ObjectWriteContext(beanInfo, objectClass, cw, externalClass, paramFieldReaders, null);
|
||||
context.objectReaderAdapter = objectReaderAdapter;
|
||||
|
||||
genFields(paramFieldReaders, cw, TYPE_OBJECT_READER_NONE_DEFAULT_CONSTRUCTOR);
|
||||
|
||||
cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_SUPER, context.classNameType, TYPE_OBJECT_READER_NONE_DEFAULT_CONSTRUCTOR, new String[]{});
|
||||
|
||||
{
|
||||
String MD_INIT = "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;JLjava/util/function/Function;Ljava/util/List;[Ljava/lang/String;[Lcom/alibaba/fastjson2/reader/FieldReader;[Lcom/alibaba/fastjson2/reader/FieldReader;[Ljava/lang/Class;[Ljava/lang/String;)V";
|
||||
|
||||
MethodWriter mw = cw.visitMethod(
|
||||
Opcodes.ACC_PUBLIC,
|
||||
"<init>",
|
||||
MD_INIT,
|
||||
fieldReaderArray.length <= 12 ? 32 : 128);
|
||||
mw.aload(THIS);
|
||||
mw.aload(1); // CLASS
|
||||
mw.aload(2); // TYPE_KEY
|
||||
mw.aload(3); // TYPE_NAME
|
||||
mw.lload(4); // FEATURES
|
||||
mw.aload(6); // CREATOR
|
||||
mw.aload(7); // alternateConstructors
|
||||
mw.aload(8); // paramNames
|
||||
mw.aload(9); // paramFieldReaders
|
||||
mw.aload(10); // setterFieldReaders
|
||||
mw.aload(11); // seeAlso
|
||||
mw.aload(12); // seeAlsoNames
|
||||
mw.invokespecial(TYPE_OBJECT_READER_NONE_DEFAULT_CONSTRUCTOR, "<init>", MD_INIT);
|
||||
|
||||
int FIELD_READER_ARRAY = 9;
|
||||
genInitFields(paramFieldReaders, context.classNameType, true, THIS, FIELD_READER_ARRAY, mw, TYPE_OBJECT_READER_NONE_DEFAULT_CONSTRUCTOR);
|
||||
|
||||
mw.return_();
|
||||
mw.visitMaxs(3, 3);
|
||||
}
|
||||
|
||||
String TYPE_OBJECT = objectClass == null ? ASMUtils.TYPE_OBJECT : ASMUtils.type(objectClass);
|
||||
|
||||
genMethodReadObject(context, beanInfo.readerFeatures, TYPE_OBJECT, paramFieldReaders, cw, context.classNameType);
|
||||
|
||||
byte[] code = cw.toByteArray();
|
||||
try {
|
||||
Class<?> readerClass = classLoader.defineClassPublic(context.classNameFull, code, 0, code.length);
|
||||
|
||||
Constructor<?> constructor = readerClass.getConstructors()[0];
|
||||
return (ObjectReaderNoneDefaultConstructor) constructor
|
||||
.newInstance(
|
||||
objectClass,
|
||||
beanInfo.typeKey,
|
||||
beanInfo.typeName,
|
||||
beanInfo.readerFeatures,
|
||||
constructorFunction,
|
||||
alternateConstructors,
|
||||
parameterNames,
|
||||
paramFieldReaders,
|
||||
fieldReaderArray,
|
||||
null,
|
||||
null
|
||||
);
|
||||
} catch (Throwable e) {
|
||||
throw new JSONException(
|
||||
"create objectReader error"
|
||||
+ (objectClass == null ? "" : ", objectType " + objectClass.getTypeName()), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ObjectReader<T> createObjectReader(
|
||||
Class<T> objectClass,
|
||||
@ -401,34 +546,7 @@ public class ObjectReaderCreatorASM
|
||||
(e) -> objectClass.getName().equals(e) ? objectClass : null
|
||||
);
|
||||
|
||||
ObjectWriteContext context = new ObjectWriteContext(beanInfo, objectClass, cw, externalClass, fieldReaderArray);
|
||||
|
||||
String className = "ORG_" + seed.incrementAndGet() + "_" + fieldReaderArray.length + (objectClass == null ? "" : "_" + objectClass.getSimpleName());
|
||||
String classNameType;
|
||||
String classNameFull;
|
||||
|
||||
Package pkg = ObjectReaderCreatorASM.class.getPackage();
|
||||
if (pkg != null) {
|
||||
String packageName = pkg.getName();
|
||||
int packageNameLength = packageName.length();
|
||||
int charsLength = packageNameLength + 1 + className.length();
|
||||
char[] chars = new char[charsLength];
|
||||
packageName.getChars(0, packageName.length(), chars, 0);
|
||||
chars[packageNameLength] = '.';
|
||||
className.getChars(0, className.length(), chars, packageNameLength + 1);
|
||||
classNameFull = new String(chars);
|
||||
|
||||
chars[packageNameLength] = '/';
|
||||
for (int i = 0; i < packageNameLength; ++i) {
|
||||
if (chars[i] == '.') {
|
||||
chars[i] = '/';
|
||||
}
|
||||
}
|
||||
classNameType = new String(chars);
|
||||
} else {
|
||||
classNameType = className;
|
||||
classNameFull = className;
|
||||
}
|
||||
ObjectWriteContext context = new ObjectWriteContext(beanInfo, objectClass, cw, externalClass, fieldReaderArray, defaultConstructor);
|
||||
|
||||
final boolean generatedFields = fieldReaderArray.length < 128;
|
||||
|
||||
@ -479,7 +597,7 @@ public class ObjectReaderCreatorASM
|
||||
genFields(fieldReaderArray, cw, objectReaderSuper);
|
||||
}
|
||||
|
||||
cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_SUPER, classNameType, objectReaderSuper, new String[]{});
|
||||
cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_SUPER, context.classNameType, objectReaderSuper, new String[]{});
|
||||
|
||||
{
|
||||
final int CLASS = 1, SUPPLIER = 2, FIELD_READER_ARRAY = 3;
|
||||
@ -504,7 +622,7 @@ public class ObjectReaderCreatorASM
|
||||
mw.aload(FIELD_READER_ARRAY);
|
||||
mw.invokespecial(objectReaderSuper, "<init>", METHOD_DESC_ADAPTER_INIT);
|
||||
|
||||
genInitFields(fieldReaderArray, classNameType, generatedFields, THIS, FIELD_READER_ARRAY, mw, objectReaderSuper);
|
||||
genInitFields(fieldReaderArray, context.classNameType, generatedFields, THIS, FIELD_READER_ARRAY, mw, objectReaderSuper);
|
||||
|
||||
mw.return_();
|
||||
mw.visitMaxs(3, 3);
|
||||
@ -559,16 +677,16 @@ public class ObjectReaderCreatorASM
|
||||
boolean disableArrayMapping = context.disableSupportArrayMapping();
|
||||
boolean disableJSONB = context.disableJSONB();
|
||||
|
||||
ObjectReaderAdapter objectReaderAdapter = new ObjectReaderAdapter(objectClass, beanInfo.typeKey, beanInfo.typeName, readerFeatures, null, supplier, null, fieldReaderArray);
|
||||
context.objectReaderAdapter = new ObjectReaderAdapter(objectClass, beanInfo.typeKey, beanInfo.typeName, readerFeatures, null, supplier, null, fieldReaderArray);
|
||||
|
||||
if (!disableJSONB) {
|
||||
genMethodReadJSONBObject(context, defaultConstructor, readerFeatures, TYPE_OBJECT, fieldReaderArray, cw, classNameType, objectReaderAdapter);
|
||||
genMethodReadJSONBObject(context, readerFeatures, TYPE_OBJECT, fieldReaderArray, cw, context.classNameType);
|
||||
if (!disableArrayMapping) {
|
||||
genMethodReadJSONBObjectArrayMapping(context, defaultConstructor, readerFeatures, TYPE_OBJECT, fieldReaderArray, cw, classNameType, objectReaderAdapter);
|
||||
genMethodReadJSONBObjectArrayMapping(context, readerFeatures, TYPE_OBJECT, fieldReaderArray, cw, context.classNameType);
|
||||
}
|
||||
}
|
||||
|
||||
genMethodReadObject(context, defaultConstructor, readerFeatures, TYPE_OBJECT, fieldReaderArray, cw, classNameType, objectReaderAdapter);
|
||||
genMethodReadObject(context, readerFeatures, TYPE_OBJECT, fieldReaderArray, cw, context.classNameType);
|
||||
|
||||
if (objectReaderSuper == TYPE_OBJECT_READER_ADAPTER
|
||||
|| objectReaderSuper == TYPE_OBJECT_READER_1
|
||||
@ -584,14 +702,14 @@ public class ObjectReaderCreatorASM
|
||||
|| objectReaderSuper == TYPE_OBJECT_READER_11
|
||||
|| objectReaderSuper == TYPE_OBJECT_READER_12
|
||||
) {
|
||||
genMethodGetFieldReader(fieldReaderArray, cw, classNameType, objectReaderAdapter);
|
||||
genMethodGetFieldReaderLCase(fieldReaderArray, cw, classNameType, objectReaderAdapter);
|
||||
genMethodGetFieldReader(fieldReaderArray, cw, context.classNameType, context.objectReaderAdapter);
|
||||
genMethodGetFieldReaderLCase(fieldReaderArray, cw, context.classNameType, context.objectReaderAdapter);
|
||||
}
|
||||
}
|
||||
|
||||
byte[] code = cw.toByteArray();
|
||||
try {
|
||||
Class<?> readerClass = classLoader.defineClassPublic(classNameFull, code, 0, code.length);
|
||||
Class<?> readerClass = classLoader.defineClassPublic(context.classNameFull, code, 0, code.length);
|
||||
|
||||
Constructor<?> constructor = readerClass.getConstructors()[0];
|
||||
return (ObjectReaderBean) constructor
|
||||
@ -839,7 +957,7 @@ public class ObjectReaderCreatorASM
|
||||
MethodWriter mw,
|
||||
String objectReaderSuper
|
||||
) {
|
||||
if (objectReaderSuper != TYPE_OBJECT_READER_ADAPTER || !generatedFields) {
|
||||
if ((objectReaderSuper != TYPE_OBJECT_READER_ADAPTER && objectReaderSuper != TYPE_OBJECT_READER_NONE_DEFAULT_CONSTRUCTOR) || !generatedFields) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -853,7 +971,7 @@ public class ObjectReaderCreatorASM
|
||||
}
|
||||
|
||||
private void genFields(FieldReader[] fieldReaderArray, ClassWriter cw, String objectReaderSuper) {
|
||||
if (objectReaderSuper == TYPE_OBJECT_READER_ADAPTER) {
|
||||
if (objectReaderSuper == TYPE_OBJECT_READER_ADAPTER || objectReaderSuper == TYPE_OBJECT_READER_NONE_DEFAULT_CONSTRUCTOR) {
|
||||
for (int i = 0; i < fieldReaderArray.length; i++) {
|
||||
FieldWriter fv = cw.visitField(Opcodes.ACC_PUBLIC, fieldReader(i), DESC_FIELD_READER);
|
||||
}
|
||||
@ -873,16 +991,16 @@ public class ObjectReaderCreatorASM
|
||||
|
||||
private <T> void genMethodReadJSONBObject(
|
||||
ObjectWriteContext context,
|
||||
Constructor defaultConstructor,
|
||||
long readerFeatures,
|
||||
String TYPE_OBJECT,
|
||||
FieldReader[] fieldReaderArray,
|
||||
ClassWriter cw,
|
||||
String classNameType,
|
||||
ObjectReaderAdapter objectReaderAdapter
|
||||
String classNameType
|
||||
) {
|
||||
Class objectClass = context.objectClass;
|
||||
boolean fieldBased = (readerFeatures & JSONReader.Feature.FieldBased.mask) != 0;
|
||||
Constructor defaultConstructor = context.defaultConstructor;
|
||||
ObjectReaderAdapter objectReaderAdapter = context.objectReaderAdapter;
|
||||
|
||||
MethodWriter mw = cw.visitMethod(Opcodes.ACC_PUBLIC,
|
||||
"readJSONBObject",
|
||||
@ -961,16 +1079,13 @@ public class ObjectReaderCreatorASM
|
||||
mw.visitLabel(object_);
|
||||
}
|
||||
|
||||
genCreateObject(mw, context, classNameType, TYPE_OBJECT, FEATURES, fieldBased, defaultConstructor, objectReaderAdapter.creator);
|
||||
genCreateObject(mw, context, classNameType, TYPE_OBJECT, FEATURES, fieldBased);
|
||||
mw.astore(OBJECT);
|
||||
|
||||
mw.aload(JSON_READER);
|
||||
mw.invokevirtual(TYPE_JSON_READER, "nextIfObjectStart", "()Z");
|
||||
mw.pop();
|
||||
|
||||
genCreateObject(mw, context, classNameType, TYPE_OBJECT, FEATURES, fieldBased, defaultConstructor, objectReaderAdapter.creator);
|
||||
mw.astore(OBJECT);
|
||||
|
||||
// for (int i = 0; i < entry_cnt; ++i) {
|
||||
Label for_start_i_ = new Label(), for_end_i_ = new Label(), for_inc_i_ = new Label();
|
||||
if (!disableAutoType) {
|
||||
@ -1259,14 +1374,13 @@ public class ObjectReaderCreatorASM
|
||||
|
||||
private <T> void genMethodReadJSONBObjectArrayMapping(
|
||||
ObjectWriteContext context,
|
||||
Constructor defaultConstructor,
|
||||
long readerFeatures,
|
||||
String TYPE_OBJECT,
|
||||
FieldReader[] fieldReaderArray,
|
||||
ClassWriter cw,
|
||||
String classNameType,
|
||||
ObjectReaderAdapter objectReaderAdapter
|
||||
String classNameType
|
||||
) {
|
||||
Constructor defaultConstructor = context.defaultConstructor;
|
||||
boolean fieldBased = (readerFeatures & JSONReader.Feature.FieldBased.mask) != 0;
|
||||
|
||||
MethodWriter mw = cw.visitMethod(Opcodes.ACC_PUBLIC,
|
||||
@ -1305,7 +1419,7 @@ public class ObjectReaderCreatorASM
|
||||
mw.visitLabel(notNull_);
|
||||
}
|
||||
|
||||
genCreateObject(mw, context, classNameType, TYPE_OBJECT, FEATURES, fieldBased, defaultConstructor, objectReaderAdapter.creator);
|
||||
genCreateObject(mw, context, classNameType, TYPE_OBJECT, FEATURES, fieldBased);
|
||||
mw.astore(OBJECT);
|
||||
|
||||
Label fieldEnd_ = new Label(), entryCountMatch_ = new Label();
|
||||
@ -1386,13 +1500,11 @@ public class ObjectReaderCreatorASM
|
||||
|
||||
private <T> void genMethodReadObject(
|
||||
ObjectWriteContext context,
|
||||
Constructor defaultConstructor,
|
||||
long readerFeatures,
|
||||
String TYPE_OBJECT,
|
||||
FieldReader[] fieldReaderArray,
|
||||
ClassWriter cw,
|
||||
String classNameType,
|
||||
ObjectReaderAdapter objectReaderAdapter
|
||||
String classNameType
|
||||
) {
|
||||
boolean fieldBased = (readerFeatures & JSONReader.Feature.FieldBased.mask) != 0;
|
||||
|
||||
@ -1493,13 +1605,62 @@ public class ObjectReaderCreatorASM
|
||||
mw.ifeq(notNull_);
|
||||
|
||||
mw.aconst_null();
|
||||
mw.astore(OBJECT);
|
||||
mw.goto_(end_);
|
||||
mw.areturn();
|
||||
|
||||
mw.visitLabel(notNull_);
|
||||
|
||||
genCreateObject(mw, context, classNameType, TYPE_OBJECT, FEATURES, fieldBased, defaultConstructor, objectReaderAdapter.creator);
|
||||
if (context.objectReaderAdapter instanceof ObjectReaderNoneDefaultConstructor) {
|
||||
Label L0 = new Label(), L1 = new Label();
|
||||
mw.aload(JSON_READER);
|
||||
mw.invokevirtual(TYPE_JSON_READER, "hasAutoTypeBeforeHandler", "()Z");
|
||||
mw.ifne(L0);
|
||||
|
||||
mw.lload(FEATURES);
|
||||
mw.visitLdcInsn(JSONReader.Feature.SupportSmartMatch.mask | JSONReader.Feature.SupportAutoType.mask);
|
||||
mw.land();
|
||||
mw.lconst_0();
|
||||
mw.lcmp();
|
||||
mw.ifeq(L1);
|
||||
|
||||
mw.visitLabel(L0);
|
||||
mw.aload(THIS);
|
||||
mw.aload(JSON_READER);
|
||||
mw.aload(FIELD_TYPE);
|
||||
mw.aload(FIELD_NAME);
|
||||
mw.lload(FEATURES);
|
||||
mw.invokespecial(TYPE_OBJECT_READER_NONE_DEFAULT_CONSTRUCTOR, "readObject", METHOD_DESC_READ_OBJECT);
|
||||
mw.areturn();
|
||||
|
||||
mw.visitLabel(L1);
|
||||
|
||||
for (FieldReader fieldReader : fieldReaderArray) {
|
||||
Class fieldClass = fieldReader.fieldClass;
|
||||
int var = mwc.var(fieldReader);
|
||||
if (fieldClass == byte.class || fieldClass == short.class || fieldClass == int.class || fieldClass == boolean.class || fieldClass == char.class) {
|
||||
mw.iconst_0();
|
||||
mw.istore(var);
|
||||
} else if (fieldClass == long.class) {
|
||||
mw.lconst_0();
|
||||
mw.lstore(var);
|
||||
} else if (fieldClass == float.class) {
|
||||
mw.iconst_0();
|
||||
mw.i2f();
|
||||
mw.fstore(var);
|
||||
} else if (fieldClass == double.class) {
|
||||
mw.iconst_0();
|
||||
mw.i2d();
|
||||
mw.dstore(var);
|
||||
} else {
|
||||
mw.aconst_null();
|
||||
mw.astore(var);
|
||||
}
|
||||
}
|
||||
mw.aconst_null();
|
||||
mw.astore(mwc.var("map"));
|
||||
} else {
|
||||
genCreateObject(mw, context, classNameType, TYPE_OBJECT, FEATURES, fieldBased);
|
||||
mw.astore(OBJECT);
|
||||
}
|
||||
|
||||
// for (int i = 0; i < entry_cnt; ++i) {
|
||||
Label for_start_i_ = new Label(), for_end_i_ = new Label(), for_inc_i_ = new Label();
|
||||
@ -1556,17 +1717,24 @@ public class ObjectReaderCreatorASM
|
||||
|
||||
mw.visitLabel(hashCode64Start);
|
||||
|
||||
if (switchGen && context.objectReaderAdapter instanceof ObjectReaderNoneDefaultConstructor) {
|
||||
mw.aload(JSON_READER);
|
||||
mw.invokevirtual(TYPE_JSON_READER, "skipName", "()Z");
|
||||
mw.pop();
|
||||
} else {
|
||||
mw.aload(JSON_READER);
|
||||
mw.invokevirtual(TYPE_JSON_READER, "readFieldNameHashCode", "()J");
|
||||
mw.dup2();
|
||||
mw.lstore(HASH_CODE64);
|
||||
|
||||
mw.visitLdcInsn(-1L);
|
||||
mw.lcmp();
|
||||
mw.ifeq(for_end_i_);
|
||||
}
|
||||
|
||||
mw.visitLabel(hashCode64End);
|
||||
|
||||
if (!disableAutoType) {
|
||||
if (!disableAutoType && !(context.objectReaderAdapter instanceof ObjectReaderNoneDefaultConstructor)) {
|
||||
Label noneAutoType_ = new Label();
|
||||
|
||||
// if (i != 0 && hash == HASH_TYPE && jsonReader.isSupportAutoType())
|
||||
@ -1598,19 +1766,24 @@ public class ObjectReaderCreatorASM
|
||||
|
||||
// continue
|
||||
if (switchGen) {
|
||||
if (context.objectReaderAdapter instanceof ObjectReaderNoneDefaultConstructor) {
|
||||
mw.aload(JSON_READER);
|
||||
mw.invokevirtual(TYPE_JSON_READER, "skipValue", "()V");
|
||||
} else {
|
||||
mw.aload(THIS);
|
||||
mw.lload(HASH_CODE64);
|
||||
mw.aload(JSON_READER);
|
||||
mw.lload(FEATURES);
|
||||
mw.aload(OBJECT);
|
||||
mw.invokevirtual(TYPE_OBJECT_READER_ADAPTER, "readFieldValue", READ_FIELD_READER_UL);
|
||||
}
|
||||
mw.goto_(for_inc_i_); // continue
|
||||
} else if (fieldReaderArray.length > 6) {
|
||||
// use switch
|
||||
Map<Integer, List<Long>> map = new TreeMap();
|
||||
|
||||
for (int i = 0; i < objectReaderAdapter.hashCodes.length; i++) {
|
||||
long hashCode64 = objectReaderAdapter.hashCodes[i];
|
||||
for (int i = 0; i < context.objectReaderAdapter.hashCodes.length; i++) {
|
||||
long hashCode64 = context.objectReaderAdapter.hashCodes[i];
|
||||
int hashCode32 = (int) (hashCode64 ^ (hashCode64 >>> 32));
|
||||
List<Long> hashCode64List = map.computeIfAbsent(hashCode32, k -> new ArrayList<>());
|
||||
hashCode64List.add(hashCode64);
|
||||
@ -1656,8 +1829,8 @@ public class ObjectReaderCreatorASM
|
||||
mw.lcmp();
|
||||
mw.ifne(next);
|
||||
|
||||
int m = Arrays.binarySearch(objectReaderAdapter.hashCodes, hashCode64);
|
||||
int index = objectReaderAdapter.mapping[m];
|
||||
int m = Arrays.binarySearch(context.objectReaderAdapter.hashCodes, hashCode64);
|
||||
int index = context.objectReaderAdapter.mapping[m];
|
||||
|
||||
FieldReader fieldReader = fieldReaderArray[index];
|
||||
|
||||
@ -1824,11 +1997,19 @@ public class ObjectReaderCreatorASM
|
||||
mw.visitLabel(processExtra_);
|
||||
}
|
||||
if (!switchGen) {
|
||||
if (context.objectReaderAdapter instanceof ObjectReaderNoneDefaultConstructor) {
|
||||
mw.aload(THIS);
|
||||
mw.aload(JSON_READER);
|
||||
mw.aload(mwc.var("map"));
|
||||
mw.lload(FEATURES);
|
||||
mw.invokevirtual(TYPE_OBJECT_READER_ADAPTER, "processExtra", METHOD_DESC_PROCESS_EXTRA_2);
|
||||
} else {
|
||||
mw.aload(THIS);
|
||||
mw.aload(JSON_READER);
|
||||
mw.aload(OBJECT);
|
||||
mw.lload(FEATURES);
|
||||
mw.invokevirtual(TYPE_OBJECT_READER_ADAPTER, "processExtra", METHOD_DESC_PROCESS_EXTRA);
|
||||
}
|
||||
mw.goto_(for_inc_i_); // continue
|
||||
}
|
||||
|
||||
@ -1842,7 +2023,60 @@ public class ObjectReaderCreatorASM
|
||||
|
||||
mw.visitLabel(end_);
|
||||
|
||||
if (context.objectReaderAdapter instanceof ObjectReaderNoneDefaultConstructor) {
|
||||
ObjectReaderNoneDefaultConstructor objectReaderNoneDefaultConstructor = (ObjectReaderNoneDefaultConstructor) context.objectReaderAdapter;
|
||||
boolean constructDirect = true;
|
||||
if (classLoader.isExternalClass(context.objectClass)
|
||||
|| (objectReaderNoneDefaultConstructor.constructor != null && !Modifier.isPublic(objectReaderNoneDefaultConstructor.constructor.getModifiers()))
|
||||
|| (context.objectClass != null && !Modifier.isPublic(context.objectClass.getModifiers()))
|
||||
) {
|
||||
constructDirect = false;
|
||||
}
|
||||
|
||||
if (constructDirect) {
|
||||
mw.new_(TYPE_OBJECT);
|
||||
mw.dup();
|
||||
StringBuilder buf = new StringBuilder().append("(");
|
||||
for (FieldReader fieldReader : fieldReaderArray) {
|
||||
mw.loadLocal(fieldReader.fieldClass, mwc.var(fieldReader));
|
||||
buf.append(ASMUtils.desc(fieldReader.fieldClass));
|
||||
}
|
||||
buf.append(")V");
|
||||
mw.invokespecial(TYPE_OBJECT, "<init>", buf.toString());
|
||||
} else {
|
||||
mw.aload(THIS);
|
||||
mw.iconst_n(fieldReaderArray.length);
|
||||
mw.anewArray("java/lang/Object");
|
||||
for (int i = 0; i < fieldReaderArray.length; i++) {
|
||||
FieldReader fieldReader = fieldReaderArray[i];
|
||||
mw.dup();
|
||||
mw.iconst_n(i);
|
||||
mw.loadLocal(fieldReader.fieldClass, mwc.var(fieldReader));
|
||||
if (fieldReader.fieldClass == int.class) {
|
||||
mw.invokestatic("java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;");
|
||||
} else if (fieldReader.fieldClass == long.class) {
|
||||
mw.invokestatic("java/lang/Long", "valueOf", "(J)Ljava/lang/Long;");
|
||||
} else if (fieldReader.fieldClass == float.class) {
|
||||
mw.invokestatic("java/lang/Float", "valueOf", "(F)Ljava/lang/Float;");
|
||||
} else if (fieldReader.fieldClass == double.class) {
|
||||
mw.invokestatic("java/lang/Double", "valueOf", "(D)Ljava/lang/Double;");
|
||||
} else if (fieldReader.fieldClass == boolean.class) {
|
||||
mw.invokestatic("java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;");
|
||||
} else if (fieldReader.fieldClass == short.class) {
|
||||
mw.invokestatic("java/lang/Short", "valueOf", "(S)Ljava/lang/Short;");
|
||||
} else if (fieldReader.fieldClass == byte.class) {
|
||||
mw.invokestatic("java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;");
|
||||
} else if (fieldReader.fieldClass == char.class) {
|
||||
mw.invokestatic("java/lang/Character", "valueOf", "(C)Ljava/lang/Character;");
|
||||
}
|
||||
mw.aastore();
|
||||
}
|
||||
mw.invokevirtual(TYPE_OBJECT_READER_NONE_DEFAULT_CONSTRUCTOR, "createInstance", "([Ljava/lang/Object;)Ljava/lang/Object;");
|
||||
}
|
||||
mw.areturn();
|
||||
} else {
|
||||
mw.aload(OBJECT);
|
||||
}
|
||||
mw.areturn();
|
||||
|
||||
mw.visitMaxs(5, 10);
|
||||
@ -2661,10 +2895,10 @@ public class ObjectReaderCreatorASM
|
||||
String classNameType,
|
||||
String TYPE_OBJECT,
|
||||
int FEATURES,
|
||||
boolean fieldBased,
|
||||
Constructor defaultConstructor,
|
||||
Supplier creator
|
||||
boolean fieldBased
|
||||
) {
|
||||
Constructor defaultConstructor = context.defaultConstructor;
|
||||
Supplier creator = context.objectReaderAdapter.creator;
|
||||
Class objectClass = context.objectClass;
|
||||
final int JSON_READER = 1;
|
||||
|
||||
@ -2687,7 +2921,7 @@ public class ObjectReaderCreatorASM
|
||||
mw.checkcast(TYPE_OBJECT);
|
||||
}
|
||||
} else {
|
||||
newObject(mw, TYPE_OBJECT, defaultConstructor);
|
||||
newObject(mw, TYPE_OBJECT, context.defaultConstructor);
|
||||
}
|
||||
|
||||
if (context.hasStringField) {
|
||||
@ -2755,7 +2989,9 @@ public class ObjectReaderCreatorASM
|
||||
String TYPE_FIELD_CLASS = ASMUtils.type(fieldClass);
|
||||
String DESC_FIELD_CLASS = ASMUtils.desc(fieldClass);
|
||||
|
||||
if (!(context.objectReaderAdapter instanceof ObjectReaderNoneDefaultConstructor)) {
|
||||
mw.aload(OBJECT);
|
||||
}
|
||||
int fieldModifier = 0;
|
||||
if ((fieldBased || method == null) && field != null) {
|
||||
fieldModifier = field.getModifiers();
|
||||
@ -2919,7 +3155,9 @@ public class ObjectReaderCreatorASM
|
||||
|
||||
mw.aload(JSON_READER);
|
||||
mw.invokevirtual(TYPE_JSON_READER, "skipValue", "()V");
|
||||
if (!(context.objectReaderAdapter instanceof ObjectReaderNoneDefaultConstructor)) {
|
||||
mw.pop();
|
||||
}
|
||||
mw.goto_(endSet_);
|
||||
|
||||
mw.visitLabel(endIgnoreCheck_);
|
||||
@ -2962,7 +3200,8 @@ public class ObjectReaderCreatorASM
|
||||
fieldFeatures,
|
||||
itemType,
|
||||
TYPE_FIELD_CLASS,
|
||||
context
|
||||
context,
|
||||
fieldBased
|
||||
);
|
||||
} else {
|
||||
final String FIELD_OBJECT_READER = fieldObjectReader(i);
|
||||
@ -3131,6 +3370,11 @@ public class ObjectReaderCreatorASM
|
||||
mw.visitVarInsn(LOAD, FIELD_VALUE);
|
||||
mw.invokevirtual("sun/misc/Unsafe", methodName, methodDes);
|
||||
}
|
||||
} else if (context.objectReaderAdapter instanceof ObjectReaderNoneDefaultConstructor) {
|
||||
if (!fieldClass.isPrimitive()) {
|
||||
mw.checkcast(ASMUtils.type(fieldClass));
|
||||
}
|
||||
mw.storeLocal(fieldClass, mwc.var(fieldReader));
|
||||
} else {
|
||||
boolean invokeFieldReaderAccept = context.externalClass || method == null || !context.publicClass;
|
||||
|
||||
@ -3526,7 +3770,8 @@ public class ObjectReaderCreatorASM
|
||||
long fieldFeatures,
|
||||
Type itemType,
|
||||
String TYPE_FIELD_CLASS,
|
||||
ObjectWriteContext context
|
||||
ObjectWriteContext context,
|
||||
boolean fieldBased
|
||||
) {
|
||||
if (itemType == null) {
|
||||
itemType = Object.class;
|
||||
@ -3536,7 +3781,12 @@ public class ObjectReaderCreatorASM
|
||||
String ITEM_OBJECT_READER = fieldItemObjectReader(i);
|
||||
MethodWriter mw = mwc.mw;
|
||||
|
||||
Integer LIST = mwc.var(fieldClass);
|
||||
int LIST;
|
||||
if (context.objectReaderAdapter instanceof ObjectReaderNoneDefaultConstructor) {
|
||||
LIST = mwc.var(fieldReader);
|
||||
} else {
|
||||
LIST = mwc.var(fieldClass);
|
||||
}
|
||||
Integer AUTO_TYPE_OBJECT_READER = mwc.var(ObjectReader.class);
|
||||
|
||||
String LIST_TYPE = fieldClass.isInterface() ? "java/util/ArrayList" : TYPE_FIELD_CLASS;
|
||||
@ -3833,13 +4083,18 @@ public class ObjectReaderCreatorASM
|
||||
final boolean hasStringField;
|
||||
final int fieldNameLengthMin;
|
||||
final int fieldNameLengthMax;
|
||||
final String classNameType;
|
||||
final String classNameFull;
|
||||
final Constructor defaultConstructor;
|
||||
ObjectReaderAdapter objectReaderAdapter;
|
||||
|
||||
public ObjectWriteContext(
|
||||
BeanInfo beanInfo,
|
||||
Class objectClass,
|
||||
ClassWriter cw,
|
||||
boolean externalClass,
|
||||
FieldReader[] fieldReaders
|
||||
FieldReader[] fieldReaders,
|
||||
Constructor defaultConstructor
|
||||
) {
|
||||
this.beanInfo = beanInfo;
|
||||
this.objectClass = objectClass;
|
||||
@ -3847,6 +4102,7 @@ public class ObjectReaderCreatorASM
|
||||
this.publicClass = objectClass == null || Modifier.isPublic(objectClass.getModifiers());
|
||||
this.externalClass = externalClass;
|
||||
this.fieldReaders = fieldReaders;
|
||||
this.defaultConstructor = defaultConstructor;
|
||||
|
||||
int fieldNameLengthMin = 0, fieldNameLengthMax = 0;
|
||||
boolean hasStringField = false;
|
||||
@ -3876,6 +4132,31 @@ public class ObjectReaderCreatorASM
|
||||
this.hasStringField = hasStringField;
|
||||
this.fieldNameLengthMin = fieldNameLengthMin;
|
||||
this.fieldNameLengthMax = fieldNameLengthMax;
|
||||
|
||||
String className = "ORG_" + seed.incrementAndGet() + "_" + fieldReaders.length + (objectClass == null ? "" : "_" + objectClass.getSimpleName());
|
||||
|
||||
Package pkg = ObjectReaderCreatorASM.class.getPackage();
|
||||
if (pkg != null) {
|
||||
String packageName = pkg.getName();
|
||||
int packageNameLength = packageName.length();
|
||||
int charsLength = packageNameLength + 1 + className.length();
|
||||
char[] chars = new char[charsLength];
|
||||
packageName.getChars(0, packageName.length(), chars, 0);
|
||||
chars[packageNameLength] = '.';
|
||||
className.getChars(0, className.length(), chars, packageNameLength + 1);
|
||||
classNameFull = new String(chars);
|
||||
|
||||
chars[packageNameLength] = '/';
|
||||
for (int i = 0; i < packageNameLength; ++i) {
|
||||
if (chars[i] == '.') {
|
||||
chars[i] = '/';
|
||||
}
|
||||
}
|
||||
classNameType = new String(chars);
|
||||
} else {
|
||||
classNameType = className;
|
||||
classNameFull = className;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean disableSupportArrayMapping() {
|
||||
@ -4296,6 +4577,24 @@ public class ObjectReaderCreatorASM
|
||||
return var;
|
||||
}
|
||||
|
||||
int var(FieldReader fieldReader) {
|
||||
return var("_param_" + fieldReader.fieldName, fieldReader.fieldClass);
|
||||
}
|
||||
|
||||
int var(String name, Class type) {
|
||||
Integer var = variants.get(name);
|
||||
if (var == null) {
|
||||
var = maxVariant;
|
||||
variants.put(name, var);
|
||||
if (type == long.class || type == double.class) {
|
||||
maxVariant += 2;
|
||||
} else {
|
||||
maxVariant += 1;
|
||||
}
|
||||
}
|
||||
return var;
|
||||
}
|
||||
|
||||
int var2(Object key) {
|
||||
Integer var = variants.get(key);
|
||||
if (var == null) {
|
||||
|
@ -5,6 +5,7 @@ import com.alibaba.fastjson2.util.Fnv;
|
||||
import com.alibaba.fastjson2.util.TypeUtils;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
@ -16,8 +17,9 @@ public class ObjectReaderNoneDefaultConstructor<T>
|
||||
extends ObjectReaderAdapter<T> {
|
||||
final String[] paramNames;
|
||||
final FieldReader[] setterFieldReaders;
|
||||
private final Function<Map<Long, Object>, T> creator;
|
||||
final Function<Map<Long, Object>, T> creatorFunction;
|
||||
final Map<Long, FieldReader> paramFieldReaderMap;
|
||||
final Constructor noneDefaultConstructor;
|
||||
|
||||
public ObjectReaderNoneDefaultConstructor(
|
||||
Class objectClass,
|
||||
@ -47,12 +49,17 @@ public class ObjectReaderNoneDefaultConstructor<T>
|
||||
);
|
||||
|
||||
this.paramNames = paramNames;
|
||||
this.creator = creator;
|
||||
this.creatorFunction = creator;
|
||||
this.setterFieldReaders = setterFieldReaders;
|
||||
this.paramFieldReaderMap = new HashMap<>();
|
||||
for (FieldReader paramFieldReader : paramFieldReaders) {
|
||||
paramFieldReaderMap.put(paramFieldReader.fieldNameHash, paramFieldReader);
|
||||
}
|
||||
if (creatorFunction instanceof ConstructorFunction) {
|
||||
noneDefaultConstructor = ((ConstructorFunction) creator).constructor;
|
||||
} else {
|
||||
noneDefaultConstructor = null;
|
||||
}
|
||||
}
|
||||
|
||||
static FieldReader[] concat(FieldReader[] a, FieldReader[] b) {
|
||||
@ -65,9 +72,14 @@ public class ObjectReaderNoneDefaultConstructor<T>
|
||||
return a;
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public Collection<FieldReader> getParameterFieldReaders() {
|
||||
return paramFieldReaderMap.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T createInstanceNoneDefaultConstructor(Map<Long, Object> values) {
|
||||
return creator.apply(values);
|
||||
return creatorFunction.apply(values);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -342,7 +354,7 @@ public class ObjectReaderNoneDefaultConstructor<T>
|
||||
}
|
||||
|
||||
Map<Long, Object> argsMap = valueMap == null ? Collections.emptyMap() : valueMap;
|
||||
T object = creator.apply(argsMap);
|
||||
T object = creatorFunction.apply(argsMap);
|
||||
|
||||
if (setterFieldReaders != null && valueMap != null) {
|
||||
for (int i = 0; i < setterFieldReaders.length; i++) {
|
||||
@ -521,4 +533,13 @@ public class ObjectReaderNoneDefaultConstructor<T>
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
public T createInstance(Object[] args) {
|
||||
try {
|
||||
return (T) noneDefaultConstructor.newInstance(args);
|
||||
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException |
|
||||
InvocationTargetException e) {
|
||||
throw new JSONException("invoke constructor error, " + constructor, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1356,18 +1356,50 @@ public class IOUtils {
|
||||
return UNSAFE.getInt(buf, ARRAY_BYTE_BASE_OFFSET + pos) == ALSE;
|
||||
}
|
||||
|
||||
public static boolean notALSE(byte[] buf, int pos) {
|
||||
return UNSAFE.getInt(buf, ARRAY_BYTE_BASE_OFFSET + pos) != ALSE;
|
||||
}
|
||||
|
||||
public static boolean isALSE(char[] buf, int pos) {
|
||||
return getLongUnaligned(buf, pos) == ALSE_64;
|
||||
}
|
||||
|
||||
public static boolean notALSE(char[] buf, int pos) {
|
||||
return getLongUnaligned(buf, pos) != ALSE_64;
|
||||
}
|
||||
|
||||
public static boolean isNULL(byte[] buf, int pos) {
|
||||
return UNSAFE.getInt(buf, ARRAY_BYTE_BASE_OFFSET + pos) == NULL_32;
|
||||
}
|
||||
|
||||
public static boolean notNULL(byte[] buf, int pos) {
|
||||
return UNSAFE.getInt(buf, ARRAY_BYTE_BASE_OFFSET + pos) != NULL_32;
|
||||
}
|
||||
|
||||
public static boolean isTRUE(byte[] buf, int pos) {
|
||||
return UNSAFE.getInt(buf, ARRAY_BYTE_BASE_OFFSET + pos) == TRUE;
|
||||
}
|
||||
|
||||
public static boolean notTRUE(byte[] buf, int pos) {
|
||||
return UNSAFE.getInt(buf, ARRAY_BYTE_BASE_OFFSET + pos) != TRUE;
|
||||
}
|
||||
|
||||
public static boolean isTRUE(char[] buf, int pos) {
|
||||
return UNSAFE.getLong(buf, ARRAY_BYTE_BASE_OFFSET + ((long) pos << 1)) == TRUE_64;
|
||||
}
|
||||
|
||||
public static boolean notTRUE(char[] buf, int pos) {
|
||||
return UNSAFE.getLong(buf, ARRAY_BYTE_BASE_OFFSET + ((long) pos << 1)) != TRUE_64;
|
||||
}
|
||||
|
||||
public static boolean isNULL(char[] buf, int pos) {
|
||||
return getLongUnaligned(buf, pos) == NULL_64;
|
||||
}
|
||||
|
||||
public static boolean notNULL(char[] buf, int pos) {
|
||||
return getLongUnaligned(buf, pos) != NULL_64;
|
||||
}
|
||||
|
||||
public static void putNULL(byte[] buf, int pos) {
|
||||
UNSAFE.putInt(buf, ARRAY_CHAR_BASE_OFFSET + pos, NULL_32);
|
||||
}
|
||||
|
9
pom.xml
9
pom.xml
@ -1019,6 +1019,15 @@
|
||||
<module>test-jdk17</module>
|
||||
</modules>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>enable-for-jdk21+</id>
|
||||
<activation>
|
||||
<jdk>[21,)</jdk>
|
||||
</activation>
|
||||
<modules>
|
||||
<module>benchmark_21</module>
|
||||
</modules>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>enable-codegen</id>
|
||||
<activation>
|
||||
|
@ -28,5 +28,9 @@
|
||||
<artifactId>fastjson2</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
@ -2,6 +2,10 @@ package com.alibaba.fastjson2;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public class RecordTest {
|
||||
@ -14,7 +18,49 @@ public class RecordTest {
|
||||
|
||||
JSONObject object = JSON.parseObject(str);
|
||||
assertEquals(item.value, object.get("value"));
|
||||
|
||||
Item item2 = JSON.parseObject(str, Item.class);
|
||||
assertEquals(item.value, item2.value);
|
||||
}
|
||||
|
||||
record Item(int value) { }
|
||||
public record Item(int value) {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test1() {
|
||||
Item1 item = new Item1(Arrays.asList("abc"));
|
||||
String str = JSON.toJSONString(item);
|
||||
Item1 item2 = JSON.parseObject(str, Item1.class);
|
||||
assertEquals(item.value, item2.value);
|
||||
}
|
||||
|
||||
public record Item1(List<String> value) {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test2() {
|
||||
Item2 item = new Item2(Arrays.asList(new Item(123)));
|
||||
String str = JSON.toJSONString(item);
|
||||
Item2 item2 = JSON.parseObject(str, Item2.class);
|
||||
assertEquals(item.value.size(), item2.value.size());
|
||||
assertEquals(item.value.get(0).value, item2.value.get(0).value);
|
||||
}
|
||||
|
||||
public record Item2(List<Item> value) {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test3() {
|
||||
Item3 item = new Item3(new User(true, "abc"));
|
||||
String str = JSON.toJSONString(item);
|
||||
Item3 item2 = JSON.parseObject(str, Item3.class);
|
||||
assertEquals(item.user.default_profile, item2.user.default_profile);
|
||||
assertEquals(item.user.screen_name, item2.user.screen_name);
|
||||
}
|
||||
|
||||
public record User(boolean default_profile, String screen_name) {
|
||||
}
|
||||
|
||||
public record Item3(User user) implements Serializable {
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user