Introduce BytesUtils
[lgpl/argeo-commons.git] / org.argeo.util / src / org / argeo / util / DigestUtils.java
index 52cbc5d21a3b801885a88555c1ad9d88463938b2..e1a19160bd5a04afe23f834b6556e41bf53d91dd 100644 (file)
@@ -1,47 +1,58 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
 package org.argeo.util;
 
+import static org.argeo.util.BytesUtils.toHexString;
+
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.nio.ByteBuffer;
 import java.nio.channels.FileChannel;
+import java.nio.channels.FileChannel.MapMode;
+import java.nio.file.Files;
+import java.nio.file.Path;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 
 /** Utilities around cryptographic digests */
 public class DigestUtils {
-       private static Boolean debug = true;
-       // TODO: make it writable
+       public final static String MD5 = "MD5";
+       public final static String SHA1 = "SHA1";
+       public final static String SHA256 = "SHA-256";
+       public final static String SHA512 = "SHA-512";
+
+       private static Boolean debug = false;
+       // TODO: make it configurable
        private final static Integer byteBufferCapacity = 100 * 1024;// 100 KB
 
-       public static String digest(String algorithm, byte[] bytes) {
+       public static byte[] sha1(byte[]... bytes) {
+               try {
+                       MessageDigest digest = MessageDigest.getInstance(SHA1);
+                       for (byte[] arr : bytes)
+                               digest.update(arr);
+                       byte[] checksum = digest.digest();
+                       return checksum;
+               } catch (NoSuchAlgorithmException e) {
+                       throw new UnsupportedOperationException("SHA1 is not avalaible", e);
+               }
+       }
+
+       public static byte[] digestAsBytes(String algorithm, byte[]... bytes) {
                try {
                        MessageDigest digest = MessageDigest.getInstance(algorithm);
-                       digest.update(bytes);
+                       for (byte[] arr : bytes)
+                               digest.update(arr);
                        byte[] checksum = digest.digest();
-                       String res = encodeHexString(checksum);
-                       return res;
-               } catch (Exception e) {
-                       throw new UtilsException("Cannot digest with algorithm " + algorithm, e);
+                       return checksum;
+               } catch (NoSuchAlgorithmException e) {
+                       throw new UnsupportedOperationException("Cannot digest with algorithm " + algorithm, e);
                }
        }
 
+       public static String digest(String algorithm, byte[]... bytes) {
+               return toHexString(digestAsBytes(algorithm, bytes));
+       }
+
        public static String digest(String algorithm, InputStream in) {
                try {
                        MessageDigest digest = MessageDigest.getInstance(algorithm);
@@ -56,10 +67,12 @@ public class DigestUtils {
                        }
 
                        byte[] checksum = digest.digest();
-                       String res = encodeHexString(checksum);
+                       String res = toHexString(checksum);
                        return res;
-               } catch (Exception e) {
-                       throw new UtilsException("Cannot digest with algorithm " + algorithm, e);
+               } catch (NoSuchAlgorithmException e) {
+                       throw new IllegalArgumentException("Cannot digest with algorithm " + algorithm, e);
+               } catch (IOException e) {
+                       throw new RuntimeException(e);
                } finally {
                        StreamUtils.closeQuietly(in);
                }
@@ -77,7 +90,7 @@ public class DigestUtils {
                        ByteBuffer bb = fc.map(FileChannel.MapMode.READ_ONLY, 0, sz);
                        return digest(algorithm, bb);
                } catch (IOException e) {
-                       throw new UtilsException("Cannot digest " + file + " with algorithm " + algorithm, e);
+                       throw new IllegalArgumentException("Cannot digest " + file + " with algorithm " + algorithm, e);
                } finally {
                        StreamUtils.closeQuietly(fis);
                        if (fc.isOpen())
@@ -95,13 +108,57 @@ public class DigestUtils {
                        MessageDigest digest = MessageDigest.getInstance(algorithm);
                        digest.update(bb);
                        byte[] checksum = digest.digest();
-                       String res = encodeHexString(checksum);
+                       String res = toHexString(checksum);
                        long end = System.currentTimeMillis();
                        if (debug)
                                System.out.println((end - begin) + " ms / " + ((end - begin) / 1000) + " s");
                        return res;
                } catch (NoSuchAlgorithmException e) {
-                       throw new UtilsException("Cannot digest with algorithm " + algorithm, e);
+                       throw new IllegalArgumentException("Cannot digest with algorithm " + algorithm, e);
+               }
+       }
+
+       public static String sha1hex(Path path) {
+               return digest(SHA1, path, byteBufferCapacity);
+       }
+
+       public static String digest(String algorithm, Path path, long bufferSize) {
+               byte[] digest = digestAsBytes(algorithm, path, bufferSize);
+               return toHexString(digest);
+       }
+
+       public static byte[] digestAsBytes(String algorithm, Path file, long bufferSize) {
+               long begin = System.currentTimeMillis();
+               try {
+                       MessageDigest md = MessageDigest.getInstance(algorithm);
+                       FileChannel fc = FileChannel.open(file);
+                       long fileSize = Files.size(file);
+                       if (fileSize <= bufferSize) {
+                               ByteBuffer bb = fc.map(MapMode.READ_ONLY, 0, fileSize);
+                               md.update(bb);
+                       } else {
+                               long lastCycle = (fileSize / bufferSize) - 1;
+                               long position = 0;
+                               for (int i = 0; i <= lastCycle; i++) {
+                                       ByteBuffer bb;
+                                       if (i != lastCycle) {
+                                               bb = fc.map(MapMode.READ_ONLY, position, bufferSize);
+                                               position = position + bufferSize;
+                                       } else {
+                                               bb = fc.map(MapMode.READ_ONLY, position, fileSize - position);
+                                               position = fileSize;
+                                       }
+                                       md.update(bb);
+                               }
+                       }
+                       long end = System.currentTimeMillis();
+                       if (debug)
+                               System.out.println((end - begin) + " ms / " + ((end - begin) / 1000) + " s");
+                       return md.digest();
+               } catch (NoSuchAlgorithmException e) {
+                       throw new IllegalArgumentException("Cannot digest " + file + "  with algorithm " + algorithm, e);
+               } catch (IOException e) {
+                       throw new RuntimeException("Cannot digest " + file + "  with algorithm " + algorithm, e);
                }
        }
 
@@ -123,6 +180,7 @@ public class DigestUtils {
                        System.out.println(algorithm + ": " + digest(algorithm, file));
                        algorithm = "SHA";
                        System.out.println(algorithm + ": " + digest(algorithm, file));
+                       System.out.println(algorithm + ": " + sha1hex(file.toPath()));
                        algorithm = "SHA-256";
                        System.out.println(algorithm + ": " + digest(algorithm, file));
                        algorithm = "SHA-512";
@@ -130,21 +188,10 @@ public class DigestUtils {
                }
        }
 
-       final private static char[] hexArray = "0123456789ABCDEF".toCharArray();
-
-       /**
-        * From
-        * http://stackoverflow.com/questions/9655181/how-to-convert-a-byte-array-to
-        * -a-hex-string-in-java
-        */
-       private static String encodeHexString(byte[] bytes) {
-               char[] hexChars = new char[bytes.length * 2];
-               for (int j = 0; j < bytes.length; j++) {
-                       int v = bytes[j] & 0xFF;
-                       hexChars[j * 2] = hexArray[v >>> 4];
-                       hexChars[j * 2 + 1] = hexArray[v & 0x0F];
-               }
-               return new String(hexChars);
+       /** @deprecated Use {@link BytesUtils#toHexString(byte[])} instead */
+       @Deprecated
+       public static String encodeHexString(byte[] bytes) {
+               return BytesUtils.toHexString(bytes);
        }
 
 }