Panda3D
uniqueIdAllocator.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file uniqueIdAllocator.cxx
10  * @author schuyler
11  * @date 2003-03-13
12  */
13 
14 #include "pandabase.h"
15 #include "pnotify.h"
16 #include "notifyCategoryProxy.h"
17 
18 #include "uniqueIdAllocator.h"
19 
20 using std::endl;
21 
22 NotifyCategoryDecl(uniqueIdAllocator, EXPCL_PANDA_PUTIL, EXPTP_PANDA_PUTIL);
23 NotifyCategoryDef(uniqueIdAllocator, "");
24 
25 const uint32_t UniqueIdAllocator::IndexEnd = (uint32_t)-1;
26 const uint32_t UniqueIdAllocator::IndexAllocated = (uint32_t)-2;
27 
28 #ifndef NDEBUG //[
29  // Non-release build:
30  #define uniqueIdAllocator_debug(msg) \
31  if (uniqueIdAllocator_cat.is_debug()) { \
32  uniqueIdAllocator_cat->debug() << msg << endl; \
33  } else {}
34 
35  #define uniqueIdAllocator_info(msg) \
36  uniqueIdAllocator_cat->info() << msg << endl
37 
38  #define uniqueIdAllocator_warning(msg) \
39  uniqueIdAllocator_cat->warning() << msg << endl
40 #else //][
41  // Release build:
42  #define uniqueIdAllocator_debug(msg) ((void)0)
43  #define uniqueIdAllocator_info(msg) ((void)0)
44  #define uniqueIdAllocator_warning(msg) ((void)0)
45 #endif //]
46 
47 #define audio_error(msg) \
48  audio_cat->error() << msg << endl
49 
50 
51 /**
52  * Create a free id pool in the range [min:max].
53  */
55 UniqueIdAllocator(uint32_t min, uint32_t max)
56  : _min(min), _max(max) {
57  uniqueIdAllocator_debug("UniqueIdAllocator("<<min<<", "<<max<<")");
58 
59  nassertv(_max >= _min);
60  _size = _max-_min+1; // +1 because min and max are inclusive.
61  nassertv(_size != 0); // size must be > 0.
62 
63  _table = (uint32_t *)PANDA_MALLOC_ARRAY(_size * sizeof(uint32_t));
64  nassertv(_table); // This should be redundant if new throws an exception.
65 
66  for (uint32_t i = 0; i < _size; ++i) {
67  _table[i] = i + 1;
68  }
69  _table[_size - 1] = IndexEnd;
70  _next_free = 0;
71  _last_free = _size - 1;
72  _free = _size;
73 }
74 
75 /**
76  *
77  */
78 UniqueIdAllocator::
79 ~UniqueIdAllocator() {
80  uniqueIdAllocator_debug("~UniqueIdAllocator()");
81  PANDA_FREE_ARRAY(_table);
82 }
83 
84 
85 /**
86  * Returns an id between _min and _max (that were passed to the constructor).
87  * IndexEnd is returned if no ids are available.
88  */
89 uint32_t UniqueIdAllocator::
91  if (_next_free == IndexEnd) {
92  // ...all ids allocated.
93  uniqueIdAllocator_warning("allocate Error: no more free ids.");
94  return IndexEnd;
95  }
96  uint32_t index = _next_free;
97  nassertr(_table[index] != IndexAllocated, IndexEnd);
98 
99  _next_free = _table[_next_free];
100  _table[index] = IndexAllocated;
101 
102  --_free;
103 
104  uint32_t id = index + _min;
105  uniqueIdAllocator_debug("allocate() returning " << id);
106  return id;
107 }
108 
109 /**
110  * This may be called to mark a particular id as having already been allocated
111  * (for instance, by a prior pass). The specified id is removed from the
112  * available pool.
113  *
114  * Because of the limitations of this algorithm, this is most efficient when
115  * it is called before the first call to allocate(), and when all the calls to
116  * initial_reserve_id() are made in descending order by id. However, this is
117  * a performance warning only; if performance is not an issue, any id may be
118  * reserved at any time.
119  */
121 initial_reserve_id(uint32_t id) {
122  nassertv(id >= _min && id <= _max); // Attempt to reserve out-of-range id.
123  uint32_t index = id - _min; // Convert to _table index.
124 
125  nassertv(_table[index] != IndexAllocated);
126 
127  if (_free == 1) {
128  // We just reserved the last element in the free chain.
129  _next_free = IndexEnd;
130 
131  } else if (_next_free == index) {
132  // We're reserving the head of the free chain.
133  _next_free = _table[index];
134 
135  } else {
136  // Since we don't store back pointers in the free chain, we have to search
137  // for the element in the free chain that points to this index.
138 
139 /*
140  * However, there is an easy optimal case: because we expect that this call
141  * will be made before any calls to allocate(), hopefully is it still true
142  * that the _table is still set up such that _table[i] = i+1 (and if the
143  * numbers are reserved in descending order, this will be true at least for
144  * all i <= index). Thus, the free link to slot [index] is expected to be the
145  * slot right before it, or if not, it usually won't be far before it.
146  */
147 
148  uint32_t prev_index = index;
149  while (prev_index > 0 && _table[prev_index - 1] != index) {
150  --prev_index;
151  }
152  if (prev_index > 0 && _table[prev_index - 1] == index) {
153  // We've found it.
154  --prev_index;
155 
156  } else {
157  // OK, it wasn't found below; we have to search above.
158  prev_index = index + 1;
159  while (prev_index < _size && _table[prev_index] != index) {
160  ++prev_index;
161  }
162  }
163 
164  nassertv(_table[prev_index] == index);
165  _table[prev_index] = _table[index];
166 
167  if (index == _last_free) {
168  _last_free = prev_index;
169  }
170  }
171 
172  _table[index] = IndexAllocated;
173  --_free;
174 }
175 
176 
177 /**
178  * Free an allocated index (index must be between _min and _max that were
179  * passed to the constructor).
180  */
182 free(uint32_t id) {
183  uniqueIdAllocator_debug("free("<<id<<")");
184 
185  nassertv(id >= _min && id <= _max); // Attempt to free out-of-range id.
186  uint32_t index = id - _min; // Convert to _table index.
187  nassertv(_table[index] == IndexAllocated); // Attempt to free non-allocated id.
188  if (_next_free != IndexEnd) {
189  nassertv(_table[_last_free] == IndexEnd);
190  _table[_last_free] = index;
191  }
192  _table[index] = IndexEnd; // Mark this element as the end of the list.
193  _last_free = index;
194 
195  if (_next_free == IndexEnd) {
196  // ...the free list was empty.
197  _next_free = index;
198  }
199 
200  ++_free;
201 }
202 
203 
204 /**
205  * return the decimal fraction of the pool that is used. The range is 0 to
206  * 1.0 (e.g. 75% would be 0.75).
207  */
208 PN_stdfloat UniqueIdAllocator::
209 fraction_used() const {
210  return PN_stdfloat(_size-_free)/_size;
211 }
212 
213 /**
214  * ...intended for debugging only.
215  */
217 output(std::ostream &out) const {
218  out << "UniqueIdAllocator(" << _min << ", " << _max << "), "
219  << _free << " id's remaining of " << _size;
220 }
221 
222 /**
223  * ...intended for debugging only.
224  */
226 write(std::ostream &out) const {
227  out << "_min: " << _min << "; _max: " << _max
228  << ";\n_next_free: " << int32_t(_next_free)
229  << "; _last_free: " << int32_t(_last_free)
230  << "; _size: " << _size
231  << "; _free: " << _free
232  << "; used: " << _size - _free
233  << "; fraction_used: " << fraction_used()
234  << ";\n";
235 
236  out << "Table:";
237  for (uint32_t i = 0; i < _size; ++i) {
238  out << " " << int32_t(_table[i]);
239  }
240  out << "\n";
241 
242  out << "Free chain:";
243  uint32_t index = _next_free;
244  while (index != IndexEnd) {
245  out << " " << index + _min;
246  index = _table[index];
247  }
248  out << "\n";
249 }
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void initial_reserve_id(uint32_t id)
This may be called to mark a particular id as having already been allocated (for instance,...
uint32_t allocate()
Returns an id between _min and _max (that were passed to the constructor).
void write(std::ostream &out) const
...intended for debugging only.
void output(std::ostream &out) const
...intended for debugging only.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void free(uint32_t index)
Free an allocated index (index must be between _min and _max that were passed to the constructor).
PN_stdfloat fraction_used() const
return the decimal fraction of the pool that is used.
UniqueIdAllocator(uint32_t min=0, uint32_t max=20)
Create a free id pool in the range [min:max].
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.