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);
}
Exemple #2
0
double read_double(context* ctx)
{
	double data = 0.0f;
	unsigned __int64 tmp = read_u64(ctx);
	::memcpy(&data,&tmp,8);
	return data;
}
Exemple #3
0
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);
	}
}
Exemple #4
0
	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);
}
Exemple #10
0
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());
 }