|
15 | 15 | (:import (java.io PrintWriter PushbackReader StringWriter |
16 | 16 | Writer StringReader EOFException))) |
17 | 17 |
|
| 18 | +;; CUSTOM PUSHBACK READER |
| 19 | + |
| 20 | +(set! *warn-on-reflection* true) |
| 21 | + |
| 22 | +(definterface InternalPBR |
| 23 | + (^int readChar []) |
| 24 | + (^long readChars [^chars buffer ^long start ^long bufflen]) |
| 25 | + (^void unreadChar [^int c]) |
| 26 | + (^void unreadChars [^chars buffer ^int off ^int bufflen]) |
| 27 | + (^java.io.Reader toReader [])) |
| 28 | + |
| 29 | +(deftype ReaderPBR [^PushbackReader rdr] |
| 30 | + InternalPBR |
| 31 | + (readChar [_] |
| 32 | + (.read rdr)) |
| 33 | + (readChars [_ buffer start bufflen] |
| 34 | + (.read rdr ^chars buffer start bufflen)) |
| 35 | + (unreadChar [_ c] |
| 36 | + (.unread rdr c)) |
| 37 | + (unreadChars [_ buffer start bufflen] |
| 38 | + (.unread rdr buffer start bufflen)) |
| 39 | + (toReader [_] |
| 40 | + rdr)) |
| 41 | + |
| 42 | +(comment |
| 43 | + (compile 'clojure.data.json) |
| 44 | + ) |
| 45 | + |
| 46 | +(deftype StringPBR [^String s ^:unsynchronized-mutable ^long pos ^long len] |
| 47 | + InternalPBR |
| 48 | + (readChar [_] |
| 49 | + (if (< pos len) |
| 50 | + (let [p pos] |
| 51 | + (set! pos (unchecked-inc pos)) |
| 52 | + (let [c (int (.charAt s p))] |
| 53 | + c)) |
| 54 | + (let [i (int -1)] |
| 55 | + i))) |
| 56 | + (readChars [_ buffer start bufflen] |
| 57 | + (let [remaining (- len pos) |
| 58 | + n (Math/min remaining bufflen)] |
| 59 | + (when (pos? n) |
| 60 | + (let [p pos |
| 61 | + end (+ p n)] |
| 62 | + (set! pos end) |
| 63 | + (.getChars ^String s p end ^chars buffer start))) |
| 64 | + (if (pos? n) n -1))) |
| 65 | + (unreadChar [_ _c] |
| 66 | + (set! pos (unchecked-dec pos)) |
| 67 | + nil) |
| 68 | + (unreadChars [_ buffer start bufflen] |
| 69 | + (set! pos (unchecked-subtract pos bufflen)) |
| 70 | + nil) |
| 71 | + (toReader [_] |
| 72 | + (StringReader. (.subSequence s pos len)))) |
| 73 | + |
| 74 | +(defn- pushback-pbr |
| 75 | + [^PushbackReader r] |
| 76 | + (->ReaderPBR r)) |
| 77 | + |
| 78 | +(defn- string-pbr |
| 79 | + [^String s] |
| 80 | + (->StringPBR s 0 (.length s))) |
| 81 | + |
18 | 82 | ;;; JSON READER |
19 | 83 |
|
20 | 84 | (set! *warn-on-reflection* true) |
|
50 | 114 | ~@(when (odd? (count clauses)) |
51 | 115 | [(last clauses)]))) |
52 | 116 |
|
53 | | -(defn- read-hex-char [^PushbackReader stream] |
| 117 | +(defn- read-hex-char [^InternalPBR stream] |
54 | 118 | ;; Expects to be called with the head of the stream AFTER the |
55 | 119 | ;; initial "\u". Reads the next four characters from the stream. |
56 | | - (let [a (.read stream) |
57 | | - b (.read stream) |
58 | | - c (.read stream) |
59 | | - d (.read stream)] |
| 120 | + (let [a (.readChar stream) |
| 121 | + b (.readChar stream) |
| 122 | + c (.readChar stream) |
| 123 | + d (.readChar stream)] |
60 | 124 | (when (or (neg? a) (neg? b) (neg? c) (neg? d)) |
61 | 125 | (throw (EOFException. |
62 | 126 | "JSON error (end-of-file inside Unicode character escape)"))) |
63 | 127 | (let [s (str (char a) (char b) (char c) (char d))] |
64 | 128 | (char (Integer/parseInt s 16))))) |
65 | 129 |
|
66 | | -(defn- read-escaped-char [^PushbackReader stream] |
| 130 | +(defn- read-escaped-char [^InternalPBR stream] |
67 | 131 | ;; Expects to be called with the head of the stream AFTER the |
68 | 132 | ;; initial backslash. |
69 | | - (let [c (.read stream)] |
| 133 | + (let [c (.readChar stream)] |
70 | 134 | (when (neg? c) |
71 | 135 | (throw (EOFException. "JSON error (end-of-file inside escaped char)"))) |
72 | 136 | (codepoint-case c |
|
78 | 142 | \t \tab |
79 | 143 | \u (read-hex-char stream)))) |
80 | 144 |
|
81 | | -(defn- slow-read-string [^PushbackReader stream ^String already-read] |
| 145 | +(defn- slow-read-string [^InternalPBR stream ^String already-read] |
82 | 146 | (let [buffer (StringBuilder. already-read)] |
83 | 147 | (loop [] |
84 | | - (let [c (.read stream)] |
| 148 | + (let [c (.readChar stream)] |
85 | 149 | (when (neg? c) |
86 | 150 | (throw (EOFException. "JSON error (end-of-file inside string)"))) |
87 | 151 | (codepoint-case c |
|
91 | 155 | (do (.append buffer (char c)) |
92 | 156 | (recur))))))) |
93 | 157 |
|
94 | | -(defn- read-quoted-string [^PushbackReader stream] |
| 158 | +(defn- read-quoted-string [^InternalPBR stream] |
95 | 159 | ;; Expects to be called with the head of the stream AFTER the |
96 | 160 | ;; opening quotation mark. |
97 | 161 | (let [buffer ^chars (char-array 64) |
98 | | - read (.read stream buffer 0 64) |
| 162 | + read (.readChars stream buffer 0 64) |
99 | 163 | end-index (unchecked-dec-int read)] |
100 | 164 | (when (neg? read) |
101 | 165 | (throw (EOFException. "JSON error (end-of-file inside string)"))) |
|
104 | 168 | (codepoint-case c |
105 | 169 | \" (let [off (unchecked-inc-int i) |
106 | 170 | len (unchecked-subtract-int read off)] |
107 | | - (.unread stream buffer off len) |
| 171 | + (.unreadChars stream buffer off len) |
108 | 172 | (String. buffer 0 i)) |
109 | 173 | \\ (let [off i |
110 | 174 | len (unchecked-subtract-int read off)] |
111 | | - (.unread stream buffer off len) |
| 175 | + (.unreadChars stream buffer off len) |
112 | 176 | (slow-read-string stream (String. buffer 0 i))) |
113 | 177 | (if (= i end-index) |
114 | | - (do (.unread stream c) |
| 178 | + (do (.unreadChar stream c) |
115 | 179 | (slow-read-string stream (String. buffer 0 i))) |
116 | 180 | (recur (unchecked-inc-int i)))))))) |
117 | 181 |
|
|
127 | 191 | (bigdec string) |
128 | 192 | (Double/valueOf string))) |
129 | 193 |
|
130 | | -(defn- read-number [^PushbackReader stream bigdec?] |
| 194 | +(defn- read-number [^InternalPBR stream bigdec?] |
131 | 195 | (let [buffer (StringBuilder.) |
132 | 196 | decimal? (loop [stage :minus] |
133 | | - (let [c (.read stream)] |
| 197 | + (let [c (.readChar stream)] |
134 | 198 | (case stage |
135 | 199 | :minus |
136 | 200 | (codepoint-case c |
|
168 | 232 | (recur :exp-symbol)) |
169 | 233 | ;; early exit |
170 | 234 | :whitespace |
171 | | - (do (.unread stream c) |
| 235 | + (do (.unreadChar stream c) |
172 | 236 | false) |
173 | 237 | (\, \] \} -1) |
174 | | - (do (.unread stream c) |
| 238 | + (do (.unreadChar stream c) |
175 | 239 | false) |
176 | 240 | (throw (Exception. "JSON error (invalid number literal)"))) |
177 | 241 | ;; previous character is a "0" |
|
185 | 249 | (recur :exp-symbol)) |
186 | 250 | ;; early exit |
187 | 251 | :whitespace |
188 | | - (do (.unread stream c) |
| 252 | + (do (.unreadChar stream c) |
189 | 253 | false) |
190 | 254 | (\, \] \} -1) |
191 | | - (do (.unread stream c) |
| 255 | + (do (.unreadChar stream c) |
192 | 256 | false) |
193 | 257 | ;; Disallow zero-padded numbers or invalid characters |
194 | 258 | (throw (Exception. "JSON error (invalid number literal)"))) |
|
210 | 274 | (recur :exp-symbol)) |
211 | 275 | ;; early exit |
212 | 276 | :whitespace |
213 | | - (do (.unread stream c) |
| 277 | + (do (.unreadChar stream c) |
214 | 278 | true) |
215 | 279 | (\, \] \} -1) |
216 | | - (do (.unread stream c) |
| 280 | + (do (.unreadChar stream c) |
217 | 281 | true) |
218 | 282 | (throw (Exception. "JSON error (invalid number literal)"))) |
219 | 283 | ;; previous character is a "e" or "E" |
|
240 | 304 | (do (.append buffer (char c)) |
241 | 305 | (recur :exp-digit)) |
242 | 306 | :whitespace |
243 | | - (do (.unread stream c) |
| 307 | + (do (.unreadChar stream c) |
244 | 308 | true) |
245 | 309 | (\, \] \} -1) |
246 | | - (do (.unread stream c) |
| 310 | + (do (.unreadChar stream c) |
247 | 311 | true) |
248 | 312 | (throw (Exception. "JSON error (invalid number literal)"))))))] |
249 | 313 | (if decimal? |
250 | 314 | (read-decimal (str buffer) bigdec?) |
251 | 315 | (read-integer (str buffer))))) |
252 | 316 |
|
253 | | -(defn- next-token [^PushbackReader stream] |
254 | | - (loop [c (.read stream)] |
| 317 | +(defn- next-token [^InternalPBR stream] |
| 318 | + (loop [c (.readChar stream)] |
255 | 319 | (if (< 32 c) |
256 | 320 | (int c) |
257 | 321 | (codepoint-case (int c) |
258 | | - :whitespace (recur (.read stream)) |
| 322 | + :whitespace (recur (.readChar stream)) |
259 | 323 | -1 -1)))) |
260 | 324 |
|
261 | 325 | (defn invalid-array-exception [] |
262 | 326 | (Exception. "JSON error (invalid array)")) |
263 | 327 |
|
264 | | -(defn- read-array* [^PushbackReader stream options] |
| 328 | +(defn- read-array* [^InternalPBR stream options] |
265 | 329 | ;; Handles all array values after the first. |
266 | 330 | (loop [result (transient [])] |
267 | 331 | (let [r (conj! result (-read stream true nil options))] |
|
270 | 334 | \, (recur r) |
271 | 335 | (throw (invalid-array-exception)))))) |
272 | 336 |
|
273 | | -(defn- read-array [^PushbackReader stream options] |
| 337 | +(defn- read-array [^InternalPBR stream options] |
274 | 338 | ;; Expects to be called with the head of the stream AFTER the |
275 | 339 | ;; opening bracket. |
276 | 340 | ;; Only handles array value. |
277 | 341 | (let [c (int (next-token stream))] |
278 | 342 | (codepoint-case c |
279 | 343 | \] [] |
280 | 344 | \, (throw (invalid-array-exception)) |
281 | | - (do (.unread stream c) |
| 345 | + (do (.unreadChar stream c) |
282 | 346 | (read-array* stream options))))) |
283 | 347 |
|
284 | | -(defn- read-key [^PushbackReader stream] |
| 348 | +(defn- read-key [^InternalPBR stream] |
285 | 349 | (let [c (int (next-token stream))] |
286 | 350 | (if (= c (codepoint \")) |
287 | 351 | (let [key (read-quoted-string stream)] |
|
292 | 356 | nil |
293 | 357 | (throw (Exception. (str "JSON error (non-string key in object), found `" (char c) "`, expected `\"`"))))))) |
294 | 358 |
|
295 | | -(defn- read-object [^PushbackReader stream options] |
| 359 | +(defn- read-object [^InternalPBR stream options] |
296 | 360 | ;; Expects to be called with the head of the stream AFTER the |
297 | 361 | ;; opening bracket. |
298 | 362 | (let [key-fn (get options :key-fn) |
|
317 | 381 | (throw (Exception. "JSON error empty entry in object is not allowed")))))))) |
318 | 382 |
|
319 | 383 | (defn- -read |
320 | | - [^PushbackReader stream eof-error? eof-value options] |
| 384 | + [^InternalPBR stream eof-error? eof-value options] |
321 | 385 | (let [c (int (next-token stream))] |
322 | 386 | (codepoint-case c |
323 | 387 | ;; Read numbers |
324 | 388 | (\- \0 \1 \2 \3 \4 \5 \6 \7 \8 \9) |
325 | | - (do (.unread stream c) |
| 389 | + (do (.unreadChar stream c) |
326 | 390 | (read-number stream (:bigdec options))) |
327 | 391 |
|
328 | 392 | ;; Read strings |
329 | 393 | \" (read-quoted-string stream) |
330 | 394 |
|
331 | 395 | ;; Read null as nil |
332 | | - \n (if (and (= (codepoint \u) (.read stream)) |
333 | | - (= (codepoint \l) (.read stream)) |
334 | | - (= (codepoint \l) (.read stream))) |
| 396 | + \n (if (and (= (codepoint \u) (.readChar stream)) |
| 397 | + (= (codepoint \l) (.readChar stream)) |
| 398 | + (= (codepoint \l) (.readChar stream))) |
335 | 399 | nil |
336 | 400 | (throw (Exception. "JSON error (expected null)"))) |
337 | 401 |
|
338 | 402 | ;; Read true |
339 | | - \t (if (and (= (codepoint \r) (.read stream)) |
340 | | - (= (codepoint \u) (.read stream)) |
341 | | - (= (codepoint \e) (.read stream))) |
| 403 | + \t (if (and (= (codepoint \r) (.readChar stream)) |
| 404 | + (= (codepoint \u) (.readChar stream)) |
| 405 | + (= (codepoint \e) (.readChar stream))) |
342 | 406 | true |
343 | 407 | (throw (Exception. "JSON error (expected true)"))) |
344 | 408 |
|
345 | 409 | ;; Read false |
346 | | - \f (if (and (= (codepoint \a) (.read stream)) |
347 | | - (= (codepoint \l) (.read stream)) |
348 | | - (= (codepoint \s) (.read stream)) |
349 | | - (= (codepoint \e) (.read stream))) |
| 410 | + \f (if (and (= (codepoint \a) (.readChar stream)) |
| 411 | + (= (codepoint \l) (.readChar stream)) |
| 412 | + (= (codepoint \s) (.readChar stream)) |
| 413 | + (= (codepoint \e) (.readChar stream))) |
350 | 414 | false |
351 | 415 | (throw (Exception. "JSON error (expected false)"))) |
352 | 416 |
|
|
364 | 428 | (str "JSON error (unexpected character): " (char c)))))))) |
365 | 429 |
|
366 | 430 | (defn- -read1 |
367 | | - [^PushbackReader stream eof-error? eof-value options] |
| 431 | + [^InternalPBR stream eof-error? eof-value options] |
368 | 432 | (let [val (-read stream eof-error? eof-value options)] |
369 | 433 | (if-let [extra-data-fn (:extra-data-fn options)] |
370 | 434 | (if (or eof-error? (not (identical? eof-value val))) |
371 | | - (let [c (.read stream)] |
| 435 | + (let [c (.readChar stream)] |
372 | 436 | (if (neg? c) |
373 | 437 | val |
374 | 438 | (do |
375 | | - (.unread stream c) |
376 | | - (extra-data-fn val stream)))) |
| 439 | + (.unreadChar stream c) |
| 440 | + (extra-data-fn val (.toReader stream))))) |
377 | 441 | val) |
378 | 442 | val))) |
379 | 443 |
|
|
386 | 450 | (defn on-extra-throw-remaining |
387 | 451 | "Pass as :extra-data-fn to `read` or `read-str` to throw if data is found |
388 | 452 | after the first object and return the remaining data in ex-data :remaining." |
389 | | - [val ^java.io.PushbackReader rdr] |
| 453 | + [val rdr] |
390 | 454 | (let [remaining (slurp rdr)] |
391 | 455 | (throw (ex-info (str "Found extra data after json object: " remaining) |
392 | 456 | {:val val, :remaining remaining})))) |
|
443 | 507 | [reader & {:as options}] |
444 | 508 | (let [{:keys [eof-error? eof-value] |
445 | 509 | :or {eof-error? true}} options |
446 | | - pbr (if (instance? PushbackReader reader) |
447 | | - reader |
448 | | - (PushbackReader. reader 64))] |
| 510 | + pbr (pushback-pbr |
| 511 | + (if (instance? PushbackReader reader) |
| 512 | + reader |
| 513 | + (PushbackReader. reader 64)))] |
449 | 514 | (->> options |
450 | 515 | (merge default-read-options) |
451 | 516 | (-read1 pbr eof-error? eof-value)))) |
|
458 | 523 | :or {eof-error? true}} options] |
459 | 524 | (->> options |
460 | 525 | (merge default-read-options) |
461 | | - (-read1 (PushbackReader. (StringReader. string) 64) eof-error? eof-value)))) |
| 526 | + (-read1 (string-pbr string) eof-error? eof-value)))) |
462 | 527 |
|
463 | 528 | ;;; JSON WRITER |
464 | 529 |
|
|
0 commit comments