I've build up some file input/output utils over the past year that may be of use to the Clojure community. They allow one to do fine-grained I/O with native Java numeric types (both signed & unsigned), as well as create & delete temporary files & directories.
Full API docs can be found: - on CljDoc.org: https://cljdoc.org/d/tupelo/tupelo/0.9.186 - and GitHub Pages: https://cloojure.github.io/doc/tupelo/tupelo.io.html The unit tests show the code in action. For all the primitive numeric types, plus byte-array and String, the library can write and read to a DataOutputStream. Note that there are unsigned variants of writing/reading as well as the normal signed numeric versions. (with-open [dos (DataOutputStream. (FileOutputStream. dummy-file))] (doto dos (write-string-bytes "hello") (write-bytes (byte-array [1 2 3 4])) (write-byte 42) (write-byte -42) (write-byte Byte/MIN_VALUE) (write-byte Byte/MAX_VALUE) (write-byte-unsigned 142) (write-byte-unsigned types/BYTE_UNSIGNED_MIN_VALUE) (write-byte-unsigned types/BYTE_UNSIGNED_MAX_VALUE) (write-short 9999) (write-short -9999) (write-short Short/MIN_VALUE) (write-short Short/MAX_VALUE) (write-short-unsigned 55999) (write-short-unsigned types/SHORT_UNSIGNED_MIN_VALUE) (write-short-unsigned types/SHORT_UNSIGNED_MAX_VALUE) (write-integer int-val) (write-integer Integer/MIN_VALUE) (write-integer Integer/MAX_VALUE) (write-integer-unsigned int-val) (write-integer-unsigned types/INTEGER_UNSIGNED_MIN_VALUE) (write-integer-unsigned types/INTEGER_UNSIGNED_MAX_VALUE) (write-long long-val) (write-long Long/MIN_VALUE) (write-long Long/MAX_VALUE) (write-long-unsigned long-val) (write-long-unsigned types/LONG_UNSIGNED_MIN_VALUE) (write-long-unsigned types/LONG_UNSIGNED_MAX_VALUE) (write-float pi-float) (write-float Float/MIN_VALUE) (write-float Float/MAX_VALUE) (write-double pi-double) (write-double Double/MIN_VALUE) (write-double Double/MAX_VALUE) )) and the corresponding reads: (with-open [dis (DataInputStream. (io/input-stream dummy-file))] (is= (read-string-bytes 5 dis) "hello") (is= (vec (read-bytes 4 dis)) [1 2 3 4]) (is= (read-byte dis) 42) (is= (read-byte dis) -42) (is= (read-byte dis) Byte/MIN_VALUE) (is= (read-byte dis) Byte/MAX_VALUE) (is= (read-byte-unsigned dis) 142) (is= (read-byte-unsigned dis) types/BYTE_UNSIGNED_MIN_VALUE) (is= (read-byte-unsigned dis) types/BYTE_UNSIGNED_MAX_VALUE) (is= (read-short dis) 9999) (is= (read-short dis) -9999) (is= (read-short dis) Short/MIN_VALUE) (is= (read-short dis) Short/MAX_VALUE) (is= (read-short-unsigned dis) 55999) (is= (read-short-unsigned dis) types/SHORT_UNSIGNED_MIN_VALUE) (is= (read-short-unsigned dis) types/SHORT_UNSIGNED_MAX_VALUE) (is= (read-integer dis) int-val) (is= (read-integer dis) Integer/MIN_VALUE) (is= (read-integer dis) Integer/MAX_VALUE) (is= (read-integer-unsigned dis) int-val) (is= (read-integer-unsigned dis) types/INTEGER_UNSIGNED_MIN_VALUE) (is= (read-integer-unsigned dis) types/INTEGER_UNSIGNED_MAX_VALUE) (is= (read-long dis) long-val) (is= (read-long dis) Long/MIN_VALUE) (is= (read-long dis) Long/MAX_VALUE) (is= (read-long-unsigned dis) long-val) (is= (read-long-unsigned dis) types/LONG_UNSIGNED_MIN_VALUE) (is= (read-long-unsigned dis) types/LONG_UNSIGNED_MAX_VALUE) (is= (read-float dis) pi-float) (is= (read-float dis) Float/MIN_VALUE) (is= (read-float dis) Float/MAX_VALUE) (is= (read-double dis) pi-double) (is= (read-double dis) Double/MIN_VALUE) (is= (read-double dis) Double/MAX_VALUE)) ) Other tests show that an Exception is thrown if the supplied value is out of range: ;----------------------------------------------------------------------------- (throws? (write-byte dos (inc Byte/MAX_VALUE))) (throws? (write-byte dos (dec Byte/MIN_VALUE))) (throws? (write-short dos (inc Short/MAX_VALUE))) (throws? (write-short dos (dec Short/MIN_VALUE))) (throws? (write-integer dos (inc Integer/MAX_VALUE))) (throws? (write-integer dos (dec Integer/MIN_VALUE))) (throws? (write-long dos (* 2M (bigdec Long/MAX_VALUE)))) (throws? (write-long dos (* -2M (bigdec Long/MIN_VALUE)))) (throws? (write-byte-unsigned dos (inc types/BYTE_UNSIGNED_MAX_VALUE))) (throws? (write-byte-unsigned dos (dec types/BYTE_UNSIGNED_MIN_VALUE))) (throws? (write-short-unsigned dos (inc types/SHORT_UNSIGNED_MAX_VALUE))) (throws? (write-short-unsigned dos (dec types/SHORT_UNSIGNED_MIN_VALUE))) (throws? (write-integer-unsigned dos (inc types/INTEGER_UNSIGNED_MAX_VALUE))) (throws? (write-integer-unsigned dos (dec types/INTEGER_UNSIGNED_MIN_VALUE))) (throws? (write-long-unsigned dos (inc types/LONG_UNSIGNED_MAX_VALUE))) (throws? (write-long-unsigned dos (dec types/LONG_UNSIGNED_MIN_VALUE))) Other useful functions include the ability to make temporary disk files & directories, and to recursively delete a dir: (dotest ; Create nested dirs & files, then delete recursively (let [tmp-path (create-temp-directory "some-stuff") tmp-name (str tmp-path) dir-one (create-temp-directory tmp-path "dir-one") dir-two (create-temp-directory dir-one "dir-two") tmp-one-a (create-temp-file dir-one "aaa" nil) tmp-one-b (create-temp-file dir-one "bbb" ".dummy") tmp-two-a (create-temp-file dir-two "aaa" ".tmp") tmp-two-b (create-temp-file dir-two "bbb" ".tmp") count-files-fn (s/fn [dir-name :- s/Str] (let [dir-file (io/file dir-name) counts (for [file (file-seq dir-file)] (if (.exists file) 1 0)) total (apply + counts)] total))] (when false ; debug printouts (spyx (str tmp-path)) (spyx (str tmp-name)) (spyx (str dir-one)) (spyx (str tmp-one-a)) (spyx (str tmp-one-b)) (spyx (str tmp-two-a)) (spyx (str tmp-two-b)) (pprint/pprint (vec (sort (map str (file-seq (.toFile tmp-path)))))) ; Sample debug output: ; (str tmp-path) => "/tmp/some-stuff-562114607264734833" ; (str tmp-name) => "/tmp/some-stuff-562114607264734833" ; (str dir-one) => "/tmp/some-stuff-562114607264734833/dir-one-15514198984069907970" ; (str tmp-one-a) => "/tmp/some-stuff-562114607264734833/dir-one-15514198984069907970/aaa-345552808102662294.tmp" ; (str tmp-one-b) => "/tmp/some-stuff-562114607264734833/dir-one-15514198984069907970/bbb-14875687905791190659.dummy" ; (str tmp-two-a) => "/tmp/some-stuff-562114607264734833/dir-one-15514198984069907970/dir-two-15168249174986120265/aaa-14487327636081006800.tmp" ; (str tmp-two-b) => "/tmp/some-stuff-562114607264734833/dir-one-15514198984069907970/dir-two-15168249174986120265/bbb-8158500208162744706.tmp" ; ; File names produced: ; ["/tmp/some-stuff-562114607264734833" ; "/tmp/some-stuff-562114607264734833/dir-one-15514198984069907970" ; "/tmp/some-stuff-562114607264734833/dir-one-15514198984069907970/aaa-345552808102662294.tmp" ; "/tmp/some-stuff-562114607264734833/dir-one-15514198984069907970/bbb-14875687905791190659.dummy" ; "/tmp/some-stuff-562114607264734833/dir-one-15514198984069907970/dir-two-15168249174986120265" ; "/tmp/some-stuff-562114607264734833/dir-one-15514198984069907970/dir-two-15168249174986120265/aaa-14487327636081006800.tmp" ; "/tmp/some-stuff-562114607264734833/dir-one-15514198984069907970/dir-two-15168249174986120265/bbb-8158500208162744706.tmp"] ) (is= 7 (count-files-fn tmp-name)) (is= 7 (delete-directory tmp-name)) (is= 0 (count-files-fn tmp-name)) (is= 0 (delete-directory tmp-name)) ; idempotent )) There are also a few stream type-testing predicates: (let [in-stream (io/input-stream dummy-file) out-stream (io/output-stream dummy-file) dis (DataInputStream. in-stream) dos (DataOutputStream. out-stream)] (isnt (data-input-stream? in-stream)) (is (input-stream? in-stream)) (is (input-stream? dis)) (is (data-input-stream? dis)) (isnt (data-output-stream? out-stream)) (is (output-stream? out-stream)) (is (output-stream? dos)) (is (data-output-stream? dos)))) Enjoy! Alan Thompson -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en --- You received this message because you are subscribed to the Google Groups "Clojure" group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/clojure/CAN67zA3ySGfZHk3GK6E%3DZm5xwi%2B6OSTBoCCCAmXt_QdWmSwC6w%40mail.gmail.com.