 
 ;;; JSON READER
 
-(defmacro ^{:private true} codepoint [c]
+(defmacro ^:private codepoint [c]
   (int c))
 
 (declare read-json-reader)
+
+(defn- codepoint-clause [[test result]]
+  (cond (list? test)
+          [(map int test) result]
+        (= test :whitespace)
+          ['(9 10 13 32) result]
+        (= test :simple-ascii)
+          [(remove #{(codepoint \") (codepoint \\) (codepoint \/)}
+                   (range 32 127))
+           result]
+        :else
+          [(int test) result]))
+
+(defmacro ^:private codepoint-case [e & clauses]
+   `(case ~e
+      ~@(mapcat codepoint-clause (partition 2 clauses))
+      ~@(when (odd? (count clauses))
+          [(last clauses)])))
 
 (defn- read-json-array [^PushbackReader stream keywordize?]
   ;; Expects to be called with the head of the stream AFTER the
   ;; opening bracket.
   (loop [c (.read stream), result (transient [])]
     (when (neg? c) (throw (EOFException. "JSON error (end-of-file inside array)")))
-    (cond
-     (Character/isWhitespace c) (recur (.read stream) result)
-     (= c (codepoint \,)) (recur (.read stream) result)
-     (= c (codepoint \])) (persistent! result)
-     :else (do (.unread stream c)
-               (let [element (read-json-reader stream keywordize? true nil)]
-                 (recur (.read stream) (conj! result element)))))))
+    (codepoint-case c
+      :whitespace (recur (.read stream) result)
+      \, (recur (.read stream) result)
+      \] (persistent! result)
+      (do (.unread stream c)
+          (let [element (read-json-reader stream keywordize? true nil)]
+            (recur (.read stream) (conj! result element)))))))
 
 (defn- read-json-object [^PushbackReader stream keywordize?]
   ;; Expects to be called with the head of the stream AFTER the
   ;; opening bracket.
   (loop [c (.read stream), key nil, result (transient {})]
     (when (neg? c) (throw (EOFException. "JSON error (end-of-file inside array)")))
-    (cond
-     (Character/isWhitespace c) (recur (.read stream) key result)
-
-     (= c (codepoint \,)) (recur (.read stream) nil result)
-
-     (= c (codepoint \:)) (recur (.read stream) key result)
-
-     (= c (codepoint \})) (if (nil? key)
-                            (persistent! result)
-                            (throw (Exception. "JSON error (key missing value in object)")))
-
-     :else (do (.unread stream c)
-               (let [element (read-json-reader stream keywordize? true nil)]
-                 (if (nil? key)
-                   (if (string? element)
-                     (recur (.read stream) element result)
-                     (throw (Exception. "JSON error (non-string key in object)")))
-                   (recur (.read stream) nil
-                          (assoc! result (if keywordize? (keyword key) key)
-                                  element))))))))
+    (codepoint-case c
+     :whitespace (recur (.read stream) key result)
+
+     \, (recur (.read stream) nil result)
+
+     \: (recur (.read stream) key result)
+
+     \} (if (nil? key)
+           (persistent! result)
+           (throw (Exception. "JSON error (key missing value in object)")))
+
+     (do (.unread stream c)
+         (let [element (read-json-reader stream keywordize? true nil)]
+           (if (nil? key)
+             (if (string? element)
+               (recur (.read stream) element result)
+               (throw (Exception. "JSON error (non-string key in object)")))
+             (recur (.read stream) nil
+                    (assoc! result (if keywordize? (keyword key) key)
+                            element))))))))
 
 (defn- read-json-hex-character [^PushbackReader stream]
   ;; Expects to be called with the head of the stream AFTER the

    (.append sb \")
    (dotimes [i (count s)]
      (let [cp (int (.charAt s i))]
        (cond
         ;; Handle printable JSON escapes before ASCII
         (= cp 34) (.append sb "\\\"")
         (= cp 92) (.append sb "\\\\")
         (= cp 47) (.append sb "\\/")
         ;; Print simple ASCII characters
         (< 31 cp 127) (.append sb (.charAt s i))
         ;; Handle non-printable JSON escapes
         (= cp 8) (.append sb "\\b")
         (= cp 12) (.append sb "\\f")
         (= cp 10) (.append sb "\\n")
         (= cp 13) (.append sb "\\r")
         (= cp 9) (.append sb "\\t")
	 ;; Any other character is Unicode
         :else (if escape-unicode?
		 ;; Hexadecimal-escaped
		 (.append sb (format "\\u%04x" cp))
		 (.appendCodePoint sb cp)))))
    (.append sb \")
    (.print out (str sb))))


	 (if eof-error?
	   (throw (EOFException. "JSON error (end-of-file)"))
	   eof-value)
	 (codepoint-case c
           :whitespace (recur (.read stream))

           ;; Read numbers with Clojure reader
           (\- \0 \1 \2 \3 \4 \5 \6 \7 \8 \9)
           (do (.unread stream c)
               (read stream true nil))

           ;; Read strings
           \" (read-json-quoted-string stream)

           ;; Read null as nil
           \n (let [ull [(.read stream)
                         (.read stream)
                         (.read stream)]]
                (if (= ull [(codepoint \u) (codepoint \l) (codepoint \l)])
                  nil
                  (throw (Exception. (str "JSON error (expected null): " c ull)))))

           ;; Read true
           \t (let [rue [(.read stream)
                         (.read stream)
                         (.read stream)]]
                (if (= rue [(codepoint \r) (codepoint \u) (codepoint \e)])
                  true
                  (throw (Exception. (str "JSON error (expected true): " c rue)))))

           ;; Read false
           \f (let [alse [(.read stream)
                          (.read stream)
                          (.read stream)
                          (.read stream)]]
                (if (= alse [(codepoint \a) (codepoint \l) (codepoint \s) (codepoint \e)])
                  false
                  (throw (Exception. (str "JSON error (expected false): " c alse)))))

           ;; Read JSON objects
           \{ (read-json-object stream keywordize?)

           ;; Read JSON arrays
           \[ (read-json-array stream keywordize?)

           (throw (Exception. (str "JSON error (unexpected character): " (char c)))))))))

(defprotocol Read-JSON-From
  (read-json-from [input keywordize? eof-error? eof-value]
