ivl 679
|
00001 /* This file is part of the ivl C++ library <http://image.ntua.gr/ivl>. 00002 A C++ template library extending syntax towards mathematical notation. 00003 00004 Copyright (C) 2012 Yannis Avrithis <iavr@image.ntua.gr> 00005 Copyright (C) 2012 Kimon Kontosis <kimonas@image.ntua.gr> 00006 00007 ivl is free software; you can redistribute it and/or modify 00008 it under the terms of the GNU Lesser General Public License 00009 version 3 as published by the Free Software Foundation. 00010 00011 Alternatively, you can redistribute it and/or modify it under the terms 00012 of the GNU General Public License version 2 as published by the Free 00013 Software Foundation. 00014 00015 ivl is distributed in the hope that it will be useful, 00016 but WITHOUT ANY WARRANTY; without even the implied warranty of 00017 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 00018 See the GNU General Public License for more details. 00019 00020 You should have received a copy of the GNU General Public License 00021 and a copy of the GNU Lesser General Public License along 00022 with ivl. If not, see <http://www.gnu.org/licenses/>. */ 00023 00044 template <class T, 00045 class DERIVED_INFO, 00046 int N, 00047 bool USE_REALLOC, 00048 bool USE_PREALLOC 00049 > 00050 class array<T, 00051 data::stack<N, USE_REALLOC, USE_PREALLOC, DERIVED_INFO> > 00052 : 00053 public array_common_base<array<T, data::stack<N, 00054 USE_REALLOC, USE_PREALLOC, DERIVED_INFO> > > 00055 00056 { 00057 00058 private: 00059 typedef array prv_this_type; 00060 typedef typename prv_this_type::common_base_class common_base_class; 00061 /*array_common_base<array<T, data::stack<N, 00062 USE_REALLOC, USE_PREALLOC, DERIVED_INFO> > > common_base_class;*/ 00063 00064 typedef T dt_array_type[(N == 0) ? 1 : N]; 00065 typedef typename types::t_if<types::t_expr<(N == 0)>, 00066 types::term, dt_array_type>::type dt_type; 00067 00068 dt_type dt; 00069 T* base_ptr; 00070 size_t len; 00071 00072 00073 // plain fast ilog2 with binary search for up to 4GB integer x 00074 /* 00075 inline unsigned int ilog2(register size_t x) 00076 { 00077 register unsigned int l=0; 00078 if(x >= 1<<16) { x>>=16; l|=16; } // 00079 if(x >= 1<<8) { x>>=8; l|=8; } 00080 if(x >= 1<<4) { x>>=4; l|=4; } 00081 if(x >= 1<<2) { x>>=2; l|=2; } 00082 if(x >= 1<<1) l|=1; 00083 return l; 00084 }*/ 00085 00086 00087 // ivl devel: toggle this to change the memory management 00088 // scheme for realloc-enabled arrays. 00089 // false: use realloc directly 00090 // true: use realloc under memory management layer. 00091 static const bool custom_realloc_mm = false; 00092 00093 typedef typename types::is_builtin<T> use_realloc_t; 00094 static const bool use_realloc = use_realloc_t::value || USE_REALLOC; 00095 // use the memory management scheme that do preallocate some space. 00096 // opposite: compact data 00097 static const bool use_prealloc = USE_PREALLOC; 00098 00099 static const unsigned int lu_ilog[16]; 00100 00101 static const unsigned int lu_256[256]; 00102 00103 // some tricky memory management 00104 // container is sized on either the most significant bit or 00105 // the multiple of 64K of the array size, whichever is smaller. 00106 template <bool X_LT_64K> 00107 inline unsigned int ilog2_16b(register size_t x) 00108 { 00109 register unsigned int l=0; 00110 if(!X_LT_64K) x&=((1<<16)-1); 00111 if(x >= 1<<8) { x>>=8; l|=8; } 00112 if(x >= 1<<4) { x>>=4; l|=4; } 00113 l |= lu_ilog[x]; 00114 return l; 00115 } 00116 /* 00117 inline size_t memmanage_sig(register size_t x) 00118 { 00119 // save a few if's for small inputs. 00120 // this array should be optimal for small data 00121 if(x < 256) 00122 return lu_256[x]; 00123 00124 return (x & (~((1<<16) - 1))) | ilog2_16b<false>(x); 00125 } not used. no gain. 00126 */ 00127 inline size_t memmanage_sz(register size_t x) 00128 { 00129 // this should be optimized-out by the compiler 00130 if(use_prealloc || (use_realloc && !custom_realloc_mm)) return x; 00131 00132 // save a few if's for small inputs. 00133 // this array should be optimal for small data 00134 if(x < 256) 00135 return lu_256[x]; 00136 00137 size_t y = (x & (~((1<<16) - 1))); 00138 if(!y) 00139 return 1 << (ilog2_16b<true>(x) + 1); 00140 else 00141 return y + (1 << 16); 00142 } 00143 00144 00145 00146 00147 inline void grow(size_t l) 00148 { 00149 len = l; 00150 if(len <= N) 00151 base_ptr = dt; 00152 else 00153 base_ptr = (T*)malloc(sizeof(T) * memmanage_sz(len)); 00154 00155 for(size_t i = 0; i < l; i++) 00156 new(&base_ptr[i]) T(); 00157 } 00158 00159 inline void grow(size_t l, const T& val) 00160 { 00161 len = l; 00162 if(len <= N) 00163 base_ptr = dt; 00164 else 00165 base_ptr = (T*)malloc(sizeof(T) * memmanage_sz(len)); 00166 00167 for(size_t i = 0; i < l; i++) 00168 new(&base_ptr[i]) T(val); 00169 } 00170 00171 template<bool copy, bool pad> 00172 void regrow(size_t l, const T* padding = 0); 00173 00174 public: 00175 typedef array this_type; 00176 00177 typedef this_type this_array_type; 00178 00179 typedef this_type array_type; 00180 00181 typedef T elem_type; 00182 00183 typedef T& reference; 00184 typedef const T& const_reference; 00185 typedef reference best_reference; 00186 00187 typedef typename this_type::derived_type derived_type; 00188 00189 typedef typename common_base_class::base_class base_class; 00190 00192 typedef size_t size_type; 00193 00195 typedef ptrdiff_t diff_type; 00196 00197 using common_base_class::init_size_with;//todo:fix 00198 00199 00200 void resize(size_t len) 00201 { 00202 if(len != this->len) regrow<true, false>(len); 00203 } 00204 00205 void resize(size_t len, const T& s) // with padding 00206 { 00207 if(len != this->len) regrow<true, true>(len, &s); 00208 } 00209 00211 void reshape(size_t len) { resize(len); } 00212 void reshape(size_t len, const T& s) { resize(len, s); } 00213 00215 //todo: optimize, remove ifs etc, and maybe even make custom regrow. 00216 void init(size_t len) { resize(0); resize(len); } 00217 void init(size_t len, const T& s) { resize(0); resize(len, s); } 00218 00219 void clear() { resize(0); } 00220 /* 00221 void reset(size_t len) 00222 { 00223 regrow<false, false>(len); 00224 } 00225 00226 void reset(size_t len, const T& s) 00227 { 00228 regrow<false, true>(len, &s); 00229 } 00230 */ 00231 typedef data::ptr_iterator<T, false> iterator; 00232 typedef data::ptr_iterator<T, true> const_iterator; 00233 typedef std::reverse_iterator<iterator> reverse_iterator; 00234 typedef std::reverse_iterator<const_iterator> const_reverse_iterator; 00235 00236 typedef iterator best_iterator; 00237 00238 typedef ptrdiff_t iter_border_walker; 00239 00240 iterator begin() { return iterator(base_ptr); } 00241 iterator end() { return iterator(base_ptr + len); } 00242 const_iterator begin() const { return const_iterator(base_ptr); } 00243 const_iterator end() const { return const_iterator(base_ptr + len); } 00244 00245 reverse_iterator rbegin() 00246 { return reverse_iterator(iterator(base_ptr + len)); } 00247 reverse_iterator rend() 00248 { return reverse_iterator(iterator(base_ptr)); } 00249 const_reverse_iterator rbegin() const 00250 { return const_reverse_iterator(const_iterator(base_ptr + len)); } 00251 const_reverse_iterator rend() const 00252 { return const_reverse_iterator(const_iterator(base_ptr)); } 00253 00254 T* c_ptr() { return base_ptr; } 00255 const T* c_ptr() const { return base_ptr; } 00256 00259 //TODO: explain this better. 00261 size_t length() const { return len; } 00263 size_type size() const { return length(); } 00265 size_t numel() const { return length(); } 00268 iter_border_walker first_to_last() const { return this->length() - 1; } 00269 iter_border_walker begin_to_end() const { return this->length(); } 00270 00271 void swap(this_type& v) 00272 { 00273 //need a more sophisticated implementation. 00274 //for now will use this simple one. 00275 T* old_ptr = base_ptr; 00276 size_t old_len = len; 00277 array tmp; 00278 if(v.length() <= N) { 00279 tmp = *this; 00280 old_ptr = tmp.c_ptr(); 00281 *this = v; 00282 } else { 00283 base_ptr = v.base_ptr; 00284 len = v.len; 00285 } 00286 00287 if(old_len <= N) { 00288 for(size_t i = 0; i < old_len; i++) 00289 v[i] = old_ptr[i]; 00290 } else { 00291 v.base_ptr = old_ptr; 00292 v.len = old_len; 00293 } 00294 } 00295 00301 using common_base_class::operator[]; 00303 const T& operator[](size_t offset) const { 00304 CHECK(offset >= 0 && offset < length(), erange); 00305 return base_ptr[offset]; 00306 } 00308 T& operator[](size_t offset) { 00309 CHECK(offset >= 0 && offset < length(), erange); 00310 return base_ptr[offset]; 00311 } 00317 00318 array() { grow(0); } 00319 00321 explicit array(size_t count) { grow(count); } 00322 00324 explicit array(int count) { grow(size_t(count)); } 00325 00327 explicit array(long int count) { grow(size_t(count)); } 00328 00335 array(size_t count, const T& s) { grow(count, s); } 00336 00343 array(size_t count, const T *ptr) { grow(count); ivl::copy(*this, ptr); } 00344 00346 template <class J> 00347 array(const internal::tuple_rvalue<J>& r) 00348 { grow(0); r.tuple_output(internal::reftpl(*this)); } 00349 00351 array(const this_type& a) { grow(a.length()); ivl::copy(*this, a); } 00352 00361 template <class J, class S> 00362 array(const array<J, S>& a, size_t n) 00363 { 00364 grow(n); 00365 ivl::copy_out(*this, a); 00366 } 00367 00370 template <class J, class S> 00371 array(const array<J, S>& a) 00372 { 00373 grow(a.length()); 00374 ivl::copy(*this, a); 00375 } 00376 00386 template <class J, class S> 00387 array(size_t count, const array<J, S>& a) 00388 { 00389 grow(count); 00390 this->init_size_with(a); 00391 } 00392 00395 00396 ~array() { 00397 // destroy array content 00398 for(size_t i = 0; i < len; i++) 00399 (&(base_ptr[i]))->~T(); 00400 00401 if(len > N) free(base_ptr); 00402 base_ptr = 0; 00403 } 00404 00405 00417 using base_class::operator=; 00418 00419 this_type& operator=(const this_type& a) 00420 { 00421 common_base_class::operator=(a); 00422 return *this; 00423 } 00424 00425 /* 00426 this_type& operator=(const this_type& a) // LEFT IN THE CONST MODE FOR A COPY CONSTRUCTOR! 00427 { 00428 // if(this != &a) TODO: make this a rule: make clear that ... 00429 // Note: the data class handles the check: if(this != &a), 00430 // and only IF needed, so there is no case of cpu waste 00431 if(this == &a) return *this; 00432 derived().resize(a.length()); 00433 ivl::copy(*this, a); 00434 return *this; 00435 } 00436 00437 template<class S, class K> 00438 derived_type& operator=(const ivl::array<S, K>& a) 00439 { 00440 derived().resize(a.length()); 00441 ivl::copy(*this, a); 00442 return derived(); 00443 } 00444 00445 template<class S, class K> 00446 derived_type& operator=(const T& s) 00447 { 00448 derived().resize(1); 00449 (*this)[0] = s; 00450 return derived(); 00451 } 00452 */ 00453 00454 00455 }; 00456 00457 template <class T, class D, int N, bool USE_REALLOC, bool USE_PREALLOC> 00458 const unsigned int array<T, data:: 00459 stack<N, USE_REALLOC, USE_PREALLOC, D> >::lu_ilog[16] = 00460 { 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3 }; 00461 00462 00463 template <class T, class D, int N, bool USE_REALLOC, bool USE_PREALLOC> 00464 const unsigned int array<T, data:: 00465 stack<N, USE_REALLOC, USE_PREALLOC, D> >::lu_256[256] = 00466 { 00467 0, 2, 4, 4, 8, 8, 8, 8, 16, 16, 16, 16, 16, 16, 16, 16, 32, 32, 32, 32, 00468 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 64, 64, 64, 64, 64, 64, 00469 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 00470 64, 64, 64, 64, 64, 64, 64, 64, 128, 128, 128, 128, 128, 128, 128, 128, 00471 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 00472 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 00473 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 00474 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 00475 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 00476 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 00477 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 00478 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 00479 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 00480 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 00481 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 00482 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 00483 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 00484 256, 256 00485 }; 00486 00487 // This is the array resize implementation! 00488 // looks quite complicated but many if expressions are 00489 // templates, and most of the code is cases, so it shouldn't 00490 // perform that bad. There are also two possible calls to the 00491 // memmanage_sz function, which is optimized, however it does 00492 // consume some time, for sure less than the vector's stored capacity. 00493 template <class T, class D, int N, bool USE_REALLOC, bool USE_PREALLOC> 00494 template <bool copy, bool pad> 00495 void array<T, data::stack<N, USE_REALLOC, USE_PREALLOC, D> >:: 00496 regrow(size_t l, const T* padding) 00497 { 00498 if(l > N && len <= N) { 00499 base_ptr = (T*)malloc(sizeof(T) * memmanage_sz(l)); 00500 // copy resized content 00501 if(copy) { 00502 for(size_t i = 0; i < len; i++) 00503 new(&(base_ptr[i])) T(dt[i]); 00504 } 00505 00506 // destroy array content 00507 for(size_t i = 0; i < len; i++) 00508 (&(dt[i]))->~T(); 00509 } 00510 else if(l <= N && len > N) { 00511 // copy resized content 00512 if(copy) 00513 for(size_t i = 0; i < l; i++) 00514 new(&(dt[i])) T(base_ptr[i]); 00515 00516 // destroy array content 00517 for(size_t i = 0; i < len; i++) 00518 (&(base_ptr[i]))->~T(); 00519 00520 free(base_ptr); 00521 base_ptr = dt; 00522 } 00523 else if(l < N) { 00524 // destroy array content 00525 for(size_t i = l; i < len; i++) 00526 (&(base_ptr[i]))->~T(); 00527 } 00528 else if(l > N) { 00529 size_t sz = memmanage_sz(l); 00530 if(sz != memmanage_sz(len)) { 00531 if(use_realloc || !copy) 00532 { 00533 // case 1 00534 // let the system manage the memory hoping it will do 00535 // that better. 00536 if(copy) { 00537 // destroy array content 00538 for(size_t i = l; i < len; i++) 00539 (&(base_ptr[i]))->~T(); 00540 00541 base_ptr = (T*)realloc(base_ptr, sizeof(T) * sz); 00542 } 00543 else // if(!copy) 00544 { 00545 // this scenario is a questionmark. 00546 // could it be possible that realloc is faster than 00547 // malloc for small change in size? I wouldn't think, so: 00548 00549 // destroy array content 00550 for(size_t i = 0; i < len; i++) 00551 (&(base_ptr[i]))->~T(); 00552 00553 free(base_ptr); 00554 base_ptr = (T*)malloc(sizeof(T) * sz); 00555 } 00556 00557 } else { // if (!use_realloc && copy) 00558 T* old_ptr = base_ptr; 00559 base_ptr = (T*)malloc(sizeof(T) * memmanage_sz(len)); 00560 size_t lc = (len < l ? len : l); 00561 00562 // copy resized content 00563 for(size_t i = 0; i < lc; i++) 00564 new(&(base_ptr[i])) T(old_ptr[i]); 00565 00566 // destroy old array content 00567 for(size_t i = 0; i < len; i++) 00568 (&(old_ptr[i]))->~T(); 00569 00570 free(old_ptr); 00571 } 00572 } 00573 else //if(sz == memmanage_sz(len)) 00574 { 00575 // destroy array content 00576 for(size_t i = l; i < len; i++) 00577 (&(base_ptr[i]))->~T(); 00578 } 00579 } 00580 00581 if(!copy) len = 0; 00582 if(pad) 00583 for(; len < l; len++) 00584 new(&(base_ptr[len])) T(*padding); 00585 else 00586 for(; len < l; len++) 00587 new(&(base_ptr[len])) T(); 00588 len = l; 00589 }