ivl 679
ivl/details/array/impl/specialization/little_class.hpp
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 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations