Last active
January 2, 2016 07:39
-
-
Save spiechu/8271212 to your computer and use it in GitHub Desktop.
My first sloppy steps into Scala.
Check corresponding blog post at http://spiechu.pl/2014/01/05/computing-file-hashes-using-scala
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Typical Java package declaration. | |
package pl.spiechu.hashutils | |
// Typical class imports. More than one in curly brackets. | |
// Java classes work just fine in Scala. | |
import java.io.{ File, FileInputStream } | |
import java.security.MessageDigest | |
/** | |
* Singleton object. You cannot create an instance using 'new'. | |
* Construct built-in into language. | |
* | |
* @author Dawid Spiechowicz ([email protected]) | |
*/ | |
object HashGenerator { | |
// We're inside constructor body. | |
/** | |
* Input stream buffer size. | |
* | |
* Final val is constant expression. | |
*/ | |
final val BUFFER_SIZE = 8192 | |
/** | |
* Hash algorithm type. | |
*/ | |
final val HASH_TYPE = "SHA-256" | |
// Val ensures me that no one will reassign something to that. | |
// Think about it as 'final' Java keyword. | |
// Notice no empty bracket after name in array type. | |
val buffer = new Array[Byte](BUFFER_SIZE) | |
// You can leave off most of dots. | |
val shaDigest = MessageDigest getInstance (HASH_TYPE) | |
/** | |
* You don't need to provide return type, | |
* but you should because of public API clarity. | |
*/ | |
def getSha1HashUsingFuncChain(file: File): String = { | |
// You can leave off parentheses in no arg functions. | |
shaDigest reset | |
val fis = new FileInputStream(file) | |
// Infinite looping until read() result equals -1, | |
// until that happens run foreach(). | |
Stream | |
.continually(fis read (buffer)) | |
.takeWhile(_ != -1) | |
.foreach(shaDigest update (buffer, 0, _)) | |
// Last expression in function is always returned. | |
getHashString | |
} | |
/** | |
* Works fine without return type provided. | |
*/ | |
def getSha1HashUsingMatcher(file: File) = { | |
shaDigest reset | |
val fis = new FileInputStream(file) | |
// Define inner function that can be called recurrently. | |
// Recursive method needs a return type. Think of 'Unit' as void. | |
def readStream(): Unit = { | |
// read() result intercepted by matcher. | |
fis.read(buffer) match { | |
case -1 => () | |
// Do digest update job and call recurrent. | |
// Notice that readStream call is the last, | |
// so compiler can perform tail recursion optimization. | |
case num => shaDigest update (buffer, 0, num); readStream | |
} | |
} | |
// Launch first iteration. | |
readStream | |
getHashString | |
} | |
/** | |
* Default 'def' refers to 'public' access, You can narrow it. | |
*/ | |
protected def getHashString() = { | |
val hexbuilder = new StringBuilder | |
// Iterate over byte array and cut off everything beside one byte. | |
// Eclipse complains about implicit conversion to 'byteArrayOps', | |
// stay tuned for the next blog post at spiechu.pl explaining this. | |
shaDigest.digest.foreach(bt => hexbuilder append (Integer toHexString (0xFF & bt))) | |
hexbuilder toString | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment