// SPDX-FileCopyrightText: 2026 Filip Leonarski, Paul Scherrer Institute // SPDX-License-Identifier: GPL-3.0-only #include "CBFByteOffset.h" #include #include #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; } } // 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; out[written] = static_cast(value); written++; } 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"); }