Skip to content

Commit a230d0d

Browse files
committed
[#12] Multi-segment namespace
1 parent 41842eb commit a230d0d

File tree

5 files changed

+169
-9
lines changed

5 files changed

+169
-9
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ lib
66
pom.xml
77
pom.xml.asc
88
target
9+
/.cpcache

README.md

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
1-
# digest
1+
# clj-commons/digest
22

33
[![Clojars Project](https://img.shields.io/clojars/v/org.clj-commons/digest.svg)](https://clojars.org/org.clj-commons/digest)
44
[![cljdoc badge](https://cljdoc.org/badge/org.clj-commons/digest)](https://cljdoc.org/d/org.clj-commons/digest)
55
[![CircleCI Status](https://circleci.com/gh/clj-commons/clj-digest.svg?style=svg)](https://circleci.com/gh/clj-commons/clj-digest)
66

7-
`digest` - Message digest library for Clojure. Providing md5, sha-256, ...
7+
`clj-commons/digest` - A message digest library for Clojure. Providing md5, sha-256, ...
88

99
There are several digest functions (such as `md5`, `sha-256` ...) in this
1010
namespace. Each can handle the following input types:
1111

12-
* java.lang.String
13-
* byte array
14-
* java.io.File
15-
* java.io.InputStream
12+
* `java.lang.String`
13+
* `byte array`
14+
* `java.io.File`
15+
* `java.io.InputStream`
1616
* Sequence of byte array
1717

1818
# Usage
1919

20-
user=> (require 'digest)
20+
user=> (require '[clj-commons.digest :as digest])
2121
nil
2222
; On a string
2323
user=> (digest/md5 "clojure")
@@ -28,8 +28,13 @@ namespace. Each can handle the following input types:
2828
user=> (digest/sha-256 (as-file "/tmp/hello.txt"))
2929
"163883d3e0e3b0c028d35b626b98564be8d9d649ed8adb8b929cb8c94c735c59"
3030

31+
# Deprecation
32+
33+
The single-segment `digest` namespace is deprecated since `1.4.10+`. Use
34+
`clj-commons.digest` instead.
35+
3136
# Installation
32-
Add `[digest "1.4.10"]` to your `project.clj`.
37+
Add `[org.clojars/digest "1.4.10"]` to your `project.clj`.
3338

3439
# License
3540
Copyright&copy; 2017 Miki Tebeka <[email protected]>

src/clj_commons/digest.clj

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
(ns
2+
^{:author "Miki Tebeka <[email protected]>"
3+
:doc "Message digest algorithms for Clojure"}
4+
clj-commons.digest
5+
(:require [clojure.string :refer [join lower-case split]])
6+
(:import (java.io File FileInputStream InputStream)
7+
(java.security MessageDigest Provider Security)
8+
(java.util Arrays)))
9+
10+
; Default buffer size for reading
11+
(def ^:dynamic *buffer-size* 1024)
12+
13+
(defn- read-some
14+
"Read some data from reader. Return [data size] if there's more to read,
15+
otherwise nil."
16+
[^InputStream reader]
17+
(let [^bytes buffer (make-array Byte/TYPE *buffer-size*)
18+
size (.read reader buffer)]
19+
(when (pos? size)
20+
(if (= size *buffer-size*) buffer (Arrays/copyOf buffer size)))))
21+
22+
(defn- byte-seq
23+
"Return a sequence of [data size] from reader."
24+
[^InputStream reader]
25+
(take-while some? (repeatedly (partial read-some reader))))
26+
27+
(defn- signature
28+
"Get signature (string) of digest."
29+
[^MessageDigest algorithm]
30+
(let [size (* 2 (.getDigestLength algorithm))
31+
sig (.toString (BigInteger. 1 (.digest algorithm)) 16)
32+
padding (join (repeat (- size (count sig)) "0"))]
33+
(str padding sig)))
34+
35+
(defprotocol Digestible
36+
(-digest [message algorithm]))
37+
38+
(extend-protocol Digestible
39+
(class (make-array Byte/TYPE 0))
40+
(-digest [message algorithm]
41+
(-digest [message] algorithm))
42+
43+
java.util.Collection
44+
;; Code "borrowed" from
45+
;; * http://www.holygoat.co.uk/blog/entry/2009-03-26-1
46+
;; * http://www.rgagnon.com/javadetails/java-0416.html
47+
(-digest [message algorithm]
48+
(let [^MessageDigest algo (MessageDigest/getInstance algorithm)]
49+
(.reset algo)
50+
(doseq [^bytes b message] (.update algo b))
51+
(signature algo)))
52+
53+
String
54+
(-digest [message algorithm]
55+
(-digest [(.getBytes message)] algorithm))
56+
57+
InputStream
58+
(-digest [reader algorithm]
59+
(-digest (byte-seq reader) algorithm))
60+
61+
File
62+
(-digest [file algorithm]
63+
(with-open [f (FileInputStream. file)]
64+
(-digest f algorithm)))
65+
66+
nil
67+
(-digest [message algorithm]
68+
nil))
69+
70+
(defn digest
71+
"Returns digest for message with given algorithm."
72+
[algorithm message]
73+
(-digest message algorithm))
74+
75+
(defn algorithms
76+
"List supported digest algorithms."
77+
[]
78+
(let [providers (vec (Security/getProviders))
79+
names (mapcat (fn [^Provider p] (enumeration-seq (.keys p))) providers)
80+
digest-names (filter #(re-find #"MessageDigest\.[A-Z0-9-]+$" %) names)]
81+
(set (map #(last (split % #"\.")) digest-names))))
82+
83+
(defn- create-fn!
84+
[algorithm-name]
85+
(let [update-meta (fn [meta]
86+
(assoc meta
87+
:doc (str "Encode the given message with the " algorithm-name " algorithm.")
88+
:arglists '([message])))]
89+
(-> (intern *ns*
90+
(symbol (lower-case algorithm-name))
91+
(partial digest algorithm-name))
92+
(alter-meta! update-meta))))
93+
94+
(defn- create-fns
95+
"Create utility function for each digest algorithms.
96+
For example will create an md5 function for MD5 algorithm."
97+
[]
98+
(doseq [algorithm (algorithms)]
99+
(create-fn! algorithm)))
100+
101+
; Create utility functions such as md5, sha-256 ...
102+
(create-fns)
103+
104+
;;;; Hints for clj-kondo
105+
106+
(comment
107+
(declare sha3-384 sha-256 sha3-256 sha-384 sha3-512 sha-1 sha-224 sha1 sha-512 md2 sha sha3-224 md5)
108+
)

src/digest.clj

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
(ns
22
^{:author "Miki Tebeka <[email protected]>"
3-
:doc "Message digest algorithms for Clojure"}
3+
:doc "Message digest algorithms for Clojure"
4+
;; single segment namespace is deprecated, use clj-commons/digest
5+
:deprecated true}
46
digest
57
(:require [clojure.string :refer [join lower-case split]])
68
(:import (java.io File FileInputStream InputStream)

test/clj_commons/digest_test.clj

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
(ns clj-commons.digest-test
2+
(:require [clj-commons.digest :as d]
3+
[clojure.string :refer [lower-case includes?]]
4+
[clojure.test :refer [deftest is]])
5+
(:import java.io.File))
6+
7+
(deftest md5-test
8+
(is (= (d/digest "md5" "clojure") "32c0d97f82a20e67c6d184620f6bd322")))
9+
10+
(deftest sha-256-test
11+
(is (= (d/sha-256 "clojure")
12+
"4f3ea34e0a3a6196a18ec24b51c02b41d5f15bd04b4a94aa29e4f6badba0f5b0")))
13+
14+
(deftest algorithms-test
15+
(let [names (d/algorithms)]
16+
(is (seq names))
17+
(is (names "SHA-1"))))
18+
19+
(deftest utils-test
20+
(for [name (d/algorithms)]
21+
(dorun (is (ns-resolve *ns* (symbol (lower-case name)))))))
22+
23+
(deftest function-metadata-test
24+
(is (includes? (:doc (meta #'d/sha-256))
25+
"SHA-256"))
26+
(is (= '([message])
27+
(:arglists (meta #'d/md5)))))
28+
29+
(def ^:dynamic *image-md5* "49c39580caf91363e4a4cacfa5564489")
30+
(def ^:dynamic *image-sha1*
31+
"96f2328cf279b95ddb1dee36df0c91cd7821e741")
32+
33+
(deftest file-test
34+
(let [f (File. "test/snail.png")]
35+
(is (= (d/md5 f) *image-md5*))
36+
(is (= (d/sha-1 f) *image-sha1*))))
37+
38+
; Just making sure that we don't explode on nil
39+
(deftest nil-test
40+
(d/md5 nil))
41+
42+
(deftest length-test
43+
(is (= (d/sha (File. "test/quote.txt"))
44+
"dc93ad3c1e212bf598b9bf700914e832c9bdade5")))

0 commit comments

Comments
 (0)