Skip to content

Instantly share code, notes, and snippets.

@tyano
Created February 11, 2014 18:20
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tyano/8940795 to your computer and use it in GitHub Desktop.
Save tyano/8940795 to your computer and use it in GitHub Desktop.
MD5ハッシュ突き合わせ(Clojure) - 単体でパフォーマンスチューニングしたものを、core.asyncを使って並列化。引数の末尾に :map をつけると、map/filterを使ったバージョンでもテスト可能。primitiveのloopのほうがちょっとだけ速い。並列化はcore数程度で頭打ち。100とかにしても遅くなるだけ。
(ns mdsample.core
(:require [clojure.core.reducers :as r]
[clojure.core.async :refer [chan >! <!! go close! alts!!]])
(:import [java.security MessageDigest]
[javax.xml.bind DatatypeConverter]
[java.util Arrays]
[java.util Queue]
[java.util.concurrent ConcurrentLinkedQueue]))
(defn- hexdigest
[^MessageDigest digester ^String s]
(let [d (doto digester
(.reset)
(.update (.getBytes s)))]
(.digest d)))
(defn- left-pad [^long num]
(let [pad-count (- 6 (count (str num)))]
(str (apply str (repeat pad-count \0)) num)))
(def ^Queue digester-queue (ConcurrentLinkedQueue.))
(defmacro digester-> [& body]
"キューからMessegeDigestを取り出して、それをbodyにスレッディングするマクロです。
キューが空の場合は新しいMessegaDigestを生成します。
使い終わったMessageDigestは、処理終了後にキューに戻されます。
並行処理時にキューを共有するための仕組みです。"
`(let [d# (or (.poll digester-queue)
(MessageDigest/getInstance "MD5"))]
(try
(-> d# ~@body)
(finally
(.add digester-queue d#)))))
(defn- find-password-with-map [salt pw ^long start ^long step]
"map, filterで検索する"
(->> (range start 1000000 step)
(map left-pad)
(map (fn [six-num] [six-num (digester-> (hexdigest (str salt "$" six-num)))]))
(filter #(Arrays/equals ^bytes pw ^bytes (second %)))
first))
(defn- find-password-with-loop [salt pw ^long start ^long step]
"primitiveのloopで検索する"
(loop [i start ret nil]
(if (or (> i (long 1000000)) ret)
ret
(let [six-num (left-pad i)
hex (digester-> (hexdigest (str salt "$" six-num)))]
(if (Arrays/equals ^bytes pw ^bytes hex)
(recur (+ i step) [six-num (DatatypeConverter/printHexBinary hex)])
(recur (+ i step) ret))))))
(defn find-password
([salt pw parallel-count type]
(.clear digester-queue)
(let [pw (DatatypeConverter/parseHexBinary pw)
ch (chan)
find-fn (case type :map find-password-with-map find-password-with-loop)]
(dotimes [i parallel-count]
(go (when-let [data (find-fn salt pw (long i) (long parallel-count))]
(>! ch data))))
(let [result (<!! ch)]
(close! ch)
result)))
([salt pw parallel-count]
(find-password salt pw parallel-count :in)))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment