OSDN Git Service

Some improvements to Java bindings + updated README file.
authorLoRd_MuldeR <mulder2@gmx.de>
Sun, 14 Jan 2018 16:01:25 +0000 (17:01 +0100)
committerLoRd_MuldeR <mulder2@gmx.de>
Sun, 14 Jan 2018 16:02:32 +0000 (17:02 +0100)
README.md
bindings/Java/example/src/com/muldersoft/mhash384/example/ExampleApp.java
bindings/Java/library/src/com/muldersoft/mhash384/MHash384.java
tools/JavaBytes/src/com/muldersoft/mhash384/utils/JavaBytes.java

index 4fb574b..7ceada7 100644 (file)
--- a/README.md
+++ b/README.md
@@ -155,11 +155,11 @@ We use a counter that keeps track of the MIX-table row (permutation). The counte
 
 ### The RND table
 
-Furthermore, in each update step, every byte of the hash value will *additionally* be XOR'ed with a constant byte value. There is a distinct constant for each hash byte position, and a different set of constants is employed in subsequent update steps.
+Furthermore, in each update step, to every byte of the hash value a constant byte value will be *added*. There is a distinct constant for each hash byte position, and a different set of constants is employed in subsequent update steps. In any case, the addition is performed modulus 256 &ndash; which means that results greater than or equal to 256 will wrap around to zero.
 
 For this purpose, MHash-384 uses a table of *256* pre-defined 384-Bit (48-byte) words. This table is referred to as the *RND-table*. Each of its rows contains 48 "random" byte values &ndash; one for each hash byte position. The contents of the RND-table are based entirely on "true" **random** numbers. The randomness was collected from atmospheric noise, courtesy of [Random.org](https://www.random.org/).
 
-The rationale is to ensure that there will be enough variation in the hash value bytes, even when there is very few variations in the given input bytes. Note that XOR'ing a perfectly uniform byte sequence with a *random* byte sequence yields a "random-looking" byte sequence, whereas XOR'ing two random byte sequences still yields a "random-looking" byte sequence. In other words, XOR'ing the given input sequence with the random byte sequence can only make the result appear *more* random.
+The rationale is to ensure that there will be enough variation in the hash value bytes, even when there is very few variations in the given input bytes. Note that blending a perfectly uniform byte sequence with a *random* byte sequence yields a "random-looking" byte sequence, whereas blending two random byte sequences still yields a "random-looking" byte sequence. In other words, blending the given input sequence with the random byte sequence can only make the result "look" *more* random.
 
 Of course, even though the RND-table was generated from "true" random numbers, the exactly same table is used in each hash computation, so the hash function remains deterministic. Also, we use the same counter to select the current RND-table row that is used to select the current MIX-table row, i.e. the "active" RND-table row will wrap around after 256 update steps.
 
@@ -221,6 +221,8 @@ The MHash-384 algorithm can be summed up with the following simple pseudocode:
          done
        end.
 
+*Note:* The `⊕` symbol refers to the binary XOR operation, whereas `+` refers to the arithmetic add operation.
+
 
 # Detailed API Specification
 
index 2e7745f..8376d63 100644 (file)
@@ -40,6 +40,7 @@ import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.util.List;
 import java.util.concurrent.Semaphore;
+import java.util.function.Consumer;
 
 import javax.swing.JButton;
 import javax.swing.JFileChooser;
@@ -50,6 +51,7 @@ import javax.swing.JPanel;
 import javax.swing.JProgressBar;
 import javax.swing.JTextField;
 import javax.swing.SwingWorker;
+import javax.swing.WindowConstants;
 import javax.swing.border.EmptyBorder;
 
 import com.muldersoft.mhash384.MHash384;
@@ -60,11 +62,11 @@ public class ExampleApp extends JFrame {
     private static final long serialVersionUID = -3576679013210278556L;
 
     public ExampleApp() {
-        initUI();
-
+        final JButton[] buttons = initUI();
+        
         addWindowListener(new WindowListener() {
             @Override
-            public void windowOpened(WindowEvent e) {
+            public void windowOpened(final WindowEvent e) {
                 try {
                     final List<Integer> version = MHash384.getVersion();
                     setTitle(String.format("MHashJava384 - Example App v%d.%d.%d", version.get(0), version.get(1), version.get(2)));
@@ -77,38 +79,40 @@ public class ExampleApp extends JFrame {
             }
 
             @Override
-            public void windowClosing(WindowEvent e) {
+            public void windowClosing(final WindowEvent e) {
+                for(final JButton button : buttons) {
+                    if(button.isEnabled()) {
+                        ExampleApp.this.dispose();
+                        break;
+                    }
+                }
             }
 
             @Override
-            public void windowClosed(WindowEvent e) {
-            }
+            public void windowClosed(final WindowEvent e) { }
 
             @Override
-            public void windowIconified(WindowEvent e) {
-            }
+            public void windowIconified(final WindowEvent e) { }
 
             @Override
-            public void windowDeiconified(WindowEvent e) {
-            }
+            public void windowDeiconified(final WindowEvent e) { }
 
             @Override
-            public void windowActivated(WindowEvent e) {
-            }
+            public void windowActivated(final WindowEvent e) { }
 
             @Override
-            public void windowDeactivated(WindowEvent e) {
-            }
+            public void windowDeactivated(final WindowEvent e) { }
         });
     }
 
-    private void initUI() {
+    private JButton[] initUI() {
         setDefaultCloseOperation(EXIT_ON_CLOSE);
         setTitle("MHashJava384 - Example App [Launching]");
         setSize(800, 384);
         setMinimumSize(getSize());
         setLocationRelativeTo(null);
-
+        setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
+        
         final JTextField editFile = new JTextField();
         final JTextField editHash = new JTextField();
         editFile.setEditable(false);
@@ -167,7 +171,7 @@ public class ExampleApp extends JFrame {
             public boolean dispatchKeyEvent(final KeyEvent e) {
                 final boolean busy =  (!(buttonExecute.isEnabled() || buttonBrowse.isEnabled()));
                 if ((e.getKeyCode() == KeyEvent.VK_F12) && (e.getID() == KeyEvent.KEY_PRESSED) && (!busy)) {
-                    runSelfTest(editHash, new JButton[] { buttonBrowse, buttonExecute }, abortFlag);
+                    runSelfTest(editHash, progressBar, new JButton[] { buttonBrowse, buttonExecute }, abortFlag);
                     return true;
                 }
                 if ((e.getKeyCode() == KeyEvent.VK_ESCAPE) && (e.getID() == KeyEvent.KEY_PRESSED) && busy) {
@@ -177,6 +181,8 @@ public class ExampleApp extends JFrame {
                 return false;
             }
         });
+        
+        return new JButton[] { buttonBrowse, buttonExecute };
     }
 
     private boolean browseForFile(final JTextField editFile, final JTextField editHash, final JProgressBar progressBar) {
@@ -245,22 +251,29 @@ public class ExampleApp extends JFrame {
             }
 
             @Override
-            protected void process(List<Integer> chunks) {
+            protected void process(final List<Integer> chunks) {
                 progress.setValue(maxValue(chunks));
             }
         };
     }
 
-    private void runSelfTest(final JTextField editHash, final JButton[] buttons, final Semaphore abortFlag) {
+    private void runSelfTest(final JTextField editHash, final JProgressBar progress, final JButton[] buttons, final Semaphore abortFlag) {
         System.out.println("[SELF-TEST]");
         for (final JButton button : buttons) {
             button.setEnabled(false);
         }
-        final SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() {
+        final SwingWorker<Boolean, Integer[]> worker = new SwingWorker<Boolean, Integer[]>() {
+            private final Integer[] lastProgress = new Integer[] { -1, -1 }; 
+            
             @Override
             protected Boolean doInBackground() throws Exception {
                 editHash.setText("Self-test is running, please be patient...");
-                MHash384.selfTest(abortFlag);
+                MHash384.selfTest(abortFlag, new Consumer<Integer[]>() {
+                    @Override
+                    public void accept(final Integer[] progress) {
+                        publish(progress);
+                    }
+                });
                 return true;
             }
 
@@ -268,7 +281,12 @@ public class ExampleApp extends JFrame {
             protected void done() {
                 try {
                     if (get()) { /* check result */
-                        JOptionPane.showMessageDialog(ExampleApp.this, "Self-test completed successfully.", "Self-test", JOptionPane.INFORMATION_MESSAGE);
+                        final String successMessage = "Self-test completed successfully.";
+                        editHash.setText(successMessage);
+                        JOptionPane.showMessageDialog(ExampleApp.this, successMessage, "Self-test", JOptionPane.INFORMATION_MESSAGE);
+                    }
+                    else {
+                        throw new AssertionError("Self-test has failed. See log for details!");
                     }
                 }
                 catch (Throwable err) {
@@ -283,6 +301,19 @@ public class ExampleApp extends JFrame {
                     }
                 }
             }
+            
+            @Override
+            protected void process(final List<Integer[]> chunks) {
+                final Integer[] value = maxValueEx(chunks);
+                if(value.length >= 3) {
+                    if((lastProgress[0] != value[0]) || (lastProgress[1] != value[1])) {
+                        editHash.setText(String.format("Self-test is running (step %d of %d), please be patient...", value[1] + 1, value[0]));
+                        lastProgress[0] = value[0];
+                        lastProgress[1] = value[1];
+                    }
+                    progress.setValue(value[2]);
+                }
+            }
         };
         worker.execute();
     }
@@ -333,6 +364,27 @@ public class ExampleApp extends JFrame {
         return result;
     }
 
+    private static Integer[] maxValueEx(final List<Integer[]> chunks) {
+        if(!chunks.isEmpty()) {
+            Integer[] result = chunks.get(0);
+            for (final Integer[] chunk : chunks) {
+                for(int i = 0; i < Math.min(result.length, chunk.length); ++i) {
+                    if(chunk[i] > result[i]) {
+                        result = chunk;
+                        break;
+                    }
+                    else if(chunk[i] < result[i]) {
+                        break;
+                    }
+                }
+            }
+            return result;
+        }
+        else {
+            return new Integer[0];
+        }
+    }
+    
     private Throwable unwrapException(final Throwable err) {
         final Throwable next = err.getCause();
         if(next != null) {
index 747a55c..19d4c17 100644 (file)
@@ -30,6 +30,7 @@ import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 import java.util.concurrent.Semaphore;
+import java.util.function.Consumer;
 
 import static java.util.Objects.requireNonNull;
 
@@ -1214,7 +1215,7 @@ public final class MHash384 {
         final ByteString mix = TABLE_MIX.get(m_rowIdx);
         final ByteString rnd = TABLE_RND.get(m_rowIdx);
         for (int i = 0; i < HASH_LENGTH; ++i) {
-            final int val = (src[mix.at(i)] ^ xor.at(i) ^ rnd.at(i)) & 0xFF;
+            final int val = ((src[mix.at(i)] ^ xor.at(i)) + rnd.at(i)) & 0xFF;
             dst[i] ^= TABLE_SBX.get(val).at(i);
         }
         m_rowIdx = (m_rowIdx + 1) & 0xFF;
@@ -1269,7 +1270,7 @@ public final class MHash384 {
         final ByteString mix = TABLE_MIX.get(m_rowIdx);
         final ByteString rnd = TABLE_RND.get(m_rowIdx);
         for (int i = 0; i < HASH_LENGTH; ++i) {
-            final int val = (src[mix.at(i)] ^ xor.at(i) ^ rnd.at(i)) & 0xFF;
+            final int val = ((src[mix.at(i)] ^ xor.at(i)) + rnd.at(i)) & 0xFF;
             result[i] = (byte)(dst[i] ^ TABLE_SBX.get(val).at(i));
         }
         return new ByteString(result);
@@ -1310,14 +1311,14 @@ public final class MHash384 {
     
     public final static void selfTest() {
         try {
-            selfTest(null);
+            selfTest(null, null);
         }
         catch (InterruptedException e) {
             throw new RuntimeException("Interrupted: " + e.hashCode(), e);
         }
     }
     
-    public final static void selfTest(final Semaphore abortFlag) throws InterruptedException {
+    public final static void selfTest(final Semaphore abortFlag, final Consumer<Integer[]> callback) throws InterruptedException {
         List<TestVector> TEST_VECTOR = Arrays.asList(
             /*00*/ new TestVector(0x0000001, ""),
             /*01*/ new TestVector(0x0000001, "abc"),
@@ -1338,22 +1339,22 @@ public final class MHash384 {
         );
 
         final List<ByteString> TEST_RESULT = buildTable(
-            /*00*/ "{^@\u00A3]*'!$\u0093\u0020'\u00F0\u009F\u000B\u009F&q\u00D9\u00EF\u00F4\u00C7\u00E6\u00F9tf\u00EAg1a}\u00FA\u009B\u008B\u0000F\u00CB\u008Dj\u00A1\u00E5\u00AD\u00F5\u0020\u00FC\u00E3L2",
-            /*01*/ "\u0094\u0016\u008Fq'\u00DD\u00E6\u001D\u00B6\u0090\u00AC\u00C4\u00DC\u00FC\u00BF5\u00EA\u00F7z\u0015\u001F\u008B\u00BA\u00EEO\u00C3\u00F7\u0004W=\u00FD\r\u00FE\u009C\u00D5\u00C4\u00C3\u00E0\"z\u00C6X*Q\u00F2\u00C4\u00D8d",
-            /*02*/ "U\u0005\u00C0;\u00FF\u0014-{G7\u00FA\u007F6u\u000F\u008E\u00094\u00A3e\u00FE\u00F6\u0015\u0092\u00E3\u00D3\u00B0\u00D3\u0008\u0086}\u00AADd\u00EB5\u00EA'\u0007z\u00BE\u00AC\u00BEy\u00B6W\u00150",
-            /*03*/ "n[\u0090\u00D2V\u0081\u00B4TP\u00E2\u001F\u00C8bs\u00EB\u00AD\u00B7\u0091sXi\u00CEm\u00EE\u00AE\u001A\u00D5'\u0080Z\u00ACb\u00D8\u008E\u00AE\u00DDY/\u0020\u0015\u00FE\u0080_\u0001\u00F62%2",
-            /*04*/ "\u001FA7/D\u00B6cL{c\u00B6\u00D8k\u00C2\u00AF\u0006\u0083rx\u00E6\u0087!vs\u0096I\u00B0c\u000F\u00D6\u00BBr\u00AD\u00E6\u00938*\u0094\u00B8\u00C2\u00F8t\u00CF\u0098^\u00CEA\u00F2",
-            /*05*/ ":*\u0083(\r\u00B4\u00E5\u0095\u00E0\u0012\u0011\u00192\u00A7\u008D`\u008A\u00E7\u009B*\u0001\u00C8\u001D\u00CB\u00B4\u00F6aCH\u00D4\u00BF\u00D6\u0098U@\u00AF\u00BC\u00D8\u0017\u0013\u00FF\u0086\u00CE\u008B__\u008D\u0002",
-            /*06*/ "!\u00D1\u00B5\u00D8\u00B8\u00850\u00EC\u00CB@9v\u0008Q7\u009D\u001E2\u0004\u00F8\u0088\u009D\u009C\u00C7<\u00E6#\u0013\u00F7\u00D77T\u00DF\u0098\u00F3\u00EE\u00F2n\u00C57\u00D8AU\u00B8\u0091\u0015\")",
-            /*07*/ "\u00EC\u0018\u00E2\u00AFJ\u00C0\u00D8bj\u00E5\u0016Y\u0093\u00DE)\u00DB\u00B2\u00010\u00D1\u00CE[p\u00A5R\u00DA\u00EC\u00D7\u0097\u00E1]\u009B\u00B5A\u001BJd\u00C7\u008E\u001F\u000Cn\u00EBVzV\r\u0085",
-            /*08*/ "\u0011\\!\u00BE\u0085\u00D7\u0086\u00D59;c\\;\u00B55zd\u00CA\u00DDX\u009A\u00C8?D\u00F8Q9\u00D0\u00A5r\u00F0\u00A1\u00F6\u00A9TG\u0014\u0012\u00D9g\u00F0\u009C]\u00EF\u00C5\u0000\u001A\u00A2",
-            /*09*/ "\u00B4e\u00C4\u00CAN\u0013\u0093\u0092\u001EQ\u00A6\u00E4O\u0094\u00BA^\u00F3A\u00AA\u0019W\u000B\u00BD\u0093\u00DB\u00DAf\u00E5\u001E\u009A\u00F0yP\u001B\u00D6w\u00175D\u00F4\u00E6Q\u00BCu\u00D3\u009Eb'",
-            /*0A*/ ">\u009C\u008A\u00AC\u00E6\u0082\u00E2\u00BC\u00B3\u001A\u00F7&\u00F6\u0018\u0017\u00DB\u00DB\u001F\u00BBa\u00C8=6\u0094\u009E\u00B8\u00CF\u00BF\u000FL\u00C8h\u00D2\u000FC\\i&\u00AA\u00F8\u00AB\u0096.\u00BC}\u00DC\u0002\u0005",
-            /*0B*/ "\u00AA{\u00C9(*\u00D0]\u00B2=\u000C<\u00B5\u00E7\u001E\u00EF&/\u00D5\u009A\u00EC##\u00F3\u00A4\u00B2\u00AAR\u00C9\u00D3\u00B1#\u00D3^\u001A\u00D1\u00200\u00E4J\u00CF~S/-\u00E9\"\u00D7\u008A",
-            /*0C*/ "fg\u00B6\u008BD\u00B0\u00E4\u00B0\u00C24\u00C9E\u0008^y$?\u00A0Y\u00209\u00E4w\u00C2\u00A2s\u00A6vp<o=\u00F8Q\u00CD|\u00EBO\u007F;3\u00D8\u0094Lv=\u00C5\u00AF",
-            /*0D*/ "\u00A1\u00E3\u00B0\u00B2\u000BW2\u00BD\u00BC\u008B\u00C7\u0090}X\u0014\u00A4Ov\u00D0\u00C2]:cM\u0012\u009FV\u0018\u00C4\u00B7\u0017K\u00C8\u00FA\u0020'\u0085gx\u00A8H\u00B2yRZ\u00D1\u000C\u0000",
-            /*0E*/ "Fs\u00F5U?V\u00F0~|\u001D\u00C1)\u00D7\u0095\u008E\u00F2\u00D2\u00EFbk^&I\u001E\u00DBd\u00BE\u00D9\u00C2\u00DF\u0080\u00FC.\u00AA\u00A1+\u00A6'\u00DA\u008C\u00E2P9(\u001D\u0004\u00BE\u00E3",
-            /*0F*/ "4\u00D4\u000F\u00D3\u0095I\u0087w\u00C2\u000Cz\u008E\u00A5\u00EB\u00ED\u0011Z\u001C\u00B7|th\u0098\"\u009B\u0096\u0090m\u00A6\u00B79\u00B6\u0091\u00C4s\u0089z\u00FB\u0080\u0016@\u00A5\u0019\u00E2\u00A9\u0003\u00C3\u00E0"
+            /*00*/ "J\u0007\u008B\u00E6V\u00B2\u0094d8\u009Cq\u0009\u00F0\u00F4\u0008\u00B0\u00AD\u009C\u0092\u00BD\u00B46\u0003cs\u00ACP\u00BC\u0099<!\u00F74\u008B\u009E\u00E2\u0009\u0080\u00F7\u007FM+\u00EDI\u000E\u00BC\u009D.",
+            /*01*/ "\u00F8A\u00FD(\u00F9u\u00A8'\u00EDWU\u00CD\u00BA\u00E2\u00A0\u00F0\u00FBM\u00D8\u00A0,\u0084\u0010\u0004\u000F\u00EC\u008BBY\u000E\u00EFp\u00FF\u000FPJ\u00E4O8\u001B\u00AF\u0099Y\u0096\u0003v\u00C9\u00E9",
+            /*02*/ "`\u001F\u008D~1\u0094H\u00A6>2\u0086\u0000d\u0091/4M\u00D7\u0015R\u00ACpa1\u00199n\u00B4\u00BB\u00CB\u00AC\u0095\u0095\u00E6\u00F1\u0080l\u0012\u007F\u0083\u00B0z}D\u009A\u00FA\u0083q",
+            /*03*/ "\u00EF\u00AC\u00BD\u00E1\u008Dj\u00B7)Et\u00D2\u00F7h\u0086I\u00D9mt\u00AE\u00A6\u0085\u00F6\u00D448v\u00BB\u009C%|\u00ED\u00B5\u00A4\u00E6\u000F\u008E\u0001B\u0096\u00FB^F\u00C1z\u00C6y\u00E7\u00A8",
+            /*04*/ "\u00DA#\u008B\u00FF\u0084+\u00DAx\u00D3\u000F\u001F\u00A2\u00A1-\u00FA}j\u00C5\u00F1\u00B0(\u00BBA\u00FB\u001F\u00FA\u0085\u00D3c!\u00FE\u0008\u00A6\u0000d\u00CD(\u00CB\u00B0\u00F5s\u00EAak\u00FE\u0008\u00C2r",
+            /*05*/ "\u009291\u0081\u00E1A=\u0083\u00DB\u009B\u0083R\u0020\u0083\u00FCj\u00EE\u00CE\u00E2x\u00AA\u00CFS\u00E1\u00F2Oz96\u0005u\u0014\u001Fq\u001Ev,\u00D1\u00BBC\u00968\u00F7\u0011\u00EC\u00D9\u001C\u0015",
+            /*06*/ "\u000B\u0099'\u001Ao4@R\u00FF8\u00016\u00F5(\u00BC\u00F3`*\u00ABs=Y\u0086\u00B2W\u00EC\u0091e\u00AAaP\u00A1y%T\u00B1\u009E~\u00D9\u0011\u00BE\u00B6Hm\u00C0\u009D\u0085\u00A2",
+            /*07*/ "L\u00A3\u001A%i,\u00FC|q\u008A\u009D\u00D1\u00E0]\u00F8\u00BE\u00AA\u00E58f\u009F\u00E7\u0011\u000Frc\u00EF\u00C0\u00E3\u00CE\u0010\u008E\u00CD\u00A0\u00C3\u00D9\u0085FB\u00AA\u00C7V\u00DF\u00C4\u00C6\u00D4\"\u008E",
+            /*08*/ "\u009B\u00F98\u00C83\u00E2XD>\u00DE\u00FDQ\nb-\u00ED\u00EDr\u001C\u0001E\u00D5\u00C9\u00D0\u00F9\u0088\u0015t\n#\u009D\u00D8P[\u00FF{-\u000Ej\u001B\nM\u00F2\u00C5\u00F6{A(",
+            /*09*/ "\u00877D\u0097\u009E\u00FA\u0003/x\u00D0f\u00970\u0013\u00FDD/\u0091\u0097\u00E7\u00D4\u0012\u00B9\u00AA\u00EF\u00E5\u00CB\u00B1t\u00BEc\u00C0\u00CC\u0002\u00A6\u00E00\u00E6\u0014u\u00EB\u0089J\u00E1-\u0016WQ",
+            /*0A*/ "V\u007F2T\u00FDOX\u00C9f\u00F5\u0011\u0099'\u00F1/\u000BY>Z\u00CF\u0090\u0002\u0088\u00F1\u00C5\u009D\u00CC\u00C0\u00E4w\u00C3J\u0000\u00F4\u00BFkq\u00F2\u00F9\u0008\u00FD\u001A\u00F2\u009C'\u0088\u0087\u0097",
+            /*0B*/ "\u000Es+\u00B3\u008D\u00C55\u009D\u00A7\u00C9{c\u00D5,^\u00E1\u0020s3}\u00E7N\u0016\u0095H@\u00D6\u00E3c\u000E\u00C3[\u00D2\u00FF\u00EA\u00D6]\u00FD\u0011\\\u001BX\u001E\u00EF\u001B2\u00DD*",
+            /*0C*/ "\u008E~\u00C76\u00DF\u00F6\u00C7\u009CU\u00AE\u0012\u0099\u0005]\u009Da\u00D3&.\u0084\u00EE\u00CCV3G)k\u0089\u00BF\u0081/\u00DC\u00C60\u00D2b\u00CE\u00ACL\u00BE!\u001FW\u009E\u00B6\u00EC\u001C\u00B2",
+            /*0D*/ "4\u00DE\u0019Q\u001B\u00E9\u00A2\u0084\u00D8\u00B9\u00B4\u00C7m\u00D6\u0093O\u00C0\u00B5\u00EB\u00C2\rn\u00B7\u00E3\u009BT\u0013\u00B39\u008D4\u009F\u0080k\u00136z\u0011\u00F4\u00B5\u00D4\u00C3\u00D0\u00CEo\u008E\u00A3\u00A7",
+            /*0E*/ "\u000F\u00F2\u00A2\u00F0\u00ACoC\u00AFH\u00CE\u008A8\u000B\u001A\u00B14\u00CEj1v\u00CE\u00C8\u0013\u00B2\u00AEl;z\u00B6\u00DA\u00EF\u00D1\u00BE\u0099\u00E7\u00F3\u00C9\u00E6\u001A\u001B>c\u001D\u0007|'5\u0013",
+            /*0F*/ "\u0009\u0019\u00F0*r\u001CJ\u00DA\u0098y\u00C3\u00BEl\u00EC\u0098\u00FBn\u00A0\u00D4S\u00C2\r\u00B4\u00BD\u009B(~\u00D6%+\u0098%\u00CFf\u00A0e\u00A2\u0009\u00AF\u00C8\u00D5\u00A2*\u00DF.E|n"
         );
 
         validateTableData(TABLE_INI, TABLE_INI_SIZE);
@@ -1363,19 +1364,27 @@ public final class MHash384 {
         validateTableData(TABLE_SBX, TABLE_SBX_SIZE);
         
         final Iterator<ByteString> expected = TEST_RESULT.iterator();
-        for (final TestVector testVector : TEST_VECTOR) {
+        for (int testIndex = 0; testIndex < TEST_VECTOR.size(); ++testIndex) {
             final MHash384 subject = new MHash384();
+            final TestVector testVector = TEST_VECTOR.get(testIndex);
             for (int j = 0; j < testVector.iterations; ++j) {
+                if(callback != null) {
+                    callback.accept(new Integer[] { TEST_VECTOR.size(), testIndex, (int)Math.round((j / (double)testVector.iterations) * 100.0) });
+                }
                 subject.update(testVector.message);
                 if((abortFlag != null) && abortFlag.tryAcquire()) {
                     throw new InterruptedException("Operation aborted by user!");
                 }
             }
+            if(callback != null) {
+                callback.accept(new Integer[] { TEST_VECTOR.size(), testIndex, 100 });
+            }
             final ByteString result = subject.digest();
             System.out.println(result);
             if (!result.equals(expected.next())) {
                 throw new AssertionError("Test vector did NOT compare equal");
             }
+            Thread.yield();
         }
     }
 }
index 75639d0..10858fc 100644 (file)
@@ -1075,22 +1075,22 @@ public class JavaBytes {
     ));
 
     private static final List<String> INPUT_TST = Collections.unmodifiableList(Arrays.asList(
-            "7B5E40A35D2A272124932027F09F0B9F2671D9EFF4C7E6F97466EA6731617DFA9B8B0046CB8D6AA1E5ADF520FCE34C32", /*00*/
-            "94168F7127DDE61DB690ACC4DCFCBF35EAF77A151F8BBAEE4FC3F704573DFD0DFE9CD5C4C3E0227AC6582A51F2C4D864", /*01*/
-            "5505C03BFF142D7B4737FA7F36750F8E0934A365FEF61592E3D3B0D308867DAA4464EB35EA27077ABEACBE79B6571530", /*02*/
-            "6E5B90D25681B45450E21FC86273EBADB791735869CE6DEEAE1AD527805AAC62D88EAEDD592F2015FE805F01F6322532", /*03*/
-            "1F41372F44B6634C7B63B6D86BC2AF06837278E6872176739649B0630FD6BB72ADE693382A94B8C2F874CF985ECE41F2", /*04*/
-            "3A2A83280DB4E595E012111932A78D608AE79B2A01C81DCBB4F6614348D4BFD6985540AFBCD81713FF86CE8B5F5F8D02", /*05*/
-            "21D1B5D8B88530ECCB4039760851379D1E3204F8889D9CC73CE62313F7D73754DF98F3EEF26EC537D84155B891152229", /*06*/
-            "EC18E2AF4AC0D8626AE5165993DE29DBB20130D1CE5B70A552DAECD797E15D9BB5411B4A64C78E1F0C6EEB567A560D85", /*07*/
-            "115C21BE85D786D5393B635C3BB5357A64CADD589AC83F44F85139D0A572F0A1F6A954471412D967F09C5DEFC5001AA2", /*08*/
-            "B465C4CA4E1393921E51A6E44F94BA5EF341AA19570BBD93DBDA66E51E9AF079501BD677173544F4E651BC75D39E6227", /*09*/
-            "3E9C8AACE682E2BCB31AF726F61817DBDB1FBB61C83D36949EB8CFBF0F4CC868D20F435C6926AAF8AB962EBC7DDC0205", /*0A*/
-            "AA7BC9282AD05DB23D0C3CB5E71EEF262FD59AEC2323F3A4B2AA52C9D3B123D35E1AD12030E44ACF7E532F2DE922D78A", /*0B*/
-            "6667B68B44B0E4B0C234C945085E79243FA0592039E477C2A273A676703C6F3DF851CD7CEB4F7F3B33D8944C763DC5AF", /*0C*/
-            "A1E3B0B20B5732BDBC8BC7907D5814A44F76D0C25D3A634D129F5618C4B7174BC8FA2027856778A848B279525AD10C00", /*0D*/
-            "4673F5553F56F07E7C1DC129D7958EF2D2EF626B5E26491EDB64BED9C2DF80FC2EAAA12BA627DA8CE25039281D04BEE3", /*0E*/
-            "34D40FD395498777C20C7A8EA5EBED115A1CB77C746898229B96906DA6B739B691C473897AFB801640A519E2A903C3E0"  /*0F*/
+            "4A078BE656B29464389C7109F0F408B0AD9C92BDB436036373AC50BC993C21F7348B9EE20980F77F4D2BED490EBC9D2E", /*00*/
+            "F841FD28F975A827ED5755CDBAE2A0F0FB4DD8A02C8410040FEC8B42590EEF70FF0F504AE44F381BAF9959960376C9E9", /*01*/
+            "601F8D7E319448A63E32860064912F344DD71552AC70613119396EB4BBCBAC9595E6F1806C127F83B07A7D449AFA8371", /*02*/
+            "EFACBDE18D6AB7294574D2F7688649D96D74AEA685F6D4343876BB9C257CEDB5A4E60F8E014296FB5E46C17AC679E7A8", /*03*/
+            "DA238BFF842BDA78D30F1FA2A12DFA7D6AC5F1B028BB41FB1FFA85D36321FE08A60064CD28CBB0F573EA616BFE08C272", /*04*/
+            "92393181E1413D83DB9B83522083FC6AEECEE278AACF53E1F24F7A39360575141F711E762CD1BB439638F711ECD91C15", /*05*/
+            "0B99271A6F344052FF380136F528BCF3602AAB733D5986B257EC9165AA6150A1792554B19E7ED911BEB6486DC09D85A2", /*06*/
+            "4CA31A25692CFC7C718A9DD1E05DF8BEAAE538669FE7110F7263EFC0E3CE108ECDA0C3D9854642AAC756DFC4C6D4228E", /*07*/
+            "9BF938C833E258443EDEFD510A622DEDED721C0145D5C9D0F98815740A239DD8505BFF7B2D0E6A1B0A4DF2C5F67B4128", /*08*/
+            "873744979EFA032F78D066973013FD442F9197E7D412B9AAEFE5CBB174BE63C0CC02A6E030E61475EB894AE12D165751", /*09*/
+            "567F3254FD4F58C966F5119927F12F0B593E5ACF900288F1C59DCCC0E477C34A00F4BF6B71F2F908FD1AF29C27888797", /*0A*/
+            "0E732BB38DC5359DA7C97B63D52C5EE12073337DE74E16954840D6E3630EC35BD2FFEAD65DFD115C1B581EEF1B32DD2A", /*0B*/
+            "8E7EC736DFF6C79C55AE1299055D9D61D3262E84EECC563347296B89BF812FDCC630D262CEAC4CBE211F579EB6EC1CB2", /*0C*/
+            "34DE19511BE9A284D8B9B4C76DD6934FC0B5EBC20D6EB7E39B5413B3398D349F806B13367A11F4B5D4C3D0CE6F8EA3A7", /*0D*/
+            "0FF2A2F0AC6F43AF48CE8A380B1AB134CE6A3176CEC813B2AE6C3B7AB6DAEFD1BE99E7F3C9E61A1B3E631D077C273513", /*0E*/
+            "0919F02A721C4ADA9879C3BE6CEC98FB6EA0D453C20DB4BD9B287ED6252B9825CF66A065A209AFC8D5A22ADF2E457C6E"  /*0F*/
     ));
 
     // =========================================================================