ATLAS Offline Software
JaggedVecVectorFactory.icc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration.
3  */
4 /**
5  * @file AthContainers/tools/JaggedVecVectorFactory.icc
6  * @author scott snyder <snyder@bnl.gov>
7  * @date Apr, 2024
8  * @brief Factory object that creates vectors using @c AuxTypeVector,
9  * specialized for JaggedVec.
10  */
11 
12 
13 #include "AthContainers/tools/AuxTypeVector.h"
14 #include "AthContainers/AuxTypeRegistry.h"
15 #include "AthContainers/exceptions.h"
16 
17 
18 namespace SG {
19 
20 
21 /**
22  * @brief Create a vector object of this type.
23  * @param auxid ID for the variable being created.
24  * @param size Initial size of the new vector.
25  * @param capacity Initial capacity of the new vector.
26  * @param isLinked True if this variable is linked from another one.
27  * Must be false.
28  */
29 template <class T, class ALLOC>
30 std::unique_ptr<IAuxTypeVector>
31 JaggedVecVectorFactory<T, ALLOC>::create (SG::auxid_t auxid,
32  size_t size, size_t capacity,
33  [[maybe_unused]] bool isLinked) const
34 {
35  assert (!isLinked);
36  const AuxTypeRegistry& r = AuxTypeRegistry::instance();
37  auxid_t linked_id = r.linkedVariable (auxid);
38 
39  using linkedAlloc = typename std::allocator_traits<ALLOC>::template rebind_alloc<T>;
40  auto linkedVec =
41  std::make_unique<AuxTypeVector<T, linkedAlloc> > (linked_id, 0, 0, true);
42  return std::make_unique<AuxTypeVector_t> (auxid, size, capacity,
43  std::move (linkedVec));
44 }
45 
46 
47 /**
48  * @brief Create a vector object of this type from a data blob.
49  * @param auxid ID for the variable being created.
50  * @param data The vector object.
51  * @param linkedVector The interface for another variable linked to this one.
52  * (We do not take ownership.)
53  * @param isPacked If true, @c data is a @c PackedContainer.
54  * @param ownFlag If true, the newly-created IAuxTypeVector object
55  * will take ownership of @c data.
56  * @param isLinked True if this variable is linked from another one.
57  *
58  * @c data should be a pointer to a
59  * std::vector<SG::JaggedVec<CONT>, ALLOC<...> > object obtained with new.
60  * For this method, isPacked and isLinked must both be false.
61  */
62 template <class CONT, class ALLOC>
63 std::unique_ptr<IAuxTypeVector>
64 JaggedVecVectorFactory<CONT, ALLOC>::createFromData (SG::auxid_t auxid,
65  void* data,
66  IAuxTypeVector* linkedVector,
67  [[maybe_unused]] bool isPacked,
68  bool ownFlag,
69  [[maybe_unused]] bool isLinked) const
70 {
71  assert (!isPacked && !isLinked && linkedVector != nullptr);
72  using Holder = SG::JaggedVecVectorHolder<CONT, ALLOC>;
73  using vector_type = typename Holder::vector_type;
74  return std::make_unique<Holder>
75  (auxid, reinterpret_cast<vector_type*>(data), linkedVector, ownFlag);
76 }
77 
78 
79 /**
80  * @brief Copy elements between vectors.
81  * @param auxid The aux data item being operated on.
82  * @param dst Container for the destination vector.
83  * @param dst_index Index of the first destination element in the vector.
84  * @param src Container for the source vector.
85  * @param src_index Index of the first source element in the vector.
86  * @param n Number of elements to copy.
87  * @param for_output If true, apply thinning.
88  *
89  * @c dst and @ src can be either the same or different.
90  */
91 template <class T, class ALLOC>
92 void JaggedVecVectorFactory<T, ALLOC>::copyImpl (SG::auxid_t auxid,
93  AuxVectorData& dst,
94  size_t dst_index,
95  const AuxVectorData& src,
96  size_t src_index,
97  size_t n,
98  bool for_output) const
99 {
100  if (n == 0) return;
101 
102  // Check for overlaps.
103  if (&src == &dst) {
104  size_t src_end = src_index + n;
105  size_t dst_end = dst_index + n;
106  if ((src_end > dst_index && src_end <= dst_end) ||
107  (dst_end > src_index && dst_end <= src_end))
108  {
109  // Overlapping copies not implemented for jagged vectors.
110  // Talk to core software if this is an issue.
111  throwJaggedVecOverlappingCopy();
112  }
113  }
114 
115  vector_value_type* v_dst = reinterpret_cast<vector_value_type*> (dst.getDataArray (auxid));
116  const vector_value_type* v_src = &dst==&src ? v_dst : reinterpret_cast<const vector_value_type*> (src.getDataArray (auxid));
117  IAuxTypeVector* dst_lv = dst.getStore()->linkedVector (auxid);
118  const SG::auxid_t payload_auxid = dst_lv->auxid();
119  const AuxTypeRegistry& r = AuxTypeRegistry::instance();
120  const IAuxTypeVectorFactory* payload_fac = r.getFactory (payload_auxid);
121 
122  const size_t dst_first = v_dst[dst_index].begin(dst_index);
123  size_t src_first = v_src[src_index].begin(src_index);
124  const size_t src_first_orig = src_first;
125  const size_t n_dst = v_dst[dst_index+n-1].end() - dst_first;
126  const size_t n_src = v_src[src_index+n-1].end() - src_first;
127 
128  // First copy the Elt elements. We'll fix up the indices below.
129  std::copy_n (v_src+src_index, n, v_dst+dst_index);
130 
131  // Adjust the size of the destination payload container.
132  if (n_src != n_dst) {
133  if (!dst_lv->shift (dst_first+n_dst, n_src - n_dst)) {
134  dst.clearCache (payload_auxid);
135  }
136  if (&dst == &src && src_first > dst_first) {
137  src_first += (n_src - n_dst);
138  }
139  }
140 
141  // Copy the payload elements.
142  if (for_output) {
143  payload_fac->copyForOutput (payload_auxid, dst, dst_first, src, src_first, n_src);
144  }
145  else {
146  payload_fac->copy (payload_auxid, dst, dst_first, src, src_first, n_src);
147  }
148 
149  // Fixup the Elt entries --- first the ones we copied, then the following
150  // ones in the destination container.
151  std::for_each_n (v_dst+dst_index, n, Shift (dst_first - src_first_orig));
152  std::for_each (v_dst+dst_index+n, v_dst+dst.size_v(), Shift (n_src - n_dst));
153 }
154 
155 
156 /**
157  * @brief Copy elements between vectors.
158  * @param auxid The aux data item being operated on.
159  * @param dst Container for the destination vector.
160  * @param dst_index Index of the first destination element in the vector.
161  * @param src Container for the source vector.
162  * @param src_index Index of the first source element in the vector.
163  * @param n Number of elements to copy.
164  *
165  * @c dst and @ src can be either the same or different.
166  */
167 template <class T, class ALLOC>
168 void JaggedVecVectorFactory<T, ALLOC>::copy (SG::auxid_t auxid,
169  AuxVectorData& dst,
170  size_t dst_index,
171  const AuxVectorData& src,
172  size_t src_index,
173  size_t n) const
174 {
175  copyImpl (auxid, dst, dst_index, src, src_index, n, false);
176 }
177 
178 
179 /**
180  * @brief Copy elements between vectors, possibly applying thinning.
181  * @param auxid The aux data item being operated on.
182  * @param dst Container for the destination vector.
183  * @param dst_index Index of the first destination element in the vector.
184  * @param src Container for the source vector.
185  * @param src_index Index of source element in the vector.
186  * @param src_index Index of the first source element in the vector.
187  * @param n Number of elements to copy.
188  *
189  * @c dst and @ src can be either the same or different.
190  */
191 template <class CONT, class ALLOC>
192 void JaggedVecVectorFactory<CONT, ALLOC>::copyForOutput
193  (SG::auxid_t auxid,
194  AuxVectorData& dst, size_t dst_index,
195  const AuxVectorData& src, size_t src_index,
196  size_t n) const
197 {
198  copyImpl (auxid, dst, dst_index, src, src_index, n, true);
199 }
200 
201 
202 /**
203  * @brief Swap elements between vectors.
204  * @param auxid The aux data item being operated on.
205  * @param a Container for the first vector.
206  * @param aindex Index of the first element in the first vector.
207  * @param b Container for the second vector.
208  * @param bindex Index of the first element in the second vector.
209  * @param n Number of elements to swap.
210  *
211  * @c a and @ b can be either the same or different.
212  * However, the ranges should not overlap.
213  */
214 template <class T, class ALLOC>
215 void JaggedVecVectorFactory<T, ALLOC>::swap (SG::auxid_t auxid,
216  AuxVectorData& a, size_t aindex,
217  AuxVectorData& b, size_t bindex,
218  size_t n) const
219 {
220  if (n == 0) return;
221 
222  vector_value_type* v_a = reinterpret_cast<vector_value_type*> (a.getDataArray (auxid));
223  vector_value_type* v_b = &a==&b ? v_a : reinterpret_cast<vector_value_type*> (b.getDataArray (auxid));
224  IAuxTypeVector* alv = a.getStore()->linkedVector (auxid);
225  IAuxTypeVector* blv = &a==&b ? alv : b.getStore()->linkedVector (auxid);
226  const SG::auxid_t payload_auxid = alv->auxid();
227  const AuxTypeRegistry& r = AuxTypeRegistry::instance();
228  const IAuxTypeVectorFactory* payload_fac = r.getFactory (payload_auxid);
229 
230  size_t a_first = v_a[aindex].begin(aindex);
231  size_t b_first = v_b[bindex].begin(bindex);
232  const size_t a_first_orig = a_first;
233  const size_t b_first_orig = b_first;
234  const size_t n_a = v_a[aindex+n-1].end() - a_first;
235  const size_t n_b = v_b[bindex+n-1].end() - b_first;
236 
237  // First swap the Elt entries. We'll fix up the indices below.
238  std::swap_ranges (v_a+aindex, v_a+aindex+n, v_b+bindex);
239 
240  // We have two payload ranges to swap, a and b.
241  // One is probably longer than the other, so we'll need to adjust the
242  // payload vector lengths.
243  // But first, swap the common part of the payloads.
244  const size_t n_common = std::min (n_a, n_b);
245  payload_fac->swap (payload_auxid, a, a_first, b, b_first, n_common);
246 
247  // Now we move the tail; that is, the piece of one payload range
248  // that is not in the other. Define a function to reduce duplicate
249  // code. Here, 1 is the range that is longer and 2 the range
250  // that is shorter.
251  auto shiftTail = [payload_fac, n_common, payload_auxid]
252  (AuxVectorData& vd1,
253  IAuxTypeVector* lv1,
254  size_t& first1,
255  const size_t n1,
256  AuxVectorData& vd2,
257  IAuxTypeVector* lv2,
258  size_t& first2)
259  {
260  // Lengthen the shorter payload to receive the extra elements from
261  // the longer one.
262  if (!lv2->shift (first2 + n_common, n1 - n_common)) {
263  vd2.clearCache (payload_auxid);
264  }
265  if (&vd1 == &vd2 && first1 > first2) {
266  // Special case for self-swapping: keep indices consistent.
267  first1 += (n1 - n_common);
268  }
269  // Copy the extra elements.
270  payload_fac->copy (payload_auxid,
271  vd2, first2 + n_common, vd1, first1 + n_common,
272  n1 - n_common);
273  // Now remove those elements from the longer one.
274  lv1->shift (first1 + n1, - (n1 - n_common));
275  if (&vd1 == &vd2 && first2 > first1) {
276  // Special case for self-swapping: keep indices consistent.
277  first2 -= (n1 - n_common);
278  }
279  };
280 
281  // Now move the tail, depending on which is larger.
282  if (n_a > n_b) {
283  shiftTail (a, alv, a_first, n_a, b, blv, b_first);
284  }
285  else if (n_b > n_a) {
286  shiftTail (b, blv, b_first, n_b, a, alv, a_first);
287  }
288 
289  // Now adjust the indices in the Elt ranges that were swapped.
290  // In the case of self-swapping, the _first indices may have changed,
291  // so need to remember to use the original values.
292  std::for_each_n (v_a+aindex, n, Shift (a_first - b_first_orig));
293  std::for_each_n (v_b+bindex, n, Shift (b_first - a_first_orig));
294 
295  if (n_a != n_b) {
296  if (&a == &b) {
297  // Self-swapping case.
298  // Adjust the indices between the two ranges that were swapped
299  // (but we needn't do anything if the number of payload items
300  // was the same).
301  if (aindex < bindex) {
302  std::for_each (v_a+aindex+n, v_a+bindex, Shift (n_b - n_a));
303  }
304  else {
305  std::for_each (v_a+bindex+n, v_a+aindex, Shift (n_a - n_b));
306  }
307  }
308  else {
309  // Distinct container case. Adjust indices after the swapped ranges.
310  std::for_each (v_a+aindex+n, v_a+a.size_v(), Shift (n_b - n_a));
311  std::for_each (v_b+bindex+n, v_b+b.size_v(), Shift (n_a - n_b));
312  }
313  }
314 }
315 
316 
317 /**
318  * @brief Clear a range of elements within a vector.
319  * @param auxid The aux data item being operated on.
320  * @param dst Container holding the element
321  * @param dst_index Index of the first element in the vector.
322  * @param n Number of elements to clear.
323  */
324 template <class T, class ALLOC>
325 void JaggedVecVectorFactory<T, ALLOC>::clear (SG::auxid_t auxid,
326  AuxVectorData& dst,
327  size_t dst_index,
328  size_t n) const
329 {
330  if (n == 0) return;
331 
332  vector_value_type* v = reinterpret_cast<vector_value_type*> (dst.getDataArray (auxid));
333  IAuxTypeVector* lv = dst.getStore()->linkedVector (auxid);
334  size_t begin = v[dst_index].begin(dst_index);
335  size_t end = v[dst_index+n-1].end();
336  size_t n_payload = end - begin;
337 
338  // Erase the payload elements.
339  lv->shift (end, - n_payload);
340 
341  // Clear out the given range.
342  Elt zero = Elt (begin);
343  std::fill_n (v+dst_index, n, zero);
344 
345  // Adjust indices for following elements.
346  std::for_each (v+dst_index+n, v+dst.size_v(), Shift (-n_payload));
347 }
348 
349 
350 } // namespace SG