Umasoft
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Groups Pages
ChunkUtil.h
1 //
2 // ChunkUtil.h: support routines for the ChunkLOD code
3 //
4 
5 #include <stdlib.h>
6 #include <assert.h>
7 #include <new> // for placement new
8 
9 #include "vtdata/MathTypes.h"
10 
11 #ifndef DOXYGEN_SHOULD_SKIP_THIS // the whole file
12 
13 inline int iclamp(int i, int Min, int Max) {
14  assert( Min <= Max );
15  return std::max(Min, std::min(i, Max));
16 }
17 
18 inline float fclamp(float f, float Min, float Max) {
19  assert( Min <= Max );
20  return std::max(Min, std::min(f, Max));
21 }
22 
23 inline int iabs(int i) { if (i < 0) return -i; else return i; }
24 
25 int WriteUint32(FILE *fp, uint val);
26 int WriteUint16(FILE *fp, unsigned short val);
27 int WriteFloat32(FILE *fp, float val);
28 void WriteByte(FILE* dst, uchar b);
29 uint ReadUint32(FILE *fp);
30 unsigned short ReadUint16(FILE *fp);
31 double ReadDouble64(FILE *fp);
32 
33 int quadtree_node_count(int depth);
34 
35 
36 // Generic C++ containers. Problem: STL is murder on compile times,
37 // and is hard to debug. And also it's really easy to shoot yourself
38 // in the foot. In general I don't like it very much.
39 //
40 // These are not as comprehensive or general, but I think might be
41 // more usable for day-to-day coding.
42 //
43 // Basic approach: array<> acts like a stripped-down version of
44 // std::vector<>, or a slightly beefed up native array.
45 //
46 // hash<> is a very simple take on std::hash_map, without the awful
47 // iterator-oriented interface.
48 
49 template<class T>
50 class array {
51 // Resizable array. Don't put anything in here that can't be moved
52 // around by bitwise copy. Don't keep the address of an element; the
53 // array contents will move around as it gets resized.
54 //
55 // Default constructor and destructor get called on the elements as
56 // they are added or removed from the active part of the array.
57 public:
58  array() : m_buffer(0), m_size(0), m_buffer_size(0) {}
59  array(int size_hint) : m_buffer(0), m_size(0), m_buffer_size(0) { resize(size_hint); }
60  ~array() {
61  clear();
62  }
63 
64  // Basic array access.
65  T& operator[](int index) { assert(index >= 0 && index < m_size); return m_buffer[index]; }
66  const T& operator[](int index) const { assert(index >= 0 && index < m_size); return m_buffer[index]; }
67  int size() const { return m_size; }
68 
69  void push_back(const T& val)
70  // Insert the given element at the end of the array.
71  {
72  int new_size = m_size + 1;
73  resize(new_size);
74  (*this)[new_size-1] = val;
75  }
76 
77  // Access the first element.
78  T& front() { return (*this)[0]; }
79  const T& front() const { return (*this)[0]; }
80 
81  // Access the last element.
82  T& back() { return (*this)[m_size-1]; }
83  const T& back() const { return (*this)[m_size-1]; }
84 
85  void clear()
86  // Empty and destruct all elements.
87  {
88  resize(0);
89  }
90 
91  // void remove(int index);
92 
93  void operator=(const array<T>& a)
94  // Array copy. Copies the contents of a into this array.
95  {
96  resize(a.size());
97  for (int i = 0; i < m_size; i++) {
98  *(m_buffer + i) = a[i];
99  }
100  }
101 
102  void resize(int new_size)
103  // Preserve existing elements. Newly created elements are
104  // initialized with default element of T. Removed elements
105  // are destructed.
106  {
107  assert(new_size >= 0);
108 
109  int old_size = m_size;
110  m_size = new_size;
111 
112  // Destruct old elements.
113  {for (int i = new_size; i < old_size; i++) {
114  (m_buffer + i)->~T();
115  }}
116 
117  if (new_size == 0) {
118  m_buffer_size = 0;
119  } else if (m_size <= m_buffer_size && m_size > m_buffer_size >> 1) {
120  // don't compact yet.
121  return;
122  } else {
123  m_buffer_size = m_size + (m_size >> 2);
124  }
125 
126  reserve(m_buffer_size);
127 
128  // Copy default T into new elements.
129  {for (int i = old_size; i < new_size; i++) {
130  new (m_buffer + i) T; // placement new
131  }}
132  }
133 
134  void reserve(int rsize)
135  {
136  assert(m_size >= 0);
137  m_buffer_size = rsize;
138 
139  // Resize the buffer.
140  if (m_buffer_size == 0) {
141  if (m_buffer) {
142  free(m_buffer);
143  }
144  m_buffer = 0;
145  } else {
146  if (m_buffer) {
147  m_buffer = (T*) realloc(m_buffer, sizeof(T) * m_buffer_size);
148  } else {
149  m_buffer = (T*) malloc(sizeof(T) * m_buffer_size);
150  }
151  }
152  }
153 
154  void transfer_members(array<T>* a)
155  // UNSAFE! Low-level utility function: replace this array's
156  // members with a's members.
157  {
158  m_buffer = a->m_buffer;
159  m_size = a->m_size;
160  m_buffer_size = a->m_buffer_size;
161 
162  a->m_buffer = 0;
163  a->m_size = 0;
164  a->m_buffer_size = 0;
165  }
166 
167 private:
168  T* m_buffer;
169  int m_size;
170  int m_buffer_size;
171 };
172 
173 
174 template<class T>
176 // Computes a hash of an object's representation.
177 {
178 public:
179  static int compute(const T& data)
180  {
181  uchar* p = (uchar*) &data;
182  int size = sizeof(T);
183 
184  // Hash function suggested by http://www.cs.yorku.ca/~oz/hash.html
185  // Due to Dan Bernstein. Allegedly very good on strings.
186  int h = 5381;
187  while (size > 0) {
188  h = ((h << 5) + h) ^ *p;
189  p++;
190  size--;
191  }
192 
193  // Alternative: "sdbm" hash function, suggested at same web page above.
194  // h = 0;
195  // for bytes { h = (h << 16) + (h << 6) - hash + *p; }
196 
197  return h;
198  }
199 };
200 
201 
202 template<class T, class U, class hash_functor = fixed_size_hash<T> >
203 class hash {
204 // Fairly stupid hash table.
205 //
206 // Never shrinks, unless you explicitly clear() or resize() it.
207 // Expands on demand, though. For best results, if you know roughly
208 // how big your table will be, default it to that size when you create
209 // it.
210 public:
211  hash() { m_entry_count = 0; m_size_mask = 0; }
212  hash(int size_hint) { m_entry_count = 0; m_size_mask = 0; resize(size_hint); }
213  ~hash() {
214  clear();
215  }
216 
217  // @@ need a "remove()" or "set()" function, to replace/remove existing key.
218 
219  void add(T key, U value)
220  // Add a new value to the hash table, under the specified key.
221  {
222  assert(get(key, NULL) == false);
223 
224  m_entry_count++;
225  check_expand();
226 
227  int hash_value = hash_functor::compute(key);
228  entry e;
229  e.key = key;
230  e.value = value;
231 
232  int index = hash_value & m_size_mask; // % m_table.size();
233  m_table[index].push_back(e);
234  }
235 
236  void clear()
237  // Remove all entries from the hash table.
238  {
239  for (int i = 0; i < m_table.size(); i++) {
240  m_table[i].clear();
241  }
242  m_table.clear();
243  m_entry_count = 0;
244  m_size_mask = 0;
245  }
246 
247 
248  bool get(T key, U* value)
249  // Retrieve the value under the given key.
250  //
251  // If there's no value under the key, then return false and leave
252  // *value alone.
253  //
254  // If there is a value, return true, and set *value to the entry's
255  // value.
256  //
257  // If value == NULL, return true or false according to the
258  // presence of the key, but don't touch *value.
259  {
260  if (m_table.size() == 0) {
261  return false;
262  }
263 
264  int hash_value = hash_functor::compute(key);
265  int index = hash_value % m_table.size();
266  for (int i = 0; i < m_table[index].size(); i++) {
267  if (m_table[index][i].key == key) {
268  if (value) {
269  *value = m_table[index][i].value;
270  }
271  return true;
272  }
273  }
274  return false;
275  }
276 
277  void check_expand()
278  // Resize the hash table, if it looks like the size is too small
279  // for the current number of entries.
280  {
281  int new_size = m_table.size();
282 
283  if (m_table.size() == 0 && m_entry_count > 0) {
284  // Need to expand. Let's make the base table size 256
285  // elements.
286  new_size = 256;
287  } else if (m_table.size() * 2 < m_entry_count) {
288  // Expand.
289  new_size = (m_entry_count * 3) >> 1;
290  }
291 
292  if (new_size != m_table.size()) {
293  resize(new_size);
294  }
295  }
296 
297 
298  void resize(int new_size)
299  // Resize the hash table to the given size (Rehash the contents of
300  // the current table).
301  {
302  if (new_size <= 0) {
303  // Special case.
304  clear();
305  return;
306  }
307 
308  // Force new_size to be a power of two.
309  int bits = vt_log2(new_size-1) + 1;
310  assert((1 << bits) >= new_size);
311 
312  new_size = 1 << bits;
313  m_size_mask = new_size - 1;
314 
315  array< array<entry> > new_table(new_size);
316 
317  // Copy all entries to the new table.
318  {for (int i = 0; i < m_table.size(); i++) {
319  for (int j = 0; j < m_table[i].size(); j++) {
320  entry& e = m_table[i][j];
321  int hash_value = hash_functor::compute(e.key);
322 
323  int index = hash_value & m_size_mask; // % new_table.size();
324  new_table[index].push_back(e);
325  }
326  m_table[i].clear();
327  }}
328  m_table.clear();
329 
330  // Replace old table data with new table's data.
331  m_table.transfer_members(&new_table);
332  }
333 
334 private:
335  struct entry {
336  T key;
337  U value;
338  };
339 
340  int m_entry_count;
341  int m_size_mask;
342  array< array<entry> > m_table;
343 };
344 
345 
346 template<class data_type>
347 class mmap_array {
348 // Use this class for dealing with huge 2D arrays.
349 public:
350  mmap_array(int width, int height, bool writeable, const char* filename = NULL) :
351  m_width(width),
352  m_height(height),
353  m_writeable(writeable)
354  {
355  m_data = malloc(total_bytes());
356  if (m_data == NULL) {
357  throw "mmap_array: can't map memory!";
358  }
359  }
360 
361  ~mmap_array()
362  {
363  free(m_data);
364  }
365 
366  data_type& get(int x, int z)
367  // Get a writable reference to an element.
368  {
369  assert(m_writeable == true);
370  return const_cast<data_type&>(const_cast<const mmap_array<data_type>*>(this)->get(x, z));
371  }
372 
373  const data_type& get(int x, int z) const
374  // Get a const reference to an element.
375  {
376  assert(x >= 0 && x < m_width);
377  assert(z >= 0 && z < m_height);
378 
379  // @@ could do clever indexing here, for better paging locality...
380 
381  return ((data_type*) m_data)[x + z * m_width];
382  }
383 
384 private:
385  int total_bytes() { return m_height * m_width * sizeof(data_type); }
386 
387  void* m_data;
388  int m_width;
389  int m_height;
390  bool m_writeable;
391 };
392 
393 #endif // DOXYGEN_SHOULD_SKIP_THIS - the whole file
394