read.clj
Branches > main > 4768247 > files / src / net / bowmap / packet / read.clj
(ns net.bowmap.packet.read
(:require
[clojure.alpha.spec :as s]
[dev.grahp.error :refer [ensure-not! raise]]
[net.bowmap.packet.types :as t])
(:import
[io.netty.buffer ByteBuf]
[java.nio.charset Charset]
[java.util UUID]))
(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-uuid! [buf]
(UUID. (ByteBuf/.readLong buf) (ByteBuf/.readLong buf)))
(defn read-enum! [codec m buf]
(m (codec buf)))
#_(defn read-array [codec len buf])
(defn read-table! [buf codec-fields]
(reduce (fn [acc {:keys [field codec]}]
(if-some [x (codec buf)]
(assoc acc field x)
acc))
{} codec-fields))
(declare readers)
(defn reader
"Returns a function of a ByteBuf that reads the respective type, as defined by 'readers'.
Throws if no such type exists."
[t]
(if (vector? t)
(let [[t & args] t]
(apply (reader t) args))
(or (readers t)
(raise (str "No such type! " (name t)) {:type t}))))
(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!
:uuid read-uuid!
:identifer read-string!
:table (fn [table]
(let [table (->> (s/conform ::t/table table)
(mapv #(assoc % :codec (reader (:type %)))))]
(fn [buf]
(read-table! buf table))))
:enum (fn [t m]
(fn [buf] (read-enum! (reader t) m buf)))})