Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to read a whole binary file (Nippy) into byte array in Clojure?

I need to convert Nippy data structures stored on disk into something that can be read by Nippy? Nippy uses byte arrays, so I need some way to convert the file into a byte array. I have tried

(clojure.java.io/to-byte-array (clojure.java.io/file folder-path file-path))

but this gives

java.lang.IllegalArgumentException: Value out of range for byte: ? 

Then I try:

(into-array Byte/TYPE  (map byte (slurp (clojure.java.io/file folder-path file-path)))) 

but somehow the namespace is wrong, and I can't find the right one.

To write the Nippy structures in the first place, I am using:

(with-open [w (clojure.java.io/output-stream file-path)]
    (.write w (nippy/freeze data)))))
like image 636
user1559027 Avatar asked Apr 11 '14 17:04

user1559027


5 Answers

Here's how I do it generically with clojure built-ins

(defn slurp-bytes
  "Slurp the bytes from a slurpable thing"
  [x]
  (with-open [out (java.io.ByteArrayOutputStream.)]
    (clojure.java.io/copy (clojure.java.io/input-stream x) out)
    (.toByteArray out)))
like image 87
Matt W-D Avatar answered Nov 18 '22 03:11

Matt W-D


I'm not aware of anything built-in to Clojure that will handle this. You definitely don't want slurp because that will decode the stream contents as text.

You could write your own method to do this, basically reading from the InputStream into a buffer and writing the buffer to a java.io.ByteArrayOutputStream. Or you could use the IOUtils class from Apache Commons IO:

 (require '[clojure.java.io :as io])
 (import '[org.apache.commons.io IOUtils])

 (IOUtils/toByteArray (io/input-stream file-path))

You should also take a look at Nippy's thaw-from-in! and freeze-to-out! functions:

 (import '[java.io DataInputStream DataOutputStream])

 (with-open [w (io/output-stream file-path)]
   (nippy/freeze-to-out! (DataOutputStream. w) some-data))

 (with-open [r (io/input-stream file-path)]
   (nippy/thaw-from-in! (DataInputStream. r)))
like image 42
Alex Avatar answered Nov 18 '22 03:11

Alex


Since you know the .length of the file, you can allocate once and use DataInputStream's readFully method. No additional libraries, buffer copies, or loops required.

(defn file-to-byte-array
  [^java.io.File file]
  (let [result (byte-array (.length file))]
    (with-open [in (java.io.DataInputStream. (clojure.java.io/input-stream file))]
      (.readFully in result))
    result))
like image 7
ɲeuroburɳ Avatar answered Nov 18 '22 03:11

ɲeuroburɳ


A quick make-shift solution may be this code:

(defn slurpb [is]
  "Convert an input stream is to byte array"
  (with-open [baos (java.io.ByteArrayOutputStream.)]
    (let [ba (byte-array 2000)]
      (loop [n (.read is ba 0 2000)]
        (when (> n 0)
          (.write baos ba 0 n)
          (recur (.read is ba 0 2000))))
      (.toByteArray baos))))

;;test
(String. (slurpb (java.io.ByteArrayInputStream. (.getBytes "hello"))))
like image 2
user3742065 Avatar answered Nov 18 '22 04:11

user3742065


Please note that I just cut Nippy v2.13.0 which now includes a pair of helper utils to help simplify this use case: freeze-to-file and thaw-from-file.

Release details at: https://github.com/ptaoussanis/nippy/releases/tag/v2.13.0

Cheers!

like image 2
Peter Taoussanis Avatar answered Nov 18 '22 04:11

Peter Taoussanis