void MetroHash128::Finalize(uint8_t * const hash) { // finalize bulk loop, if used if (bytes >= 32) { state.v[2] ^= rotate_right(((state.v[0] + state.v[3]) * k0) + state.v[1], 21) * k1; state.v[3] ^= rotate_right(((state.v[1] + state.v[2]) * k1) + state.v[0], 21) * k0; state.v[0] ^= rotate_right(((state.v[0] + state.v[2]) * k0) + state.v[3], 21) * k1; state.v[1] ^= rotate_right(((state.v[1] + state.v[3]) * k1) + state.v[2], 21) * k0; } // process any bytes remaining in the input buffer const uint8_t * ptr = reinterpret_cast<const uint8_t*>(input.b); const uint8_t * const end = ptr + (bytes % 32); if ((end - ptr) >= 16) { state.v[0] += read_u64(ptr) * k2; ptr += 8; state.v[0] = rotate_right(state.v[0],33) * k3; state.v[1] += read_u64(ptr) * k2; ptr += 8; state.v[1] = rotate_right(state.v[1],33) * k3; state.v[0] ^= rotate_right((state.v[0] * k2) + state.v[1], 45) * k1; state.v[1] ^= rotate_right((state.v[1] * k3) + state.v[0], 45) * k0; } if ((end - ptr) >= 8) { state.v[0] += read_u64(ptr) * k2; ptr += 8; state.v[0] = rotate_right(state.v[0],33) * k3; state.v[0] ^= rotate_right((state.v[0] * k2) + state.v[1], 27) * k1; } if ((end - ptr) >= 4) { state.v[1] += read_u32(ptr) * k2; ptr += 4; state.v[1] = rotate_right(state.v[1],33) * k3; state.v[1] ^= rotate_right((state.v[1] * k3) + state.v[0], 46) * k0; } if ((end - ptr) >= 2) { state.v[0] += read_u16(ptr) * k2; ptr += 2; state.v[0] = rotate_right(state.v[0],33) * k3; state.v[0] ^= rotate_right((state.v[0] * k2) + state.v[1], 22) * k1; } if ((end - ptr) >= 1) { state.v[1] += read_u8 (ptr) * k2; state.v[1] = rotate_right(state.v[1],33) * k3; state.v[1] ^= rotate_right((state.v[1] * k3) + state.v[0], 58) * k0; } state.v[0] += rotate_right((state.v[0] * k0) + state.v[1], 13); state.v[1] += rotate_right((state.v[1] * k1) + state.v[0], 37); state.v[0] += rotate_right((state.v[0] * k2) + state.v[1], 13); state.v[1] += rotate_right((state.v[1] * k3) + state.v[0], 37); bytes = 0; // do any endian conversion here memcpy(hash, state.v, 16); }
double read_double(context* ctx) { double data = 0.0f; unsigned __int64 tmp = read_u64(ctx); ::memcpy(&data,&tmp,8); return data; }
static void read_u64_func(void** state) { uint64_t comp_buf = 0; uint32_t idx = 0; for(uint64_t i = 0; i < CHAR_SIZE - 2; i += 8) { comp_buf = (('a' + i) << 56) | (('b' + i) << 48) | (('c' + i) << 40) | (('d' + i) << 32) | (('e' + i) << 24) | (('f' + i) << 16) | (('g' + i) << 8) | ('h' + i); assert_int_equal(read_u64(buffer, &idx), comp_buf); } }
u32 xbinary_reader::read(u64 & b) { if (_can_read(len_, cursor_, sizeof(b))) { xbyte const* ptr = buffer_ + cursor_; cursor_ += sizeof(b); b = read_u64(ptr); return sizeof(b); } return 0; }
void MetroHash128::Update(const uint8_t * const buffer, const uint64_t length) { const uint8_t * ptr = reinterpret_cast<const uint8_t*>(buffer); const uint8_t * const end = ptr + length; // input buffer may be partially filled if (bytes % 32) { uint64_t fill = 32 - (bytes % 32); if (fill > length) fill = length; memcpy(input.b + (bytes % 32), ptr, static_cast<size_t>(fill)); ptr += fill; bytes += fill; // input buffer is still partially filled if ((bytes % 32) != 0) return; // process full input buffer state.v[0] += read_u64(&input.b[ 0]) * k0; state.v[0] = rotate_right(state.v[0],29) + state.v[2]; state.v[1] += read_u64(&input.b[ 8]) * k1; state.v[1] = rotate_right(state.v[1],29) + state.v[3]; state.v[2] += read_u64(&input.b[16]) * k2; state.v[2] = rotate_right(state.v[2],29) + state.v[0]; state.v[3] += read_u64(&input.b[24]) * k3; state.v[3] = rotate_right(state.v[3],29) + state.v[1]; } // bulk update bytes += (end - ptr); while (ptr <= (end - 32)) { // process directly from the source, bypassing the input buffer state.v[0] += read_u64(ptr) * k0; ptr += 8; state.v[0] = rotate_right(state.v[0],29) + state.v[2]; state.v[1] += read_u64(ptr) * k1; ptr += 8; state.v[1] = rotate_right(state.v[1],29) + state.v[3]; state.v[2] += read_u64(ptr) * k2; ptr += 8; state.v[2] = rotate_right(state.v[2],29) + state.v[0]; state.v[3] += read_u64(ptr) * k3; ptr += 8; state.v[3] = rotate_right(state.v[3],29) + state.v[1]; } // store remaining bytes in input buffer if (ptr < end) memcpy(input.b, ptr, end - ptr); }
void metrohash128crc_1(const uint8_t * key, uint64_t len, uint32_t seed, uint8_t * out) { static const uint64_t k0 = 0xC83A91E1; static const uint64_t k1 = 0x8648DBDB; static const uint64_t k2 = 0x7BDEC03B; static const uint64_t k3 = 0x2F5870A5; const uint8_t * ptr = reinterpret_cast<const uint8_t*>(key); const uint8_t * const end = ptr + len; uint64_t v[4]; v[0] = ((static_cast<uint64_t>(seed) - k0) * k3) + len; v[1] = ((static_cast<uint64_t>(seed) + k1) * k2) + len; if (len >= 32) { v[2] = ((static_cast<uint64_t>(seed) + k0) * k2) + len; v[3] = ((static_cast<uint64_t>(seed) - k1) * k3) + len; do { v[0] ^= _mm_crc32_u64(v[0], read_u64(ptr)); ptr += 8; v[1] ^= _mm_crc32_u64(v[1], read_u64(ptr)); ptr += 8; v[2] ^= _mm_crc32_u64(v[2], read_u64(ptr)); ptr += 8; v[3] ^= _mm_crc32_u64(v[3], read_u64(ptr)); ptr += 8; } while (ptr <= (end - 32)); v[2] ^= rotate_right(((v[0] + v[3]) * k0) + v[1], 34) * k1; v[3] ^= rotate_right(((v[1] + v[2]) * k1) + v[0], 37) * k0; v[0] ^= rotate_right(((v[0] + v[2]) * k0) + v[3], 34) * k1; v[1] ^= rotate_right(((v[1] + v[3]) * k1) + v[2], 37) * k0; } if ((end - ptr) >= 16) { v[0] += read_u64(ptr) * k2; ptr += 8; v[0] = rotate_right(v[0],34) * k3; v[1] += read_u64(ptr) * k2; ptr += 8; v[1] = rotate_right(v[1],34) * k3; v[0] ^= rotate_right((v[0] * k2) + v[1], 30) * k1; v[1] ^= rotate_right((v[1] * k3) + v[0], 30) * k0; } if ((end - ptr) >= 8) { v[0] += read_u64(ptr) * k2; ptr += 8; v[0] = rotate_right(v[0],36) * k3; v[0] ^= rotate_right((v[0] * k2) + v[1], 23) * k1; } if ((end - ptr) >= 4) { v[1] ^= _mm_crc32_u64(v[0], read_u32(ptr)); ptr += 4; v[1] ^= rotate_right((v[1] * k3) + v[0], 19) * k0; } if ((end - ptr) >= 2) { v[0] ^= _mm_crc32_u64(v[1], read_u16(ptr)); ptr += 2; v[0] ^= rotate_right((v[0] * k2) + v[1], 13) * k1; } if ((end - ptr) >= 1) { v[1] ^= _mm_crc32_u64(v[0], read_u8 (ptr)); v[1] ^= rotate_right((v[1] * k3) + v[0], 17) * k0; } v[0] += rotate_right((v[0] * k0) + v[1], 11); v[1] += rotate_right((v[1] * k1) + v[0], 26); v[0] += rotate_right((v[0] * k0) + v[1], 11); v[1] += rotate_right((v[1] * k1) + v[0], 26); memcpy(out, v, 16); }
void metrohash128crc_2(const uint8_t * key, uint64_t len, uint32_t seed, uint8_t * out) { static const uint64_t k0 = 0xEE783E2F; static const uint64_t k1 = 0xAD07C493; static const uint64_t k2 = 0x797A90BB; static const uint64_t k3 = 0x2E4B2E1B; const uint8_t * ptr = reinterpret_cast<const uint8_t*>(key); const uint8_t * const end = ptr + len; uint64_t v[4]; v[0] = ((static_cast<uint64_t>(seed) - k0) * k3) + len; v[1] = ((static_cast<uint64_t>(seed) + k1) * k2) + len; if (len >= 32) { v[2] = ((static_cast<uint64_t>(seed) + k0) * k2) + len; v[3] = ((static_cast<uint64_t>(seed) - k1) * k3) + len; do { v[0] ^= _mm_crc32_u64(v[0], read_u64(ptr)); ptr += 8; v[1] ^= _mm_crc32_u64(v[1], read_u64(ptr)); ptr += 8; v[2] ^= _mm_crc32_u64(v[2], read_u64(ptr)); ptr += 8; v[3] ^= _mm_crc32_u64(v[3], read_u64(ptr)); ptr += 8; } while (ptr <= (end - 32)); v[2] ^= rotate_right(((v[0] + v[3]) * k0) + v[1], 12) * k1; v[3] ^= rotate_right(((v[1] + v[2]) * k1) + v[0], 19) * k0; v[0] ^= rotate_right(((v[0] + v[2]) * k0) + v[3], 12) * k1; v[1] ^= rotate_right(((v[1] + v[3]) * k1) + v[2], 19) * k0; } if ((end - ptr) >= 16) { v[0] += read_u64(ptr) * k2; ptr += 8; v[0] = rotate_right(v[0],41) * k3; v[1] += read_u64(ptr) * k2; ptr += 8; v[1] = rotate_right(v[1],41) * k3; v[0] ^= rotate_right((v[0] * k2) + v[1], 10) * k1; v[1] ^= rotate_right((v[1] * k3) + v[0], 10) * k0; } if ((end - ptr) >= 8) { v[0] += read_u64(ptr) * k2; ptr += 8; v[0] = rotate_right(v[0],34) * k3; v[0] ^= rotate_right((v[0] * k2) + v[1], 22) * k1; } if ((end - ptr) >= 4) { v[1] ^= _mm_crc32_u64(v[0], read_u32(ptr)); ptr += 4; v[1] ^= rotate_right((v[1] * k3) + v[0], 14) * k0; } if ((end - ptr) >= 2) { v[0] ^= _mm_crc32_u64(v[1], read_u16(ptr)); ptr += 2; v[0] ^= rotate_right((v[0] * k2) + v[1], 15) * k1; } if ((end - ptr) >= 1) { v[1] ^= _mm_crc32_u64(v[0], read_u8 (ptr)); v[1] ^= rotate_right((v[1] * k3) + v[0], 18) * k0; } v[0] += rotate_right((v[0] * k0) + v[1], 15); v[1] += rotate_right((v[1] * k1) + v[0], 27); v[0] += rotate_right((v[0] * k0) + v[1], 15); v[1] += rotate_right((v[1] * k1) + v[0], 27); memcpy(out, v, 16); }
void metrohash128_2(const uint8_t * key, uint64_t len, uint32_t seed, uint8_t * out) { static const uint64_t k0 = 0xD6D018F5; static const uint64_t k1 = 0xA2AA033B; static const uint64_t k2 = 0x62992FC1; static const uint64_t k3 = 0x30BC5B29; const uint8_t * ptr = reinterpret_cast<const uint8_t*>(key); const uint8_t * const end = ptr + len; uint64_t v[4]; v[0] = ((static_cast<uint64_t>(seed) - k0) * k3) + len; v[1] = ((static_cast<uint64_t>(seed) + k1) * k2) + len; if (len >= 32) { v[2] = ((static_cast<uint64_t>(seed) + k0) * k2) + len; v[3] = ((static_cast<uint64_t>(seed) - k1) * k3) + len; do { v[0] += read_u64(ptr) * k0; ptr += 8; v[0] = rotate_right(v[0],29) + v[2]; v[1] += read_u64(ptr) * k1; ptr += 8; v[1] = rotate_right(v[1],29) + v[3]; v[2] += read_u64(ptr) * k2; ptr += 8; v[2] = rotate_right(v[2],29) + v[0]; v[3] += read_u64(ptr) * k3; ptr += 8; v[3] = rotate_right(v[3],29) + v[1]; } while (ptr <= (end - 32)); v[2] ^= rotate_right(((v[0] + v[3]) * k0) + v[1], 33) * k1; v[3] ^= rotate_right(((v[1] + v[2]) * k1) + v[0], 33) * k0; v[0] ^= rotate_right(((v[0] + v[2]) * k0) + v[3], 33) * k1; v[1] ^= rotate_right(((v[1] + v[3]) * k1) + v[2], 33) * k0; } if ((end - ptr) >= 16) { v[0] += read_u64(ptr) * k2; ptr += 8; v[0] = rotate_right(v[0],29) * k3; v[1] += read_u64(ptr) * k2; ptr += 8; v[1] = rotate_right(v[1],29) * k3; v[0] ^= rotate_right((v[0] * k2) + v[1], 29) * k1; v[1] ^= rotate_right((v[1] * k3) + v[0], 29) * k0; } if ((end - ptr) >= 8) { v[0] += read_u64(ptr) * k2; ptr += 8; v[0] = rotate_right(v[0],29) * k3; v[0] ^= rotate_right((v[0] * k2) + v[1], 29) * k1; } if ((end - ptr) >= 4) { v[1] += read_u32(ptr) * k2; ptr += 4; v[1] = rotate_right(v[1],29) * k3; v[1] ^= rotate_right((v[1] * k3) + v[0], 25) * k0; } if ((end - ptr) >= 2) { v[0] += read_u16(ptr) * k2; ptr += 2; v[0] = rotate_right(v[0],29) * k3; v[0] ^= rotate_right((v[0] * k2) + v[1], 30) * k1; } if ((end - ptr) >= 1) { v[1] += read_u8 (ptr) * k2; v[1] = rotate_right(v[1],29) * k3; v[1] ^= rotate_right((v[1] * k3) + v[0], 18) * k0; } v[0] += rotate_right((v[0] * k0) + v[1], 33); v[1] += rotate_right((v[1] * k1) + v[0], 33); v[0] += rotate_right((v[0] * k2) + v[1], 33); v[1] += rotate_right((v[1] * k3) + v[0], 33); // do any endian conversion here memcpy(out, v, 16); }
void MetroHash128::Hash(const uint8_t * buffer, const uint64_t length, uint8_t * const hash, const uint64_t seed) { const uint8_t * ptr = reinterpret_cast<const uint8_t*>(buffer); const uint8_t * const end = ptr + length; uint64_t v[4]; v[0] = (static_cast<uint64_t>(seed) - k0) * k3; v[1] = (static_cast<uint64_t>(seed) + k1) * k2; if (length >= 32) { v[2] = (static_cast<uint64_t>(seed) + k0) * k2; v[3] = (static_cast<uint64_t>(seed) - k1) * k3; do { v[0] += read_u64(ptr) * k0; ptr += 8; v[0] = rotate_right(v[0],29) + v[2]; v[1] += read_u64(ptr) * k1; ptr += 8; v[1] = rotate_right(v[1],29) + v[3]; v[2] += read_u64(ptr) * k2; ptr += 8; v[2] = rotate_right(v[2],29) + v[0]; v[3] += read_u64(ptr) * k3; ptr += 8; v[3] = rotate_right(v[3],29) + v[1]; } while (ptr <= (end - 32)); v[2] ^= rotate_right(((v[0] + v[3]) * k0) + v[1], 21) * k1; v[3] ^= rotate_right(((v[1] + v[2]) * k1) + v[0], 21) * k0; v[0] ^= rotate_right(((v[0] + v[2]) * k0) + v[3], 21) * k1; v[1] ^= rotate_right(((v[1] + v[3]) * k1) + v[2], 21) * k0; } if ((end - ptr) >= 16) { v[0] += read_u64(ptr) * k2; ptr += 8; v[0] = rotate_right(v[0],33) * k3; v[1] += read_u64(ptr) * k2; ptr += 8; v[1] = rotate_right(v[1],33) * k3; v[0] ^= rotate_right((v[0] * k2) + v[1], 45) * k1; v[1] ^= rotate_right((v[1] * k3) + v[0], 45) * k0; } if ((end - ptr) >= 8) { v[0] += read_u64(ptr) * k2; ptr += 8; v[0] = rotate_right(v[0],33) * k3; v[0] ^= rotate_right((v[0] * k2) + v[1], 27) * k1; } if ((end - ptr) >= 4) { v[1] += read_u32(ptr) * k2; ptr += 4; v[1] = rotate_right(v[1],33) * k3; v[1] ^= rotate_right((v[1] * k3) + v[0], 46) * k0; } if ((end - ptr) >= 2) { v[0] += read_u16(ptr) * k2; ptr += 2; v[0] = rotate_right(v[0],33) * k3; v[0] ^= rotate_right((v[0] * k2) + v[1], 22) * k1; } if ((end - ptr) >= 1) { v[1] += read_u8 (ptr) * k2; v[1] = rotate_right(v[1],33) * k3; v[1] ^= rotate_right((v[1] * k3) + v[0], 58) * k0; } v[0] += rotate_right((v[0] * k0) + v[1], 13); v[1] += rotate_right((v[1] * k1) + v[0], 37); v[0] += rotate_right((v[0] * k2) + v[1], 13); v[1] += rotate_right((v[1] * k3) + v[0], 37); // do any endian conversion here memcpy(hash, v, 16); }
bool db_dwarf_line_at_pc(const char *linetab, size_t linetabsize, uintptr_t pc, const char **outdirname, const char **outbasename, int *outline) { struct dwbuf table = { .buf = linetab, .len = linetabsize }; /* * For simplicity, we simply brute force search through the entire * line table each time. */ uint32_t unitsize; struct dwbuf unit; next: /* Line tables are a sequence of compilation unit entries. */ if (!read_u32(&table, &unitsize) || unitsize >= 0xfffffff0 || !read_buf(&table, &unit, unitsize)) return (false); uint16_t version; uint32_t header_size; if (!read_u16(&unit, &version) || version > 2 || !read_u32(&unit, &header_size)) goto next; struct dwbuf headerstart = unit; uint8_t min_insn_length, default_is_stmt, line_range, opcode_base; int8_t line_base; if (!read_u8(&unit, &min_insn_length) || !read_u8(&unit, &default_is_stmt) || !read_s8(&unit, &line_base) || !read_u8(&unit, &line_range) || !read_u8(&unit, &opcode_base)) goto next; /* * Directory and file names are next in the header, but for now we * skip directly to the line number program. */ struct dwbuf names = unit; unit = headerstart; if (!skip_bytes(&unit, header_size)) return (false); /* VM registers. */ uint64_t address = 0, file = 1, line = 1, column = 0; uint8_t is_stmt = default_is_stmt; bool basic_block = false, end_sequence = false; bool prologue_end = false, epilogue_begin = false; /* Last line table entry emitted, if any. */ bool have_last = false; uint64_t last_line = 0, last_file = 0; /* Time to run the line program. */ uint8_t opcode; while (read_u8(&unit, &opcode)) { bool emit = false, reset_basic_block = false; if (opcode >= opcode_base) { /* "Special" opcodes. */ uint8_t diff = opcode - opcode_base; address += diff / line_range; line += line_base + diff % line_range; emit = true; } else if (opcode == 0) { /* "Extended" opcodes. */ uint64_t extsize; struct dwbuf extra; if (!read_uleb128(&unit, &extsize) || !read_buf(&unit, &extra, extsize) || !read_u8(&extra, &opcode)) goto next; switch (opcode) { case DW_LNE_end_sequence: emit = true; end_sequence = true; break; case DW_LNE_set_address: switch (extra.len) { case 4: { uint32_t address32; if (!read_u32(&extra, &address32)) goto next; address = address32; break; } case 8: if (!read_u64(&extra, &address)) goto next; break; default: DWARN("unexpected address length: %zu", extra.len); goto next; } break; case DW_LNE_define_file: /* XXX: hope this isn't needed */ default: DWARN("unknown extended opcode: %d", opcode); goto next; } } else { /* "Standard" opcodes. */ switch (opcode) { case DW_LNS_copy: emit = true; reset_basic_block = true; break; case DW_LNS_advance_pc: { uint64_t delta; if (!read_uleb128(&unit, &delta)) goto next; address += delta * min_insn_length; break; } case DW_LNS_advance_line: { int64_t delta; if (!read_sleb128(&unit, &delta)) goto next; line += delta; break; } case DW_LNS_set_file: if (!read_uleb128(&unit, &file)) goto next; break; case DW_LNS_set_column: if (!read_uleb128(&unit, &column)) goto next; break; case DW_LNS_negate_stmt: is_stmt = !is_stmt; break; case DW_LNS_set_basic_block: basic_block = true; break; case DW_LNS_const_add_pc: address += (255 - opcode_base) / line_range; break; case DW_LNS_set_prologue_end: prologue_end = true; break; case DW_LNS_set_epilogue_begin: epilogue_begin = true; break; default: DWARN("unknown standard opcode: %d", opcode); goto next; } } if (emit) { if (address > pc) { /* Found an entry after our target PC. */ if (!have_last) { /* Give up on this program. */ break; } /* Return the last entry. */ *outline = last_line; return (read_filename(&names, outdirname, outbasename, opcode_base, file)); } last_file = file; last_line = line; have_last = true; } if (reset_basic_block) basic_block = false; } goto next; }
void metrohash64crc_1(const uint8_t * key, uint64_t len, uint32_t seed, uint8_t * out) { static const uint64_t k0 = 0xC83A91E1; static const uint64_t k1 = 0x8648DBDB; static const uint64_t k2 = 0x7BDEC03B; static const uint64_t k3 = 0x2F5870A5; const uint8_t * ptr = reinterpret_cast<const uint8_t*>(key); const uint8_t * const end = ptr + len; uint64_t hash = ((static_cast<uint64_t>(seed) + k2) * k0) + len; if (len >= 32) { uint64_t v[4]; v[0] = hash; v[1] = hash; v[2] = hash; v[3] = hash; do { v[0] ^= _mm_crc32_u64(v[0], read_u64(ptr)); ptr += 8; v[1] ^= _mm_crc32_u64(v[1], read_u64(ptr)); ptr += 8; v[2] ^= _mm_crc32_u64(v[2], read_u64(ptr)); ptr += 8; v[3] ^= _mm_crc32_u64(v[3], read_u64(ptr)); ptr += 8; } while (ptr <= (end - 32)); v[2] ^= rotate_right(((v[0] + v[3]) * k0) + v[1], 33) * k1; v[3] ^= rotate_right(((v[1] + v[2]) * k1) + v[0], 33) * k0; v[0] ^= rotate_right(((v[0] + v[2]) * k0) + v[3], 33) * k1; v[1] ^= rotate_right(((v[1] + v[3]) * k1) + v[2], 33) * k0; hash += v[0] ^ v[1]; } if ((end - ptr) >= 16) { uint64_t v0 = hash + (read_u64(ptr) * k0); ptr += 8; v0 = rotate_right(v0,33) * k1; uint64_t v1 = hash + (read_u64(ptr) * k1); ptr += 8; v1 = rotate_right(v1,33) * k2; v0 ^= rotate_right(v0 * k0, 35) + v1; v1 ^= rotate_right(v1 * k3, 35) + v0; hash += v1; } if ((end - ptr) >= 8) { hash += read_u64(ptr) * k3; ptr += 8; hash ^= rotate_right(hash, 33) * k1; } if ((end - ptr) >= 4) { hash ^= _mm_crc32_u64(hash, read_u32(ptr)); ptr += 4; hash ^= rotate_right(hash, 15) * k1; } if ((end - ptr) >= 2) { hash ^= _mm_crc32_u64(hash, read_u16(ptr)); ptr += 2; hash ^= rotate_right(hash, 13) * k1; } if ((end - ptr) >= 1) { hash ^= _mm_crc32_u64(hash, read_u8(ptr)); hash ^= rotate_right(hash, 25) * k1; } hash ^= rotate_right(hash, 33); hash *= k0; hash ^= rotate_right(hash, 33); memcpy(out, &hash, 8); }
virtual uint32_t read_u32() override { return static_cast<uint32_t>(read_u64()); }