public function readVarint(Stream $stream)
    {
        // Optimize common case (single byte varints)
        $byte = $this->readByte($stream);

        if ($byte < 0x80) {
            return $byte;
        }

        $length = $stream->getSize();
        $offset = $stream->tell();
        $result = $byte & 0x7f;
        $shift  = 7;

        // fastpath 32bit varints (5bytes) by unrolling the loop
        if ($length - $offset >= 4) {
            // 2
            $byte    = $this->readByte($stream);
            $result |= ($byte & 0x7f) << 7;

            if ($byte < 0x80) {
                return $result;
            }

            // 3
            $byte    = $this->readByte($stream);
            $result |= ($byte & 0x7f) << 14;

            if ($byte < 0x80) {
                return $result;
            }

            // 4
            $byte    = $this->readByte($stream);
            $result |= ($byte & 0x7f) << 21;

            if ($byte < 0x80) {
                return $result;
            }

            // 5
            $byte    = $this->readByte($stream);
            $result |= ($byte & 0x7f) << 28;

            if ($byte < 0x80) {
                return $result;
            }

            $shift = 35;
        }

        // If we're just at the end of the buffer or handling a 64bit varint
        do {
            $byte    = $this->readByte($stream);
            $result |= ($byte & 0x7f) << $shift;
            $shift  += 7;
        } while ($byte > 0x7f);

        return $result;
    }