varint.clj

Bowmap · bowmap.git · Tickets

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))