ATLAS Offline Software
Loading...
Searching...
No Matches
ChaiCoralConverter.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
4
6
7#include <chai/Container.h>
8#include <chai/VectorContainer.h>
9
10#include "CoralBase/Attribute.h"
11#include "CoralBase/AttributeList.h"
12#include "CoralBase/AttributeListSpecification.h"
13#include "CoralBase/AttributeSpecification.h"
14#include "CoralBase/Blob.h"
15
16#include <cstring>
17#include <limits>
18#include <map>
19#include <memory>
20#include <stdexcept>
21#include <string>
22#include <typeindex>
23#include <unordered_map>
24
25namespace {
26 // Releases a CORAL specification on scope exit, so a throw partway through
27 // building a CondAttrListCollection or AthenaAttributeList doesn't leak it
28 struct SpecReleaser {
29 void operator()(coral::AttributeListSpecification* spec) const {
30 if (spec) {
31 spec->release();
32 }
33 }
34 };
35 using SpecHolder = std::unique_ptr<coral::AttributeListSpecification, SpecReleaser>;
36
37 // CORAL C++ type -> chai::Type, the reverse of the table below
38 // Done as a lookup table because it uses typeid
39 chai::Type chaiTypeFor(const coral::AttributeSpecification& spec) {
40 static const std::unordered_map<std::type_index, chai::Type> table{
41 {std::type_index(typeid(bool)), chai::Bool},
42 {std::type_index(typeid(char)), chai::Int8},
43 {std::type_index(typeid(unsigned char)), chai::UInt8},
44 {std::type_index(typeid(short)), chai::Int16},
45 {std::type_index(typeid(unsigned short)), chai::UInt16},
46 {std::type_index(typeid(int)), chai::Int32},
47 {std::type_index(typeid(unsigned int)), chai::UInt32},
48 {std::type_index(typeid(long long)), chai::Int64},
49 {std::type_index(typeid(unsigned long long)), chai::UInt64},
50 {std::type_index(typeid(float)), chai::Float},
51 {std::type_index(typeid(double)), chai::Double},
52 {std::type_index(typeid(std::string)), chai::String},
53 {std::type_index(typeid(coral::Blob)), chai::Blob},
54 };
55 const auto it = table.find(std::type_index(spec.type()));
56 if (it == table.end()) {
57 throw std::runtime_error(
58 "ChaiCoralConverter: unsupported CORAL type '" + spec.typeName() + "' for field '" + spec.name() + "'");
59 }
60 return it->second;
61 }
62}
63
64// chai::Type Coral C++ type CHAI fixed-width type
65//
66// Bool bool bool
67// Int8 / UInt8 char / unsigned char int8_t / uint8_t
68// Int16 / UInt16 short / unsigned short int16_t / uint16_t
69// Int32 / UInt32 int / unsigned int int32_t / uint32_t
70// Int64 / UInt64 long long / ull int64_t / uint64_t
71// Float / Double float / double float / double
72// String std::string std::string
73// Blob coral::Blob (byte copy) chai::BlobData
74coral::AttributeListSpecification* ChaiCoralConverter::toCoralSpec(const chai::FieldSpec& fields) {
75 auto* pSpec = new coral::AttributeListSpecification();
76 try {
77 for (const auto& field : fields.fields()) {
78 switch (field.type) {
79 case chai::Bool: pSpec->extend(field.name, typeid(bool)); break;
80 case chai::Int8: pSpec->extend(field.name, typeid(char)); break;
81 case chai::UInt8: pSpec->extend(field.name, typeid(unsigned char)); break;
82 case chai::Int16: pSpec->extend(field.name, typeid(short)); break;
83 case chai::UInt16: pSpec->extend(field.name, typeid(unsigned short)); break;
84 case chai::Int32: pSpec->extend(field.name, typeid(int)); break;
85 case chai::UInt32: pSpec->extend(field.name, typeid(unsigned int)); break;
86 case chai::Int64: pSpec->extend(field.name, typeid(long long)); break;
87 case chai::UInt64: pSpec->extend(field.name, typeid(unsigned long long)); break;
88 case chai::Float: pSpec->extend(field.name, typeid(float)); break;
89 case chai::Double: pSpec->extend(field.name, typeid(double)); break;
90 case chai::String: pSpec->extend(field.name, typeid(std::string)); break;
91 case chai::Blob: pSpec->extend(field.name, typeid(coral::Blob)); break;
92 case chai::Unknown: // Fall-through
93 default:
94 throw std::runtime_error(
95 "ChaiCoralConverter::toCoralSpec: unrecognised CHAI type for field '" + field.name + "'");
96 }
97 }
98 } catch (...) {
99 pSpec->release();
100 throw;
101 }
102 return pSpec;
103}
104
105chai::FieldSpec ChaiCoralConverter::toFieldSpec(const coral::AttributeListSpecification& spec) {
106 std::vector<chai::Field> fields;
107 fields.reserve(spec.size());
108 const unsigned int s = spec.size();
109 for (unsigned int i = 0; i != s; ++i) {
110 const auto& attrSpec = spec[i];
111 fields.push_back({attrSpec.name(), chaiTypeFor(attrSpec)});
112 }
113 return chai::FieldSpec(std::move(fields));
114}
115
116coral::AttributeList ChaiCoralConverter::toAttributeList(const coral::AttributeListSpecification& spec,
117 const chai::Values& values) {
118 // The specification is built from a tag's first payload, so a later
119 // payload with a different field count is caught here
120 if (values.size() != spec.size()) {
121 throw std::runtime_error("ChaiCoralConverter::toAttributeList: field count mismatch, payload "
122 + std::to_string(values.size()) + " vs specification " + std::to_string(spec.size()));
123 }
124 coral::AttributeList attr(spec, true);
125 const unsigned int s = attr.size();
126 for (unsigned int i = 0; i != s; ++i) {
127 auto& att = attr[i];
128 // Look up each value by name rather than position
129 const std::string& fieldName = att.specification().name();
130 const std::size_t idx = values.fieldIndex(fieldName);
131 if (values.isNull(idx)) {
132 att.setNull();
133 continue;
134 }
135 switch (values.type(idx)) {
136 case chai::Bool:
137 att.setValue<bool>(values.get<bool>(idx));
138 break;
139 case chai::Int8:
140 att.setValue<char>(values.get<int8_t>(idx));
141 break;
142 case chai::UInt8:
143 att.setValue<unsigned char>(values.get<uint8_t>(idx));
144 break;
145 case chai::Int16:
146 att.setValue<short>(values.get<int16_t>(idx));
147 break;
148 case chai::UInt16:
149 att.setValue<unsigned short>(values.get<uint16_t>(idx));
150 break;
151 case chai::Int32:
152 att.setValue<int>(values.get<int32_t>(idx));
153 break;
154 case chai::UInt32:
155 att.setValue<unsigned int>(values.get<uint32_t>(idx));
156 break;
157 case chai::Int64:
158 att.setValue<long long>(values.get<int64_t>(idx));
159 break;
160 case chai::UInt64:
161 att.setValue<unsigned long long>(values.get<uint64_t>(idx));
162 break;
163 case chai::Float:
164 att.setValue<float>(values.get<float>(idx));
165 break;
166 case chai::Double:
167 att.setValue<double>(values.get<double>(idx));
168 break;
169 case chai::String:
170 att.setValue<std::string>(values.get<std::string>(idx));
171 break;
172 case chai::Blob: {
173 const auto& bytes = values.get<chai::BlobData>(idx).m_bytes;
174 coral::Blob blob(bytes.size());
175 // Skip the memcpy if the blob is empty
176 if (!bytes.empty()) {
177 std::memcpy(blob.startingAddress(), bytes.data(), bytes.size());
178 }
179 att.setValue<coral::Blob>(blob);
180 break;
181 }
182 case chai::Unknown: // Fall-through
183 default:
184 throw std::runtime_error(
185 "ChaiCoralConverter::toAttributeList: unrecognised CHAI type for field '" + fieldName + "'");
186 }
187 }
188 return attr;
189}
190
191std::vector<coral::AttributeList> ChaiCoralConverter::toAttributeListVec(const coral::AttributeListSpecification& spec,
192 const std::vector<chai::ValuesPtr>& rows) {
193 std::vector<coral::AttributeList> result;
194 result.reserve(rows.size());
195 for (std::size_t i = 0; i != rows.size(); ++i) {
196 if (!rows[i]) {
197 throw std::invalid_argument(
198 "ChaiCoralConverter::toAttributeListVec: null row at index " + std::to_string(i));
199 }
200 result.push_back(toAttributeList(spec, *rows[i]));
201 }
202 return result;
203}
204
205chai::ValuesPtr ChaiCoralConverter::toValues(chai::FieldSpecPtr fields, const coral::AttributeList& attrList) {
206 if (!fields) {
207 throw std::invalid_argument("ChaiCoralConverter::toValues: null field specification");
208 }
209 if (fields->size() != attrList.size()) {
210 throw std::runtime_error("ChaiCoralConverter::toValues: field count mismatch, specification "
211 + std::to_string(fields->size()) + " vs payload " + std::to_string(attrList.size()));
212 }
213 auto values = std::make_shared<chai::Values>(fields);
214 const std::size_t n = fields->size();
215 for (std::size_t i = 0; i != n; ++i) {
216 const chai::Field& field = (*fields)[i];
217 const int idx = attrList.specification().index(field.name);
218 if (idx < 0) {
219 throw std::runtime_error(
220 "ChaiCoralConverter::toValues: field '" + field.name + "' not found in payload");
221 }
222 const auto& att = attrList[static_cast<unsigned int>(idx)];
223 const chai::Type payloadType = chaiTypeFor(att.specification());
224 if (payloadType != field.type) {
225 throw std::runtime_error(
226 "ChaiCoralConverter::toValues: type mismatch for field '" + field.name + "': specification "
227 + field.typeString() + " vs payload " + chai::typeToString(payloadType));
228 }
229 if (att.isNull()) {
230 values->push();
231 continue;
232 }
233 // Have to cast to the exact fixed-width types for integers since push is templated
234 switch (field.type) {
235 case chai::Bool:
236 values->push(att.data<bool>());
237 break;
238 case chai::Int8:
239 values->push(static_cast<int8_t>(att.data<char>()));
240 break;
241 case chai::UInt8:
242 values->push(static_cast<uint8_t>(att.data<unsigned char>()));
243 break;
244 case chai::Int16:
245 values->push(static_cast<int16_t>(att.data<short>()));
246 break;
247 case chai::UInt16:
248 values->push(static_cast<uint16_t>(att.data<unsigned short>()));
249 break;
250 case chai::Int32:
251 values->push(static_cast<int32_t>(att.data<int>()));
252 break;
253 case chai::UInt32:
254 values->push(static_cast<uint32_t>(att.data<unsigned int>()));
255 break;
256 case chai::Int64:
257 values->push(static_cast<int64_t>(att.data<long long>()));
258 break;
259 case chai::UInt64:
260 values->push(static_cast<uint64_t>(att.data<unsigned long long>()));
261 break;
262 case chai::Float:
263 values->push(att.data<float>());
264 break;
265 case chai::Double:
266 values->push(att.data<double>());
267 break;
268 case chai::String:
269 values->push(att.data<std::string>());
270 break;
271 case chai::Blob: {
272 const coral::Blob& blob = att.data<coral::Blob>();
273 std::vector<uint8_t> bytes(blob.size());
274 // Skip the memcpy if the blob is empty
275 if (blob.size() != 0) {
276 std::memcpy(bytes.data(), blob.startingAddress(), blob.size());
277 }
278 values->push(chai::BlobData(std::move(bytes)));
279 break;
280 }
281 case chai::Unknown: // Fall-through
282 default:
283 throw std::runtime_error(
284 "ChaiCoralConverter::toValues: unrecognised CHAI type for field '" + field.name + "'");
285 }
286 }
287 return values;
288}
289
290std::vector<chai::ValuesPtr> ChaiCoralConverter::toValuesVec(chai::FieldSpecPtr fields,
291 const std::vector<coral::AttributeList>& rows) {
292 if (!fields) {
293 throw std::invalid_argument("ChaiCoralConverter::toValuesVec: null field specification");
294 }
295 std::vector<chai::ValuesPtr> result;
296 result.reserve(rows.size());
297 for (std::size_t i = 0; i != rows.size(); ++i) {
298 result.push_back(toValues(fields, rows[i]));
299 }
300 return result;
301}
302
303std::unique_ptr<AthenaAttributeList> ChaiCoralConverter::toAthenaAttributeList(const chai::Container& container) {
304 const auto ids = container.channelIds();
305 if (ids.size() != 1) {
306 throw std::runtime_error(
307 "ChaiCoralConverter::toAthenaAttributeList: expected exactly one populated channel, found "
308 + std::to_string(ids.size()));
309 }
310 const SpecHolder spec(toCoralSpec(container.fieldSpec()));
311 return std::make_unique<AthenaAttributeList>(toAttributeList(*spec, container[ids[0]]));
312}
313
314chai::Container ChaiCoralConverter::toContainer(const AthenaAttributeList& attrList, uint64_t channelId,
315 const std::string& channelName) {
316 const coral::AttributeListSpecification& coralSpec = attrList.specification();
317 if (coralSpec.size() == 0) {
318 throw std::runtime_error("ChaiCoralConverter::toContainer: zero-attribute specification");
319 }
320 const auto fields = std::make_shared<const chai::FieldSpec>(toFieldSpec(coralSpec));
321 chai::Container container(chai::PayloadSpec(*fields, chai::ChannelSpec({{channelId, channelName}})));
322 container.add(channelId, toValues(fields, attrList));
323 return container;
324}
325
326std::unique_ptr<CondAttrListCollection> ChaiCoralConverter::toCondAttrListCollection(
327 const chai::Container& container, bool hasRunLumiBlockTime) {
328 const SpecHolder spec(toCoralSpec(container.fieldSpec()));
329 auto collection = std::make_unique<CondAttrListCollection>(hasRunLumiBlockTime);
330 const auto names = container.channelSpec().channelNames();
331 for (const auto id : container.channelIds()) {
332 // Catch IDs too wide for ChanNum before the cast truncates them
333 if (id > std::numeric_limits<CondAttrListCollection::ChanNum>::max()) {
334 throw std::runtime_error(
335 "ChaiCoralConverter::toCondAttrListCollection: channel id " + std::to_string(id)
336 + " exceeds ChanNum range");
337 }
338 const auto chan = static_cast<CondAttrListCollection::ChanNum>(id);
339 collection->addShared(chan, toAttributeList(*spec, container[id]));
340 const auto nameIt = names.find(id);
341 if (nameIt != names.end() && !nameIt->second.empty()) {
342 collection->add(chan, nameIt->second);
343 }
344 }
345 return collection;
346}
347
348chai::Container ChaiCoralConverter::toContainer(const CondAttrListCollection& collection) {
349 if (collection.size() == 0) {
350 throw std::runtime_error("ChaiCoralConverter::toContainer: empty collection");
351 }
352 const coral::AttributeListSpecification& coralSpec = collection.begin()->second.specification();
353 if (coralSpec.size() == 0) {
354 throw std::runtime_error("ChaiCoralConverter::toContainer: zero-attribute specification");
355 }
356 // Container fixes its channel set at construction, so names are collected before building it
357 std::map<uint64_t, std::string> channelNames;
358 for (const auto& pair : collection) {
359 channelNames[static_cast<uint64_t>(pair.first)] = collection.chanName(pair.first);
360 }
361 const auto fields = std::make_shared<const chai::FieldSpec>(toFieldSpec(coralSpec));
362 chai::Container container(chai::PayloadSpec(*fields, chai::ChannelSpec(channelNames)));
363 for (const auto& [chan, attrList] : collection) {
364 const uint64_t id = chan;
365 container.add(id, toValues(fields, attrList));
366 }
367 return container;
368}
369
370unsigned int ChaiCoralConverter::valuesSize(const chai::Values& values) {
371 unsigned int total = 0;
372 const std::size_t n = values.size();
373 for (std::size_t i = 0; i != n; ++i) {
374 if (values.isNull(i)) continue;
375 switch (values.type(i)) {
376 case chai::Bool: total += sizeof(bool); break;
377 case chai::Int8: total += sizeof(int8_t); break;
378 case chai::UInt8: total += sizeof(uint8_t); break;
379 case chai::Int16: total += sizeof(int16_t); break;
380 case chai::UInt16: total += sizeof(uint16_t); break;
381 case chai::Int32: total += sizeof(int32_t); break;
382 case chai::UInt32: total += sizeof(uint32_t); break;
383 case chai::Int64: total += sizeof(int64_t); break;
384 case chai::UInt64: total += sizeof(uint64_t); break;
385 case chai::Float: total += sizeof(float); break;
386 case chai::Double: total += sizeof(double); break;
387 case chai::String:
388 total += values.get<std::string>(i).size();
389 break;
390 case chai::Blob:
391 total += values.get<chai::BlobData>(i).m_bytes.size();
392 break;
393 case chai::Unknown: // Fall-through
394 default:
395 throw std::runtime_error(
396 "ChaiCoralConverter::valuesSize: unrecognised CHAI type at field index " + std::to_string(i));
397 }
398 }
399 return total;
400}
401
402unsigned int ChaiCoralConverter::payloadSize(const chai::Container& container) {
403 unsigned int total = 0;
404 for (const auto channelId : container.channelIds()) {
405 total += valuesSize(container[channelId]);
406 }
407 return total;
408}
409
410unsigned int ChaiCoralConverter::payloadSize(const chai::VectorContainer& container) {
411 unsigned int total = 0;
412 for (const auto channelId : container.channelIds()) {
413 const auto& rows = container.rows(channelId);
414 for (std::size_t i = 0; i != rows.size(); ++i) {
415 if (!rows[i]) {
416 throw std::invalid_argument(
417 "ChaiCoralConverter::payloadSize: null row at channel " + std::to_string(channelId)
418 + " index " + std::to_string(i));
419 }
420 total += valuesSize(*rows[i]);
421 }
422 }
423 return total;
424}
Converts between CHAI payload containers and CORAL objects.
void operator()(T1)
size_t size() const
Number of registered mappings.
An AttributeList represents a logical row of attributes in a metadata table.
static coral::AttributeList toAttributeList(const coral::AttributeListSpecification &spec, const chai::Values &values)
Convert one channel's CHAI Values to a coral::AttributeList, one attribute per field in spec order.
static unsigned int valuesSize(const chai::Values &values)
Total byte size of one channel's field values, using the same type mapping as toAttributeList().
static unsigned int payloadSize(const chai::Container &container)
Total byte size of every populated channel in a scalar payload.
static std::vector< coral::AttributeList > toAttributeListVec(const coral::AttributeListSpecification &spec, const std::vector< chai::ValuesPtr > &rows)
Convert one channel's CHAI vector-payload rows to a coral::AttributeList per row, in row order,...
static coral::AttributeListSpecification * toCoralSpec(const chai::FieldSpec &fields)
Build a coral::AttributeListSpecification from a CHAI FieldSpec, one attribute per field in field ord...
static chai::FieldSpec toFieldSpec(const coral::AttributeListSpecification &spec)
Build a chai::FieldSpec from a coral::AttributeListSpecification, one field per attribute in spec ord...
static chai::Container toContainer(const AthenaAttributeList &attrList, uint64_t channelId=0, const std::string &channelName="")
Convert an AthenaAttributeList to a single-channel chai::Container.
static std::unique_ptr< AthenaAttributeList > toAthenaAttributeList(const chai::Container &container)
Convert a chai::Container's single populated channel to an AthenaAttributeList.
static std::unique_ptr< CondAttrListCollection > toCondAttrListCollection(const chai::Container &container, bool hasRunLumiBlockTime)
Convert a chai::Container to a CondAttrListCollection, one row per populated channel.
static chai::ValuesPtr toValues(chai::FieldSpecPtr fields, const coral::AttributeList &attrList)
Convert one coral::AttributeList to CHAI Values, one value per field in FieldSpec order.
static std::vector< chai::ValuesPtr > toValuesVec(chai::FieldSpecPtr fields, const std::vector< coral::AttributeList > &rows)
Convert a coral::AttributeList per row, in row order, to CHAI Values sharing one FieldSpecPtr,...
This class is a collection of AttributeLists where each one is associated with a channel number.
const_iterator begin() const
Access to Chan/AttributeList pairs via iterators.
const std::string & chanName(ChanNum chanNum) const
find name for particular channel
size_type size() const
number of Chan/AttributeList pairs
STL class.