    :sha3-384 #(SHA3Digest. 284)
    :sha3-512 #(SHA3Digest. 512)})
 
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Protocol definitions (abstractions)
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defprotocol IDigest
+  (^:private digest* [input algorithm] "Low level interface, always returns bytes"))
+
+(defprotocol IHash
+  "Mac engine common interface definition."
+  (^:private update [_ bytes offset length] "Update bytes in a current instance.")
+  (^:private end [_] "Return the computed mac and reset the engine."))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Low level api
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defn update!
+  ([engine input]
+   (update engine input 0 (count input)))
+  ([engine input offset]
+   (update engine input offset (count input)))
+  ([engine input offset length]
+   (update engine input offset length)))
+
+(defn end!
+  [engine]
+  (end engine))
+
 (defn resolve-digest
   "Helper function for make Digest instances
   from algorithm parameter."

   (instance? IFn alg) (alg)
   (instance? Digest alg) alg))

(defprotocol IDigest
  (^:private digest* [input algorithm] "Low level interface, always returns bytes"))

(extend-protocol IDigest
  (Class/forName "[B")
  (digest* [^bytes input ^Keyword alg]
    (let [digest (resolve-digest alg)
          buffer (byte-array (.getDigestSize digest))]
      (.update digest input 0 (count input))
      (.doFinal digest buffer 0)
      buffer))

  String
  (digest* [^String input ^Keyword alg]
    (digest* (str->bytes input) alg))

  java.io.InputStream
  (digest* [^java.io.InputStream input ^Keyword alg]
    (let [digest  (resolve-digest alg)
          buffer1 (byte-array 5120)
          buffer2 (byte-array (.getDigestSize digest))]
      (loop []
        (let [readed (.read input buffer1 0 5120)]
          (when-not (= readed -1)
            (.update digest buffer1 0 readed)
            (recur))))
      (.doFinal digest buffer2 0)
      buffer2))

  java.io.File
  (digest* [^java.io.File input ^Keyword alg]
    (digest* (io/input-stream input) alg))

  java.net.URL
  (digest* [^java.net.URL input ^Keyword alg]
    (digest* (io/input-stream input) alg))

  java.net.URI
  (digest* [^java.net.URI input ^Keyword alg]
    (digest* (io/input-stream input) alg)))

(defn digest
  "Generic function for create cryptographic hash."

(def sha3-512 #(digest % :sha3-512))
(def sha1 #(digest % :sha1))
(def md5 #(digest % :md5))
