read.clj
Branches > main > e863077 > 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')))))))
(comment
(import [io.netty.buffer Unpooled])
(can-read-packet? (Unpooled/wrappedBuffer (byte-array [1 1]))))
(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"))))
(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!})
(defn read-enum! [codec m buf]
(m (codec buf)))
(defn read-array [codec len buf])
; Perhaps have :optional and :required, so all non :optional or :prefixed-optional fields are implicitly :required
; Otherwise nils can easily creep into these functions and cause random NPEs
(def composite-readers
{:enum (fn [t enum-fn]
`(fn [buf#] (read-enum! ~(readers t) ~enum-fn buf#)))})