26 for (std::size_t i = 0; i < count; ++i) {
27 const auto delta = deltas[i];
31 if (delta > std::numeric_limits<VertexId>::max() - value)
32 throw std::overflow_error(
"vectorized fixed-width delta decode overflow");
39inline std::size_t
unpack_scalar(
const std::uint8_t* input, std::size_t count,
40 std::uint8_t lane_bytes, std::uint32_t* deltas) {
41 for (std::size_t i = 0; i < count; ++i) {
42 std::uint32_t value = 0;
43 for (std::uint8_t b = 0; b < lane_bytes; ++b)
44 value |=
static_cast<std::uint32_t
>(input[i * lane_bytes + b]) << (8U * b);
50#if (defined(__GNUC__) || defined(__clang__)) && \
51 (defined(__x86_64__) || defined(__i386__))
52__attribute__((target(
"avx2")))
53inline std::
size_t unpack_avx2(const std::uint8_t* input, std::
size_t count,
54 std::uint8_t lane_bytes, std::uint32_t* deltas) {
56 if (lane_bytes == 1) {
57 for (; i + 8 <= count; i += 8) {
58 const __m128i bytes = _mm_loadl_epi64(
reinterpret_cast<const __m128i*
>(input + i));
59 const __m256i values = _mm256_cvtepu8_epi32(bytes);
60 _mm256_storeu_si256(
reinterpret_cast<__m256i*
>(deltas + i), values);
62 }
else if (lane_bytes == 2) {
63 for (; i + 8 <= count; i += 8) {
64 const __m128i words = _mm_loadu_si128(
reinterpret_cast<const __m128i*
>(input + i * 2));
65 const __m256i values = _mm256_cvtepu16_epi32(words);
66 _mm256_storeu_si256(
reinterpret_cast<__m256i*
>(deltas + i), values);
68 }
else if (lane_bytes == 4) {
69 for (; i + 8 <= count; i += 8) {
70 const __m256i values = _mm256_loadu_si256(
reinterpret_cast<const __m256i*
>(input + i * 4));
71 _mm256_storeu_si256(
reinterpret_cast<__m256i*
>(deltas + i), values);
74 if (i < count)
unpack_scalar(input + i * lane_bytes, count - i, lane_bytes, deltas + i);
79#if defined(__GNUC__) || defined(__clang__)
80 return __builtin_cpu_supports(
"avx2");
89#if defined(__aarch64__) || defined(__ARM_NEON)
90inline std::size_t unpack_neon(
const std::uint8_t* input, std::size_t count,
91 std::uint8_t lane_bytes, std::uint32_t* deltas) {
93 if (lane_bytes == 1) {
94 for (; i + 8 <= count; i += 8) {
95 const uint8x8_t b = vld1_u8(input + i);
96 const uint16x8_t w = vmovl_u8(b);
97 vst1q_u32(deltas + i, vmovl_u16(vget_low_u16(w)));
98 vst1q_u32(deltas + i + 4, vmovl_u16(vget_high_u16(w)));
100 }
else if (lane_bytes == 2) {
101 for (; i + 8 <= count; i += 8) {
102 const uint16x8_t w = vld1q_u16(
reinterpret_cast<const std::uint16_t*
>(input + i * 2));
103 vst1q_u32(deltas + i, vmovl_u16(vget_low_u16(w)));
104 vst1q_u32(deltas + i + 4, vmovl_u16(vget_high_u16(w)));
106 }
else if (lane_bytes == 4) {
107 for (; i + 8 <= count; i += 8) {
108 vst1q_u32(deltas + i, vld1q_u32(
reinterpret_cast<const std::uint32_t*
>(input + i * 4)));
109 vst1q_u32(deltas + i + 4, vld1q_u32(
reinterpret_cast<const std::uint32_t*
>(input + (i + 4) * 4)));
112 if (i < count)
unpack_scalar(input + i * lane_bytes, count - i, lane_bytes, deltas + i);
136 std::size_t expected_value_offset = 0;
137 std::size_t expected_payload_offset = 0;
140 for (
const auto& block : encoded.
blocks) {
141 if (block.value_offset != expected_value_offset || block.payload_offset != expected_payload_offset)
142 throw std::invalid_argument(
"non-contiguous fixed-width block metadata");
143 if (block.value_count == 0 || block.value_offset + block.value_count > encoded.
value_count)
144 throw std::invalid_argument(
"invalid fixed-width block value bounds");
145 if (block.lane_bytes != 1 && block.lane_bytes != 2 && block.lane_bytes != 4)
146 throw std::invalid_argument(
"invalid fixed-width lane size");
147 const std::size_t block_bytes = block.value_count *
static_cast<std::size_t
>(block.lane_bytes);
148 if (block.payload_offset > encoded.
payload.size() || block_bytes > encoded.
payload.size() - block.payload_offset)
149 throw std::invalid_argument(
"truncated fixed-width block");
151 std::vector<std::uint32_t> deltas(block.value_count);
152 const auto* input = encoded.
payload.data() + block.payload_offset;
154#if (defined(__GNUC__) || defined(__clang__)) && \
155 (defined(__x86_64__) || defined(__i386__))
156 detail::unpack_avx2(input, block.value_count, block.lane_bytes, deltas.data());
161#if defined(__aarch64__) || defined(__ARM_NEON)
162 detail::unpack_neon(input, block.value_count, block.lane_bytes, deltas.data());
171 expected_value_offset += block.value_count;
172 expected_payload_offset += block_bytes;
175 if (expected_value_offset != encoded.
value_count || expected_payload_offset != encoded.
payload.size())
176 throw std::invalid_argument(
"fixed-width adjacency metadata mismatch");