ATLAS Offline Software
Loading...
Searching...
No Matches
ColumnVectorWrapper.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
8//
9// includes
10//
11
13
17
18#include <boost/core/demangle.hpp>
19
20#include <algorithm>
21#include <stdexcept>
22
23//
24// method implementations
25//
26
27namespace columnar
28{
29 ColumnVectorHeader ::
30 ColumnVectorHeader ()
31 {
33
34 // we always reserve the first column as having a null value
35 m_elements.at(nullIndex).debugName = "<null>";
36 m_elements.at(nullIndex).isOptional = true;
37
38 // we always reserve the second column as the size column
39 m_elements.at(sizeIndex).debugName = "<size>";
40 m_elements.at(sizeIndex).type = &typeid(std::size_t);
41 m_elements.at(sizeIndex).isOptional = false;
42 m_elements.at(sizeIndex).readOnly = true;
43 }
44
45
46
47 [[nodiscard]] std::size_t ColumnVectorHeader ::
48 addColumn (const ColumnInfo& columnInfo)
49 {
50 // Check for existing column with the same name (deduplication for multi-tool support)
51 auto iter = m_nameToIndex.find(columnInfo.name);
52 if (iter != m_nameToIndex.end())
53 {
54 auto& existingHeader = m_elements.at(iter->second);
55
56 // Variant link key columns with different targets get auto-renamed
57 if (!columnInfo.keyColumnForVariantLink.empty() && !existingHeader.keyColumnForVariantLink.empty() && existingHeader.variantLinkTargetNames != columnInfo.variantLinkTargetNames)
58 {
59 std::string uniqueName;
60 for (unsigned suffix = 1; ; ++suffix)
61 {
62 uniqueName = columnInfo.name + "." + std::to_string(suffix);
63 if (m_nameToIndex.find(uniqueName) == m_nameToIndex.end())
64 break;
65 }
66 ColumnInfo renamedInfo = columnInfo;
67 renamedInfo.name = std::move(uniqueName);
68 return addColumn(renamedInfo);
69 }
70
71 // Check that relevant fields match
72 if (existingHeader.type != columnInfo.type)
73 throw std::runtime_error("column " + columnInfo.name + " type mismatch");
74 if (existingHeader.offsetName != columnInfo.offsetName)
75 throw std::runtime_error("column " + columnInfo.name + " offset name mismatch: " + existingHeader.offsetName + " vs " + columnInfo.offsetName);
76 if (existingHeader.isOffset != columnInfo.isOffset)
77 throw std::runtime_error("column " + columnInfo.name + " isOffset mismatch");
78 if (existingHeader.fixedDimensions != columnInfo.fixedDimensions)
79 throw std::runtime_error("column " + columnInfo.name + " fixed dimensions mismatch");
80 if (existingHeader.soleLinkTargetName != columnInfo.soleLinkTargetName)
81 throw std::runtime_error("column " + columnInfo.name + " sole link target name mismatch");
82 if (existingHeader.isVariantLink != columnInfo.isVariantLink)
83 throw std::runtime_error("column " + columnInfo.name + " isVariantLink mismatch");
84 if (existingHeader.variantLinkTargetNames != columnInfo.variantLinkTargetNames)
85 throw std::runtime_error("column " + columnInfo.name + " variant link target names mismatch");
86 if (existingHeader.keyColumnForVariantLink != columnInfo.keyColumnForVariantLink)
87 throw std::runtime_error("column " + columnInfo.name + " key column for variant link mismatch");
88
89 // Handle access mode conflicts
90 if (columnInfo.accessMode == ColumnAccessMode::output && existingHeader.readOnly)
91 throw std::runtime_error("column " + columnInfo.name + " already registered as input, cannot register as output");
92 // Promote to read-write if new column needs write access (update mode)
93 if (columnInfo.accessMode == ColumnAccessMode::update)
94 existingHeader.readOnly = false;
95 // If existing is already non-readOnly (output or update), keep it that way
96 return iter->second;
97 }
98
99 m_elements.emplace_back();
100 m_elements.at(sizeIndex).arraySize = m_elements.size();
101
102 auto& header = m_elements.back();
103 header.debugName = columnInfo.name;
104 header.type = columnInfo.type;
105 header.accessMode = columnInfo.accessMode;
106 header.offsetName = columnInfo.offsetName;
107 header.soleLinkTargetName = columnInfo.soleLinkTargetName;
108 header.isVariantLink = columnInfo.isVariantLink;
109 header.variantLinkTargetNames = columnInfo.variantLinkTargetNames;
110 header.keyColumnForVariantLink = columnInfo.keyColumnForVariantLink;
111 header.fixedDimensions = columnInfo.fixedDimensions;
112
113 switch (columnInfo.accessMode)
114 {
116 break;
118 header.readOnly = false;
119 break;
121 header.readOnly = false;
122 break;
123 }
124 for (unsigned dim : columnInfo.fixedDimensions)
125 header.arraySize *= dim;
126 header.isOffset = columnInfo.isOffset;
127 if (!columnInfo.isOptional)
128 header.isOptional = false;
129 if (!columnInfo.offsetName.empty())
130 header.offsetIndex = unsetIndex;
131
132 const std::size_t newIndex = m_elements.size() - 1;
133 m_nameToIndex.emplace(columnInfo.name, newIndex);
134 return newIndex;
135 }
136
137
138
139 void ColumnVectorHeader ::
140 setOffsetColumn (std::size_t columnIndex, std::size_t offsetIndex)
141 {
142 if (columnIndex >= m_elements.size())
143 throw std::logic_error ("column index out of range");
144 if (offsetIndex >= m_elements.size())
145 throw std::logic_error ("offset index out of range");
146 auto& column = m_elements.at(columnIndex);
147 auto& offset = m_elements.at(offsetIndex);
148 if (!offset.isOffset)
149 throw std::runtime_error ("trying to set " + offset.debugName + " as offset column for " + column.debugName + ", but it is not marked as offset column");
150 if (column.offsetIndex != unsetIndex && column.offsetIndex != offsetIndex)
151 throw std::runtime_error ("trying to set " + offset.debugName + " as offset column for " + column.debugName + ", but it is already set to " + m_elements.at(column.offsetIndex).debugName);
152 column.offsetIndex = offsetIndex;
153 }
154
155
156
157 void ColumnVectorHeader ::
158 checkSelf () const
159 {
160 if (m_elements.size() < numFixedColumns)
161 throw std::logic_error ("header has too few m_elements, expected at least " + std::to_string(numFixedColumns) + " but got " + std::to_string(m_elements.size()));
162 if (m_elements[sizeIndex].arraySize != m_elements.size())
163 throw std::logic_error ("size column has wrong array size, expected " + std::to_string(m_elements.size()) + " but got " + std::to_string(m_elements[sizeIndex].arraySize));
164
165 // skipping the first column, which is always the null column
166 // and behaves in a funny manner
167 for (std::size_t columnIndex = 1u; columnIndex != m_elements.size(); ++ columnIndex)
168 {
169 const auto& column = m_elements[columnIndex];
170 if (column.debugName.empty())
171 throw std::logic_error ("column " + std::to_string(columnIndex) + " has no name");
172 if (column.type == nullptr)
173 throw std::logic_error ("column " + column.debugName + " has no type");
174 if (column.isOffset && *column.type != typeid(ColumnarOffsetType))
175 throw std::logic_error ("column " + column.debugName + " is an offset column that is of type " + boost::core::demangle(column.type->name()) + " instead of ColumnarOffsetType");
176 }
177
178 for (std::size_t columnIndex = 1u; columnIndex != m_elements.size(); ++ columnIndex)
179 {
180 const auto& column = m_elements[columnIndex];
181 if (column.offsetIndex != nullIndex)
182 {
183 if (column.offsetIndex == unsetIndex)
184 throw std::logic_error ("column " + column.debugName + " has an offset index that was never set");
185 if (column.offsetIndex >= m_elements.size())
186 throw std::logic_error ("column " + column.debugName + " has invalid offset index " + std::to_string(column.offsetIndex) + " (max is " + std::to_string(m_elements.size()-1) + ")");
187 const auto& offsetElement = m_elements[column.offsetIndex];
188 if (!offsetElement.isOffset)
189 throw std::logic_error ("column " + column.debugName + " has offset index that is not marked as offset");
190 }
191 }
192 }
193
194
195
196 void ColumnVectorHeader ::
197 checkData (std::span<const void* const> data) const
198 {
199 if (data.size() != m_elements.size())
200 throw std::logic_error ("data vector has wrong size, expected " + std::to_string(m_elements.size()) + " but got " + std::to_string(data.size()));
201 if (data[nullIndex] != nullptr)
202 throw std::logic_error ("null column is not set to a nullptr value");
203 const auto* sizeVector = static_cast<const std::size_t*>(data[sizeIndex]);
204 for (std::size_t columnIndex = 0u; columnIndex != m_elements.size(); ++ columnIndex)
205 {
206 const auto& column = m_elements[columnIndex];
207 if (data[columnIndex] == nullptr)
208 {
209 if (column.isOptional)
210 continue;
211 if (column.offsetIndex != nullIndex && data[column.offsetIndex] == nullptr)
212 continue;
213 throw std::logic_error ("column " + column.debugName + " was not set");
214 }
215 if (column.isOffset)
216 {
217 const auto size = sizeVector[columnIndex];
218 if (size < 1)
219 throw std::runtime_error ("offset column " + column.debugName + " has size " + std::to_string(size) + ", but needs at least 1 element");
220 auto *offsets = static_cast<const ColumnarOffsetType*>(data[columnIndex]);
221 if (offsets[0] != 0)
222 throw std::runtime_error ("offset column doesn't start with 0: " + column.debugName);
223 for (std::size_t i = 1u; i != size; ++ i)
224 {
225 if (offsets[i] < offsets[i-1])
226 throw std::runtime_error ("offset column " + column.debugName + " is not monotonically increasing at index " + std::to_string(i) + ": " + std::to_string(offsets[i-1]) + " > " + std::to_string(offsets[i]));
227 }
228 }
229 }
230 for (std::size_t columnIndex = 0u; columnIndex != m_elements.size(); ++ columnIndex)
231 {
232 if (data[columnIndex] == nullptr)
233 continue;
234 const auto& column = m_elements[columnIndex];
235 std::size_t expectedSize = 1u;
236 if (column.offsetIndex != ColumnVectorHeader::nullIndex)
237 {
238 if (data[column.offsetIndex] == nullptr)
239 throw std::runtime_error ("column " + column.debugName + " uses offset column " + m_elements[column.offsetIndex].debugName + " that is not set");
240 const auto offsetIndex = column.offsetIndex;
241 auto *offsetsPtr = static_cast<const ColumnarOffsetType*>(data[offsetIndex]);
242 expectedSize = offsetsPtr[sizeVector[offsetIndex]-1];
243 }
244 expectedSize *= column.arraySize;
245
246 if (column.isOffset)
247 expectedSize += 1u;
248
249 if (sizeVector[columnIndex] != expectedSize)
250 throw std::runtime_error ("column size doesn't match expected size: " + column.debugName + ", found " + std::to_string (sizeVector[columnIndex]) + " vs exptected=" + std::to_string (expectedSize) + " isOffset=" + std::to_string (column.isOffset));
251 }
252 }
253
254
255
256 ColumnVectorData ::
257 ColumnVectorData (const ColumnVectorHeader *val_header)
258 : m_header (val_header),
259 m_data (val_header->numColumns(), nullptr),
260 m_dataSize (val_header->numColumns(), 0u)
261 {
263 }
264
265
266
267 void ColumnVectorData ::
268 setColumnVoid (std::size_t columnIndex, std::size_t size, const void *dataPtr, const std::type_info& type, bool isConst)
269 {
270 if (columnIndex == ColumnVectorHeader::nullIndex)
271 throw std::logic_error ("cannot set the null column");
272 if (columnIndex >= m_header->numColumns())
273 throw std::logic_error ("invalid column index: " + std::to_string(columnIndex) + " (max is " + std::to_string(m_header->numColumns()-1) + ")");
274 auto& header = m_header->getColumn (columnIndex);
275
276 // If dataPtr is null, we use a dummy value to avoid issues in which
277 // we check whether a column exists by checking for a null pointer,
278 // which would return false for columns that were set with a
279 // nullptr.
280 if (dataPtr == nullptr)
281 {
282 if (size != 0) [[unlikely]]
283 throw std::logic_error ("dataPtr is null but size is not zero for column: " + header.debugName);
284 static const unsigned dummyValue = 0;
285 dataPtr = &dummyValue;
286 }
287
288 if (type != *header.type)
289 throw std::runtime_error ("invalid type for column: " + header.debugName);
290 if (isConst && !header.readOnly)
291 throw std::runtime_error ("assigning const vector to a column that is not read-only: " + header.debugName);
292 if (m_data[columnIndex] != nullptr)
293 throw std::runtime_error ("column filled multiple times: " + header.debugName);
294 auto *castDataPtr ATLAS_THREAD_SAFE = const_cast<void*>(dataPtr);
295 m_data[columnIndex] = castDataPtr;
296 m_dataSize[columnIndex] = size;
297 }
298
299
300
301 std::pair<std::size_t,const void*> ColumnVectorData ::
302 getColumnVoid (std::size_t columnIndex, const std::type_info *type, bool isConst) const
303 {
304 if (columnIndex >= m_header->numColumns())
305 throw std::runtime_error ("invalid column index: " + std::to_string(columnIndex) + " (max is " + std::to_string(m_header->numColumns()-1) + ")");
306
307 auto& header = m_header->getColumn (columnIndex);
308 if (*type != *header.type)
309 throw std::runtime_error ("invalid type for column: " + header.debugName);
310 if (!isConst && header.readOnly)
311 throw std::runtime_error ("retrieving non-const vector from a read-only column: " + header.debugName);
312 if (m_data[columnIndex] != nullptr)
313 return std::make_pair (m_dataSize[columnIndex], m_data[columnIndex]);
314 else
315 return std::make_pair (0u, nullptr);
316 }
317
318
319
320 void ColumnVectorData ::
321 callNoCheck (const IColumnarTool& tool)
322 {
323 tool.callVoid (m_data.data());
324 }
325
326
327
328 std::unordered_map<std::string, ColumnInfo> ColumnVectorHeader ::
329 getAllColumnInfo () const
330 {
331 std::unordered_map<std::string, ColumnInfo> result;
332 // Skip the fixed columns (null and size)
333 for (std::size_t i = numFixedColumns; i < m_elements.size(); ++i)
334 {
335 const auto& header = m_elements[i];
336 ColumnInfo info;
337 info.name = header.debugName;
338 info.index = static_cast<unsigned>(i);
339 info.type = header.type;
340 info.accessMode = header.accessMode;
341 info.offsetName = header.offsetName;
342 info.fixedDimensions = header.fixedDimensions;
343 info.isOffset = header.isOffset;
344 info.isOptional = header.isOptional;
345 info.soleLinkTargetName = header.soleLinkTargetName;
346 info.isVariantLink = header.isVariantLink;
347 info.variantLinkTargetNames = header.variantLinkTargetNames;
348 info.keyColumnForVariantLink = header.keyColumnForVariantLink;
349 result.emplace(info.name, std::move(info));
350 }
351 return result;
352 }
353}
char data[hepevt_bytes_allocation_ATLAS]
Definition HepEvt.cxx:11
Define macros for attributes used to control the static checker.
#define ATLAS_THREAD_SAFE
const ColumnVectorHeader * m_header
void setColumn(std::size_t columnIndex, std::size_t size, CT *dataPtr)
set the data for the given column
std::vector< std::size_t > m_dataSize
the header information for the entire columnar data vector
std::size_t addColumn(const ColumnInfo &columnInfo)
add a column for the given ColumnInfo, returning its index
static constexpr std::size_t nullIndex
the index used for an invalid index (always has to be 0)
static constexpr std::size_t numFixedColumns
the number of fix elements in the columnar data vector
std::unordered_map< std::string, std::size_t > m_nameToIndex
map from column name to index for deduplication
static constexpr std::size_t unsetIndex
the number used for an unset but non-null index
std::vector< ColumnVectorElementHeader > m_elements
the elements in the columnar data vector
static constexpr std::size_t sizeIndex
the index used for the column size column
an interface for tools that operate on columnar data
@ update
an updateable column
Definition ColumnInfo.h:27
@ output
an output column
Definition ColumnInfo.h:24
@ input
an input column
Definition ColumnInfo.h:21
std::size_t ColumnarOffsetType
the type used for the size and offsets in the columnar data
#define unlikely(x)
a struct that contains meta-information about each column that's needed to interface the column with ...
Definition ColumnInfo.h:35
std::string offsetName
the name of the offset column used for this column (or empty string for none)
Definition ColumnInfo.h:74
std::string soleLinkTargetName
for simple link columns: the name of the target container
Definition ColumnInfo.h:131
std::string keyColumnForVariantLink
if this is a key column for a variant link, the name of the associated link column
Definition ColumnInfo.h:177
std::vector< unsigned > fixedDimensions
the fixed dimensions this column has (if any)
Definition ColumnInfo.h:82
bool isOptional
whether this column is optional
Definition ColumnInfo.h:121
std::vector< std::string > variantLinkTargetNames
for variant link key columns: the names of the containers we can link to
Definition ColumnInfo.h:162
std::string name
the name of the column
Definition ColumnInfo.h:42
bool isOffset
whether this is an offset column
Definition ColumnInfo.h:92
ColumnAccessMode accessMode
the access mode for the column
Definition ColumnInfo.h:58
bool isVariantLink
whether this is a variant link column
Definition ColumnInfo.h:139
const std::type_info * type
the type of the individual entries in the column
Definition ColumnInfo.h:54