(ns cljam.io.util.byte-buffer
  (:refer-clojure :exclude [read-string])
  (:require [cljam.util :refer [string->bytes]])
  (:import [java.nio Buffer ByteBuffer ByteOrder]))

Creates a new little-endian byte buffer wrapping given data.

(defn make-lsb-byte-buffer
  ^ByteBuffer [^bytes data]
  (.order (ByteBuffer/wrap data) ByteOrder/LITTLE_ENDIAN))

Creates a new big-endian byte buffer wrapping given data.

(defn make-msb-byte-buffer
  ^ByteBuffer [^bytes data]
  (.order (ByteBuffer/wrap data) ByteOrder/BIG_ENDIAN))

Creates a new little-endian byte buffer with given capacity.

(defn allocate-lsb-byte-buffer
  (^ByteBuffer []
   (.order (ByteBuffer/allocate 8) ByteOrder/LITTLE_ENDIAN))
  (^ByteBuffer [size]
   (.order (ByteBuffer/allocate (int size)) ByteOrder/LITTLE_ENDIAN)))

Creates a new big-endian byte buffer with given capacity.

(defn allocate-msb-byte-buffer
  (^ByteBuffer []
   (.order (ByteBuffer/allocate 8) ByteOrder/BIG_ENDIAN))
  (^ByteBuffer [size]
   (.order (ByteBuffer/allocate (int size)) ByteOrder/BIG_ENDIAN)))

Skips over 'length' bytes of data, discarding the skipped bytes.

(defn skip
  [^ByteBuffer bb ^long length]
  (.position ^Buffer bb (+ (.position bb) length)))

Reads 1 byte. Returns an unsigned byte value as long.

(defn read-ubyte
  [^ByteBuffer bb]
  (bit-and (.get bb) 0xFF))

Reads 2 bytes. Returns an unsigned short value as long.

(defn read-ushort
  [^ByteBuffer bb]
  (bit-and (.getShort bb) 0xFFFF))

Reads 4 bytes. Returns an unsigned int value as long.

(defn read-uint
  [^ByteBuffer bb]
  (bit-and (.getInt bb) 0xFFFFFFFF))

Reads 'length' bytes to buffer starting from offset bytes. Returns a new byte-array if called without buffer.

(defn read-bytes
  ([^ByteBuffer bb ^long length]
   (let [ba (byte-array length)]
     (.get bb ba)
     ba))
  ([^ByteBuffer bb buffer ^long offset ^long length]
   (.get bb buffer (int offset) (int length))
   buffer))

Reads 'length' ints to buffer starting from offset ints. Returns a new int-array if called without buffer.

(defn read-ints
  (^ints [bb length]
   (read-ints bb (int-array length) 0 length))
  (^ints [^ByteBuffer bb ^ints buffer ^long offset ^long length]
   (dotimes [i length]
     (aset buffer (+ offset i) (.getInt bb)))
   buffer))

Reads 'length' bytes. Returns a String.

(defn read-string
  [^ByteBuffer bb ^long length]
  (let [ba (byte-array length)]
    (.get bb ba)
    (String. ba)))

Reads until next null character. Returns a String without the null.

(defn read-null-terminated-string
  [^ByteBuffer bb]
  (let [start (.position bb)
        end (do (while (not (zero? (.get bb))))
                (.position bb))
        offset (.arrayOffset bb)]
    (String. (.array bb) (+ offset start) (dec (- end start)))))

Writes 1 byte.

(defn write-ubyte
  [^ByteBuffer bb b]
  (.put bb (unchecked-byte b)))

Writes a 2-byte unsigned short value.

(defn write-ushort
  [^ByteBuffer bb n]
  (.putShort bb (unchecked-short n)))

Writes a 4-byte unsigned integer value.

(defn write-uint
  [^ByteBuffer bb n]
  (.putInt bb (unchecked-int n)))

Writes a string as a sequence of ascii characters.

(defn write-string
  [^ByteBuffer bb s]
  (.put bb (string->bytes s)))