This is a highly optimized blake3 implementation for scala, scala-js, and scala-native, without any dependencies. This implementation has a constant memory footprint (about 5kb) which hasn't depended on the size of hashed data or the size of the output hash.
If you're looking for the faster possible hash function for scala.js I suggest to use this one, instead of SHA because this implementation use only 32 bits number which is natively supported by JS.
You can use it as
libraryDependencies += "pt.kcry" %%% "blake3" % "x.x.x"
API is pretty simple:
scala> import pt.kcry.blake3.Blake3
scala> Blake3.newHasher().update("Some string").doneHex(64)
val res1: String = 2e5524f3481046587080604ae4b4ceb44b721f3964ce0764627dee2c171de4c2
scala> Blake3.newDeriveKeyHasher("whats the Elvish word for friend").update("Some string").doneHex(64)
val res2: String = c2e79fe73dde16a13b4aa5a947b0e9cd7277ea8e68da250759de3ae62372b340
scala> Blake3.newKeyedHasher("whats the Elvish word for friend").update("Some string").doneHex(64)
val res3: String = 79943402309f9bb05338193f21fb57d98ab848bdcac67e5e097340f116ff90ba
scala> Blake3.hex("Some string", 64)
val res4: String = 2e5524f3481046587080604ae4b4ceb44b721f3964ce0764627dee2c171de4c2
scala> Blake3.bigInt("Some string", 32)
val res5: BigInt = 777331955
scala>
Hasher.update
is mutable when Hasher.done
isn't.
Hasher.update
supports different input such as: byte array, part of byte array, single byte or string,
and many others like OutputStream
or ByteBuffer
.
Hasher.done
supports different output such as:
done(out: Array[Byte])
to fill full provided array;done(out: Array[Byte], offset: Int, len: Int)
to fill specified part of provided array;done(out: OutputStream, len: Int)
to fill specifiedOutputStream
;done(out: ByteBuffer)
to fill specifiedByteBuffer
;done()
that returns a single byte hash value;doneShort()
,doneInt()
anddoneLong()
that returns a single short, int or long hash value;doneBigInt(bitLength: Int)
that returns positive BigInt with specified length in bits;doneHex(resultLength: Int)
that returns hex encoded string with specified output length in characters;doneBaseXXX(len: Int)
that returns string representative of XXX encoded as it defined in RFC 4648 without padding;doneXor(...)
that applied hash to existed value via XOR;doneCallBack(..)
anddoneXorCallBack(...)
which is used callback to for each produced byte.
This implementation is thread-safe and you can use it in a multithreaded environment. Anyway, this implementation doesn't currently include any multithreading optimizations.
As a baseline for benchmarks, I've used the original C version c-0.3.7 via the JNI interface that was implemented as part of BLAKE3jni.
All benchmarks were performed on two machines:
Zulu11.56+19-CA (build 11.0.15+10-LTS)
at Intel® Core™ i7-8700B with AVX2 assembly optimization inside the baseline,Zulu11.56+19-CA (build 11.0.15+10-LTS)
at Apple M1 without any assembly optimization inside the baseline.
Short summary:
- it is about 4 times slower than AVX2 assembly version via JNI which is expected,
- it is about 20% slower than the original C version via JNI,
- it has a constant memory footprint (yeah, no GC on hashing!),
- increasing result hash size has the same impact as hashing.
The full version of the results are available as:
- for Intel® Core™ i7-8700B at jmh-result.intel.json or via Intel @ JMH Visualizer.
- for Apple M1 at jmh-result.m1.json or via M1 @ JMH Visualizer.