ATLAS Offline Software
Loading...
Searching...
No Matches
ColumnVectorWrapper.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2025 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 // Check that relevant fields match
57 if (existingHeader.type != columnInfo.type)
58 throw std::runtime_error("column " + columnInfo.name + " type mismatch");
59 if (existingHeader.offsetName != columnInfo.offsetName)
60 throw std::runtime_error("column " + columnInfo.name + " offset name mismatch: " + existingHeader.offsetName + " vs " + columnInfo.offsetName);
61 if (existingHeader.isOffset != columnInfo.isOffset)
62 throw std::runtime_error("column " + columnInfo.name + " isOffset mismatch");
63 if (existingHeader.fixedDimensions != columnInfo.fixedDimensions)
64 throw std::runtime_error("column " + columnInfo.name + " fixed dimensions mismatch");
65 if (existingHeader.linkTargetNames != columnInfo.linkTargetNames)
66 throw std::runtime_error("column " + columnInfo.name + " link target names mismatch");
67 if (existingHeader.variantLinkKeyColumn != columnInfo.variantLinkKeyColumn)
68 throw std::runtime_error("column " + columnInfo.name + " variant link key column mismatch");
69
70 // Handle access mode conflicts
71 if (columnInfo.accessMode == ColumnAccessMode::output && existingHeader.readOnly)
72 throw std::runtime_error("column " + columnInfo.name + " already registered as input, cannot register as output");
73 // Promote to read-write if new column needs write access (update mode)
74 if (columnInfo.accessMode == ColumnAccessMode::update)
75 existingHeader.readOnly = false;
76 // If existing is already non-readOnly (output or update), keep it that way
77 return iter->second;
78 }
79
80 m_elements.emplace_back();
81 m_elements.at(sizeIndex).arraySize = m_elements.size();
82
83 auto& header = m_elements.back();
84 header.debugName = columnInfo.name;
85 header.type = columnInfo.type;
86 header.accessMode = columnInfo.accessMode;
87 header.offsetName = columnInfo.offsetName;
88 header.linkTargetNames = columnInfo.linkTargetNames;
89 header.variantLinkKeyColumn = columnInfo.variantLinkKeyColumn;
90 header.fixedDimensions = columnInfo.fixedDimensions;
91
92 switch (columnInfo.accessMode)
93 {
95 break;
97 header.readOnly = false;
98 break;
100 header.readOnly = false;
101 break;
102 }
103 for (unsigned dim : columnInfo.fixedDimensions)
104 header.arraySize *= dim;
105 header.isOffset = columnInfo.isOffset;
106 if (!columnInfo.isOptional)
107 header.isOptional = false;
108 if (!columnInfo.offsetName.empty())
109 header.offsetIndex = unsetIndex;
110
111 const std::size_t newIndex = m_elements.size() - 1;
112 m_nameToIndex.emplace(columnInfo.name, newIndex);
113 return newIndex;
114 }
115
116
117
118 void ColumnVectorHeader ::
119 setOffsetColumn (std::size_t columnIndex, std::size_t offsetIndex)
120 {
121 if (columnIndex >= m_elements.size())
122 throw std::logic_error ("column index out of range");
123 if (offsetIndex >= m_elements.size())
124 throw std::logic_error ("offset index out of range");
125 auto& column = m_elements.at(columnIndex);
126 auto& offset = m_elements.at(offsetIndex);
127 if (!offset.isOffset)
128 throw std::runtime_error ("trying to set " + offset.debugName + " as offset column for " + column.debugName + ", but it is not marked as offset column");
129 if (column.offsetIndex != unsetIndex && column.offsetIndex != offsetIndex)
130 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);
131 column.offsetIndex = offsetIndex;
132 }
133
134
135
136 void ColumnVectorHeader ::
137 checkSelf () const
138 {
139 if (m_elements.size() < numFixedColumns)
140 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()));
141 if (m_elements[sizeIndex].arraySize != m_elements.size())
142 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));
143
144 // skipping the first column, which is always the null column
145 // and behaves in a funny manner
146 for (std::size_t columnIndex = 1u; columnIndex != m_elements.size(); ++ columnIndex)
147 {
148 const auto& column = m_elements[columnIndex];
149 if (column.debugName.empty())
150 throw std::logic_error ("column " + std::to_string(columnIndex) + " has no name");
151 if (column.type == nullptr)
152 throw std::logic_error ("column " + column.debugName + " has no type");
153 if (column.isOffset && *column.type != typeid(ColumnarOffsetType))
154 throw std::logic_error ("column " + column.debugName + " is an offset column that is of type " + boost::core::demangle(column.type->name()) + " instead of ColumnarOffsetType");
155 }
156
157 for (std::size_t columnIndex = 1u; columnIndex != m_elements.size(); ++ columnIndex)
158 {
159 const auto& column = m_elements[columnIndex];
160 if (column.offsetIndex != nullIndex)
161 {
162 if (column.offsetIndex == unsetIndex)
163 throw std::logic_error ("column " + column.debugName + " has an offset index that was never set");
164 if (column.offsetIndex >= m_elements.size())
165 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) + ")");
166 const auto& offsetElement = m_elements[column.offsetIndex];
167 if (!offsetElement.isOffset)
168 throw std::logic_error ("column " + column.debugName + " has offset index that is not marked as offset");
169 }
170 }
171 }
172
173
174
175 void ColumnVectorHeader ::
176 checkData (std::span<const void* const> data) const
177 {
178 if (data.size() != m_elements.size())
179 throw std::logic_error ("data vector has wrong size, expected " + std::to_string(m_elements.size()) + " but got " + std::to_string(data.size()));
180 if (data[nullIndex] != nullptr)
181 throw std::logic_error ("null column is not set to a nullptr value");
182 const auto* sizeVector = static_cast<const std::size_t*>(data[sizeIndex]);
183 for (std::size_t columnIndex = 0u; columnIndex != m_elements.size(); ++ columnIndex)
184 {
185 const auto& column = m_elements[columnIndex];
186 if (data[columnIndex] == nullptr)
187 {
188 if (column.isOptional)
189 continue;
190 if (column.offsetIndex != nullIndex && data[column.offsetIndex] == nullptr)
191 continue;
192 throw std::logic_error ("column " + column.debugName + " was not set");
193 }
194 if (column.isOffset)
195 {
196 const auto size = sizeVector[columnIndex];
197 if (size < 1)
198 throw std::runtime_error ("offset column " + column.debugName + " has size " + std::to_string(size) + ", but needs at least 1 element");
199 auto *offsets = static_cast<const ColumnarOffsetType*>(data[columnIndex]);
200 if (offsets[0] != 0)
201 throw std::runtime_error ("offset column doesn't start with 0: " + column.debugName);
202 for (std::size_t i = 1u; i != size; ++ i)
203 {
204 if (offsets[i] < offsets[i-1])
205 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]));
206 }
207 }
208 }
209 for (std::size_t columnIndex = 0u; columnIndex != m_elements.size(); ++ columnIndex)
210 {
211 if (data[columnIndex] == nullptr)
212 continue;
213 const auto& column = m_elements[columnIndex];
214 std::size_t expectedSize = 1u;
215 if (column.offsetIndex != ColumnVectorHeader::nullIndex)
216 {
217 if (data[column.offsetIndex] == nullptr)
218 throw std::runtime_error ("column " + column.debugName + " uses offset column " + m_elements[column.offsetIndex].debugName + " that is not set");
219 const auto offsetIndex = column.offsetIndex;
220 auto *offsetsPtr = static_cast<const ColumnarOffsetType*>(data[offsetIndex]);
221 expectedSize = offsetsPtr[sizeVector[offsetIndex]-1];
222 }
223 expectedSize *= column.arraySize;
224
225 if (column.isOffset)
226 expectedSize += 1u;
227
228 if (sizeVector[columnIndex] != expectedSize)
229 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));
230 }
231 }
232
233
234
235 ColumnVectorData ::
236 ColumnVectorData (const ColumnVectorHeader *val_header)
237 : m_header (val_header),
238 m_data (val_header->numColumns(), nullptr),
239 m_dataSize (val_header->numColumns(), 0u)
240 {
242 }
243
244
245
246 void ColumnVectorData ::
247 setColumnVoid (std::size_t columnIndex, std::size_t size, const void *dataPtr, const std::type_info& type, bool isConst)
248 {
249 if (columnIndex == ColumnVectorHeader::nullIndex)
250 throw std::logic_error ("cannot set the null column");
251 if (columnIndex >= m_header->numColumns())
252 throw std::logic_error ("invalid column index: " + std::to_string(columnIndex) + " (max is " + std::to_string(m_header->numColumns()-1) + ")");
253 auto& header = m_header->getColumn (columnIndex);
254
255 // If dataPtr is null, we use a dummy value to avoid issues in which
256 // we check whether a column exists by checking for a null pointer,
257 // which would return false for columns that were set with a
258 // nullptr.
259 if (dataPtr == nullptr)
260 {
261 if (size != 0) [[unlikely]]
262 throw std::logic_error ("dataPtr is null but size is not zero for column: " + header.debugName);
263 static const unsigned dummyValue = 0;
264 dataPtr = &dummyValue;
265 }
266
267 if (type != *header.type)
268 throw std::runtime_error ("invalid type for column: " + header.debugName);
269 if (isConst && !header.readOnly)
270 throw std::runtime_error ("assigning const vector to a column that is not read-only: " + header.debugName);
271 if (m_data[columnIndex] != nullptr)
272 throw std::runtime_error ("column filled multiple times: " + header.debugName);
273 auto *castDataPtr ATLAS_THREAD_SAFE = const_cast<void*>(dataPtr);
274 m_data[columnIndex] = castDataPtr;
275 m_dataSize[columnIndex] = size;
276 }
277
278
279
280 std::pair<std::size_t,const void*> ColumnVectorData ::
281 getColumnVoid (std::size_t columnIndex, const std::type_info *type, bool isConst)
282 {
283 if (columnIndex >= m_header->numColumns())
284 throw std::runtime_error ("invalid column index: " + std::to_string(columnIndex) + " (max is " + std::to_string(m_header->numColumns()-1) + ")");
285
286 auto& header = m_header->getColumn (columnIndex);
287 if (*type != *header.type)
288 throw std::runtime_error ("invalid type for column: " + header.debugName);
289 if (!isConst && header.readOnly)
290 throw std::runtime_error ("retrieving non-const vector from a read-only column: " + header.debugName);
291 if (m_data[columnIndex] != nullptr)
292 return std::make_pair (m_dataSize[columnIndex], m_data[columnIndex]);
293 else
294 return std::make_pair (0u, nullptr);
295 }
296
297
298
299 void ColumnVectorData ::
300 callNoCheck (const IColumnarTool& tool)
301 {
302 tool.callVoid (m_data.data());
303 }
304
305
306
307 std::unordered_map<std::string, ColumnInfo> ColumnVectorHeader ::
308 getAllColumnInfo () const
309 {
310 std::unordered_map<std::string, ColumnInfo> result;
311 // Skip the fixed columns (null and size)
312 for (std::size_t i = numFixedColumns; i < m_elements.size(); ++i)
313 {
314 const auto& header = m_elements[i];
315 ColumnInfo info;
316 info.name = header.debugName;
317 info.index = static_cast<unsigned>(i);
318 info.type = header.type;
319 info.accessMode = header.accessMode;
320 info.offsetName = header.offsetName;
321 info.fixedDimensions = header.fixedDimensions;
322 info.isOffset = header.isOffset;
323 info.isOptional = header.isOptional;
324 info.linkTargetNames = header.linkTargetNames;
325 info.variantLinkKeyColumn = header.variantLinkKeyColumn;
326 result.emplace(info.name, std::move(info));
327 }
328 return result;
329 }
330}
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
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::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::string variantLinkKeyColumn
if this is a variant link column, this is the name of the column with the container keys
Definition ColumnInfo.h:169
std::string name
the name of the column
Definition ColumnInfo.h:42
std::vector< std::string > linkTargetNames
for link columns: the name(s) of the container(s) we link to
Definition ColumnInfo.h:154
bool isOffset
whether this is an offset column
Definition ColumnInfo.h:92
ColumnAccessMode accessMode
the access mode for the column
Definition ColumnInfo.h:58
const std::type_info * type
the type of the individual entries in the column
Definition ColumnInfo.h:54