cosmopolitan/libc/str/varint.h

88 lines
2.1 KiB
C

#ifndef COSMOPOLITAN_LIBC_VARINT_H_
#define COSMOPOLITAN_LIBC_VARINT_H_
#include "libc/limits.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
#define unzigzag(x) ((x >> 1) ^ -(x & 1))
#define zigzag(x) _Generic((x), int32_t : zigzag32, default : zigzag64)(x)
forceinline int32_t zigzag32(int32_t x) {
return (int64_t)((uint64_t)x << 1) ^ (x >> 31);
}
forceinline int64_t zigzag64(int64_t x) {
return (int64_t)((uint64_t)x << 1) ^ (x >> 63);
}
/**
* Copies variable-length integer to buffer.
*
* @param p needs 10+ bytes for 64-bit and 5+ for 32-bit
* @param x is unsigned number to encode
* @return pointer past last written byte
* @see writesint() which has more efficient encoding for signed numbers
*/
forceinline uint8_t *writevint(uint8_t *p, uint64_t x) {
do {
*p++ = (uint8_t)(x | 0x80);
x >>= 7;
} while (x > 0);
p[-1] &= 0x7f;
return p;
}
/**
* Copies variable-length signed integer to buffer.
*
* @param p needs 10+ bytes for 64-bit and 5+ for 32-bit
* @param x is unsigned number to encode
* @return pointer past last written byte
*/
forceinline uint8_t *writesint(uint8_t *p, int64_t x) {
return writevint(p, zigzag(x));
}
/**
* Reads variable-length integer from buffer.
*
* @param x is unsigned number to encode
* @return pointer past last read byte or -1ul on error
*/
forceinline uint8_t *readvint(uint8_t *p, uint8_t *pe, uint64_t *res) {
uint8_t b;
uint64_t x, o;
x = 0;
b = 0;
while (p < pe) {
o = *p++;
x |= ((uint64_t)o & 0x7f) << b;
if (!(o & 0x80)) {
*res = x;
return p;
}
if ((b += 7) > 64) {
break;
}
}
return (uint8_t *)-1ul;
}
/**
* Reads variable-length zig-zagged integer from buffer.
*
* @param x is unsigned number to encode
* @return pointer past last read byte or -1ul on error
*/
forceinline uint8_t *readsint(uint8_t *p, uint8_t *pe, int64_t *res) {
uint64_t u;
if ((p = readvint(p, pe, &u)) != (uint8_t *)-1ul) {
*res = unzigzag((int64_t)u);
return p;
} else {
return (uint8_t *)-1ul;
}
}
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_VARINT_H_ */