Everett
Loading...
Searching...
No Matches
key_detail.h
Go to the documentation of this file.
1
12#pragma once
13
14#include <everett/backend.h>
15
16#include <bit>
17#include <cstddef>
18#include <cstdint>
19#include <cstring>
20
22 // All loads remain within count bytes; neither alignment nor padding is assumed.
23 template <simd::architecture Arch = simd::scalar>
24 simd_inline std::size_t common_bytes(void const * lhs, void const * rhs, std::size_t count) noexcept {
25 auto a = static_cast<unsigned char const *>(lhs);
26 auto b = static_cast<unsigned char const *>(rhs);
27 std::size_t at = 0;
28 if constexpr (!std::is_same_v<Arch, simd::scalar>) {
29 constexpr auto lanes = backend_detail::register_bytes<Arch>;
30 using V = simd::vec<std::uint8_t, lanes, Arch>;
31 for (; count - at >= lanes; at += lanes) {
32 auto different = V::load(a + at) != V::load(b + at);
33 if constexpr (std::is_same_v<Arch, simd::neon>) {
34 // A cheap horizontal test retains the scalar mismatch locator.
35 if (any(different)) break;
36 } else if (auto bits = different.to_bitset()) return at + std::countr_zero(bits);
37 }
38 }
39 for (; count - at >= 8; at += 8) {
40 std::uint64_t x, y;
41 std::memcpy(&x, a + at, 8); std::memcpy(&y, b + at, 8);
42 if (auto different = x ^ y) {
43 if constexpr (std::endian::native == std::endian::little) return at + (std::countr_zero(different) >> 3);
44 else if constexpr (std::endian::native == std::endian::big) return at + (std::countl_zero(different) >> 3);
45 else break;
46 }
47 }
48 while (at != count && a[at] == b[at]) ++at;
49 return at;
50 }
51
52 inline std::uint64_t reverse_bytes(std::uint64_t x) noexcept {
53 x = ((x & 0x00ff00ff00ff00ffull) << 8) | ((x >> 8) & 0x00ff00ff00ff00ffull);
54 x = ((x & 0x0000ffff0000ffffull) << 16) | ((x >> 16) & 0x0000ffff0000ffffull);
55 return (x << 32) | (x >> 32);
56 }
57 inline std::uint64_t load_big(void const * source) noexcept {
58 if constexpr (std::endian::native == std::endian::little || std::endian::native == std::endian::big) {
59 std::uint64_t value; std::memcpy(&value, source, 8);
60 if constexpr (std::endian::native == std::endian::little) return reverse_bytes(value);
61 else return value;
62 } else {
63 auto bytes = static_cast<unsigned char const *>(source);
64 std::uint64_t value = 0;
65 for (unsigned i = 0; i != 8; ++i) value = (value << 8) | bytes[i];
66 return value;
67 }
68 }
69 inline void store_big(void * target, std::uint64_t value) noexcept {
70 if constexpr (std::endian::native == std::endian::little) {
71 value = reverse_bytes(value); std::memcpy(target, &value, 8);
72 } else if constexpr (std::endian::native == std::endian::big) std::memcpy(target, &value, 8);
73 else {
74 auto bytes = static_cast<unsigned char *>(target);
75 for (unsigned i = 0; i != 8; ++i) bytes[i] = static_cast<unsigned char>(value >> (56 - (i << 3)));
76 }
77 }
78}
Shares explicit SIMD architecture traits with Everett's kernels.
Definition key_detail.h:21
std::uint64_t load_big(void const *source) noexcept
Definition key_detail.h:57
std::uint64_t reverse_bytes(std::uint64_t x) noexcept
Definition key_detail.h:52
std::size_t common_bytes(void const *lhs, void const *rhs, std::size_t count) noexcept
Definition key_detail.h:24
void store_big(void *target, std::uint64_t value) noexcept
Definition key_detail.h:69