One Turn Solution
package me.etki.perfground; import java.util.LinkedList; import java.util.List; /** * @author Etki {@literal <etki@etki.name>} */ public class StringParser { enum State { EXTERIOR { @Override public State next(char c) { return c == ' ' ? EXTERIOR : KEY; } @Override public boolean expects(char c) { return c != '"' && c != '='; } }, KEY { @Override public State next(char c) { return c == '=' ? EQUALITY_SIGN : KEY; } @Override public boolean expects(char c) { return c != '"'; } }, EQUALITY_SIGN { @Override public State next(char c) { return OPENING_QUOTE; } @Override public boolean expects(char c) { return c == '"'; } }, OPENING_QUOTE { @Override public State next(char c) { return c == '"' ? CLOSING_QUOTE : VALUE; } }, VALUE { @Override public State next(char c) { return c == '"' ? CLOSING_QUOTE : VALUE; } }, CLOSING_QUOTE { @Override public State next(char c) { return c == ' ' ? EXTERIOR : KEY; } @Override public boolean expects(char c) { return c != '=' && c != '"'; } }; public State next(char c) { throw new UnsupportedOperationException("Somebody has forgotten to implement something"); } public boolean expects(char c) { return true; } } public static class Tuple { private final CharSequence key; private final CharSequence value; public Tuple(CharSequence key, CharSequence value) { this.key = key; this.value = value; } public CharSequence getKey() { return key; } public CharSequence getValue() { return value; } } public static class Region implements CharSequence { private final CharSequence parent; private final int start; private final int end; public Region(CharSequence parent, int start, int end) { this.parent = parent; this.start = start; this.end = end; } @Override public int length() { return end - start; } @Override public char charAt(int index) { return parent.charAt(start + index); } @Override public CharSequence subSequence(int start, int end) { if (end > length()) { throw new IndexOutOfBoundsException(); } return parent.subSequence(this.start + start, this.start + end); } @Override public String toString() { return parent.subSequence(start, end).toString(); } } public static List<Tuple> parse(CharSequence input) { State state = State.EXTERIOR; List<Tuple> result = new LinkedList<>(); int mark = -1; CharSequence key = null; for (int i = 0; i < input.length(); i++) { char c = input.charAt(i); if (!state.expects(c)) { String s = "Provided string contains char " + c + " right after " + state + " state, and that's " + "prohibited"; throw new IllegalArgumentException(s); } State next = state.next(c); if (next.equals(state)) { continue; } if (next.equals(State.KEY) || next.equals(State.VALUE)) { mark = i; } if (state.equals(State.KEY)) { key = new Region(input, mark, i); } else if (state.equals(State.VALUE)) { result.add(new Tuple(key, new Region(input, mark, i))); } state = next; } return result; } }
Which does not work safely
// dummy - пустой метод, который берет аргумент и передает его в blackhole # Run complete. Total time: 00:01:09 Benchmark (input) Mode Samples Score Score error Units mepStringParserBenchmark.dummy book="book-1" operation="SELL" price="100.50" volume="81" orderId="1" thrpt 1 316041178.489 NaN ops/s mepStringParserBenchmark.parse book="book-1" operation="SELL" price="100.50" volume="81" orderId="1" thrpt 1 1084975.810 NaN ops/s mepStringParserBenchmark.warmThenParse book="book-1" operation="SELL" price="100.50" volume="81" orderId="1" thrpt 1 1259713.331 NaN ops/s
If I find the time to find out the reason (why my performance is at the same level as in other examples without creating new lines and a single pass), then I will update the answer.
update
It seems that the distribution of methods according to different enum is not much in the liking of the optimizer (and it can be understood - it cannot predict which method will be called). Just a little upsurge:
enum State { ... public static State next(char c, State current) { switch (current) { case EXTERIOR: return c == ' ' ? EXTERIOR : KEY; case KEY: return c == '=' ? EQUALITY_SIGN : KEY; case EQUALITY_SIGN: return OPENING_QUOTE; case OPENING_QUOTE: return c == '"' ? CLOSING_QUOTE : VALUE; case VALUE: return c == '"' ? CLOSING_QUOTE : VALUE; case CLOSING_QUOTE: return c == ' ' ? EXTERIOR : KEY; default: throw new IllegalStateException(); } } public static boolean expects(char c, State current) { switch (current) { case EXTERIOR: return c != '"' && c != '='; case KEY: return c != '"'; case EQUALITY_SIGN: return c == '"'; case OPENING_QUOTE: return true; case VALUE: return true; case CLOSING_QUOTE: return c != '=' && c != '"'; default: throw new IllegalStateException(); } } }
almost doubles the speed of work:
Benchmark (input) Mode Samples Score Score error Units mepStringParserBenchmark.dummy book="book-1" operation="SELL" price="100.50" volume="81" orderId="1" thrpt 1 319058428.945 NaN ops/s mepStringParserBenchmark.parse book="book-1" operation="SELL" price="100.50" volume="81" orderId="1" thrpt 1 1176753.244 NaN ops/s mepStringParserBenchmark.warmThenAltParse book="book-1" operation="SELL" price="100.50" volume="81" orderId="1" thrpt 1 2057734.054 NaN ops/s mepStringParserBenchmark.warmThenParse book="book-1" operation="SELL" price="100.50" volume="81" orderId="1" thrpt 1 1112356.422 NaN ops/s
book,operation, etc., and think about what data structure they should be folded. - Nofate ♦