read.clj

Branches > main > 4ef8d83 > files / src / net / bowmap / codecs / read.clj

(ns net.bowmap.codecs.read
  (:require
   [dev.grahp.error :refer [ensure-not! raise]])
  (:import
   [io.netty.buffer ByteBuf]
   [java.nio.charset Charset]))

(defn can-read-packet?
  "Returns whether a varint length can be read from buf, and whether buf has enough bytes to read a full packet.
   Can throw if varint is too long, meaning it is completely invalid."
  [buf]
  (loop [num-read 0
         result 0]
    (if (>= num-read 5)
      (raise "VarInt too long")
      (when (< num-read (ByteBuf/.readableBytes buf))
        (let [b (ByteBuf/.getByte buf (+ (ByteBuf/.readerIndex buf)
                                        num-read))
              result' (bit-or result (bit-shift-left (bit-and b 0x7F) (* 7 num-read)))]
          (if (zero? (bit-and b 0x80))
            (>= (ByteBuf/.readableBytes buf) (+ result' num-read 1))
            (recur (inc num-read) result')))))))

(defn read-varint! [buf]
  (loop [num-read 0
         result 0]
    (if (>= num-read 5)
      (raise "VarInt too long")
      (let [b (ByteBuf/.readByte buf)
            result' (bit-or result (bit-shift-left (bit-and b 0x7F) (* 7 num-read)))]
        (if (zero? (bit-and b 0x80))
          result'
          (recur (inc num-read) result'))))))

(defn read-string! [buf]
  (let [len (read-varint! buf)]
    (ensure-not! neg? len)
    (ByteBuf/.readCharSequence buf len (Charset/forName "UTF-8"))))

(defn read-enum! [codec m buf]
  (m (codec buf)))

(defn read-array [codec len buf])

(declare readers reader)

(defn reader
  "Returns a form that will evaluate to a function that takes a buffer and reads the respective type from it."
  ([t]
   (or (readers t) (raise "No such type!" {:type t})))
  ([t & args]
   (apply (reader t) args)))

(def readers
  {:byte   `ByteBuf/.readByte
   :ubyte  `(comp unchecked-byte ByteBuf/.readByte)
   :short  `ByteBuf/.readShort
   :ushort `(comp unchecked-short ByteBuf/.readShort)
   :int    `ByteBuf/.readInt
   :long   `ByteBuf/.readLong
   :varint `read-varint!
   :string `read-string!
   :enum (fn [t m]
           `(fn [buf#] (read-enum! ~(reader t) ~m buf#)))})