Skip to content

Commit 99fd131

Browse files
committed
Optimized decoder pattern matching and eliminated unnecessary object allocations
1 parent a4c1371 commit 99fd131

File tree

1 file changed

+59
-65
lines changed

1 file changed

+59
-65
lines changed

quickfixj-core/src/main/java/quickfix/mina/message/FIXMessageDecoder.java

Lines changed: 59 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -70,24 +70,6 @@ public class FIXMessageDecoder implements MessageDecoder {
7070
private int position;
7171
private final String charsetEncoding;
7272

73-
static class BufPos {
74-
final int _offset;
75-
final int _length;
76-
77-
/**
78-
* @param offset
79-
* @param length
80-
*/
81-
public BufPos(int offset, int length) {
82-
_offset = offset;
83-
_length = length;
84-
}
85-
86-
public String toString() {
87-
return _offset + "," + _length;
88-
}
89-
}
90-
9173
private void resetState() {
9274
state = SEEKING_HEADER;
9375
bodyLength = 0;
@@ -106,14 +88,13 @@ public FIXMessageDecoder(String charset, String delimiter) throws UnsupportedEnc
10688
charsetEncoding = CharsetSupport.validate(charset);
10789
HEADER_PATTERN = getBytes("8=FIXt.?.?" + delimiter + "9=");
10890
CHECKSUM_PATTERN = getBytes("10=???" + delimiter);
109-
LOGON_PATTERN = getBytes("\00135=A" + delimiter);
91+
LOGON_PATTERN = getBytes(delimiter + "35=A" + delimiter);
11092
resetState();
11193
}
11294

11395
public MessageDecoderResult decodable(IoSession session, IoBuffer in) {
114-
BufPos bufPos = indexOf(in, in.position(), HEADER_PATTERN);
115-
int headerOffset = bufPos._offset;
116-
return headerOffset != -1 ? MessageDecoderResult.OK :
96+
boolean hasHeader = indexOf(in, in.position(), HEADER_PATTERN) != -1L;
97+
return hasHeader ? MessageDecoderResult.OK :
11798
(in.remaining() > MAX_UNDECODED_DATA_LENGTH ? MessageDecoderResult.NOT_OK : MessageDecoderResult.NEED_DATA);
11899
}
119100

@@ -148,18 +129,19 @@ private boolean parseMessage(IoBuffer in, ProtocolDecoderOutput out)
148129
while (in.hasRemaining() && !messageFound) {
149130
if (state == SEEKING_HEADER) {
150131

151-
BufPos bufPos = indexOf(in, position, HEADER_PATTERN);
152-
int headerOffset = bufPos._offset;
153-
if (headerOffset == -1) {
132+
long headerPos = indexOf(in, position, HEADER_PATTERN);
133+
if (headerPos == -1L) {
154134
break;
155135
}
136+
int headerOffset = (int)headerPos;
137+
int headerLength = (int)(headerPos >>> 32);
156138
in.position(headerOffset);
157139

158140
if (log.isDebugEnabled()) {
159141
log.debug("detected header: " + getBufferDebugInfo(in));
160142
}
161143

162-
position = headerOffset + bufPos._length;
144+
position = headerOffset + headerLength;
163145
state = PARSING_LENGTH;
164146
}
165147

@@ -201,7 +183,7 @@ private boolean parseMessage(IoBuffer in, ProtocolDecoderOutput out)
201183
}
202184

203185
if (state == PARSING_CHECKSUM) {
204-
if (startsWith(in, position, CHECKSUM_PATTERN) > 0) {
186+
if (matches(in, position, CHECKSUM_PATTERN) > 0) {
205187
// we are trying to parse the checksum but should
206188
// check if the CHECKSUM_PATTERN is preceded by SOH
207189
// or if the pattern just occurs inside of another field
@@ -268,12 +250,12 @@ private boolean hasRemaining(IoBuffer in) {
268250
return position < in.limit();
269251
}
270252

271-
private static int minMaskLength(byte[] data) {
253+
private static int minPatternLength(byte[] pattern) {
272254
int len = 0;
273-
for (byte aChar : data) {
274-
if (Character.isLetter(aChar) && Character.isLowerCase(aChar))
275-
continue;
276-
++len;
255+
for (byte b : pattern) {
256+
if (b < 'a' || b > 'z') { // if not optional character (lowercase)
257+
len++;
258+
}
277259
}
278260
return len;
279261
}
@@ -306,53 +288,65 @@ private void handleError(IoBuffer buffer, int recoveryPosition, String text,
306288
}
307289

308290
private boolean isLogon(IoBuffer buffer) {
309-
BufPos bufPos = indexOf(buffer, buffer.position(), LOGON_PATTERN);
310-
return bufPos._offset != -1;
291+
return indexOf(buffer, buffer.position(), LOGON_PATTERN) != -1L;
311292
}
312293

313-
private static BufPos indexOf(IoBuffer buffer, int position, byte[] data) {
314-
for (int offset = position, limit = buffer.limit() - minMaskLength(data) + 1; offset < limit; offset++) {
315-
int length;
316-
if (buffer.get(offset) == data[0] && (length = startsWith(buffer, offset, data)) > 0) {
317-
return new BufPos(offset, length);
294+
/**
295+
* Searches for the given pattern within a buffer,
296+
* starting at the given buffer position.
297+
*
298+
* @param buffer the buffer to search within
299+
* @param position the buffer position to start searching at
300+
* @param pattern the pattern to search for
301+
* @return a long value whose lower 32 bits contain the index of the
302+
* found pattern, and upper 32 bits contain the found pattern length;
303+
* if the pattern is not found at all, returns -1L
304+
*/
305+
private static long indexOf(IoBuffer buffer, int position, byte[] pattern) {
306+
int length;
307+
byte first = pattern[0];
308+
for (int limit = buffer.limit() - minPatternLength(pattern) + 1; position < limit; position++) {
309+
if (buffer.get(position) == first && (length = matches(buffer, position, pattern)) > 0) {
310+
return (long)length << 32 | position;
318311
}
319312
}
320-
return new BufPos(-1, 0);
313+
return -1L;
321314
}
322315

323316
/**
324-
* Checks to see if the byte_buffer[buffer_offset] starts with data[]. The
325-
* character ? is a one byte wildcard, lowercase letters are optional.
317+
* Checks if the buffer at the given offset matches the given pattern.
318+
* The character '?' is a one byte wildcard, and lowercase letters are optional.
326319
*
327-
* @param buffer
328-
* @param bufferOffset
329-
* @param data
330-
* @return
320+
* @param buffer the buffer to check
321+
* @param bufferOffset the buffer offset at which to check
322+
* @param pattern the pattern to try matching
323+
* @return the length of the matched pattern, or -1 if there is no match
331324
*/
332-
private static int startsWith(IoBuffer buffer, int bufferOffset, byte[] data) {
333-
if (bufferOffset + minMaskLength(data) > buffer.limit()) {
325+
private static int matches(IoBuffer buffer, int bufferOffset, byte[] pattern) {
326+
if (bufferOffset + minPatternLength(pattern) > buffer.limit()) {
334327
return -1;
335328
}
336329
final int initOffset = bufferOffset;
337-
int dataOffset = 0;
338-
for (int bufferLimit = buffer.limit(); dataOffset < data.length
339-
&& bufferOffset < bufferLimit; dataOffset++, bufferOffset++) {
340-
if (buffer.get(bufferOffset) != data[dataOffset] && data[dataOffset] != '?') {
341-
// Now check for optional characters, at this point we know we didn't
342-
// match, so we can just check to see if we failed a match on an optional character,
343-
// and if so then just rewind the buffer one byte and keep going.
344-
if (Character.toUpperCase(data[dataOffset]) == buffer.get(bufferOffset))
345-
continue;
346-
// Didn't match the optional character, so act like it was not included and keep going
347-
if (Character.isLetter(data[dataOffset]) && Character.isLowerCase(data[dataOffset])) {
348-
--bufferOffset;
349-
continue;
350-
}
351-
return -1;
330+
int patternOffset = 0;
331+
for (int bufferLimit = buffer.limit(); patternOffset < pattern.length
332+
&& bufferOffset < bufferLimit; patternOffset++, bufferOffset++) {
333+
byte b = pattern[patternOffset];
334+
// check exact character match or wildcard match
335+
if (buffer.get(bufferOffset) == b || b == '?')
336+
continue;
337+
// check optional character match
338+
if (b >= 'a' && b <= 'z') { // lowercase is optional
339+
// at this point we know it's not an exact match, so we only need to check the
340+
// uppercase character. If there's a match we go on as usual, and if not we
341+
// ignore the optional character by rewinding the buffer offset
342+
if (b - 'a' + 'A' != buffer.get(bufferOffset)) // no uppercase match
343+
bufferOffset--;
344+
continue;
352345
}
346+
return -1; // no match
353347
}
354-
if (dataOffset != data.length) {
355-
// when minMaskLength(data) != data.length we might run out of buffer before we run out of data
348+
if (patternOffset != pattern.length) {
349+
// when minPatternLength(pattern) != pattern.length we might run out of buffer before we run out of pattern
356350
return -1;
357351
}
358352
return bufferOffset - initOffset;

0 commit comments

Comments
 (0)