ATLAS Offline Software
pointer_list.icc
Go to the documentation of this file.
1 /*
2  Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
3 */
4 
5 // $Id$
6 /**
7  * @file CxxUtils/pointer_list.icc
8  * @author scott snyder <snyder@bnl.gov>
9  * @date Oct, 2009, from earlier code.
10  * @brief A fast way to store a variable-sized collection of pointers.
11  */
12 
13 
14 #include <cassert>
15 #include <algorithm>
16 
17 
18 namespace CxxUtils {
19 
20 
21 /**
22  * @brief Size in bytes of a block holding @c nelt elements.
23  * (excluding the end-pointer).
24  * @param nelt Number of elements.
25  */
26 inline
27 size_t pointer_list_base::list_block::size (size_t nelt)
28 {
29  return sizeof(list_block) + nelt * sizeof(value_type);
30 }
31 
32 
33 //****************************************************************************
34 
35 
36 /**
37  * @brief Allocate a new block.
38  */
39 inline
40 pointer_list_base::list_block*
41 pointer_list_base::allocator::allocate()
42 {
43  // Get a new chunk if needed.
44  if (m_nthis == m_nblock)
45  refill();
46  return &m_chunks->m_blocks[(m_nthis++) * (m_nelt+1)];
47 }
48 
49 
50 /**
51  * @brief Return the number of pointers per block (excluding the end-pointer).
52  */
53 inline
54 size_t
55 pointer_list_base::allocator::nelt() const
56 {
57  return m_nelt;
58 }
59 
60 
61 /**
62  * @brief Current number of allocated chunks.
63  */
64 inline
65 size_t
66 pointer_list_base::allocator::nchunks() const
67 {
68  return m_nchunks;
69 }
70 
71 
72 /**
73  * @brief Test to see if we're looking at the end of a block.
74  * @param p The pointer to test.
75  */
76 inline
77 bool
78 pointer_list_base::allocator::at_end (const void* p) const
79 {
80  return (reinterpret_cast<unsigned long>(p) & m_end_mask) == m_end_offs;
81 }
82 
83 
84 /**
85  * @brief Constructor.
86  * @param nblock Number of blocks to allocate per chunk.
87  */
88 template <size_t NELT>
89 pointer_list<NELT>::allocator::allocator (size_t nblock /*= 100*/)
90  : pointer_list_base::allocator (NELT, nblock, END_MASK, END_OFFS)
91 {
92 }
93 
94 
95 /**
96  * @brief Test to see if we're looking at the end of a block.
97  * @param p The pointer to test.
98  */
99 template <size_t NELT>
100 inline
101 bool
102 pointer_list<NELT>::allocator::at_end_static (const void* p)
103 {
104  return (reinterpret_cast<unsigned long>(p) & END_MASK) == END_OFFS;
105 }
106 
107 
108 //****************************************************************************
109 
110 
111 /**
112  * @brief Equality comparison.
113  */
114 template <size_t NELT>
115 inline
116 bool
117 pointer_list<NELT>::iterator::operator== (const iterator& other) const
118 {
119  return m_p == other.m_p;
120 }
121 
122 
123 /**
124  * @brief Inequality comparison.
125  */
126 template <size_t NELT>
127 inline
128 bool
129 pointer_list<NELT>::iterator::operator!= (const iterator& other) const
130 {
131  return m_p != other.m_p;
132 }
133 
134 
135 /**
136  * @brief Dereference.
137  */
138 template <size_t NELT>
139 inline
140 typename pointer_list<NELT>::iterator::reference
141 pointer_list<NELT>::iterator::operator*()
142 {
143  return *m_p;
144 }
145 
146 
147 /**
148  * @brief Advance (pre-increment).
149  */
150 template <size_t NELT>
151 inline
152 typename pointer_list<NELT>::iterator&
153 pointer_list<NELT>::iterator::operator++()
154 {
155  // Move forward.
156  ++m_p;
157 
158  // If we've hit the end, chain to the next block,
159  // if there is one. Otherwise, stay at the end;
160  // that will be where the end iterator points.
161  if (pointer_list::allocator::at_end_static (m_p)) {
162  list_block* next = reinterpret_cast<list_block*> (*m_p);
163  if (next)
164  m_p = &next->m_data[0];
165  }
166  return *this;
167 }
168 
169 
170 /**
171  * @brief Advance (post-increment).
172  */
173 template <size_t NELT>
174 inline
175 typename pointer_list<NELT>::iterator
176 pointer_list<NELT>::iterator::operator++(int)
177 {
178  iterator ret = *this;
179  ++*this;
180  return ret;
181 }
182 
183 
184 /**
185  * @brief Constructor.
186  * @param p A value within a @c pointer_list.
187  */
188 template <size_t NELT>
189 inline
190 pointer_list<NELT>::iterator::iterator (value_type* p)
191  : m_p (p)
192 {
193 }
194 
195 
196 //****************************************************************************
197 
198 
199 /**
200  * @brief Constructor.
201  * @param pool The allocator for this container.
202  */
203 inline
204 pointer_list_base::pointer_list_base (pool_type& pool)
205  : m_head (0),
206  m_insert (0),
207  m_size (0),
208  m_pool (pool)
209 {
210 }
211 
212 
213 /**
214  * @brief Add a new element to the end of the container. O(1)
215  */
216 inline
217 void pointer_list_base::push_back (value_type p)
218 {
219  // If the container is empty, allocate the first block.
220  if (!m_head)
221  firstblock();
222 
223  // Otherwise, if we're at the end of the current block, allocate a new one.
224  else if (m_pool.at_end (m_insert))
225  nextblock();
226 
227  // Add the value to the list.
228  *m_insert = p;
229  ++m_insert;
230  ++m_size;
231 
232  // If we're at the end of this block, and a following block exists,
233  // move to the following block.
234  if (m_pool.at_end (m_insert)) {
235  value_type next = *m_insert;
236  if (next)
237  m_insert = reinterpret_cast<value_type*> (next);
238  }
239 }
240 
241 
242 /**
243  * @brief The current size of the container. O(1)
244  */
245 inline
246 size_t pointer_list_base::size() const
247 {
248  return m_size;
249 }
250 
251 
252 /**
253  * @brief Test to see if the container is empty.
254  */
255 inline
256 bool pointer_list_base::empty() const
257 {
258  return m_size == 0;
259 }
260 
261 
262 /**
263  * @brief Constructor.
264  * @param pool The allocator for this container.
265  */
266 template <size_t NELT>
267 inline
268 pointer_list<NELT>::pointer_list (pool_type& pool)
269  : pointer_list_base (pool)
270 {
271 }
272 
273 
274 /**
275  * @brief Iterator at the beginning of the container.
276  */
277 template <size_t NELT>
278 inline
279 typename pointer_list<NELT>::iterator pointer_list<NELT>::begin()
280 {
281  // Start by pointing at the first element
282  // (or null, if the container's empty).
283  return iterator (m_head ? &m_head->m_data[0] : 0);
284 }
285 
286 
287 /**
288  * @brief Iterator at the end of the container.
289  */
290 template <size_t NELT>
291 inline
292 typename pointer_list<NELT>::iterator pointer_list<NELT>::end()
293 {
294  // Point at the current insertion point.
295  // (This will be null if the container's empty.)
296  return iterator (m_insert);
297 }
298 
299 
300 /**
301  * @brief Erase one element. O(n)
302  * @param it The element to erase.
303  */
304 template <size_t NELT>
305 void pointer_list<NELT>::erase (iterator it)
306 {
307  // We just copy the elements back by one.
308  iterator next = it;
309  ++next;
310  next = std::copy (next, end(), it);
311 
312  // New insertion point is where the copy stopped.
313  m_insert = next.m_p;
314 
315  // One less element.
316  --m_size;
317 }
318 
319 
320 } // namespace CxxXUtils