datablock.h

Go to the documentation of this file.
00001 
00002 /* 
00003  * Scythe Statistical Library Copyright (C) 2000-2002 Andrew D. Martin
00004  * and Kevin M. Quinn; 2002-present Andrew D. Martin, Kevin M. Quinn,
00005  * and Daniel Pemstein.  All Rights Reserved.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify under the terms of the GNU General Public License as
00009  * published by Free Software Foundation; either version 2 of the
00010  * License, or (at your option) any later version.  See the text files
00011  * COPYING and LICENSE, distributed with this source code, for further
00012  * information.
00013  * --------------------------------------------------------------------
00014  *  scythestat/datablock.h
00015  */
00016 
00040 #ifndef SCYTHE_DATABLOCK_H
00041 #define SCYTHE_DATABLOCK_H
00042 
00043 #ifdef SCYTHE_COMPILE_DIRECT
00044 #include "error.h"
00045 #else
00046 #include "scythestat/error.h"
00047 #endif
00048 
00049 namespace scythe {
00050   /* Convenience typedefs */
00051   namespace { // local to this file
00052     typedef unsigned int uint;
00053   }
00054 
00061   template <typename T_type>
00062   class DataBlock { 
00063     public:
00064       /**** CONSTRUCTORS ****/
00065       
00066       /*
00067        * Create an empty data block.
00068        */
00069       
00070       DataBlock ()
00071         : data_ (0),
00072           size_ (0),
00073           refs_ (0)
00074       {}
00075 
00076       /* 
00077        * Create a block of a given size.
00078        */
00079       explicit
00080       DataBlock (uint size)
00081         : data_ (0),
00082           size_ (0),
00083           refs_ (0)
00084       {
00085         resize(size);
00086         SCYTHE_DEBUG_MSG("Constructed new " << size << "(" << size_
00087             << ") DataBlock at address " << data_);
00088       }
00089 
00090       /*
00091        * Create an exact copy of another data block.
00092        */
00093       DataBlock (const DataBlock<T_type>& b)
00094         : data_ (b.data_),
00095           size_ (b.size_),
00096           refs_ (b.refs_)
00097       {}
00098 
00099       /**** DESTRUCTOR ****/
00100 
00101       ~DataBlock ()
00102       {
00103         SCYTHE_DEBUG_MSG("Destructing block at " << data_);
00104         deallocate();
00105       }
00106 
00107       /**** REFERENCE COUNTING ****/
00108 
00109       inline uint addReference ()
00110       {
00111         SCYTHE_DEBUG_MSG("Added reference to DataBlock at address "
00112             << data_);
00113         return ++refs_;
00114       }
00115 
00116       inline uint removeReference ()
00117       {
00118         SCYTHE_DEBUG_MSG("Removed reference to DataBlock at address "
00119            << data_);
00120         return --refs_ ;
00121       }
00122 
00123       inline uint references ()
00124       {
00125         return refs_;
00126       }
00127 
00128       /**** ACCESSORS ****/
00129 
00130       inline T_type* data()
00131       {
00132         return data_;
00133       }
00134 
00135       inline const T_type* data() const
00136       {
00137         return data_;
00138       }
00139 
00140       inline uint size () const
00141       {
00142         return size_;
00143       }
00144 
00145     protected:
00146       /**** (DE)ALLOCATION AND RESIZING ****/
00147       
00148       /* Allocate data given the current block size. */
00149       inline void allocate (uint size)
00150       {
00151         /* TODO Think about cache boundary allocations for big blocks
00152          * see blitz++ */
00153 
00154         if (data_ != 0) // Get rid of previous allocation if it exists
00155           deallocate();
00156 
00157         data_ = new (std::nothrow) T_type[size];
00158         
00159         SCYTHE_CHECK_10(data_ == 0, scythe_alloc_error,
00160             "Failure allocating DataBlock of size " << size);
00161       }
00162 
00163       /* Deallocate a block's data */
00164       inline void deallocate ()
00165       {
00166         SCYTHE_DEBUG_MSG("  Deallocating DataBlock of size " << size_
00167             << " at address " << data_);
00168         delete[] data_;
00169         data_ = 0;
00170       }
00171 
00172     public:
00173       /* TODO At the moment, references call this method directly.  Not
00174        * sure if this is the best interface choice. */
00175       /* Resize a block. */
00176       void resize (uint newsize)
00177       {
00178         if (newsize > size_)
00179           grow(newsize);
00180         else if (newsize < size_ / 4)
00181           shrink();
00182       }
00183 
00184     protected:
00185       /* Make a block larger. Expects to be called by resize and does
00186        * not reset the size_ variable. */
00187       inline void grow (uint newsize)
00188       {
00189         size_ = size_ ? size_ : 1; // make sure not zero
00190 
00191         /* TODO Can we speed this up?  In 20 iters we're at
00192          * 1048576 elems doing the math might be more costly...
00193          */
00194         while (size_ < newsize)
00195           size_ <<= 1;
00196 
00197         allocate(size_);
00198       }
00199 
00200       /* Make a block smaller. Expects to be called by resize */
00201       inline void shrink ()
00202       {
00203         size_ >>= 1;
00204         allocate(size_);
00205       }
00206 
00207     private:
00208       /**** INSTANCE VARIABLES ****/
00209       T_type *data_;   // The data array
00210       uint size_;  // The number of elements in the block
00211       uint refs_;  // The number of views looking at this block
00212   }; // end class DataBlock
00213 
00218   template <class T_type>
00219   class NullDataBlock : public DataBlock<T_type>
00220   {
00221     typedef DataBlock<T_type> T_base;
00222     public:
00223       
00224       NullDataBlock ()
00225         : DataBlock<T_type> ()
00226       {
00227         // never want to deallocate (or resize) this one
00228         T_base::addReference(); 
00229         SCYTHE_DEBUG_MSG("Constructed NULL datablock");
00230       }
00231 
00232       ~NullDataBlock ()
00233       {}
00234 
00235   }; // end class NullDataBlock
00236 
00237 
00245   template <class T_type>
00246   class DataBlockReference {
00247     public:
00248       /**** CONSTRUCTORS ****/
00249 
00250       /* Default constructor: points the object at a static null block
00251        */
00252       DataBlockReference ()
00253         : data_ (0),
00254           block_ (&nullBlock_)
00255       {
00256         block_->addReference();
00257       }
00258 
00259       /* New block constructor: creates a new underlying block of a
00260        * given size and points at it. */
00261       explicit
00262       DataBlockReference (uint size)
00263         : data_ (0),
00264           block_ (0)
00265       {
00266         block_ = new (std::nothrow) DataBlock<T_type> (size);
00267         SCYTHE_CHECK_10 (block_ == 0, scythe_alloc_error,
00268             "Could not allocate DataBlock object");
00269         
00270         data_ = block_->data();
00271         block_->addReference();
00272       }
00273 
00274       /* Refrence to an existing block constructor: points to an
00275        * offset within an existing block. */
00276       DataBlockReference (const DataBlockReference<T_type>& reference,
00277           uint offset = 0)
00278         : data_ (reference.data_ + offset),
00279           block_ (reference.block_)
00280       {
00281         block_->addReference();
00282       }
00283       
00284       /**** DESTRUCTOR ****/
00285       /* Automates removal of underlying block objects when refcount
00286        * hits nil.
00287        */
00288       virtual ~DataBlockReference ()
00289       {
00290         withdrawReference();
00291       }
00292 
00293     protected:
00294 
00295       /**** MEMBERS CALLED BY DERIVED CLASS ****/
00296       void referenceOther (const DataBlockReference<T_type>& ref,
00297           uint offset = 0)
00298       {
00299         withdrawReference ();
00300         block_ = ref.block_;
00301         block_->addReference();
00302         data_ = ref.data_ + offset;
00303       }
00304 
00305       void referenceNew (uint size)
00306       {
00307         /* If we are the only referent to this data block, resize it.
00308          * Otherwise, shift the reference to point to a newly
00309          * constructed block.
00310          */
00311         if (block_->references() == 1) {
00312           block_->resize(size);
00313           data_ = block_->data(); // This is a pretty good indication
00314           // that the interface and implementation are too tightly
00315           // coupled for resizing.
00316         } else {
00317           withdrawReference();
00318           block_ = 0;
00319           block_ = new (std::nothrow) DataBlock<T_type> (size);
00320           SCYTHE_CHECK_10(block_ == 0, scythe_alloc_error,
00321               "Could not allocate new data block");
00322           data_ = block_->data();
00323           block_->addReference();
00324         }
00325       }
00326 
00327     private:
00328       /**** INTERNAL MEMBERS ****/
00329       void withdrawReference ()
00330       {
00331         if (block_->removeReference() == 0
00332             && block_ != &nullBlock_)
00333           delete block_;
00334       }
00335 
00336       void referenceNull ()
00337       {
00338         withdrawReference();
00339         block_ = &nullBlock_;
00340         block_->addReference();
00341         data_ = 0;
00342       }
00343 
00344 
00345     /**** INSTANCE VARIABLES ****/
00346     protected:
00347       T_type* data_;  // Pointer to the underlying data (offset)
00348     
00349     private:
00350       DataBlock<T_type>* block_;
00351       static NullDataBlock<T_type> nullBlock_;
00352 
00353   }; // end class DataBlockReference
00354 
00355   /* Instantiation of the static null memory block */
00356   template <typename T>
00357   NullDataBlock<T> DataBlockReference<T>::nullBlock_;
00358 
00359 } // end namespace scythe
00360 
00361 #endif /* SCYTHE_DATABLOCK_H */

Generated on Wed Aug 15 14:53:35 2007 for Scythe-1.0.2 by  doxygen 1.4.7