Rosetta 3.5
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
ArrayPool.hh
Go to the documentation of this file.
1 // (c) Copyright Rosetta Commons Member Institutions.
2 // (c) This file is part of the Rosetta software suite and is made available under license.
3 // (c) The Rosetta software is developed by the contributing members of the Rosetta Commons.
4 // (c) For more information, see http://www.rosettacommons.org. Questions about this can be
5 // (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu.
6 
7 /// @file core/graph/ArrayPool.hh
8 /// @brief ArrayPool class declaration and implementation
9 /// @author Andrew Leaver-Fay (aleaverfay@gmail.com)
10 
11 #ifndef INCLUDED_core_graph_ArrayPool_hh
12 #define INCLUDED_core_graph_ArrayPool_hh
13 
14 /// Project Headers
15 #include <platform/types.hh>
16 
17 /// Utility headers
18 #include <utility/pointer/ReferenceCount.hh>
19 #include <utility/exit.hh>
20 #include <utility/string_util.hh>
21 
22 /// C++ headers
23 #include <list>
24 
25 #include <utility/vector1.hh>
26 
27 
28 namespace core {
29 namespace graph {
30 
31 ///@brief Class Array0 is a c-style array wrapper that does bounds checking in debug mode.
32 /// It indexes from 0 just like regular c-arrays. Class Array0 does not manage it's own
33 /// memory. It does not allocate memory if you want to make it larger, nor does it
34 /// deallocate memory when you destroy it. Bounds checking only ensures that the user
35 /// does not go outside of the memory Array0 thinks it's in charge of. If the user
36 /// should happen to point the array0 at memory that has not been allocated, Array0
37 /// is not responsible for segmentation fault that will likely occur. Garbage in,
38 /// garbage out.
39 template< class T >
40 class Array0 {
41 public:
42  /// @brief Default ctor points at null.
43  Array0() :
44  array_( 0 ),
45  size_( 0 )
46  {}
47 
48  /// @brief Array and size constructor -- point this Array0 at a block of memory
49  Array0( T * mem_begin, platform::Size size ) :
50  array_( mem_begin ),
51  size_( size )
52  {}
53 
54  /// @brief Copy constructor -- point this Array0 at a block of memory
55  Array0( Array0< T > const & other ) :
56  array_( other.array_ ),
57  size_( other.size_ )
58  {}
59 
60  /// @brief Assignment operator -- point this Array0 at a different block of memory.
61  Array0< T > const &
62  operator = ( Array0< T > const & rhs ) {
63  if ( &rhs != this ) {
64  array_ = rhs.array_;
65  size_ = rhs.size_;
66  }
67  return *this;
68  }
69 
70  /// @brief The destructor does not deallocate the memory that this Array0 points at.
71  /// That is the responsibility of some other class. Array0 is for bounds checking only.
72  ~Array0() {}
73 
74 public:
75  /// Accessors and mutators.
76 
78  assert( bounds_check( index ) );
79  return array_[ index ];
80  }
81 
82  T const & operator [] ( platform::Size index ) const {
83  assert( bounds_check( index ) );
84  return array_[ index ];
85  }
86 
87  T & operator [] ( int index ) {
88  assert( bounds_check( index ) );
89  return array_[ index ];
90  }
91 
92  T const & operator [] ( int index ) const {
93  assert( bounds_check( index ) );
94  return array_[ index ];
95  }
96 
97  platform::Size size() const { return size_; }
98 
99 private:
100 
101  bool
102  bounds_check( platform::Size index ) const {
103  return index < size_;
104  }
105 
106  bool
107  bounds_check( int index ) const {
108  return index < (int) size_ && index >= 0;
109  }
110 
111 private:
114 
115 };
116 
117 
118 /// @brief NegSpaceElement represents a single element in the singly-linked
119 /// list of negative space in an array pool.
120 template < class T >
121 class NegSpaceElement
122 {
123 public:
124  NegSpaceElement() : next_( 0 ), array_( 0 ), allocated_( false )
125  {}
126 
128  next_( next ),
129  array_( array ),
130  allocated_( false )
131  {}
132 
134  next_ = next;
135  }
136 
137  void set_array( T * array ) {
138  array_ = array;
139  }
140 
141  void set_allocated( bool setting ) {
142  allocated_ = setting;
143  }
144 
145 
146  NegSpaceElement * next() const { return next_; }
147  T * array() const { return array_; }
148  bool allocated() const { return allocated_; }
149 
150  /// @brief Remove my next element from negative space,
151  /// and return a pointer to it. Maintain negative space
152  /// integrity by pointing my next_ pointer at the next_
153  /// pointer of the removed element
155  NegSpaceElement< T > * removed = next_;
156  next_ = removed->next_;
157  return removed;
158  }
159 
160  /// @brief Add an element to negative space by inserting it
161  /// behind this element.
162  void insert_after( NegSpaceElement * element ) {
163  element->next_ = next_;
164  next_ = element;
165  }
166 
167 private:
168 
172 
173 };
174 
175 template < class T >
176 class ArrayPoolElement
177 {
178 public:
179 
181  array_( neg_ptr->array(), size ),
182  neg_ptr_( neg_ptr )
183  {}
184 
186  array_( other.array_ ),
187  neg_ptr_( other.neg_ptr_ )
188  {}
189 
191 
192  T const & operator [] ( platform::Size index ) const {
193  assert( neg_ptr_->allocated() );
194  return array_[ index ];
195  }
196 
198  assert( neg_ptr_->allocated() );
199  return array_[ index ];
200  }
201 
202  T const & operator[] ( int index ) const {
203  assert( neg_ptr_->allocated() );
204  return array_[ index ];
205  }
206 
207  T & operator[] ( int index ) {
208  assert( neg_ptr_->allocated() );
209  return array_[ index ];
210  }
211 
212  bool valid() const {
213  return neg_ptr_->allocated();
214  }
215 
217  assert( array_.size() == other.array_.size() );
218  for ( platform::Size ii = 0; ii < array_.size(); ++ii ) {
219  array_[ ii ] = other.array_[ ii ];
220  }
221  }
222 
223  template< typename > friend class ArrayPool;
224 
225 private:
228 
229 };
230 
231 template < class T >
233 {
234 public:
235  /// Creation and destruction
236 
237  /// @brief Default constructor uses a block size of 32
239  block_size_( 32 ),
240  array_size_( 0 ),
241  nblocks_( 0 ),
242  nnegative_( 0 )
243  {}
244 
245  /// @brief Constructor with block-size specification.
247  block_size_( block_size ),
248  array_size_( 0 ),
249  nblocks_( 0 ),
250  nnegative_( 0 )
251  {}
252 
253  /// @brief Constructor with block-size and array-size specification.
255  block_size_( block_size ),
256  array_size_( array_size ),
257  nblocks_( 0 ),
258  nnegative_( 0 )
259  {}
260 
262  if ( !empty() ) {
263  utility_exit_with_message( "Error in ArrayPool destructor: cannot free a non-empty ArrayPool" );
264  }
265  clear();
266  }
267 
268 public:
269  /// Methods to read information about the pool size
270 
271  /// @brief Returns the size of each array to be allocated.
273  return array_size_;
274  }
275 
276  /// @brief Returns the number of arrays to allocate in each block
278  return block_size_;
279  }
280 
281  /// @brief Returns the number of bytes occupied by all allocated and
282  /// not-yet allocated arrays.
283  //
284  /// @details Approximate the cost of each list element in the two std::lists
285  /// as 4 times the cost of a pointer.
287  return nblocks_ * ( block_size_ * (sizeof( NegSpaceElement< T > * ) + array_size_ * sizeof( T )) + 8 * sizeof( platform::Size ))
288  + sizeof( ArrayPool< T > );
289  }
290 
291 public:
292  /// Methods to modify the size of the pool
293 
294  /// @brief Set the size of the arrays that the pool is meant to allocate.
295  ///
296  /// @details The
298  if ( !empty() ) {
299  utility_exit_with_message( "ERROR: ArrayPool array size cannot be changed unless the ArrayPool is empty" );
300  }
301  if ( array_size_ != size ) {
302  clear();
303  array_size_ = size;
304  }
305  }
306 
308  if ( !empty() ) {
309  utility_exit_with_message( "ERROR: ArrayPool block size cannot be changed unless the ArrayPool is empty" );
310  }
311  if ( block_size_ != size ) {
312  clear();
313  block_size_ = size;
314  }
315  }
316 
317  /// @brief Return the number of allocated array blocks
319  return nblocks_;
320  }
321 
322  /// @brief Return the number of allocated arrays. If this were multiplied by the
323  /// array_size_, then you'd have the amount of space spent representing the T arrays
324  /// but you wouldn't have the amount of space spent representing the negative space
325  /// linked list.
327  return block_size_ * nblocks_;
328  }
329 
330  /// @brief Return the number of allocated arrays that have been requested by the
331  /// user and are now "outstanding" in that they have to be returned before the
332  /// array pool may be deleted.
334  return nallocated() - nnegative_;
335  }
336 
337  bool empty() const {
338  return nnegative_ == nallocated();
339  }
340 
341 public:
342  /// Methods for allocating and deallocating arrays from this pool
343 
344  /// @brief Request a new ArrayPoolElement from the pool. This method
345  /// will enlarge the pool if it has grown to its capacity.
348  if ( neg_begin_.next() == 0 ) create_new_block();
349  NegSpaceElement< T > * first = neg_begin_.pop();
350  assert( first != 0 );
351  assert( first->allocated() == false );
352 
353  first->set_allocated( true );
354  --nnegative_;
355 
356  return ArrayPoolElement< T >( array_size_, first );
357  }
358 
359  /// @brief Free an ArrayPoolElement to the pool. The element is invalid
360  /// after this call completes.
361  void
363  ArrayPoolElement< T > const & element
364  )
365  {
366  assert( mine( element.neg_ptr_ ) );
367 
368  NegSpaceElement< T > * neg_element = element.neg_ptr_;
369  assert( neg_element != 0 );
370  assert( neg_element->allocated() );
371 
372  neg_begin_.insert_after( neg_element );
373  neg_element->set_allocated( false );
374  ++nnegative_;
375 
376  assert( nnegative_ <= nallocated() );
377  }
378 
379 private:
380 
381  /// @brief Add a new block to the pool and add the elements of this block to the
382  /// negative space singly linked list. The block sizes and the array sizes must
383  /// be set before this method is called and, so long as the ArrayPool is not empty,
384  /// these sizes may not be altered.
385  ///
386  /// @details This array pool supports, but does not recommend using, array sizes of 0.
387  /// The resulting ArrayPoolElements have Array0 objecst that point at null, and therefore,
388  /// should never be dereferenced. However, there is nothing inherrently wrong with pointing
389  /// at null, or with having an array that is never examined, and so it is supported.
391  {
392  assert( neg_begin_.next() == 0 );
393  if ( array_size_ != 0 ) {
394 
395  NegSpaceElement< T > * const neg_block = new NegSpaceElement< T >[ block_size_ ];
396 
397  if ( neg_block == 0 ) {
398  utility_exit_with_message( "ERROR: new failed in ArrayPool when requesting neg space block of size " +
399  utility::to_string( block_size_ ) + " (" +
400  utility::to_string( block_size_ * sizeof( NegSpaceElement< T > )) +
401  " bytes)" );
402  }
403 
404  T * const t_block = new T[ block_size_ * array_size_ ];
405 
406  if ( t_block == 0 ) {
407  utility_exit_with_message( "ERROR: new failed in ArrayPool when requesting Array block of size " +
408  utility::to_string( block_size_ ) + "x" + utility::to_string( array_size_ ) + " (" +
409  utility::to_string( block_size_ * array_size_ * sizeof( T )) +
410  " bytes)" );
411  }
412 
413  NegSpaceElement< T > * neg_iter = neg_block;
414  T * t_iter = t_block;
415 
416  for ( platform::Size ii = 1; ii <= block_size_; ++ii ) {
417  neg_iter->set_array( t_iter );
418  t_iter += array_size_;
419  neg_iter += 1;
420  }
421  neg_iter = neg_block;
422  for ( platform::Size ii = 1; ii < block_size_; ++ii ) {
423  NegSpaceElement< T > * last = neg_iter;
424  ++neg_iter;
425  last->set_next( neg_iter );
426  }
427  neg_iter->set_next( 0 );
428  neg_begin_.set_next( neg_block );
429 
430  ++nblocks_;
432  neg_space_blocks_.push_back( neg_block );
433  array_blocks_.push_back( t_block );
434  } else {
435  /// 0-size array support.
436  NegSpaceElement< T > * const neg_block = new NegSpaceElement< T >[ block_size_ ];
437 
438  if ( neg_block == 0 ) {
439  utility_exit_with_message( "ERROR: new failed in ArrayPool when requesting neg space block of size " +
440  utility::to_string( block_size_ ) + " (" +
441  utility::to_string( block_size_ * sizeof( NegSpaceElement< T > )) +
442  " bytes)" );
443  }
444 
445  T * const t_block = 0;
446  NegSpaceElement< T > * neg_iter = neg_block;
447 
448  for ( platform::Size ii = 1; ii <= block_size_; ++ii ) {
449  neg_iter->set_array( t_block );
450  neg_iter += 1;
451  }
452  neg_iter = neg_block;
453  for ( platform::Size ii = 1; ii < block_size_; ++ii ) {
454  NegSpaceElement< T > * last = neg_iter;
455  ++neg_iter;
456  last->set_next( neg_iter );
457  }
458  neg_iter->set_next( 0 );
459  neg_begin_.set_next( neg_block );
460 
461  ++nblocks_;
463  neg_space_blocks_.push_back( neg_block );
464  array_blocks_.push_back( t_block );
465  }
466  }
467 
468  /// @brief Deallocate all the allocated blocks. The pool must be empty before
469  /// this should be called or dangling references are likely.
470  void clear()
471  {
472  assert( empty() );
473  for ( typename std::list< NegSpaceElement< T > * >::const_iterator
474  iter = neg_space_blocks_.begin(),
475  end_iter = neg_space_blocks_.end();
476  iter != end_iter; ++iter ) {
477  delete [] (*iter );
478  }
479  neg_space_blocks_.clear();
480 
481  for ( typename std::list< T * >::const_iterator
482  iter = array_blocks_.begin(),
483  end_iter = array_blocks_.end();
484  iter != end_iter; ++iter ) {
485  delete [] (*iter );
486  }
487  array_blocks_.clear();
488 
489  nblocks_ = 0;
490  nnegative_ = 0;
491  neg_begin_.set_next( 0 );
492  }
493 
494 
495  /// @brief Determine if a given pointer to a negative-space element belongs to this
496  /// pool. The compiler cannot ensure that when a ArrayPoolElement is handed to
497  /// the deallocate_array method that it belongs to the pool being invoked upon.
498  ///
499  /// @details This method is too slow to include in release builds, but is here to
500  /// ensure in debug mode that two pools don't accidentally start crossing their
501  /// pointers and "sharing" data, since that would be disasterous.
502  bool mine( NegSpaceElement< T > const * neg_element ) const {
503  for ( typename std::list< NegSpaceElement< T > * >::const_iterator
504  iter = neg_space_blocks_.begin(),
505  end_iter = neg_space_blocks_.end();
506  iter != end_iter; ++iter ) {
507  if ( neg_element >= (*iter ) && neg_element < (*iter) + block_size_ ) {
508  return true;
509  }
510  }
511  /// This neg space element must belong to some other pool.
512  return false;
513  }
514 
515 private:
516 
520  platform::Size nnegative_; // the number of arrays which have been allocated but not requested by the user
522 
523  std::list< NegSpaceElement< T > * > neg_space_blocks_;
524  std::list< T * > array_blocks_;
525 
526 };
527 
528 
529 }
530 }
531 
532 #endif