diff --git a/src/main/java/io/chandler/rece232/RECE232.java b/src/main/java/io/chandler/rece232/RECE232.java index 191e30a..db25770 100644 --- a/src/main/java/io/chandler/rece232/RECE232.java +++ b/src/main/java/io/chandler/rece232/RECE232.java @@ -60,10 +60,18 @@ static byte partialCRC(int crc) { private RECE232() { } + /** + * Creates a new encoder instance. The encoder may be used repeatedly for new messages, but is not thread-safe. + * @return A new RECE232Encoder + */ public static RECE232Encoder getEncoder() { return new RECE232Encoder(); } - + + /** + * Creates a new decoder instance. The decoder may be used repeatedly for new messages, but is not thread-safe. + * @return A new RECE232Decoder + */ public static RECE232Decoder getDecoder() { return new RECE232Decoder(); } @@ -163,6 +171,8 @@ public RECE232Encoder appendLongword(int bytes) { public static final class RECE232Decoder { private int nLongwords; private int[] recon; + private boolean madeCorrections = false; + private boolean skipRecoveryOnCorruptedChecksum = true; private boolean failOnCorruptedChecksum = false; private boolean convertTabs = false; @@ -270,8 +280,16 @@ private int calculateGaps(byte[] src, int i, int r, int n, int[] gaps, int gapCo } private static final int GOOD_MASK = 0b11111_111111_11111; + + /** + * Loads a message for decoding. + * If load() returns true, the message contents may be retrieved from the decoder. + * If load() returns false, other method behavior is undefined. + * @param src Message bytes + * @return True if the message was successfully decoded + */ public boolean load(byte[] src) { - + this.madeCorrections = false; int len = src.length; len -= 3; // Subtract fletcher footer, remainder should be n*8b if (len < 7) return false; // below minimum recoverable bytes @@ -346,6 +364,8 @@ public boolean load(byte[] src) { return false; } + if (fletFMask != GOOD_MASK) madeCorrections = true; + this.recon = new int[nLongwords * 8]; int[] gaps = new int[nLongwords]; Arrays.fill(gaps, -1); @@ -360,12 +380,14 @@ public boolean load(byte[] src) { // No gaps; just verify checksum if (!verifyReconChk(n*8)) { badChks[n] = true; + madeCorrections = true; if (DEBUG) System.out.println("Bad checksum " + n); } } else { int chk = 0; for (int b = n*8; b < n*8 + 8; b++) { if (b == gapIdx) continue; + madeCorrections = true; if (DEBUG) System.out.println("Fill gap chk" + b); chk ^= recon[b]; } @@ -498,6 +520,14 @@ public int getLongword(int i) { recon[i*8 + 5] << 21 | recon[i*8 + 6] << 27; } + + /** + * Checks if the decoder attempted to correct message errors + * @return + */ + public boolean madeCorrections() { + return madeCorrections; + } } } diff --git a/src/test/java/io/chandler/rece232/RECE232Tests.java b/src/test/java/io/chandler/rece232/RECE232Tests.java index 05011ca..bcf1a73 100644 --- a/src/test/java/io/chandler/rece232/RECE232Tests.java +++ b/src/test/java/io/chandler/rece232/RECE232Tests.java @@ -98,6 +98,7 @@ public void testErrors() { // Corrupt makes an obvious high bit flip // Flip makes a non-obvious bit flip String[] corruptionTests = new String[] { + "Clean", "Pass, Drop 4, Corrupt 10", "Fail, Drop 9, Corrupt 10", "Fail, Drop 0, Drop 1", @@ -162,8 +163,13 @@ public void testErrors() { } boolean corrected = dec.load(mod.array()); - if (corr.toLowerCase().contains("pass")) { + if (corr.toLowerCase().contains("pass") || corr.toLowerCase().contains("clean")) { assertTrue(corrected); + if (corr.toLowerCase().contains("clean")) { + assertFalse(dec.madeCorrections()); + } else { + assertTrue(dec.madeCorrections()); + } } else if (corr.toLowerCase().contains("fail")) { assertFalse(corrected); }