private void writeStateMachine(final String className, final ClassFile file, final CodeAttribute c, final State initial, final List<State> allStates, int noStates, final CustomStateMachine stateMachine, final ClassMethod sctor) {
//initial hasRemaining check
c.aload(BYTE_BUFFER_VAR);
c.invokevirtual(ByteBuffer.class.getName(), "hasRemaining", "()Z");
final BranchEnd nonZero = c.ifne();
//we have run out of bytes, return 0
c.iconst(0);
c.returnInstruction();
c.branchEnd(nonZero);
final List<State> states = new ArrayList<State>();
states.add(initial);
states.addAll(allStates);
Collections.sort(states);
//store the current state in a local variable
c.aload(PARSE_STATE_VAR);
c.dup();
c.getfield(parseStateClass, "stringBuilder", DescriptorUtils.makeDescriptor(StringBuilder.class));
c.astore(STATE_STRING_BUILDER_VAR);
c.dup();
c.getfield(parseStateClass, "parseState", "I");
c.dup();
c.istore(CURRENT_STATE_VAR);
//if this is state 0 there is a lot of stuff can ignore
BranchEnd optimizationEnd = c.ifeq();
c.dup();
c.getfield(parseStateClass, "pos", "I");
c.istore(STATE_POS_VAR);
c.dup();
c.getfield(parseStateClass, "current", HTTP_STRING_DESCRIPTOR);
c.astore(STATE_CURRENT_VAR);
c.getfield(parseStateClass, "currentBytes", "[B");
c.astore(STATE_CURRENT_BYTES_VAR);
//load the current state
c.iload(CURRENT_STATE_VAR);
//switch on the current state
TableSwitchBuilder builder = new TableSwitchBuilder(-2, noStates);
final IdentityHashMap<State, AtomicReference<BranchEnd>> ends = new IdentityHashMap<State, AtomicReference<BranchEnd>>();
final AtomicReference<BranchEnd> prefixMatch = builder.add();
final AtomicReference<BranchEnd> noState = builder.add();
ends.put(initial, builder.add());
for (final State s : states) {
if (s.stateno > 0) {
ends.put(s, builder.add());
}
}
c.tableswitch(builder);
stateNotFound(c, builder);
//return code
//code that synchronizes the state object and returns
setupLocalVariables(c);
final CodeLocation returnIncompleteCode = c.mark();
c.aload(PARSE_STATE_VAR);
c.dup();
c.dup();
c.dup();
c.dup();
c.iload(STATE_POS_VAR);
c.putfield(parseStateClass, "pos", "I");
c.aload(STATE_CURRENT_VAR);
c.putfield(parseStateClass, "current", HTTP_STRING_DESCRIPTOR);
c.aload(STATE_CURRENT_BYTES_VAR);
c.putfield(parseStateClass, "currentBytes", "[B");
c.iload(CURRENT_STATE_VAR);
c.putfield(parseStateClass, "parseState", "I");
c.returnInstruction();
setupLocalVariables(c);
final CodeLocation returnCompleteCode = c.mark();
c.aload(PARSE_STATE_VAR);
c.dup();
c.dup();
c.dup();
c.dup();
c.iconst(0);
c.putfield(parseStateClass, "pos", "I");
c.aconstNull();
c.putfield(parseStateClass, "current", HTTP_STRING_DESCRIPTOR);
c.aconstNull();
c.putfield(parseStateClass, "currentBytes", "[B");
c.aload(STATE_STRING_BUILDER_VAR);
c.iconst(0);
c.invokevirtual(StringBuilder.class.getName(), "setLength", "(I)V");
c.iconst(0);
c.putfield(parseStateClass, "parseState", "I");
c.returnInstruction();
//prefix
c.branchEnd(prefixMatch.get());
final CodeLocation prefixLoop = c.mark(); //loop for when we are prefix matching
handleReturnIfNoMoreBytes(c, returnIncompleteCode);
//load 3 copies of the current byte into the stack
c.aload(BYTE_BUFFER_VAR);
c.invokevirtual(ByteBuffer.class.getName(), "get", "()B");
c.dup();
c.dup();
final Set<BranchEnd> prefixHandleSpace = new HashSet<BranchEnd>();
if (stateMachine.isHeader()) {
c.iconst(':');
prefixHandleSpace.add(c.ifIcmpeq());
c.dup();
}
c.iconst(' ');
prefixHandleSpace.add(c.ifIcmpeq());
c.dup();
c.iconst('\t');
prefixHandleSpace.add(c.ifIcmpeq());
c.dup();
c.iconst('\r');
prefixHandleSpace.add(c.ifIcmpeq());
c.dup();
c.iconst('\n');
prefixHandleSpace.add(c.ifIcmpeq());
//check if we have overrun
c.aload(STATE_CURRENT_BYTES_VAR);
c.arraylength();
c.iload(STATE_POS_VAR);
BranchEnd overrun = c.ifIcmpeq();
//so we have not overrun
//now check if the character matches
c.dup();
c.aload(STATE_CURRENT_BYTES_VAR);
c.iload(STATE_POS_VAR);
c.baload();
c.isub();
BranchEnd noMatch = c.ifne();
//so they match
c.pop2(); //pop our extra bytes off the stack, we do not need it
c.iinc(STATE_POS_VAR, 1);
handleReturnIfNoMoreBytes(c, returnIncompleteCode);
c.gotoInstruction(prefixLoop);
c.branchEnd(overrun); //overrun and not match use the same code path
c.branchEnd(noMatch); //the current character did not match
c.iconst(NO_STATE);
c.istore(CURRENT_STATE_VAR);
//create the string builder
c.aload(STATE_STRING_BUILDER_VAR);
c.aload(STATE_CURRENT_VAR);
c.invokevirtual(HTTP_STRING_CLASS, "toString", "()Ljava/lang/String;");
c.iconst(0);
c.iload(STATE_POS_VAR);
c.invokevirtual(String.class.getName(), "substring", "(II)Ljava/lang/String;");
c.invokevirtual(StringBuilder.class.getName(), "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;");
c.swap();
c.invokevirtual(StringBuilder.class.getName(), "append", "(C)Ljava/lang/StringBuilder;");
c.pop2();
BranchEnd prefixToNoState = c.gotoInstruction();
//handle the space case
for (BranchEnd b : prefixHandleSpace) {
c.branchEnd(b);
}
//new state will be 0
c.iconst(0);
c.istore(CURRENT_STATE_VAR);
c.aload(STATE_CURRENT_BYTES_VAR);
c.arraylength();
c.iload(STATE_POS_VAR);
BranchEnd correctLength = c.ifIcmpeq();
c.newInstruction(HTTP_STRING_CLASS);
c.dup();
c.aload(STATE_CURRENT_BYTES_VAR);
c.iconst(0);