Orion
high-rate readout
bitstream.hpp
1 #pragma once
2 
3 #include <cassert>
4 #include <cstdint>
5 #include <type_traits>
6 #include <bitset>
7 
8 namespace itk::itkpix::endec::codec {
9 
10 template <typename T>
11 [[gnu::always_inline]] static inline constexpr T bit_extract(T value, std::size_t start, std::size_t len)
12  requires(std::is_unsigned_v<T>)
13 {
14  T result;
15  result = (value >> start) & ((T(1) << len) - 1);
16  return result;
17 }
18 
19 template <typename T>
20 [[gnu::always_inline]] static inline constexpr void bit_insert(T value, T &dst, std::size_t start,
21  std::size_t len)
22  requires(std::is_unsigned_v<T>)
23 {
24  T extracted_bits = bit_extract(value, 0, len);
25  T mask = ((T(1) << len) - 1) << start;
26  dst &= ~(mask);
27  dst |= (extracted_bits << start);
28 }
29 /*
30 template <class T>
31 concept UnsignedArray = (sizeof(typename T::value_type) == 8) &&
32  std::unsigned_integral<typename T::value_type> && requires(T a, std::size_t b) {
33  { a.size() } -> std::same_as<std::size_t>;
34  { a[b] };
35  };
36 
37 template <class T>
38 concept UnsignedVector =
39  (sizeof(typename T::value_type) == 8 || sizeof(typename T::value_type) == 4) &&
40  std::unsigned_integral<typename T::value_type> && requires(T a, T::value_type b, std::size_t c) {
41  { a.size() } -> std::same_as<std::size_t>;
42  { a.push_back(b) } -> std::same_as<void>;
43  { a[c] };
44  };
45 */
46 
47 template <typename Buffer>
48  // requires UnsignedVector<Buffer> || UnsignedArray<Buffer>
50 
51  BitStreamReader(const BitStreamReader &other)
52  : data(other.data),
53  size(other.size),
54  Offset(other.Offset),
55  _index(Offset),
56  nBitsInWord(other.nBitsInWord) {}
57 
58  using value_type = uint64_t;
59  using result_type = std::pair<bool, value_type>;
60 // using buffer_type = Buffer;
61  static constexpr std::size_t nBits = 64;
62  explicit BitStreamReader(const std::span<const uint64_t> data, std::size_t offset)
63  : data(data), size(data.size() * nBits), Offset(offset) {}
64 
65  [[gnu::always_inline]] [[nodiscard]] uint64_t first_word() const { return data[0]; }
66 
67  [[gnu::always_inline]] [[nodiscard]] std::size_t left() const {
68  return (_index > size) ? 0 : size - _index;
69  }
70 
71  [[gnu::always_inline]] [[nodiscard]] std::size_t index() const { return _index; }
72 
73  [[gnu::always_inline]] bool set(std::size_t N) {
74  if (N >= size) return false;
75  _index = N + Offset;
76  return true;
77  }
78 
79  [[gnu::always_inline]] inline bool advance(std::size_t const N) {
80  if (_index + N > size) return false;
81  auto i_bit = _index % nBits;
82  auto left = nBits - i_bit;
83 
84  if (N <= left) {
85  _index += N;
86  if (N == left) _index += Offset;
87  return true;
88  }
89  auto w2 = N - left;
90  if (w2 <= nBitsInWord) {
91  _index += (N + Offset);
92  if (w2 == nBitsInWord) _index += Offset;
93  return true;
94  }
95  _index += (2 * Offset + N);
96  return true;
97  }
98 
99  template <bool Advance>
100  [[gnu::always_inline]] inline result_type _fetch(std::size_t const N) {
101  value_type value{};
102  if (_index + N > size) return {false, value};
103  auto i_word = _index / nBits;
104  auto i_bit = _index % nBits;
105  auto left = nBits - i_bit;
106  if (N <= left) {
107  value = bit_extract(data[i_word], left - N, N);
108  if constexpr (Advance) {
109  _index += N;
110  if (N == left) _index += Offset;
111  }
112  return {true, value};
113  }
114  auto w2 = N - left;
115  if (w2 <= nBitsInWord) {
116  auto word_1 = bit_extract(data[i_word], 0, left);
117  auto word_2 = bit_extract(data[i_word + 1], nBitsInWord - w2, w2);
118  value = word_2 | (word_1 << w2);
119  if constexpr (Advance) {
120  _index += (N + Offset);
121  if (w2 == nBitsInWord) _index += Offset;
122  }
123  return {true, value};
124  }
125  auto w3 = (N - left) - nBitsInWord;
126  auto word_1 = bit_extract(data[i_word], 0, left);
127  auto word_2 = bit_extract(data[i_word + 1], 0, nBitsInWord);
128  auto word_3 = bit_extract(data[i_word + 2], nBitsInWord - w3, w3);
129  value = (word_1 << i_bit) | (word_2 << (i_bit - nBitsInWord)) | word_3;
130  if constexpr (Advance) {
131  _index += (2 * Offset + N);
132  }
133  return {true, value};
134  }
135 
136  [[gnu::always_inline]] inline result_type prefetch(const std::size_t sz) {
137  return _fetch<false>(sz);
138  }
139 
140  [[gnu::always_inline]] inline result_type fetch(const std::size_t sz) {
141  return _fetch<true>(sz);
142  }
143 
144  std::span<const uint64_t> data;
145  const std::size_t size;
146  const std::size_t Offset;
147  std::size_t _index{Offset};
148  const std::size_t nBitsInWord = nBits - Offset;
149 };
150 
151 /*
152 
153 template <typename Buffer>
154  requires UnsignedArray<Buffer> || UnsignedVector<Buffer>
155 struct BitStreamWriter {
156  BitStreamWriter(const BitStreamWriter &other)
157  : data(other.data), _size(other._size), Offset(other.Offset), index(other.index) {}
158 
159  BitStreamWriter &operator=(const BitStreamWriter &other) {
160  if (&other != this) {
161  data = other.data;
162  _size = other._size;
163  Offset = other.Offset;
164  index = other.index;
165  }
166  return *this;
167  }
168  using value_type = typename Buffer::value_type;
169  using buffer_type = Buffer;
170  static inline constexpr std::size_t nBits = sizeof(value_type) * 8;
171  static inline constexpr bool has_push_back() { return UnsignedVector<Buffer>; }
172  explicit BitStreamWriter(Buffer &data, const std::size_t offset)
173  : data(data), _size((data.size()) * nBits), Offset(offset) {}
174 
175  [[gnu::always_inline]] [[nodiscard]] std::size_t left() const { return size() - index; }
176 
177  [[gnu::always_inline]] inline bool write(value_type value, std::size_t N) {
178  if constexpr (has_push_back()) {
179  if (index + N > size()) {
180  data.push_back(0);
181  _size += nBits;
182  }
183  } else {
184  if (index + N > size()) return false;
185  }
186  auto i_word = index / nBits;
187  auto i_bit = index % nBits;
188  auto left = nBits - i_bit;
189  if (N <= left) {
190  itkpix::codec::bit_insert(value, data[i_word], left - N, N);
191  index += N;
192  if (N == left) index += Offset;
193  return true;
194  }
195  auto w2 = N - left;
196  if (w2 <= nBitsInWord) {
197  auto word_1 = value >> w2;
198  auto word_2 = value;
199  itkpix::codec::bit_insert(word_1, data[i_word], 0, left);
200  itkpix::codec::bit_insert(word_2, data[i_word + 1], nBitsInWord - w2, w2);
201  index += (N + Offset);
202  if (w2 == nBitsInWord) index += Offset;
203  return true;
204  }
205  auto w3 = (N - left) - nBitsInWord;
206  auto word_1 = value >> i_bit;
207  auto word_2 = value >> (Offset + i_bit);
208  auto word_3 = value;
209  itkpix::codec::bit_insert(word_1, data[i_word], 0, left);
210  itkpix::codec::bit_insert(word_2, data[i_word + 1], 0, nBitsInWord);
211  itkpix::codec::bit_insert(word_3, data[i_word + 2], nBitsInWord - w3, w3);
212  index += (2 * Offset + N);
213  return true;
214  }
215 
216  [[gnu::always_inline]] inline void setHeaders(uint32_t value) {
217  value_type mask = (value_type(1) << nBitsInWord) - 1;
218  for (int i = 0; i < index / nBits; i++) {
219  data[i] &= mask;
220  data[i] |= value_type(value) << nBitsInWord;
221  data[0] |= (uint64_t(1)<<63);
222  }
223  }
224  std::size_t wordSize() const {
225  auto i_word = index / nBits;
226  auto i_bit = index % nBits;
227  auto offset = (i_bit) > 0 ? 1 : 0;
228  return i_word + offset;
229  }
230 
231  std::size_t constexpr size() { return _size; }
232 
233  Buffer &data;
234  std::size_t Offset;
235  std::size_t _size;
236  std::size_t index{Offset};
237  std::size_t nBitsInWord = (nBits - Offset);
238 };
239 */
240 
241 } // itk::itkpix::endec::codec
Definition: bitstream.hpp:49