| /* Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| parcel Lucy; |
| |
| /** Provide various number-related utilies. |
| * |
| * Provide utilities for dealing with endian issues, sub-byte-width arrays, |
| * compressed integers, and so on. |
| */ |
| inert class Lucy::Util::NumberUtils cnick NumUtil { |
| |
| inert const uint8_t[8] u1masks; |
| inert const uint8_t[4] u2masks; |
| inert const uint8_t[4] u2shifts; |
| inert const uint8_t[2] u4masks; |
| inert const uint8_t[2] u4shifts; |
| |
| /** Encode an unsigned 16-bit integer as 2 bytes in the buffer provided, |
| * using big-endian byte order. |
| */ |
| inert inline void |
| encode_bigend_u16(uint16_t value, void *dest); |
| |
| /** Encode an unsigned 32-bit integer as 4 bytes in the buffer provided, |
| * using big-endian byte order. |
| */ |
| inert inline void |
| encode_bigend_u32(uint32_t value, void *dest); |
| |
| /** Encode an unsigned 64-bit integer as 8 bytes in the buffer provided, |
| * using big-endian byte order. |
| */ |
| inert inline void |
| encode_bigend_u64(uint64_t value, void *dest); |
| |
| /** Interpret a sequence of bytes as a big-endian unsigned 16-bit int. |
| */ |
| inert inline uint16_t |
| decode_bigend_u16(void *source); |
| |
| /** Interpret a sequence of bytes as a big-endian unsigned 32-bit int. |
| */ |
| inert inline uint32_t |
| decode_bigend_u32(void *source); |
| |
| /** Interpret a sequence of bytes as a big-endian unsigned 64-bit int. |
| */ |
| inert inline uint64_t |
| decode_bigend_u64(void *source); |
| |
| /** Encode a 32-bit floating point number as 4 bytes in the buffer |
| * provided, using big-endian byte order. |
| */ |
| inert inline void |
| encode_bigend_f32(float value, void *dest); |
| |
| /** Encode a 64-bit floating point number as 8 bytes in the buffer |
| * provided, using big-endian byte order. |
| */ |
| inert inline void |
| encode_bigend_f64(double value, void *dest); |
| |
| /** Interpret a sequence of bytes as a 32-bit float stored in big-endian |
| * byte order. |
| */ |
| inert inline float |
| decode_bigend_f32(void *source); |
| |
| /** Interpret a sequence of bytes as a 64-bit float stored in big-endian |
| * byte order. |
| */ |
| inert inline double |
| decode_bigend_f64(void *source); |
| |
| /** Encode a C32 at the space pointed to by <code>dest</code>. As a side |
| * effect, <code>dest</code> will be advanced to immediately after the end |
| * of the C32. |
| */ |
| inert inline void |
| encode_c32(uint32_t value, char **dest); |
| |
| /** Encode a C32 at the space pointed to by <code>dest</code>, but add |
| * "leading zeroes" so that the space consumed will always be 5 bytes. As |
| * a side effect, <code>dest</code> will be advanced to immediately after |
| * the end of the C32. |
| */ |
| inert inline void |
| encode_padded_c32(uint32_t value, char **dest); |
| |
| /** Encode a C64 at the space pointed to by <code>dest</code>. As a side |
| * effect, <code>dest</code> will be advanced to immediately after the end |
| * of the C64. |
| */ |
| inert inline void |
| encode_c64(uint64_t value, char **dest); |
| |
| /** Read a C32 from the buffer pointed to by <code>source</code>. As a |
| * side effect, advance the pointer, consuming the bytes occupied by the |
| * C32. |
| */ |
| inert inline uint32_t |
| decode_c32(char **source); |
| |
| /** Read a C64 from the buffer pointed to by <code>source</code>. As a |
| * side effect, advance the pointer, consuming the bytes occupied by the |
| * C64. |
| */ |
| inert inline uint64_t |
| decode_c64(char **source); |
| |
| /** Advance <code>source</code> past one encoded C32 or C64. |
| */ |
| inert inline void |
| skip_cint(char **source); |
| |
| /** Interpret <code>array</code> as an array of bits; return true if the |
| * bit at <code>tick</code> is set, false otherwise. |
| */ |
| inert inline bool_t |
| u1get(void *array, uint32_t tick); |
| |
| /** Interpret <code>array</code> as an array of bits; set the bit at |
| * <code>tick</code>. |
| */ |
| inert inline void |
| u1set(void *array, uint32_t tick); |
| |
| /** Interpret <code>array</code> as an array of bits; clear the bit at |
| * <code>tick</code>. |
| */ |
| inert inline void |
| u1clear(void *array, uint32_t tick); |
| |
| /** Interpret <code>array</code> as an array of bits; flip the bit at |
| * <code>tick</code>. |
| */ |
| inert inline void |
| u1flip(void *array, uint32_t tick); |
| |
| /** Interpret <code>array</code> as an array of two-bit integers; return |
| * the value at <code>tick</code>. |
| */ |
| inert inline uint8_t |
| u2get(void *array, uint32_t tick); |
| |
| /** Interpret <code>array</code> as an array of two-bit integers; set the |
| * element at <code>tick</code> to <code>value</code>. |
| */ |
| inert inline void |
| u2set(void *array, uint32_t tick, uint8_t value); |
| |
| /** Interpret <code>array</code> as an array of four-bit integers; return |
| * the value at <code>tick</code>. |
| */ |
| inert inline uint8_t |
| u4get(void *array, uint32_t tick); |
| |
| /** Interpret <code>array</code> as an array of four-bit integers; set the |
| * element at <code>tick</code> to <code>value</code>. |
| */ |
| inert inline void |
| u4set(void *array, uint32_t tick, uint8_t value); |
| } |
| |
| __C__ |
| |
| static CHY_INLINE void |
| lucy_NumUtil_encode_bigend_u16(uint16_t value, void *dest_ptr) { |
| uint8_t *dest = *(uint8_t**)dest_ptr; |
| #ifdef CHY_BIG_END |
| memcpy(dest, &value, sizeof(uint16_t)); |
| #else // little endian |
| uint8_t *source = (uint8_t*)&value; |
| dest[0] = source[1]; |
| dest[1] = source[0]; |
| #endif // CHY_BIG_END (and little endian) |
| } |
| |
| static CHY_INLINE void |
| lucy_NumUtil_encode_bigend_u32(uint32_t value, void *dest_ptr) { |
| uint8_t *dest = *(uint8_t**)dest_ptr; |
| #ifdef CHY_BIG_END |
| memcpy(dest, &value, sizeof(uint32_t)); |
| #else // little endian |
| uint8_t *source = (uint8_t*)&value; |
| dest[0] = source[3]; |
| dest[1] = source[2]; |
| dest[2] = source[1]; |
| dest[3] = source[0]; |
| #endif // CHY_BIG_END (and little endian) |
| } |
| |
| static CHY_INLINE void |
| lucy_NumUtil_encode_bigend_u64(uint64_t value, void *dest_ptr) { |
| uint8_t *dest = *(uint8_t**)dest_ptr; |
| #ifdef CHY_BIG_END |
| memcpy(dest, &value, sizeof(uint64_t)); |
| #else // little endian |
| uint8_t *source = (uint8_t*)&value; |
| dest[0] = source[7]; |
| dest[1] = source[6]; |
| dest[2] = source[5]; |
| dest[3] = source[4]; |
| dest[4] = source[3]; |
| dest[5] = source[2]; |
| dest[6] = source[1]; |
| dest[7] = source[0]; |
| #endif // CHY_BIG_END (and little endian) |
| } |
| |
| static CHY_INLINE uint16_t |
| lucy_NumUtil_decode_bigend_u16(void *source) { |
| uint8_t *const buf = (uint8_t*)source; |
| return (buf[0] << 8) | |
| (buf[1]); |
| } |
| |
| static CHY_INLINE uint32_t |
| lucy_NumUtil_decode_bigend_u32(void *source) { |
| uint8_t *const buf = (uint8_t*)source; |
| return (buf[0] << 24) | |
| (buf[1] << 16) | |
| (buf[2] << 8) | |
| (buf[3]); |
| } |
| |
| static CHY_INLINE uint64_t |
| lucy_NumUtil_decode_bigend_u64(void *source) { |
| uint8_t *const buf = (uint8_t*)source; |
| uint64_t high_bits = (buf[0] << 24) | |
| (buf[1] << 16) | |
| (buf[2] << 8) | |
| (buf[3]); |
| uint32_t low_bits = (buf[4] << 24) | |
| (buf[5] << 16) | |
| (buf[6] << 8) | |
| (buf[7]); |
| uint64_t retval = high_bits << 32; |
| retval |= low_bits; |
| return retval; |
| } |
| |
| static CHY_INLINE void |
| lucy_NumUtil_encode_bigend_f32(float value, void *dest_ptr) { |
| uint8_t *dest = *(uint8_t**)dest_ptr; |
| #ifdef CHY_BIG_END |
| memcpy(dest, &value, sizeof(float)); |
| #else |
| union { float f; uint32_t u32; } duo; |
| duo.f = value; |
| lucy_NumUtil_encode_bigend_u32(duo.u32, &dest); |
| #endif |
| } |
| |
| static CHY_INLINE void |
| lucy_NumUtil_encode_bigend_f64(double value, void *dest_ptr) { |
| uint8_t *dest = *(uint8_t**)dest_ptr; |
| #ifdef CHY_BIG_END |
| memcpy(dest, &value, sizeof(double)); |
| #else |
| union { double d; uint64_t u64; } duo; |
| duo.d = value; |
| lucy_NumUtil_encode_bigend_u64(duo.u64, &dest); |
| #endif |
| } |
| |
| static CHY_INLINE float |
| lucy_NumUtil_decode_bigend_f32(void *source) { |
| union { float f; uint32_t u32; } duo; |
| memcpy(&duo, source, sizeof(float)); |
| #ifdef CHY_LITTLE_END |
| duo.u32 = lucy_NumUtil_decode_bigend_u32(&duo.u32); |
| #endif |
| return duo.f; |
| } |
| |
| static CHY_INLINE double |
| lucy_NumUtil_decode_bigend_f64(void *source) { |
| union { double d; uint64_t u64; } duo; |
| memcpy(&duo, source, sizeof(double)); |
| #ifdef CHY_LITTLE_END |
| duo.u64 = lucy_NumUtil_decode_bigend_u64(&duo.u64); |
| #endif |
| return duo.d; |
| } |
| |
| #define LUCY_NUMUTIL_C32_MAX_BYTES ((sizeof(uint32_t) * 8 / 7) + 1) // 5 |
| #define LUCY_NUMUTIL_C64_MAX_BYTES ((sizeof(uint64_t) * 8 / 7) + 1) // 10 |
| |
| static CHY_INLINE void |
| lucy_NumUtil_encode_c32(uint32_t value, char **out_buf) { |
| uint8_t buf[LUCY_NUMUTIL_C32_MAX_BYTES]; |
| uint8_t *const limit = buf + sizeof(buf); |
| uint8_t *ptr = limit - 1; |
| int num_bytes; |
| // Write last byte first, which has no continue bit. |
| *ptr = value & 0x7f; |
| value >>= 7; |
| while (value) { |
| // Work backwards, writing bytes with continue bits set. |
| *--ptr = ((value & 0x7f) | 0x80); |
| value >>= 7; |
| } |
| num_bytes = limit - ptr; |
| memcpy(*out_buf, ptr, num_bytes); |
| *out_buf += num_bytes; |
| } |
| |
| static CHY_INLINE void |
| lucy_NumUtil_encode_c64(uint64_t value, char **out_buf) { |
| uint8_t buf[LUCY_NUMUTIL_C64_MAX_BYTES]; |
| uint8_t *const limit = buf + sizeof(buf); |
| uint8_t *ptr = limit - 1; |
| int num_bytes; |
| // Write last byte first, which has no continue bit. |
| *ptr = value & 0x7f; |
| value >>= 7; |
| while (value) { |
| // Work backwards, writing bytes with continue bits set. |
| *--ptr = ((value & 0x7f) | 0x80); |
| value >>= 7; |
| } |
| num_bytes = limit - ptr; |
| memcpy(*out_buf, ptr, num_bytes); |
| *out_buf += num_bytes; |
| } |
| |
| static CHY_INLINE void |
| lucy_NumUtil_encode_padded_c32(uint32_t value, char **out_buf) { |
| uint8_t buf[LUCY_NUMUTIL_C32_MAX_BYTES] |
| = { 0x80, 0x80, 0x80, 0x80, 0x80 }; |
| uint8_t *const limit = buf + sizeof(buf); |
| uint8_t *ptr = limit - 1; |
| // Write last byte first, which has no continue bit. |
| *ptr = value & 0x7f; |
| value >>= 7; |
| while (value) { |
| // Work backwards, writing bytes with continue bits set. |
| *--ptr = ((value & 0x7f) | 0x80); |
| value >>= 7; |
| } |
| memcpy(*out_buf, buf, LUCY_NUMUTIL_C32_MAX_BYTES); |
| *out_buf += sizeof(buf); |
| } |
| |
| // Decode a compressed integer up to size of 'var', advancing 'source' |
| #define LUCY_NUMUTIL_DECODE_CINT(var, source) \ |
| do { \ |
| var = (*source & 0x7f); \ |
| while (*source++ & 0x80) { \ |
| var = (*source & 0x7f) | (var << 7); \ |
| } \ |
| } while (0) |
| |
| static CHY_INLINE uint32_t |
| lucy_NumUtil_decode_c32(char **source_ptr) { |
| char *source = *source_ptr; |
| uint32_t decoded; |
| LUCY_NUMUTIL_DECODE_CINT(decoded, source); |
| *source_ptr = source; |
| return decoded; |
| } |
| |
| static CHY_INLINE uint64_t |
| lucy_NumUtil_decode_c64(char **source_ptr) { |
| char *source = *source_ptr; |
| uint64_t decoded; |
| LUCY_NUMUTIL_DECODE_CINT(decoded, source); |
| *source_ptr = source; |
| return decoded; |
| } |
| |
| static CHY_INLINE void |
| lucy_NumUtil_skip_cint(char **source_ptr) { |
| uint8_t *ptr = *(uint8_t**)source_ptr; |
| while ((*ptr++ & 0x80) != 0) { } |
| *source_ptr = (char*)ptr; |
| } |
| |
| static CHY_INLINE chy_bool_t |
| lucy_NumUtil_u1get(void *array, uint32_t tick) { |
| uint8_t *const u8bits = (uint8_t*)array; |
| const uint32_t byte_offset = tick >> 3; |
| const uint8_t mask = lucy_NumUtil_u1masks[tick & 0x7]; |
| return !((u8bits[byte_offset] & mask) == 0); |
| } |
| |
| static CHY_INLINE void |
| lucy_NumUtil_u1set(void *array, uint32_t tick) { |
| uint8_t *const u8bits = (uint8_t*)array; |
| const uint32_t byte_offset = tick >> 3; |
| const uint8_t mask = lucy_NumUtil_u1masks[tick & 0x7]; |
| u8bits[byte_offset] |= mask; |
| } |
| |
| static CHY_INLINE void |
| lucy_NumUtil_u1clear(void *array, uint32_t tick) { |
| uint8_t *const u8bits = (uint8_t*)array; |
| const uint32_t byte_offset = tick >> 3; |
| const uint8_t mask = lucy_NumUtil_u1masks[tick & 0x7]; |
| u8bits[byte_offset] &= ~mask; |
| } |
| |
| static CHY_INLINE void |
| lucy_NumUtil_u1flip(void *array, uint32_t tick) { |
| uint8_t *const u8bits = (uint8_t*)array; |
| const uint32_t byte_offset = tick >> 3; |
| const uint8_t mask = lucy_NumUtil_u1masks[tick & 0x7]; |
| u8bits[byte_offset] ^= mask; |
| } |
| |
| static CHY_INLINE uint8_t |
| lucy_NumUtil_u2get(void *array, uint32_t tick) { |
| uint8_t *ints = (uint8_t*)array; |
| uint8_t byte = ints[(tick >> 2)]; |
| int shift = lucy_NumUtil_u2shifts[tick & 0x3]; |
| return (byte >> shift) & 0x3; |
| } |
| |
| static CHY_INLINE void |
| lucy_NumUtil_u2set(void *array, uint32_t tick, uint8_t value) { |
| uint8_t *ints = (uint8_t*)array; |
| unsigned sub_tick = tick & 0x3; |
| int shift = lucy_NumUtil_u2shifts[sub_tick]; |
| uint8_t mask = lucy_NumUtil_u2masks[sub_tick]; |
| uint8_t new_val = value & 0x3; |
| uint8_t new_bits = new_val << shift; |
| ints[(tick >> 2)] = (ints[(tick >> 2)] & ~mask) | new_bits; |
| } |
| |
| |
| static CHY_INLINE uint8_t |
| lucy_NumUtil_u4get(void *array, uint32_t tick) { |
| uint8_t *ints = (uint8_t*)array; |
| uint8_t byte = ints[(tick >> 1)]; |
| int shift = lucy_NumUtil_u4shifts[(tick & 1)]; |
| return (byte >> shift) & 0xF; |
| } |
| |
| static CHY_INLINE void |
| lucy_NumUtil_u4set(void *array, uint32_t tick, uint8_t value) { |
| uint8_t *ints = (uint8_t*)array; |
| unsigned sub_tick = tick & 0x1; |
| int shift = lucy_NumUtil_u4shifts[sub_tick]; |
| uint8_t mask = lucy_NumUtil_u4masks[sub_tick]; |
| uint8_t new_val = value & 0xF; |
| uint8_t new_bits = new_val << shift; |
| ints[(tick >> 1)] = (ints[(tick >> 1)] & ~mask) | new_bits; |
| } |
| |
| #ifdef LUCY_USE_SHORT_NAMES |
| #define C32_MAX_BYTES LUCY_NUMUTIL_C32_MAX_BYTES |
| #define C64_MAX_BYTES LUCY_NUMUTIL_C64_MAX_BYTES |
| #endif |
| |
| __END_C__ |
| |
| |