ATLAS Offline Software
Loading...
Searching...
No Matches
BitSpec.h
Go to the documentation of this file.
1/*
2 * Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3 */
4
5#ifndef GLOBALSIM_BITSPEC_H
6#define GLOBALSIM_BITSPEC_H
7
8#include <bit>
9#include <bitset>
10#include <cstddef>
11#include <cstdint>
12#include <stdexcept>
13#include <string>
14#include <string_view>
15#include <tuple>
16#include <type_traits>
17#include <utility>
18
19#include <nlohmann/json.hpp>
20
22
23namespace GlobalSim {
24
25// ============================================================================
26// BitField
27//
28// Lo and Hi are inclusive bit positions.
29//
30// BitField<0, 10, int>
31//
32// represents an 11-bit field occupying bits [10:0].
33//
34// The bit representation is:
35//
36// std::bitset<11>
37//
38// ============================================================================
39
40 struct AuxSpec {
41 std::string_view name{};
42 std::uint64_t mask{};
43 std::size_t shift{};
44 bool has_mask{};
45
46 static constexpr std::size_t parseNumber(std::string_view str,std::size_t& pos,std::size_t end) {
47 std::size_t value{};
48 const auto [ptr, ec] = std::from_chars(
49 str.data() + pos,
50 str.data() + end,
51 value
52 );
53
54 if (ec != std::errc{})
55 throw std::invalid_argument("Expected number");
56
57 pos = ptr - str.data();
58 return value;
59 }
60
61 static constexpr AuxSpec parse(std::string_view spec) {
62 const auto open = spec.find('[');
63
64 // No mask specification.
65 if (open == std::string_view::npos) {
66 return {
67 spec,
68 std::numeric_limits<std::uint64_t>::max(),
69 0,
70 false
71 };
72 }
73
74 // Expected: name[lo:hi] or name[bit]
75 const auto colon = spec.find(':', open + 1);
76 const auto close = spec.find(']', open + 1);
77
78 if (close == std::string_view::npos ||close != spec.size() - 1) {
79 throw std::invalid_argument("Invalid AuxSpec");
80 }
81
82 std::size_t pos = open + 1;
83 const std::size_t lo = parseNumber(spec, pos, colon == std::string_view::npos ? close : colon);
84
85 std::size_t hi = lo;
86
87 if (colon != std::string_view::npos) {
88 // Range: [lo:hi]
89 if (colon == open + 1 || colon + 1 == close) {
90 throw std::invalid_argument("Invalid AuxSpec range");
91 }
92 pos = colon + 1;
93 hi = parseNumber(spec, pos, close);
94 }
95
96 if (pos != close || lo > hi || hi >= 64)
97 throw std::invalid_argument("Invalid bit range");
98
99 const std::size_t width = hi - lo + 1;
100
101 const std::uint64_t mask =
102 width == 64
103 ? std::numeric_limits<std::uint64_t>::max()
104 : ((std::uint64_t{1} << width) - 1) << lo;
105
106 return {
107 spec.substr(0, open),
108 mask,
109 lo,
110 true
111 };
112 }
113
114 };
115
116
117
118
119 //struct NoMask {};
120
121 template<unsigned Lo, unsigned Hi, typename AuxValue, typename Value=AuxValue/*, auto Mask = NoMask{}*/>
122 class BitField {
123 public:
124 static_assert(Hi >= Lo,"BitField: Hi must be >= Lo");
125 // next line makes it required that AuxValue type is big enough for this bitfield
126 static_assert(sizeof(AuxValue) * CHAR_BIT >= Hi - Lo + 1,"AuxType is too small for BitField");
127 public:
128 static constexpr unsigned lo = Lo;
129 static constexpr unsigned hi = Hi;
130 static constexpr unsigned width = Hi - Lo + 1;
131 // leaving this commented as the alternative way to do constexpr masking
132 /*static constexpr auto mask = Mask;
133 static constexpr bool has_mask = !std::is_same_v<decltype(Mask), NoMask>;
134 static constexpr std::size_t shift = [] {
135 if constexpr (has_mask)
136 return std::countr_zero(static_cast<uint64_t>(Mask));
137 else
138 return std::size_t{0};
139 }();*/
140
142 using bits_type = std::bitset<width>;
143
146
147 constexpr BitField(
148 std::string_view name,
149 std::string_view auxvar,
150 std::string_view description,
151 encoder_type encoder = nullptr,
152 decoder_type decoder = nullptr)
153 : auxspec(AuxSpec::parse(auxvar)),
154 m_name(name),
156 m_encoder(encoder),
157 m_decoder(decoder),
159
160 }
161
162 // ------------------------------------------------------------------------
163 // Metadata
164 // ------------------------------------------------------------------------
165
166 constexpr std::string_view name() const { return m_name; }
167
168 constexpr std::string_view description() const { return m_description; }
169
170 // ------------------------------------------------------------------------
171 // Get the value directly from an AOD object.
172 // ------------------------------------------------------------------------
173
174 Value value(const SG::AuxElement &obj) const {
175
176 if constexpr (std::is_integral_v<AuxValue>)
177 { // ensures have bitwise operators
178
179 /*if constexpr(has_mask) {
180 return (m_acc(obj) & Mask) >> shift;*/
181 if (auxspec.has_mask) {
182 return (m_acc(obj) & auxspec.mask) >> auxspec.shift;
183 }
184 }
185
186 return m_acc(obj);
187
188 }
189
190 // if truncate = true, then will do an encode-decode to truncate the stored value
191 Value value(const SG::AuxElement& obj, bool truncate) const {
192 if(!truncate) return value(obj);
193 return decode(encode(value(obj)));
194 }
195
196 AuxValue& store(SG::AuxElement &obj, Value value) const {
197 if constexpr (std::is_integral_v<AuxValue>)
198 { // ensures have bitwise operators, has_mask is not constexpr so must hide this from invalid AuxValue types
199 // leaving this commented as may revert to constexpr again at some point
200 // if constexpr (has_mask) {
201 // auto& val = m_wacc(obj);
202 // val = (val & ~Mask) | ((value << shift) & Mask);
203 if (auxspec.has_mask) {
204 auto &val = m_wacc(obj);
205 //avoid unintended sign extension, make everything unsigned
206 using unsigned_aux_type = std::make_unsigned_t<AuxValue>;
207 const auto mask = static_cast<unsigned_aux_type>(auxspec.mask);
208 const auto current = static_cast<unsigned_aux_type>(val);
209 const auto encoded = (static_cast<unsigned_aux_type>(value) << auxspec.shift) & mask;
210 val = static_cast<AuxValue>((current & ~mask) | encoded);
211 return val;
212 }
213 }
214 return (m_wacc(obj) = value);
215
216 }
217
218 // ------------------------------------------------------------------------
219 // Encode a Value into this field's bit representation.
220 // ------------------------------------------------------------------------
221
223 if (m_encoder)
224 return m_encoder(value);
225
226 // Default behaviour: integral conversion.
227
228 // should we check for out-of-range given size of bitset?
229
230 return bits_type{
231 static_cast<unsigned long long>(value)
232 };
233 }
234
235 // ------------------------------------------------------------------------
236 // Get and encode the value from an AOD object.
237 // ------------------------------------------------------------------------
238
239 bits_type bits(const SG::AuxElement &obj) const {
240 return encode(value(obj));
241 }
242
243 // ------------------------------------------------------------------------
244 // Decode this field's bits into a Value.
245 // ------------------------------------------------------------------------
246
248 if (m_decoder)
249 return m_decoder(bits);
250
251 return static_cast<Value>(bits.to_ullong());
252 }
253
254 // ------------------------------------------------------------------------
255 // Extract this field from a complete specification bitset.
256 // ------------------------------------------------------------------------
257
258 template<std::size_t N>
259 bits_type extract(const std::bitset <N> &packed) const {
260 static_assert(Hi < N, "BitField extends beyond the specification width");
261
262 bits_type result;
263
264 for (unsigned i = 0; i < width; ++i)
265 result[i] = packed[Lo + i];
266
267 return result;
268 }
269
270 // ------------------------------------------------------------------------
271 // Extract and decode this field from a complete specification bitset.
272 // ------------------------------------------------------------------------
273
274 template<std::size_t N>
275 Value decodeFrom(const std::bitset <N> &packed) const {
276 return decode(extract(packed));
277 }
278
279 template<std::size_t N>
280 void decodeAndAssignFrom(const std::bitset <N> &packed, SG::AuxElement &obj) const {
281 store( obj, decode(extract(packed)));
282 }
283
284 // ------------------------------------------------------------------------
285 // Pack this field from an AOD object into a complete bitset.
286 // ------------------------------------------------------------------------
287
288 template<std::size_t N>
289 void pack(
290 std::bitset <N> &result,
291 const SG::AuxElement &obj) const {
292 static_assert(
293 Hi < N,
294 "BitField extends beyond the specification width");
295
296 const bits_type field_bits = bits(obj);
297
298 for (unsigned i = 0; i < width; ++i)
299 result[Lo + i] = field_bits[i];
300 }
301
303 private:
304
305 std::string_view m_name;
306 std::string_view m_description;
307
310
311 SG::ConstAccessor <AuxValue> m_acc;
312 SG::Accessor <AuxValue> m_wacc;
313 };
314
315
316 template<typename Field>
317 class BitFieldAccessor : public Field {
318 public:
319 BitFieldAccessor(const Field &field, const SG::AuxElement &obj) : Field(field), m_field(field), m_obj(obj) {
320 }
321
322 // this just preserves old syntax behaviour of obj.field().value() and obj.field() = ...
324 return *this;
325 }
326
327 // ------------------------------------------------------------------------
328 // Value stored in the AOD object.
329 // ------------------------------------------------------------------------
330
331 auto value(bool truncate=false) const {
332 if(truncate) return m_field.value(m_obj,true);
333 return m_field.value(m_obj);
334 }
335
336
337 BitFieldAccessor &operator=(const Field::value_type &value) {
338 m_field.store(const_cast<SG::AuxElement &>(m_obj),value);
339 return *this;
340 }
341
342 BitFieldAccessor &operator=(const Field::bits_type &bits) {
343 // Decode bits and set the value in m_obj
344 // ...
345 return operator=(m_field.decode(bits));
346 }
347
348 // ------------------------------------------------------------------------
349 // Encoded representation of the AuxElement value.
350 // ------------------------------------------------------------------------
351
352 auto bits() const {
353 return m_field.bits(m_obj);
354 }
355
356 // ------------------------------------------------------------------------
357 // Metadata
358 // ------------------------------------------------------------------------
359 constexpr auto spec() const { return m_field; }
360
361
362 // ------------------------------------------------------------------------
363 // Extract this field from a complete packed representation.
364 // ------------------------------------------------------------------------
365
366 template<std::size_t N>
367 auto extract(const std::bitset <N> &packed) const {
368 return m_field.extract(packed);
369 }
370
371 // ------------------------------------------------------------------------
372 // Extract and decode this field from a complete packed representation.
373 // ------------------------------------------------------------------------
374
375 template<std::size_t N>
376 auto decodeFrom(const std::bitset <N> &packed) const {
377 return m_field.decodeFrom(packed);
378 }
379
380 private:
381 const Field &m_field;
383 };
384
385
386// ============================================================================
387// BitSpec<N>
388//
389// Common base class for concrete specifications.
390//
391// N = total width of the packed representation.
392//
393// ============================================================================
394
395 template<typename Derived, std::size_t N>
396 class BitSpec {
397 public:
398 static constexpr std::size_t width = N;
399 using bitset_type = std::bitset<N>;
400
401
402
403 // example use: MySpec::field<0>().name()
404 template<std::size_t I> static constexpr decltype(auto) field() {
405 return *std::get<I>(Derived::fields);
406 }
407
408 // example use: MySpec::numFields()
409 static constexpr std::size_t
410
412 return std::tuple_size_v < std::remove_cvref_t < decltype(Derived::fields) >> ;
413 }
414
415 // example use: MySpec::forEachField( [](const auto& field) { std::cout << field.name() << std::endl; } );
416 static constexpr void forEachField(auto &&func) {
417 std::apply([&](const auto *... field) { (func(*field), ...); }, Derived::fields);
418 }
419
420 // example use: MySpec::json()
421 static constexpr std::string json() {
422 nlohmann::json j;
423 j["data_width"] = width;
424 j["fields"] = nlohmann::json::array();
425 forEachField([&](const auto &field) {
426 nlohmann::json f;
427 f["name"] = field.name();
428 f["description"] = field.description();
429 f["start"] = field.lo;
430 f["width"] = field.width;
431 j["fields"].push_back(f);
432 });
433 return j.dump(4);
434 }
435
436 protected:
437
438 // this method is used in the DECLARE_FIELDS macro to create the tuple .. needed to inject the & symbol
439 template<typename... Fields>
440 static constexpr auto makeFields(Fields &... fields) {
441 return std::tuple{&fields...};
442 }
443
444
445 // ------------------------------------------------------------------------
446 // Pack all fields in a field tuple.
447 // ------------------------------------------------------------------------
448 public:
449 template<typename Fields>
450 static bitset_type packFields(const Fields &fields, const SG::AuxElement &obj) {
451 bitset_type result;
452
453 std::apply(
454 [&](const auto *... field) {
455 (field->pack(result, obj), ...);
456 },
457 fields);
458
459 return result;
460 }
461
462 protected:
463 // ------------------------------------------------------------------------
464 // Validate all fields.
465 //
466 // Individual field ranges are checked at compile time via:
467 //
468 // static_assert(Hi < N)
469 //
470 // Overlap between different fields is checked here.
471 // ------------------------------------------------------------------------
472
473
474 static consteval bool validateFields()
475 {
476 return std::apply(
477 []<typename... FieldPtrs>(FieldPtrs... field) {
478 return validateFieldList<
479 std::remove_cvref_t<decltype(*field)>...
480 >();
481 },
482 Derived::fields);
483 }
484
485 private:
486 // ------------------------------------------------------------------------
487 // Compare the first field against every subsequent field.
488 // ------------------------------------------------------------------------
489
490 template<typename First, typename... Rest>
491 static consteval bool validateFieldList()
492 {
493 if constexpr (sizeof...(Rest) == 0)
494 {
495 return validateField<First>();
496 }
497 else
498 {
499 return validateField<First>() &&
500 (validateField<Rest>() && ...) &&
501 ((!checkOverlap<First, Rest>()) && ...) &&
502 validateFieldList<Rest...>();
503 }
504 }
505
506 template<typename Field>
507 static consteval bool validateField() {
508 return Field::lo <= Field::hi && Field::hi < N;
509 }
510
511 // ------------------------------------------------------------------------
512 // Check two fields for overlap.
513 // ------------------------------------------------------------------------
514
515 template<typename A, typename B>
516 static consteval bool checkOverlap() {
517 if (A::lo <= B::hi && B::lo <= A::hi) {
518 return true;
519 }
520 return false;
521 }
522
523 };
524
525}
526
527// MACROS FOR DEFINING FIELDS IN BITSPECS:
528
529
530#define FE_1(m, a) m(a)
531#define FE_2(m, a, ...) m(a) FE_1(m, __VA_ARGS__)
532#define FE_3(m, a, ...) m(a) FE_2(m, __VA_ARGS__)
533#define FE_4(m, a, ...) m(a) FE_3(m, __VA_ARGS__)
534#define FE_5(m, a, ...) m(a) FE_4(m, __VA_ARGS__)
535#define FE_6(m, a, ...) m(a) FE_5(m, __VA_ARGS__)
536#define FE_7(m, a, ...) m(a) FE_6(m, __VA_ARGS__)
537#define FE_8(m, a, ...) m(a) FE_7(m, __VA_ARGS__)
538#define FE_9(m, a, ...) m(a) FE_8(m, __VA_ARGS__)
539#define FE_10(m, a, ...) m(a) FE_9(m, __VA_ARGS__)
540#define FE_11(m, a, ...) m(a) FE_10(m, __VA_ARGS__)
541#define FE_12(m, a, ...) m(a) FE_11(m, __VA_ARGS__)
542#define FE_13(m, a, ...) m(a) FE_12(m, __VA_ARGS__)
543#define FE_14(m, a, ...) m(a) FE_13(m, __VA_ARGS__)
544#define FE_15(m, a, ...) m(a) FE_14(m, __VA_ARGS__)
545#define FE_16(m, a, ...) m(a) FE_15(m, __VA_ARGS__)
546#define FE_17(m, a, ...) m(a) FE_16(m, __VA_ARGS__)
547#define FE_18(m, a, ...) m(a) FE_18(m, __VA_ARGS__)
548#define FE_19(m, a, ...) m(a) FE_19(m, __VA_ARGS__)
549#define FE_20(m, a, ...) m(a) FE_20(m, __VA_ARGS__)
550
551#define GET_FE(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10, \
552 _11,_12,_13,_14,_15,_16,_17,_18,_19,_20,NAME,...) NAME
553
554#define FOR_EACH(m, ...) \
555 GET_FE(__VA_ARGS__, \
556 FE_20, FE_19, FE_18, FE_17, \
557 FE_16, FE_15, FE_14, FE_13, \
558 FE_12, FE_11, FE_10, FE_9, \
559 FE_8, FE_7, FE_6, FE_5, \
560 FE_4, FE_3, FE_2, FE_1)(m, __VA_ARGS__)
561
562#define FIELD_PTR(name) \
563 static inline constexpr auto name##_ptr = &name;
564
565#define FIELD_PTR_VALUE(name) name##_ptr,
566
567
568
569#define FIELD_ACCESSOR(name) \
570 BitFieldAccessor<std::remove_cvref_t<decltype(*name##_ptr)>> name{*name##_ptr,m_obj};
571
572#define DECLARE_FIELDS(...) \
573 protected: \
574 FOR_EACH(FIELD_PTR, __VA_ARGS__) \
575 public: \
576 static inline constexpr auto fields = \
577 std::tuple{ FOR_EACH(FIELD_PTR_VALUE, __VA_ARGS__) }; \
578 \
579 static_assert(validateFields(),"Invalid spec: overlapping fields or fields beyond spec"); \
580 \
581 class ObjectAcc \
582 { \
583 public: \
584 explicit ObjectAcc(const SG::AuxElement& obj) \
585 : m_obj(obj) \
586 {} \
587 protected: \
588 const SG::AuxElement& m_obj; \
589 public: \
590 \
591 FOR_EACH(FIELD_ACCESSOR, __VA_ARGS__) \
592 \
593 }
594
595
596
597
598#endif
Base class for elements of a container that can have aux data.
const double width
BitFieldAccessor(const Field &field, const SG::AuxElement &obj)
Definition BitSpec.h:319
auto decodeFrom(const std::bitset< N > &packed) const
Definition BitSpec.h:376
BitFieldAccessor & operator=(const Field::value_type &value)
Definition BitSpec.h:337
BitFieldAccessor & operator()()
Definition BitSpec.h:323
const SG::AuxElement & m_obj
Definition BitSpec.h:382
BitFieldAccessor & operator=(const Field::bits_type &bits)
Definition BitSpec.h:342
constexpr auto spec() const
Definition BitSpec.h:359
auto value(bool truncate=false) const
Definition BitSpec.h:331
auto extract(const std::bitset< N > &packed) const
Definition BitSpec.h:367
Value(*)(bits_type) decoder_type
Definition BitSpec.h:145
SG::Accessor< AuxValue > m_wacc
Definition BitSpec.h:312
static constexpr unsigned width
Definition BitSpec.h:130
SG::ConstAccessor< AuxValue > m_acc
Definition BitSpec.h:311
bits_type(*)(Value) encoder_type
Definition BitSpec.h:144
Value value(const SG::AuxElement &obj, bool truncate) const
Definition BitSpec.h:191
static constexpr unsigned hi
Definition BitSpec.h:129
void decodeAndAssignFrom(const std::bitset< N > &packed, SG::AuxElement &obj) const
Definition BitSpec.h:280
bits_type extract(const std::bitset< N > &packed) const
Definition BitSpec.h:259
void pack(std::bitset< N > &result, const SG::AuxElement &obj) const
Definition BitSpec.h:289
constexpr std::string_view description() const
Definition BitSpec.h:168
constexpr BitField(std::string_view name, std::string_view auxvar, std::string_view description, encoder_type encoder=nullptr, decoder_type decoder=nullptr)
Definition BitSpec.h:147
std::string_view m_name
Definition BitSpec.h:305
constexpr std::string_view name() const
Definition BitSpec.h:166
Value value(const SG::AuxElement &obj) const
Definition BitSpec.h:174
Value decodeFrom(const std::bitset< N > &packed) const
Definition BitSpec.h:275
std::string_view m_description
Definition BitSpec.h:306
bits_type bits(const SG::AuxElement &obj) const
Definition BitSpec.h:239
decoder_type m_decoder
Definition BitSpec.h:309
bits_type encode(Value value) const
Definition BitSpec.h:222
AuxValue & store(SG::AuxElement &obj, Value value) const
Definition BitSpec.h:196
encoder_type m_encoder
Definition BitSpec.h:308
std::bitset< width > bits_type
Definition BitSpec.h:142
static constexpr unsigned lo
Definition BitSpec.h:128
Value decode(bits_type bits) const
Definition BitSpec.h:247
static consteval bool validateFields()
Definition BitSpec.h:474
static constexpr decltype(auto) field()
Definition BitSpec.h:404
static consteval bool validateFieldList()
Definition BitSpec.h:491
static constexpr void forEachField(auto &&func)
Definition BitSpec.h:416
static bitset_type packFields(const Fields &fields, const SG::AuxElement &obj)
Definition BitSpec.h:450
std::bitset< N > bitset_type
Definition BitSpec.h:399
static constexpr std::size_t width
Definition BitSpec.h:398
static consteval bool validateField()
Definition BitSpec.h:507
static constexpr std::string json()
Definition BitSpec.h:421
static consteval bool checkOverlap()
Definition BitSpec.h:516
static constexpr std::size_t numFields()
Definition BitSpec.h:411
static constexpr auto makeFields(Fields &... fields)
Definition BitSpec.h:440
tag-value pair class.
Definition Value.h:39
STL class.
std::map< std::string, std::string, std::less<> > parse(const std::string &list)
AlgTool to read in LArStripNeighborhoods, and run the BDT Algorithm.
Definition BitSpec.h:23
AuxElement(SG::AuxVectorData *container, size_t index)
Base class for elements of a container that can have aux data.
STL namespace.
std::uint64_t mask
Definition BitSpec.h:42
std::size_t shift
Definition BitSpec.h:43
static constexpr std::size_t parseNumber(std::string_view str, std::size_t &pos, std::size_t end)
Definition BitSpec.h:46
static constexpr AuxSpec parse(std::string_view spec)
Definition BitSpec.h:61
std::string_view name
Definition BitSpec.h:41