varint.clj
Branches > main > latest > files / src / net / bowmap / server / packet / varint.clj
(ns net.bowmap.server.packet.varint)
(defn encode-varlong
"Calls 'rf' with the accumulator and each of the bytes of a varlong.
When encoding a varint, turn the number into an unsigned long."
[rf init n]
(loop [n n
acc init]
(let [b (bit-and n 0x7F)
nxt (unsigned-bit-shift-right n 7)]
(if (zero? nxt)
(rf acc b)
(recur nxt (rf acc (bit-or b 0x80)))))))
(defn encode-varint
[rf init n]
(encode-varlong rf init (Integer/toUnsignedLong n)))
(defn- decode-varint*
[read-byte src max-bytes]
(loop [num-read 0
result 0]
(if (>= num-read max-bytes)
(throw (ex-info "Cannot decode varint, too many bytes."
{:max-bytes max-bytes}))
(let [b (read-byte src)]
(if (and (= num-read 9) (> b 0x01))
(throw (ex-info "Varint exceeds 64 bits while decoding"
{:last-byte b}))
(let [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 decode-varint [read-byte src]
(let [n (decode-varint* read-byte src 5)]
(if (<= n 0xFFFFFFFF)
(long (unchecked-int n))
(throw (ex-info "VarInt exceeds 32 bits."
{:value n})))))
(defn decode-varlong [read-byte src]
(decode-varint* read-byte src 10))