|
4 | 4 | (:require
|
5 | 5 | [manifold.stream :as s]
|
6 | 6 | [manifold.deferred :as d]
|
| 7 | + [byte-streams.pushback-stream :as ps] |
7 | 8 | [byte-streams.char-sequence :as cs]
|
8 | 9 | [byte-streams.utils :refer (fast-memoize)]
|
9 | 10 | [clojure.java.io :as io]
|
|
27 | 28 | FileOutputStream
|
28 | 29 | FileInputStream
|
29 | 30 | ByteArrayInputStream
|
| 31 | + ByteArrayOutputStream |
30 | 32 | PipedOutputStream
|
31 | 33 | PipedInputStream
|
32 | 34 | DataInputStream
|
|
68 | 70 | (defn- protocol? [x]
|
69 | 71 | (and (map? x) (contains? x :on-interface)))
|
70 | 72 |
|
71 |
| -(defn seq-of [x] |
72 |
| - (list 'seq-of x)) |
73 |
| - |
74 |
| -(defn stream-of [x] |
75 |
| - (list 'stream-of x)) |
| 73 | +(declare seq-of stream-of) |
76 | 74 |
|
77 | 75 | (defn seq-of? [x]
|
78 | 76 | (and (seq? x)
|
|
90 | 88 |
|
91 | 89 | (defn- abstract-type-descriptor [x]
|
92 | 90 | (cond
|
| 91 | + |
| 92 | + (and (sequential? x) (= 'var (first x))) |
| 93 | + (abstract-type-descriptor (second x)) |
| 94 | + |
| 95 | + (var? x) |
| 96 | + x |
| 97 | + |
93 | 98 | (seq-of? x)
|
94 | 99 | (list 'list '(quote seq-of) (abstract-type-descriptor (second x)))
|
95 | 100 |
|
|
105 | 110 | (:var x)
|
106 | 111 | x))))
|
107 | 112 |
|
| 113 | +(defn seq-of [x] |
| 114 | + (list 'seq-of x)) |
| 115 | + |
| 116 | +(defn stream-of [x] |
| 117 | + (list 'stream-of x)) |
| 118 | + |
108 | 119 | (defmacro def-conversion
|
109 | 120 | "Defines a conversion from one type to another."
|
110 | 121 | [[src dst :as conversion] params & body]
|
|
397 | 408 | "Returns a descriptor that can be used with `conversion-path`."
|
398 | 409 | [x]
|
399 | 410 | (cond
|
400 |
| - (or (class? x) (protocol? x)) |
| 411 | + (class? x) |
401 | 412 | x
|
402 | 413 |
|
| 414 | + (protocol? x) |
| 415 | + (:var x) |
| 416 | + |
403 | 417 | (contains? @src->dst->conversion (class x))
|
404 | 418 | (class x)
|
405 | 419 |
|
|
411 | 425 |
|
412 | 426 | (defn convert
|
413 | 427 | "Converts `x`, if possible, into type `dst`, which can be either a class or protocol. If no such conversion
|
414 |
| - is possible, an IllegalArgumentException is thrown. |
| 428 | + is possible, an IllegalArgumentException is thrown. If `x` is a stream, then the `src` type must be explicitly specified. |
415 | 429 |
|
416 | 430 | `options` is a map, whose available settings depend on what sort of transform is being performed:
|
417 | 431 |
|
|
429 | 443 | (cond
|
430 | 444 |
|
431 | 445 | (s/source? x)
|
432 |
| - (if (stream-of? dst) |
433 |
| - |
434 |
| - ;; -> (stream-of a) |
435 |
| - (let [s' (s/stream) |
436 |
| - dst (if (protocol? (second dst)) |
437 |
| - (:var (second dst)) |
438 |
| - (second dst))] |
439 |
| - (-> x |
440 |
| - (s/take! ::none) |
441 |
| - (d/chain |
442 |
| - (fn [msg] |
443 |
| - (if (identical? ::none msg) |
444 |
| - (s/close! s') |
445 |
| - (let [src (type-descriptor msg)] |
446 |
| - (if-let [f (converter src dst)] |
447 |
| - (do |
448 |
| - (s/put! s' (f msg options)) |
449 |
| - (s/connect-via x #(s/put! s' (f % options)) s')) |
450 |
| - (do |
451 |
| - (s/close! x) |
452 |
| - (s/close! s')))))))) |
453 |
| - s') |
454 |
| - |
455 |
| - |
456 |
| - (let [msg @(s/take! x ::none) |
457 |
| - src (type-descriptor msg) |
458 |
| - s' (s/stream)] |
459 |
| - (when-not (identical? ::none msg) |
460 |
| - (s/put! s' msg) |
461 |
| - (s/connect x s') |
462 |
| - (if-let [f (converter (stream-of src) dst)] |
463 |
| - (f s' options) |
464 |
| - (throw (IllegalArgumentException. (str "Don't know how to convert a stream of " src " into " dst))))))) |
| 446 | + (let [src (get options :source-type)] |
| 447 | + (assert src "must specify `:source-type` when converting streams") |
| 448 | + (if-let [f (converter src dst)] |
| 449 | + (f x options) |
| 450 | + (throw (IllegalArgumentException. (str "don't know how to convert " src " into " dst))))) |
465 | 451 |
|
466 | 452 | (not (or (nil? x) (and (sequential? x) (empty? x))))
|
467 |
| - (let [src (type-descriptor x) |
468 |
| - dst (if (protocol? dst) |
469 |
| - (:var dst) |
470 |
| - dst)] |
| 453 | + (let [src (or |
| 454 | + (when (sequential? x) |
| 455 | + (get options :source-type)) |
| 456 | + (type-descriptor x))] |
471 | 457 | (if (or
|
472 | 458 | (= src dst)
|
473 | 459 | (and (class? src) (class? dst) (.isAssignableFrom ^Class dst src)))
|
|
559 | 545 | (f source' sink' options))))
|
560 | 546 |
|
561 | 547 | (and
|
562 |
| - (conversion-path src ByteSource) |
563 |
| - (conversion-path dst ByteSink)) |
| 548 | + (conversion-path src #'ByteSource) |
| 549 | + (conversion-path dst #'ByteSink)) |
564 | 550 | (fn [source sink {:keys [close?] :or {close? true} :as options}]
|
565 |
| - (let [source' (convert source ByteSource options) |
566 |
| - sink' (convert sink ByteSink options)] |
| 551 | + (let [source' (convert source #'ByteSource options) |
| 552 | + sink' (convert sink #'ByteSink options)] |
567 | 553 | (default-transfer source' sink' options)
|
568 | 554 | (when close?
|
569 | 555 | (doseq [x [source sink source' sink']]
|
|
593 | 579 | ([source sink]
|
594 | 580 | (transfer source sink nil))
|
595 | 581 | ([source sink options]
|
| 582 | + (transfer source nil sink options)) |
| 583 | + ([source source-type sink options] |
596 | 584 | (if (s/source? source)
|
597 | 585 |
|
598 | 586 | (let [msg @(s/take! source ::none)
|
|
626 | 614 |
|
627 | 615 | ;;; conversion definitions
|
628 | 616 |
|
| 617 | +(def-conversion ^{:cost 0} [(stream-of byte-array) InputStream] |
| 618 | + [s options] |
| 619 | + (let [ps (ps/pushback-stream (get options :buffer-size 65536))] |
| 620 | + (s/consume |
| 621 | + (fn [^bytes ary] |
| 622 | + (ps/put-array ps ary 0 (alength ary))) |
| 623 | + s) |
| 624 | + (s/on-drained s #(ps/close ps)) |
| 625 | + (ps/->input-stream ps))) |
| 626 | + |
| 627 | +(def-conversion ^{:cost 0} [(stream-of ByteBuffer) InputStream] |
| 628 | + [s options] |
| 629 | + (let [ps (ps/pushback-stream (get options :buffer-size 65536))] |
| 630 | + (s/consume |
| 631 | + (fn [buf] |
| 632 | + (ps/put-buffer ps buf)) |
| 633 | + s) |
| 634 | + (s/on-drained s #(ps/close ps)) |
| 635 | + (ps/->input-stream ps))) |
| 636 | + |
629 | 637 | ;; byte-array => byte-buffer
|
630 | 638 | (def-conversion ^{:cost 0} [byte-array ByteBuffer]
|
631 | 639 | [ary {:keys [direct?] :or {direct? false}}]
|
|
747 | 755 | (.close sink))))
|
748 | 756 | source))
|
749 | 757 |
|
750 |
| -(def-conversion ^{:cost 1.5} [(seq-of ByteSource) InputStream] |
| 758 | +(def-conversion ^{:cost 1.5} [(seq-of #'ByteSource) InputStream] |
751 | 759 | [srcs options]
|
752 | 760 | (let [chunk-size (get options :chunk-size 65536)
|
753 | 761 | out (PipedOutputStream.)
|
|
762 | 770 | (.close out))))
|
763 | 771 | in))
|
764 | 772 |
|
| 773 | +(def-conversion ^{:cost 2} [#'ByteSource byte-array] |
| 774 | + [src options] |
| 775 | + (let [os (ByteArrayOutputStream.)] |
| 776 | + (transfer src os) |
| 777 | + (.toByteArray os))) |
| 778 | + |
765 | 779 | ;; generic byte-source => lazy char-sequence
|
766 |
| -(def-conversion ^{:cost 2} [ByteSource CharSequence] |
| 780 | +(def-conversion ^{:cost 2} [#'ByteSource CharSequence] |
767 | 781 | [source options]
|
768 | 782 | (cs/decode-byte-source
|
769 | 783 | #(let [bytes (take-bytes! source % options)]
|
|
1102 | 1116 | ([x]
|
1103 | 1117 | (to-byte-source x nil))
|
1104 | 1118 | ([x options]
|
1105 |
| - (convert x ByteSource options))) |
| 1119 | + (convert x #'ByteSource options))) |
1106 | 1120 |
|
1107 | 1121 | (defn to-byte-sink
|
1108 | 1122 | "Converts the object to something that satisfies `ByteSink`."
|
1109 | 1123 | ([x]
|
1110 | 1124 | (to-byte-sink x nil))
|
1111 | 1125 | ([x options]
|
1112 |
| - (convert x ByteSink options))) |
| 1126 | + (convert x #'ByteSink options))) |
1113 | 1127 |
|
1114 | 1128 | ;;;
|
1115 | 1129 |
|
|
1119 | 1133 | sign (long (if (pos? diff) -1 1))
|
1120 | 1134 | a (if (pos? diff) b' a')
|
1121 | 1135 | b (if (pos? diff) a' b')
|
1122 |
| - limit (p/>> (.remaining a) 2)] |
| 1136 | + limit (p/>> (.remaining a) 2) |
| 1137 | + a-offset (.position a) |
| 1138 | + b-offset (.position b)] |
1123 | 1139 | (let [cmp (loop [idx 0]
|
1124 | 1140 | (if (p/>= idx limit)
|
1125 | 1141 | 0
|
1126 | 1142 | (let [cmp (p/-
|
1127 |
| - (p/int->uint (.getInt a idx)) |
1128 |
| - (p/int->uint (.getInt b idx)))] |
| 1143 | + (p/int->uint (.getInt a (p/+ idx a-offset))) |
| 1144 | + (p/int->uint (.getInt b (p/+ idx b-offset))))] |
1129 | 1145 | (if (p/== 0 cmp)
|
1130 | 1146 | (recur (p/+ idx 4))
|
1131 | 1147 | (p/* sign cmp)))))]
|
|
1135 | 1151 | (if (p/>= idx limit')
|
1136 | 1152 | diff
|
1137 | 1153 | (let [cmp (p/-
|
1138 |
| - (p/byte->ubyte (.get a idx)) |
1139 |
| - (p/byte->ubyte (.get b idx)))] |
| 1154 | + (p/byte->ubyte (.get a (p/+ idx a-offset))) |
| 1155 | + (p/byte->ubyte (.get b (p/+ idx b-offset))))] |
1140 | 1156 | (if (p/== 0 cmp)
|
1141 | 1157 | (recur (p/inc idx))
|
1142 | 1158 | (p/* sign cmp))))))
|
|
0 commit comments