88 lines
2.1 KiB
C
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_ */
|