diff --git a/quickfixj-base/src/main/java/quickfix/FieldMap.java b/quickfixj-base/src/main/java/quickfix/FieldMap.java index 37b32b968..5eb394583 100644 --- a/quickfixj-base/src/main/java/quickfix/FieldMap.java +++ b/quickfixj-base/src/main/java/quickfix/FieldMap.java @@ -572,11 +572,9 @@ int calculateChecksum() { if (!groupList.isEmpty()) { if(IS_STRING_EQUIVALENT) { String value = NumbersCache.get(entry.getKey()); - for (int i = value.length(); i-- != 0;) - result += value.charAt(i); + result += MessageUtils.checksum(value, 0, value.length()); value = NumbersCache.get(groupList.size()); - for (int i = value.length(); i-- != 0;) - result += value.charAt(i); + result += MessageUtils.checksum(value, 0, value.length()); result += '=' + 1; } else { final IntField groupField = new IntField(entry.getKey()); diff --git a/quickfixj-base/src/main/java/quickfix/Message.java b/quickfixj-base/src/main/java/quickfix/Message.java index 655c8d0e7..b2aa958e4 100644 --- a/quickfixj-base/src/main/java/quickfix/Message.java +++ b/quickfixj-base/src/main/java/quickfix/Message.java @@ -213,10 +213,7 @@ private static void setBodyLength(StringBuilder stringBuilder) { private static void setChecksum(StringBuilder stringBuilder) { int checkSumIndex = stringBuilder.lastIndexOf(CHECKSUM_FIELD); - int checkSum = 0; - for(int i = checkSumIndex; i-- != 0;) - checkSum += stringBuilder.charAt(i); - String checkSumValue = NumbersCache.get((checkSum + 1) & 0xFF); // better than sum % 256 since it avoids overflow issues + String checkSumValue = NumbersCache.get(MessageUtils.checksum(stringBuilder, 0, checkSumIndex + 1)); checkSumIndex += CHECKSUM_FIELD.length(); stringBuilder.replace(checkSumIndex + (3 - checkSumValue.length()), checkSumIndex + 3, checkSumValue); } diff --git a/quickfixj-base/src/main/java/quickfix/MessageUtils.java b/quickfixj-base/src/main/java/quickfix/MessageUtils.java index f56003ae7..95fa0405e 100644 --- a/quickfixj-base/src/main/java/quickfix/MessageUtils.java +++ b/quickfixj-base/src/main/java/quickfix/MessageUtils.java @@ -292,17 +292,21 @@ public static int checksum(byte[] data, boolean isEntireMessage) { */ public static int checksum(Charset charset, String data, boolean isEntireMessage) { if (CharsetSupport.isStringEquivalent(charset)) { // optimization - skip charset encoding - int sum = 0; int end = isEntireMessage ? data.lastIndexOf("\00110=") : -1; int len = end > -1 ? end + 1 : data.length(); - for (int i = 0; i < len; i++) { - sum += data.charAt(i); - } - return sum & 0xFF; // better than sum % 256 since it avoids overflow issues + return checksum(data, 0, len); } return checksum(data.getBytes(charset), isEntireMessage); } + static int checksum(CharSequence data, int start, int end) { + int sum = 0; + for (int i = start; i < end; i++) { + sum += data.charAt(i); + } + return sum & 0xFF; // better than sum % 256 since it avoids overflow issues + } + /** * Calculates the checksum for the given message * (excluding existing checksum field, if one exists). diff --git a/quickfixj-base/src/test/java/quickfix/FieldMapTest.java b/quickfixj-base/src/test/java/quickfix/FieldMapTest.java index 148456294..6609d9366 100644 --- a/quickfixj-base/src/test/java/quickfix/FieldMapTest.java +++ b/quickfixj-base/src/test/java/quickfix/FieldMapTest.java @@ -78,4 +78,14 @@ public void testRemoveGroup() { map.removeGroup(73); assertFalse(map.hasGroup(73)); } + + @Test + public void testGroupChecksumMatchesMessageUtils() { + FieldMap map = new Message(); + Group group = new Group(73, 11); + group.setString(11, "A"); + map.addGroup(group); + + assertEquals(MessageUtils.checksum("73=1\00111=A\001"), map.calculateChecksum()); + } } diff --git a/quickfixj-base/src/test/java/quickfix/MessageUtilsTest.java b/quickfixj-base/src/test/java/quickfix/MessageUtilsTest.java index fdde176b1..0c25f3c9c 100644 --- a/quickfixj-base/src/test/java/quickfix/MessageUtilsTest.java +++ b/quickfixj-base/src/test/java/quickfix/MessageUtilsTest.java @@ -26,6 +26,9 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.fail; + +import java.nio.charset.StandardCharsets; + import org.junit.Test; /** @@ -112,4 +115,22 @@ public void testGetStringFieldWithBadValue() throws Exception { assertNull(MessageUtils.getStringField(messageString, 10)); } + @Test + public void testStringEquivalentChecksumMatchesStringBuilderFastPath() { + String messageString = "8=FIX.4.2\0019=12\00135=X\001108=30\00110=049\001"; + int checksumFieldIndex = messageString.lastIndexOf("\00110="); + StringBuilder messageBuilder = new StringBuilder(messageString); + + assertEquals(MessageUtils.checksum(messageString), MessageUtils.checksum(messageBuilder, 0, + checksumFieldIndex + 1)); + } + + @Test + public void testMultibyteChecksumUsesEncodedBytes() { + String messageString = "8=FIX.4.2\0019=18\00135=X\00158=\u6D4B\u9A8C\00110=000\001"; + + assertEquals(MessageUtils.checksum(messageString.getBytes(StandardCharsets.UTF_8), true), + MessageUtils.checksum(StandardCharsets.UTF_8, messageString, true)); + } + }