-
Notifications
You must be signed in to change notification settings - Fork 5
/
fingerprint.cljc
73 lines (60 loc) · 2.05 KB
/
fingerprint.cljc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
(ns deercreeklabs.lancaster.fingerprint
#?(:cljs
(:refer-clojure :exclude [bit-and bit-xor unsigned-bit-shift-right]))
(:require
[deercreeklabs.baracus :as ba]
[deercreeklabs.lancaster.utils :as u]
[deercreeklabs.log-utils :as lu :refer [debugs]]
#?(:cljs [goog.math :as gm])
[schema.core :as s :include-macros true]
[taoensso.timbre :as timbre :refer [debugf errorf infof]]))
#?(:cljs (def Long gm/Long))
(s/defn make-long :- Long
[x :- s/Any]
(when-not (nil? x)
#?(:clj (clojure.core/long x)
:cljs (if (u/long? x)
x
(Long.fromNumber x)))))
;; Based on
;; http://avro.apache.org/docs/current/spec.html#Schema+Fingerprints
(def seed (u/str->long "-4513414715797952619"))
(def long-one (make-long 1))
#?(:cljs (def class type))
#?(:cljs (defn bit-and [l other]
(.and l other)))
#?(:cljs (defn unsigned-bit-shift-right [l num-bits]
(.shiftRightUnsigned l num-bits)))
#?(:cljs (defn bit-xor [l other]
(.xor l other)))
(defn negate [l]
#?(:clj (unchecked-negate l)
:cljs (.negate l)))
(defn calc-fp [i]
(let [f (fn [fp j]
(let [fp (make-long fp)
mask (make-long (negate (bit-and fp long-one)))]
(bit-xor (unsigned-bit-shift-right fp 1)
(bit-and seed mask))))]
(reduce f (make-long i) (range 8))))
(def fingerprint-table
(let [f (fn [fp-table i]
(conj fp-table (calc-fp i)))]
(reduce f [] (range 256))))
(s/defn fingerprint64 :- Long
[s :- s/Str]
(let [ba (ba/utf8->byte-array s)
f (fn [acc b]
(let [b (make-long b)
acc (make-long acc)]
(bit-xor (unsigned-bit-shift-right acc 8)
(fingerprint-table (int (bit-and (bit-xor acc b)
(make-long 255)))))))
num-bytes (count ba)]
(loop [acc seed
i 0]
(if (= num-bytes i)
acc
(let [b (aget #^bytes ba i)]
(recur (f acc b)
(inc i)))))))