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;
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 revery 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 return (val = (val & ~auxspec.mask) | ((value << auxspec.shift) & auxspec.mask));
206 }
207 }
208 return (m_wacc(obj) = value);
209
210 }
211
212 // ------------------------------------------------------------------------
213 // Encode a Value into this field's bit representation.
214 // ------------------------------------------------------------------------
215
217 if (m_encoder)
218 return m_encoder(value);
219
220 // Default behaviour: integral conversion.
221
222 // should we check for out-of-range given size of bitset?
223
224 return bits_type{
225 static_cast<unsigned long long>(value)
226 };
227 }
228
229 // ------------------------------------------------------------------------
230 // Get and encode the value from an AOD object.
231 // ------------------------------------------------------------------------
232
233 bits_type bits(const SG::AuxElement &obj) const {
234 return encode(value(obj));
235 }
236
237 // ------------------------------------------------------------------------
238 // Decode this field's bits into a Value.
239 // ------------------------------------------------------------------------
240
242 if (m_decoder)
243 return m_decoder(bits);
244
245 return static_cast<Value>(bits.to_ullong());
246 }
247
248 // ------------------------------------------------------------------------
249 // Extract this field from a complete specification bitset.
250 // ------------------------------------------------------------------------
251
252 template<std::size_t N>
253 bits_type extract(const std::bitset <N> &packed) const {
254 static_assert(Hi < N, "BitField extends beyond the specification width");
255
256 bits_type result;
257
258 for (unsigned i = 0; i < width; ++i)
259 result[i] = packed[Lo + i];
260
261 return result;
262 }
263
264 // ------------------------------------------------------------------------
265 // Extract and decode this field from a complete specification bitset.
266 // ------------------------------------------------------------------------
267
268 template<std::size_t N>
269 Value decodeFrom(const std::bitset <N> &packed) const {
270 return decode(extract(packed));
271 }
272
273 template<std::size_t N>
274 void decodeAndAssignFrom(const std::bitset <N> &packed, SG::AuxElement &obj) const {
275 store( obj, decode(extract(packed)));
276 }
277
278 // ------------------------------------------------------------------------
279 // Pack this field from an AOD object into a complete bitset.
280 // ------------------------------------------------------------------------
281
282 template<std::size_t N>
283 void pack(
284 std::bitset <N> &result,
285 const SG::AuxElement &obj) const {
286 static_assert(
287 Hi < N,
288 "BitField extends beyond the specification width");
289
290 const bits_type field_bits = bits(obj);
291
292 for (unsigned i = 0; i < width; ++i)
293 result[Lo + i] = field_bits[i];
294 }
295
297 private:
298
299 std::string_view m_name;
300 std::string_view m_description;
301
304
305 SG::ConstAccessor <AuxValue> m_acc;
306 SG::Accessor <AuxValue> m_wacc;
307 };
308
309
310 template<typename Field>
311 class BitFieldAccessor : public Field {
312 public:
313 BitFieldAccessor(const Field &field, const SG::AuxElement &obj) : Field(field), m_field(field), m_obj(obj) {
314 }
315
316 // this just preserves old syntax behaviour of obj.field().value() and obj.field() = ...
318 return *this;
319 }
320
321 // ------------------------------------------------------------------------
322 // Value stored in the AOD object.
323 // ------------------------------------------------------------------------
324
325 auto value(bool truncate=false) const {
326 if(truncate) return m_field.value(m_obj,true);
327 return m_field.value(m_obj);
328 }
329
330
331 BitFieldAccessor &operator=(const Field::value_type &value) {
332 m_field.store(const_cast<SG::AuxElement &>(m_obj),value);
333 return *this;
334 }
335
336 BitFieldAccessor &operator=(const Field::bits_type &bits) {
337 // Decode bits and set the value in m_obj
338 // ...
339 return operator=(m_field.decode(bits));
340 }
341
342 // ------------------------------------------------------------------------
343 // Encoded representation of the AuxElement value.
344 // ------------------------------------------------------------------------
345
346 auto bits() const {
347 return m_field.bits(m_obj);
348 }
349
350 // ------------------------------------------------------------------------
351 // Metadata
352 // ------------------------------------------------------------------------
353 constexpr auto spec() const { return m_field; }
354
355
356 // ------------------------------------------------------------------------
357 // Extract this field from a complete packed representation.
358 // ------------------------------------------------------------------------
359
360 template<std::size_t N>
361 auto extract(const std::bitset <N> &packed) const {
362 return m_field.extract(packed);
363 }
364
365 // ------------------------------------------------------------------------
366 // Extract and decode this field from a complete packed representation.
367 // ------------------------------------------------------------------------
368
369 template<std::size_t N>
370 auto decodeFrom(const std::bitset <N> &packed) const {
371 return m_field.decodeFrom(packed);
372 }
373
374 private:
375 const Field &m_field;
377 };
378
379
380// ============================================================================
381// BitSpec<N>
382//
383// Common base class for concrete specifications.
384//
385// N = total width of the packed representation.
386//
387// ============================================================================
388
389 template<typename Derived, std::size_t N>
390 class BitSpec {
391 public:
392 static constexpr std::size_t width = N;
393 using bitset_type = std::bitset<N>;
394
395
396
397 // example use: MySpec::field<0>().name()
398 template<std::size_t I> static constexpr decltype(auto) field() {
399 return *std::get<I>(Derived::fields);
400 }
401
402 // example use: MySpec::numFields()
403 static constexpr std::size_t
404
406 return std::tuple_size_v < std::remove_cvref_t < decltype(Derived::fields) >> ;
407 }
408
409 // example use: MySpec::forEachField( [](const auto& field) { std::cout << field.name() << std::endl; } );
410 static constexpr void forEachField(auto &&func) {
411 std::apply([&](const auto *... field) { (func(*field), ...); }, Derived::fields);
412 }
413
414 // example use: MySpec::json()
415 static constexpr std::string json() {
416 nlohmann::json j;
417 j["data_width"] = width;
418 j["fields"] = nlohmann::json::array();
419 forEachField([&](const auto &field) {
420 nlohmann::json f;
421 f["name"] = field.name();
422 f["description"] = field.description();
423 f["start"] = field.lo;
424 f["width"] = field.width;
425 j["fields"].push_back(f);
426 });
427 return j.dump(4);
428 }
429
430 protected:
431
432 // this method is used in the DECLARE_FIELDS macro to create the tuple .. needed to inject the & symbol
433 template<typename... Fields>
434 static constexpr auto makeFields(Fields &... fields) {
435 return std::tuple{&fields...};
436 }
437
438
439 // ------------------------------------------------------------------------
440 // Pack all fields in a field tuple.
441 // ------------------------------------------------------------------------
442 public:
443 template<typename Fields>
444 static bitset_type packFields(const Fields &fields, const SG::AuxElement &obj) {
445 bitset_type result;
446
447 std::apply(
448 [&](const auto *... field) {
449 (field->pack(result, obj), ...);
450 },
451 fields);
452
453 return result;
454 }
455
456 protected:
457 // ------------------------------------------------------------------------
458 // Validate all fields.
459 //
460 // Individual field ranges are checked at compile time via:
461 //
462 // static_assert(Hi < N)
463 //
464 // Overlap between different fields is checked here.
465 // ------------------------------------------------------------------------
466
467
468 static consteval bool validateFields()
469 {
470 return std::apply(
471 []<typename... FieldPtrs>(FieldPtrs... field) {
472 return validateFieldList<
473 std::remove_cvref_t<decltype(*field)>...
474 >();
475 },
476 Derived::fields);
477 }
478
479 private:
480 // ------------------------------------------------------------------------
481 // Compare the first field against every subsequent field.
482 // ------------------------------------------------------------------------
483
484 template<typename First, typename... Rest>
485 static consteval bool validateFieldList()
486 {
487 if constexpr (sizeof...(Rest) == 0)
488 {
489 return validateField<First>();
490 }
491 else
492 {
493 return validateField<First>() &&
494 (validateField<Rest>() && ...) &&
495 ((!checkOverlap<First, Rest>()) && ...) &&
496 validateFieldList<Rest...>();
497 }
498 }
499
500 template<typename Field>
501 static consteval bool validateField() {
502 return Field::lo <= Field::hi && Field::hi < N;
503 }
504
505 // ------------------------------------------------------------------------
506 // Check two fields for overlap.
507 // ------------------------------------------------------------------------
508
509 template<typename A, typename B>
510 static consteval bool checkOverlap() {
511 if (A::lo <= B::hi && B::lo <= A::hi) {
512 return true;
513 }
514 return false;
515 }
516
517 };
518
519}
520
521// MACROS FOR DEFINING FIELDS IN BITSPECS:
522
523
524#define FE_1(m, a) m(a)
525#define FE_2(m, a, ...) m(a) FE_1(m, __VA_ARGS__)
526#define FE_3(m, a, ...) m(a) FE_2(m, __VA_ARGS__)
527#define FE_4(m, a, ...) m(a) FE_3(m, __VA_ARGS__)
528#define FE_5(m, a, ...) m(a) FE_4(m, __VA_ARGS__)
529#define FE_6(m, a, ...) m(a) FE_5(m, __VA_ARGS__)
530#define FE_7(m, a, ...) m(a) FE_6(m, __VA_ARGS__)
531#define FE_8(m, a, ...) m(a) FE_7(m, __VA_ARGS__)
532#define FE_9(m, a, ...) m(a) FE_8(m, __VA_ARGS__)
533#define FE_10(m, a, ...) m(a) FE_9(m, __VA_ARGS__)
534#define FE_11(m, a, ...) m(a) FE_10(m, __VA_ARGS__)
535#define FE_12(m, a, ...) m(a) FE_11(m, __VA_ARGS__)
536#define FE_13(m, a, ...) m(a) FE_12(m, __VA_ARGS__)
537#define FE_14(m, a, ...) m(a) FE_13(m, __VA_ARGS__)
538#define FE_15(m, a, ...) m(a) FE_14(m, __VA_ARGS__)
539#define FE_16(m, a, ...) m(a) FE_15(m, __VA_ARGS__)
540#define FE_17(m, a, ...) m(a) FE_16(m, __VA_ARGS__)
541#define FE_18(m, a, ...) m(a) FE_18(m, __VA_ARGS__)
542#define FE_19(m, a, ...) m(a) FE_19(m, __VA_ARGS__)
543#define FE_20(m, a, ...) m(a) FE_20(m, __VA_ARGS__)
544
545#define GET_FE(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10, \
546 _11,_12,_13,_14,_15,_16,_17,_18,_19,_20,NAME,...) NAME
547
548#define FOR_EACH(m, ...) \
549 GET_FE(__VA_ARGS__, \
550 FE_20, FE_19, FE_18, FE_17, \
551 FE_16, FE_15, FE_14, FE_13, \
552 FE_12, FE_11, FE_10, FE_9, \
553 FE_8, FE_7, FE_6, FE_5, \
554 FE_4, FE_3, FE_2, FE_1)(m, __VA_ARGS__)
555
556#define FIELD_PTR(name) \
557 static inline constexpr auto name##_ptr = &name;
558
559#define FIELD_PTR_VALUE(name) name##_ptr,
560
561
562
563#define FIELD_ACCESSOR(name) \
564 BitFieldAccessor<std::remove_cvref_t<decltype(*name##_ptr)>> name{*name##_ptr,m_obj};
565
566#define DECLARE_FIELDS(...) \
567 protected: \
568 FOR_EACH(FIELD_PTR, __VA_ARGS__) \
569 public: \
570 static inline constexpr auto fields = \
571 std::tuple{ FOR_EACH(FIELD_PTR_VALUE, __VA_ARGS__) }; \
572 \
573 static_assert(validateFields(),"Invalid spec: overlapping fields or fields beyond spec"); \
574 \
575 class ObjectAcc \
576 { \
577 public: \
578 explicit ObjectAcc(const SG::AuxElement& obj) \
579 : m_obj(obj) \
580 {} \
581 protected: \
582 const SG::AuxElement& m_obj; \
583 public: \
584 \
585 FOR_EACH(FIELD_ACCESSOR, __VA_ARGS__) \
586 \
587 };
588
589
590
591
592#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:313
auto decodeFrom(const std::bitset< N > &packed) const
Definition BitSpec.h:370
BitFieldAccessor & operator=(const Field::value_type &value)
Definition BitSpec.h:331
BitFieldAccessor & operator()()
Definition BitSpec.h:317
const SG::AuxElement & m_obj
Definition BitSpec.h:376
BitFieldAccessor & operator=(const Field::bits_type &bits)
Definition BitSpec.h:336
constexpr auto spec() const
Definition BitSpec.h:353
auto value(bool truncate=false) const
Definition BitSpec.h:325
auto extract(const std::bitset< N > &packed) const
Definition BitSpec.h:361
Value(*)(bits_type) decoder_type
Definition BitSpec.h:145
SG::Accessor< AuxValue > m_wacc
Definition BitSpec.h:306
static constexpr unsigned width
Definition BitSpec.h:130
SG::ConstAccessor< AuxValue > m_acc
Definition BitSpec.h:305
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:274
bits_type extract(const std::bitset< N > &packed) const
Definition BitSpec.h:253
void pack(std::bitset< N > &result, const SG::AuxElement &obj) const
Definition BitSpec.h:283
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:299
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:269
std::string_view m_description
Definition BitSpec.h:300
bits_type bits(const SG::AuxElement &obj) const
Definition BitSpec.h:233
decoder_type m_decoder
Definition BitSpec.h:303
bits_type encode(Value value) const
Definition BitSpec.h:216
AuxValue & store(SG::AuxElement &obj, Value value) const
Definition BitSpec.h:196
encoder_type m_encoder
Definition BitSpec.h:302
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:241
static consteval bool validateFields()
Definition BitSpec.h:468
static constexpr decltype(auto) field()
Definition BitSpec.h:398
static consteval bool validateFieldList()
Definition BitSpec.h:485
static constexpr void forEachField(auto &&func)
Definition BitSpec.h:410
static bitset_type packFields(const Fields &fields, const SG::AuxElement &obj)
Definition BitSpec.h:444
std::bitset< N > bitset_type
Definition BitSpec.h:393
static constexpr std::size_t width
Definition BitSpec.h:392
static consteval bool validateField()
Definition BitSpec.h:501
static constexpr std::string json()
Definition BitSpec.h:415
static consteval bool checkOverlap()
Definition BitSpec.h:510
static constexpr std::size_t numFields()
Definition BitSpec.h:405
static constexpr auto makeFields(Fields &... fields)
Definition BitSpec.h:434
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