package com.jsoniter;
import com.jsoniter.spi.*;
import java.util.*;
public class CodegenImplObjectHash {
// the implementation is from dsljson, it is the fastest although has the risk not matching field strictly
public static String genObjectUsingHash(Class clazz, ClassDescriptor desc) {
StringBuilder lines = new StringBuilder();
// === if null, return null
append(lines, "java.lang.Object existingObj = com.jsoniter.CodegenAccess.resetExistingObject(iter);");
append(lines, "byte nextToken = com.jsoniter.CodegenAccess.readByte(iter);");
append(lines, "if (nextToken != '{') {");
append(lines, "if (nextToken == 'n') {");
append(lines, "com.jsoniter.CodegenAccess.skipFixedBytes(iter, 3);");
append(lines, "return null;");
append(lines, "} else {");
append(lines, "nextToken = com.jsoniter.CodegenAccess.nextToken(iter);");
append(lines, "if (nextToken == 'n') {");
append(lines, "com.jsoniter.CodegenAccess.skipFixedBytes(iter, 3);");
append(lines, "return null;");
append(lines, "}");
append(lines, "} // end of if null");
append(lines, "} // end of if {");
// === if empty, return empty
// ctor requires binding
for (Binding parameter : desc.ctor.parameters) {
appendVarDef(lines, parameter);
}
append(lines, "nextToken = com.jsoniter.CodegenAccess.readByte(iter);");
append(lines, "if (nextToken != '\"') {");
append(lines, "if (nextToken == '}') {");
append(lines, "return {{newInst}};");
append(lines, "} else {");
append(lines, "nextToken = com.jsoniter.CodegenAccess.nextToken(iter);");
append(lines, "if (nextToken == '}') {");
append(lines, "return {{newInst}};");
append(lines, "} else {");
append(lines, "com.jsoniter.CodegenAccess.unreadByte(iter);");
append(lines, "}");
append(lines, "} // end of if end");
append(lines, "} else { com.jsoniter.CodegenAccess.unreadByte(iter); }// end of if not quote");
for (Binding field : desc.fields) {
appendVarDef(lines, field);
}
for (Binding setter : desc.setters) {
appendVarDef(lines, setter);
}
for (WrapperDescriptor setter : desc.wrappers) {
for (Binding param : setter.parameters) {
appendVarDef(lines, param);
}
}
// === bind fields
HashSet<Integer> knownHashes = new HashSet<Integer>();
HashMap<String, Binding> bindings = new HashMap<String, Binding>();
for (Binding binding : desc.allDecoderBindings()) {
for (String fromName : binding.fromNames) {
bindings.put(fromName, binding);
}
}
ArrayList<String> fromNames = new ArrayList<String>(bindings.keySet());
Collections.sort(fromNames, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
int x = calcHash(o1);
int y = calcHash(o2);
return (x < y) ? -1 : ((x == y) ? 0 : 1);
}
});
// === bind more fields
append(lines, "do {");
append(lines, "switch (com.jsoniter.CodegenAccess.readObjectFieldAsHash(iter)) {");
for (String fromName : fromNames) {
int intHash = calcHash(fromName);
if (intHash == 0) {
// hash collision, 0 can not be used as sentinel
return CodegenImplObjectStrict.genObjectUsingStrict(clazz, desc);
}
if (knownHashes.contains(intHash)) {
// hash collision with other field can not be used as sentinel
return CodegenImplObjectStrict.genObjectUsingStrict(clazz, desc);
}
knownHashes.add(intHash);
append(lines, "case " + intHash + ": ");
appendBindingSet(lines, desc, bindings.get(fromName));
append(lines, "continue;");
}
append(lines, "}");
append(lines, "iter.skip();");
append(lines, "} while (com.jsoniter.CodegenAccess.nextTokenIsComma(iter));");
append(lines, CodegenImplNative.getTypeName(clazz) + " obj = {{newInst}};");
for (Binding field : desc.fields) {
append(lines, String.format("obj.%s = _%s_;", field.field.getName(), field.name));
}
for (Binding setter : desc.setters) {
append(lines, String.format("obj.%s(_%s_);", setter.method.getName(), setter.name));
}
appendWrappers(desc.wrappers, lines);
append(lines, "return obj;");
return lines.toString()
.replace("{{clazz}}", clazz.getCanonicalName())
.replace("{{newInst}}", genNewInstCode(clazz, desc.ctor));
}
public static int calcHash(String fromName) {
long hash = 0x811c9dc5;
for (byte b : fromName.getBytes()) {
hash ^= b;
hash *= 0x1000193;
}
return (int) hash;
}
private static void appendBindingSet(StringBuilder lines, ClassDescriptor desc, Binding binding) {
append(lines, String.format("_%s_ = %s;", binding.name, CodegenImplNative.genField(binding)));
}
static void appendWrappers(List<WrapperDescriptor> wrappers, StringBuilder lines) {
for (WrapperDescriptor wrapper : wrappers) {
lines.append("obj.");
lines.append(wrapper.method.getName());
appendInvocation(lines, wrapper.parameters);
lines.append(";\n");
}
}
static void appendVarDef(StringBuilder lines, Binding parameter) {
String typeName = CodegenImplNative.getTypeName(parameter.valueType);
append(lines, String.format("%s _%s_ = %s;", typeName, parameter.name, CodegenImplObjectStrict.DEFAULT_VALUES.get(typeName)));
}
static String genNewInstCode(Class clazz, ConstructorDescriptor ctor) {
StringBuilder code = new StringBuilder();
if (ctor.parameters.isEmpty()) {
// nothing to bind, safe to reuse existing object
code.append("(existingObj == null ? ");
}
if (ctor.objectFactory != null) {
code.append(String.format("(%s)com.jsoniter.spi.JsoniterSpi.create(%s.class)",
clazz.getCanonicalName(), clazz.getCanonicalName()));
} else {
if (ctor.staticMethodName == null) {
code.append(String.format("new %s", clazz.getCanonicalName()));
} else {
code.append(String.format("%s.%s", clazz.getCanonicalName(), ctor.staticMethodName));
}
}
List<Binding> params = ctor.parameters;
if (ctor.objectFactory == null) {
appendInvocation(code, params);
}
if (ctor.parameters.isEmpty()) {
// nothing to bind, safe to reuse existing obj
code.append(String.format(" : (%s)existingObj)", clazz.getCanonicalName()));
}
return code.toString();
}
private static void appendInvocation(StringBuilder code, List<Binding> params) {
code.append("(");
boolean isFirst = true;
for (Binding ctorParam : params) {
if (isFirst) {
isFirst = false;
} else {
code.append(",");
}
code.append(String.format("_%s_", ctorParam.name));
}
code.append(")");
}
static void append(StringBuilder lines, String str) {
lines.append(str);
lines.append("\n");
}
}