rlm@10
|
1 ;;; base64.clj: Experimental Base-64 encoding and (later) decoding
|
rlm@10
|
2
|
rlm@10
|
3 ;; by Stuart Sierra, http://stuartsierra.com/
|
rlm@10
|
4 ;; August 19, 2009
|
rlm@10
|
5
|
rlm@10
|
6 ;; Copyright (c) Stuart Sierra, 2009. All rights reserved. The use
|
rlm@10
|
7 ;; and distribution terms for this software are covered by the Eclipse
|
rlm@10
|
8 ;; Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
|
rlm@10
|
9 ;; which can be found in the file epl-v10.html at the root of this
|
rlm@10
|
10 ;; distribution. By using this software in any fashion, you are
|
rlm@10
|
11 ;; agreeing to be bound by the terms of this license. You must not
|
rlm@10
|
12 ;; remove this notice, or any other, from this software.
|
rlm@10
|
13
|
rlm@10
|
14
|
rlm@10
|
15 (ns ^{:doc "Base-64 encoding and (maybe later) decoding.
|
rlm@10
|
16
|
rlm@10
|
17 This is mainly here as an example. It is much slower than the
|
rlm@10
|
18 Apache Commons Codec implementation or sun.misc.BASE64Encoder."
|
rlm@10
|
19 :author "Stuart Sierra"}
|
rlm@10
|
20 clojure.contrib.base64
|
rlm@10
|
21 (:import (java.io InputStream Writer ByteArrayInputStream
|
rlm@10
|
22 StringWriter)))
|
rlm@10
|
23
|
rlm@10
|
24 (def *base64-alphabet*
|
rlm@10
|
25 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=")
|
rlm@10
|
26
|
rlm@10
|
27 (defn encode
|
rlm@10
|
28 "Encodes bytes of input, writing Base 64 text on output. alphabet
|
rlm@10
|
29 is a 65-character String containing the 64 characters to use in the
|
rlm@10
|
30 encoding; the 65th character is the pad character. line-length is
|
rlm@10
|
31 the maximum number of characters per line, nil for no line breaks."
|
rlm@10
|
32 [^InputStream input ^Writer output ^String alphabet line-length]
|
rlm@10
|
33 (let [buffer (make-array Byte/TYPE 3)]
|
rlm@10
|
34 (loop [line 0]
|
rlm@10
|
35 (let [len (.read input buffer)]
|
rlm@10
|
36 (when (pos? len)
|
rlm@10
|
37 ;; Pre-boxing the bytes as Integers is more efficient for
|
rlm@10
|
38 ;; Clojure's bit operations.
|
rlm@10
|
39 (let [b0 (Integer/valueOf (int (aget buffer 0)))
|
rlm@10
|
40 b1 (Integer/valueOf (int (aget buffer 1)))
|
rlm@10
|
41 b2 (Integer/valueOf (int (aget buffer 2)))]
|
rlm@10
|
42 (cond (= len 3)
|
rlm@10
|
43 (let [s0 (bit-and 0x3F (bit-shift-right b0 2))
|
rlm@10
|
44 s1 (bit-and 0x3F
|
rlm@10
|
45 (bit-or (bit-shift-left b0 4)
|
rlm@10
|
46 (bit-shift-right b1 4)))
|
rlm@10
|
47 s2 (bit-and 0x3F
|
rlm@10
|
48 (bit-or (bit-shift-left b1 2)
|
rlm@10
|
49 (bit-shift-right b2 6)))
|
rlm@10
|
50 s3 (bit-and 0x3F b2)]
|
rlm@10
|
51 (.append output (.charAt alphabet s0))
|
rlm@10
|
52 (.append output (.charAt alphabet s1))
|
rlm@10
|
53 (.append output (.charAt alphabet s2))
|
rlm@10
|
54 (.append output (.charAt alphabet s3)))
|
rlm@10
|
55
|
rlm@10
|
56 (= len 2)
|
rlm@10
|
57 (let [s0 (bit-and 0x3F (bit-shift-right b0 2))
|
rlm@10
|
58 s1 (bit-and 0x3F
|
rlm@10
|
59 (bit-or (bit-shift-left b0 4)
|
rlm@10
|
60 (bit-shift-right b1 4)))
|
rlm@10
|
61 s2 (bit-and 0x3F (bit-shift-left b1 2))]
|
rlm@10
|
62 (.append output (.charAt alphabet s0))
|
rlm@10
|
63 (.append output (.charAt alphabet s1))
|
rlm@10
|
64 (.append output (.charAt alphabet s2))
|
rlm@10
|
65 (.append output (.charAt alphabet 64)))
|
rlm@10
|
66
|
rlm@10
|
67 (= len 1)
|
rlm@10
|
68 (let [s0 (bit-and 0x3F (bit-shift-right b0 2))
|
rlm@10
|
69 s1 (bit-and 0x3F (bit-shift-left b0 4))]
|
rlm@10
|
70 (.append output (.charAt alphabet s0))
|
rlm@10
|
71 (.append output (.charAt alphabet s1))
|
rlm@10
|
72 (.append output (.charAt alphabet 64))
|
rlm@10
|
73 (.append output (.charAt alphabet 64)))))
|
rlm@10
|
74 (if (and line-length (> (+ line 4) line-length))
|
rlm@10
|
75 (do (.append output \newline)
|
rlm@10
|
76 (recur 0))
|
rlm@10
|
77 (recur (+ line 4))))))))
|
rlm@10
|
78
|
rlm@10
|
79 (defn encode-str
|
rlm@10
|
80 "Encodes String in base 64; returns a String. If not specified,
|
rlm@10
|
81 encoding is UTF-8 and line-length is nil."
|
rlm@10
|
82 ([s] (encode-str s "UTF-8" nil))
|
rlm@10
|
83 ([^String s ^String encoding line-length]
|
rlm@10
|
84 (let [output (StringWriter.)]
|
rlm@10
|
85 (encode (ByteArrayInputStream. (.getBytes s encoding))
|
rlm@10
|
86 output *base64-alphabet* line-length)
|
rlm@10
|
87 (.toString output))))
|
rlm@10
|
88
|
rlm@10
|
89
|
rlm@10
|
90 ;;; tests
|
rlm@10
|
91
|
rlm@10
|
92 ;; (deftest t-encode-str
|
rlm@10
|
93 ;; (is (= (encode-str "") ""))
|
rlm@10
|
94 ;; (is (= (encode-str "f") "Zg=="))
|
rlm@10
|
95 ;; (is (= (encode-str "fo") "Zm8="))
|
rlm@10
|
96 ;; (is (= (encode-str "foo") "Zm9v"))
|
rlm@10
|
97 ;; (is (= (encode-str "foob") "Zm9vYg=="))
|
rlm@10
|
98 ;; (is (= (encode-str "fooba") "Zm9vYmE="))
|
rlm@10
|
99 ;; (is (= (encode-str "foobar") "Zm9vYmFy")))
|