// SPDX-FileCopyrightText: 2026 Filip Leonarski, Paul Scherrer Institute // SPDX-License-Identifier: GPL-3.0-only #include "CBFByteOffset.h" #include #include #if defined(__SSE2__) || defined(_M_X64) || defined(_M_AMD64) # include # define MINICBF_STREAM_STORE 1 #endif #include "../common/JFJochException.h" namespace { int16_t ReadI16(const uint8_t *p) { int16_t v; std::memcpy(&v, p, 2); return v; } int32_t ReadI32(const uint8_t *p) { int32_t v; std::memcpy(&v, p, 4); return v; } int64_t ReadI64(const uint8_t *p) { int64_t v; std::memcpy(&v, p, 8); return v; } // Nothing on this side reads the decoded image back: it goes to the GPU by DMA, or a CPU engine // walks it once and drops it. An ordinary store therefore pays twice over - it reads every cache // line before overwriting it, and it evicts 72 MB of live cache to make room - so the pixels go // straight past the cache. Measured on an 18 Mpx frame: 27 ms of ordinary stores against 11 ms of // these. The bytes written are the same either way; the fence is what makes them visible to the // copy that follows. void StorePixel(int32_t *out, int32_t value) { #ifdef MINICBF_STREAM_STORE _mm_stream_si32(out, value); #else *out = value; #endif } void StoreFence() { #ifdef MINICBF_STREAM_STORE _mm_sfence(); #endif } } // namespace // The x-CBF_BYTE_OFFSET scheme: a running value, each pixel stored as a delta in the smallest // container that holds it, escaping to the next size with that container's most negative value. // Following Bernstein & Hammersley (2006) Int. Tables Cryst. G, 37-43 void DecodeCBFByteOffset(const uint8_t *data, size_t size, int32_t *out, size_t n_pixels) { int64_t value = 0; size_t pos = 0; size_t written = 0; while (written < n_pixels) { if (pos >= size) throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "miniCBF byte-offset stream ended after " + std::to_string(written) + " of " + std::to_string(n_pixels) + " pixels"); int64_t delta = static_cast(data[pos]); pos += 1; if (delta == -128) { if (pos + 2 > size) break; delta = ReadI16(data + pos); pos += 2; if (delta == -32768) { if (pos + 4 > size) break; delta = ReadI32(data + pos); pos += 4; if (delta == INT32_MIN) { if (pos + 8 > size) break; delta = ReadI64(data + pos); pos += 8; } } } value += delta; StorePixel(out + written, static_cast(value)); written++; } StoreFence(); if (written != n_pixels) throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "miniCBF byte-offset stream ended after " + std::to_string(written) + " of " + std::to_string(n_pixels) + " pixels"); }