Calculations.groovy 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. package com.garypaduana.groovytools.data
  2. import com.garypaduana.groovytools.formatting.StringCleaner
  3. import java.security.MessageDigest
  4. class Calculations {
  5. static def calculateParity(String hex, int parityBitLength){
  6. hex = StringCleaner.stripNonHexCharacters(hex)
  7. if(parityBitLength > 64){
  8. throw new IllegalArgumentException("parityBitLength must be less than or equal to 64")
  9. }
  10. if((hex.length() * 4) % parityBitLength != 0) {
  11. throw new IllegalArgumentException("invalid word length found")
  12. }
  13. def pieces = []
  14. StringBuilder sb = new StringBuilder(hex)
  15. while(sb.length() >= (parityBitLength / 4)){
  16. def end = (int) (parityBitLength / 4)
  17. pieces.add(Long.decode("0x" + sb.substring(0, end)))
  18. sb.delete(0, end)
  19. }
  20. long parity = 0
  21. for(long piece : pieces){
  22. parity = parity ^ piece
  23. }
  24. return Long.toHexString(parity).toLowerCase()
  25. }
  26. /**
  27. * A generic method to generate a hex string representation of a message digest.
  28. *
  29. * @param file - the file to be analyzed.
  30. * @param digest - the digest to generate, e.g. "MD5", "SHA-1"
  31. * @param maxLength - the number of bytes to read from the file. if no bytes are desired,
  32. * you must supply -1 or the first buffered chunk will be read.
  33. * @param paddedLength - the total length of the string representation of the message digest.
  34. * e.g. MD5 = 32, SHA-1 = 40
  35. * @return
  36. */
  37. static String generateDigest(File file, String digest, long maxLength, int paddedLength) {
  38. MessageDigest md = MessageDigest.getInstance(digest)
  39. md.reset()
  40. if (file.canRead()) {
  41. file.withInputStream() { is ->
  42. byte[] buffer = new byte[8192]
  43. int read = 0
  44. int totalRead = 0
  45. while ((read = is.read(buffer)) > 0 && totalRead <= maxLength) {
  46. totalRead += read
  47. md.update(buffer, 0, read)
  48. }
  49. }
  50. }
  51. byte[] digestBytes = md.digest()
  52. BigInteger bigInt = new BigInteger(1, digestBytes)
  53. return bigInt.toString(16).padLeft(paddedLength, '0')
  54. }
  55. /**
  56. * Generates an MD5 signature using all bytes in the file.
  57. * @param file
  58. * @return
  59. */
  60. static String generateMD5(File file) {
  61. return generateDigest(file, "MD5", Long.MAX_VALUE, 32)
  62. }
  63. /**
  64. * Generates an MD5 signature using up to 1MB (2^20) bytes from the
  65. * beginning of the file. Useful to quickly determine if two large files
  66. * are different without having to read every byte in each. If there is
  67. * equality after this method is evaluated for each file then a full
  68. * processing should occur for each.
  69. * @param file
  70. * @return
  71. */
  72. static String generateShortMD5(File file) {
  73. return generateDigest(file, "MD5", 1048576, 32)
  74. }
  75. }