Main Page   Namespace List   Class Hierarchy   Alphabetical List   Compound List   File List   Namespace Members   Compound Members   File Members  

ChunkIterator.cxx

00001 /* -*-c-file-style: "hopper";-*- */
00002 /* $Header: /home/hopper/src/cvs/C++/StrMod/ChunkIterator.cxx,v 1.8 2002/09/09 05:24:08 hopper Exp $ */
00003 
00004 /*
00005  * Copyright 2000-2002 by Eric M. Hopper <hopper@omnifarious.org>
00006  * 
00007  *     This program is free software; you can redistribute it and/or modify it
00008  *     under the terms of the GNU Lesser General Public License as published
00009  *     by the Free Software Foundation; either version 2 of the License, or
00010  *     (at your option) any later version.
00011  * 
00012  *     This program is distributed in the hope that it will be useful, but
00013  *     WITHOUT ANY WARRANTY; without even the implied warranty of
00014  *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015  *     Lesser General Public License for more details.
00016  * 
00017  *     You should have received a copy of the GNU Lesser General Public
00018  *     License along with this program; if not, write to the Free Software
00019  *     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00020  */
00021 
00022 // For a log, see ChangeLog
00023 
00024 #ifdef __GNUG__
00025 #  pragma implementation "ChunkIterator.h"
00026 #endif
00027 
00028 #include "StrMod/ChunkIterator.h"
00029 #include "StrMod/UseTrackingVisitor.h"
00030 #include <LCore/RefCounting.h>
00031 #include <vector>
00032 #include <algorithm>
00033 
00034 namespace strmod {
00035 namespace strmod {
00036 
00037 /**
00038  * A bunch of data that can be share among all the StrChunk::__iterator
00039  * objects for a given StrChunk.
00040  */
00041 class StrChunk::__iterator::shared : public lcore::ReferenceCounting
00042 {
00043  public:
00044    /**
00045     * Holds information about where to find a chunk of data and how big it is.
00046     */
00047    struct rawdata {
00048       const void *base_;  ///< Location of data area
00049       unsigned int len_;  ///< Length of data area
00050    };
00051    StrChunkPtr root_;  ///< The StrChunk I'm the share iterator data area for.
00052    rawdata *dataexts_;  ///< A list of the data chunks in root_.
00053    unsigned int numexts_; ///< The number of data chunks in root_.
00054    unsigned int length_;  ///< The total length of all of the data chunks in root_.  Should be equal to root_->Length()
00055 
00056    inline shared(const StrChunkPtr &root);
00057    /**
00058     * Set the special StrChunk storage pointer back to NULL if it was pointing
00059     * at me.
00060     */
00061    ~shared()
00062    {
00063       if (getStorage(*root_) == this)
00064       {
00065          setStorage(*root_, 0);
00066       }
00067       delete[] dataexts_;
00068    }
00069    /**
00070     * Pulls out the mysterious void * inside StrChunk to check to see if
00071     * another StrChunk::__iterator has left us with a gift of a shared data
00072     * section we can use.
00073     */
00074    static shared *forStrChunk(const StrChunkPtr &chnk) {
00075       shared *tmp = static_cast<shared *>(getStorage(*chnk));
00076       if (!tmp)
00077       {
00078          tmp = new shared(chnk);
00079       }
00080       return(tmp);
00081    }
00082 };
00083 
00084 /**
00085  * The ChunkVisitor that gathers data for the StrChunk::__iterator to use.
00086  */
00087 class StrChunk::__iterator::ExtVisitor : public UseTrackingVisitor {
00088  public:
00089    //! ChunkVisitors never have very interesting constructors
00090    // Do ignore zeros though.  When iterating over data, zero length chunks
00091    // and data extents are pointless.
00092    ExtVisitor() : UseTrackingVisitor(true)  { }
00093    //! And rarely interesting destructors either.
00094    virtual ~ExtVisitor()                    { }
00095 
00096    /**
00097     * Visit the StrChunk DAG rooted at root, filling up the shared data
00098     * structure with what I find.
00099     *
00100     * @param root The StrChunk to visit.
00101     *
00102     * @param dataexts A reference to a pointer to a list of data extents.  Set
00103     * to point at the new list after it's all ben figured out.
00104     *
00105     * @param numexts A reference to an int that will be filled with the number
00106     * of data extents I found, and therefor how many data extents dataexts now
00107     * points at.
00108     */
00109    void visit(const StrChunkPtr &root,
00110               shared::rawdata *&dataexts, unsigned int &numexts)
00111    {
00112       extvec_.clear();
00113       try {
00114          startVisit(root);
00115          numexts = extvec_.size();
00116          if (numexts > 0)
00117          {
00118             dataexts = new shared::rawdata[numexts];
00119             copy(extvec_.begin(), extvec_.end(), dataexts);
00120          }
00121       } catch (...) {
00122          extvec_.clear();
00123          throw;
00124       }
00125       extvec_.clear();
00126    }
00127 
00128  protected:
00129    /*!
00130     * I don't care about chunks, just data (because the iterator has to
00131     * iterate over the data) so do nothing when told about a chunk.
00132     */
00133    virtual void use_visitStrChunk(const StrChunkPtr &chunk,
00134                                   const LinearExtent &used)
00135       throw(halt_visitation)                { }
00136 
00137    /*!
00138     * Add this new chunk of data to our list.
00139     */
00140    virtual void use_visitDataBlock(const void *start, size_t len,
00141                                    const void *realstart, size_t reallen)
00142       throw(halt_visitation)
00143    {
00144       // Many routines depend on this if statement to ensure that there are no
00145       // zero length extents.
00146       if (len <= 0)
00147       {
00148          return;
00149       }
00150 
00151       shared::rawdata data = {start, len};
00152 
00153       extvec_.push_back(data);
00154    }
00155 
00156  private:
00157    std::vector<shared::rawdata> extvec_;
00158 };
00159 
00160 //---
00161 
00162 /**
00163  * Create a shared data area and set up the funny void * storage area in
00164  * StrChunk to hold it.
00165  */
00166 inline StrChunk::__iterator::shared::shared(const StrChunkPtr &root)
00167      : ReferenceCounting(0), root_(root), dataexts_(0), numexts_(0),
00168        length_(root->Length())
00169 {
00170    assert(root);
00171    ExtVisitor visitor;
00172    visitor.visit(root, dataexts_, numexts_);
00173    if (getStorage(*root) == 0)
00174    {
00175       setStorage(*root, this);
00176    }
00177 }
00178 
00179 //---
00180 
00181 StrChunk::__iterator::__iterator()
00182      : shared_(0), abspos_(0), extpos_(0), extlast_(0), curext_(0), extbase_(0)
00183 {
00184 }
00185 
00186 StrChunk::__iterator::__iterator(const StrChunkPtr &chnk)
00187      : shared_(0), abspos_(0), extpos_(0), extlast_(0), curext_(0), extbase_(0)
00188 {
00189    assert(chnk);
00190    shared_ = shared::forStrChunk(chnk);
00191    shared_->AddReference();
00192    moveToBegin();
00193 }
00194 
00195 StrChunk::__iterator::__iterator(shared *sh)
00196      : shared_(sh), abspos_(0), extpos_(0), extlast_(0),
00197        curext_(0), extbase_(0)
00198 {
00199    if (shared_)
00200    {
00201       shared_->AddReference();
00202       moveToBegin();
00203    }
00204 }
00205 
00206 StrChunk::__iterator::__iterator(const __iterator &other)
00207      : shared_(other.shared_), abspos_(other.abspos_), extpos_(other.extpos_),
00208        extlast_(other.extlast_), curext_(other.curext_),
00209        extbase_(other.extbase_)
00210 {
00211    if (shared_)
00212    {
00213       shared_->AddReference();
00214    }
00215 }
00216 
00217 StrChunk::__iterator::~__iterator()
00218 {
00219    if (shared_)
00220    {
00221       if (shared_->NumReferences() <= 1)
00222       {
00223          delete shared_;
00224       }
00225       else
00226       {
00227          shared_->DelReference();
00228       }
00229       shared_ = 0;
00230    }
00231 }
00232 
00233 inline bool StrChunk::__iterator::isFor(const StrChunkPtr &chnk) const
00234 {
00235    return shared_ && (shared_->root_.GetPtr() == chnk.GetPtr());
00236 }
00237 
00238 const StrChunk::__iterator &
00239 StrChunk::__iterator::operator =(const __iterator &other)
00240 {
00241    // The order of operations here handle self assignment
00242    shared_->DelReference();
00243    other.shared_->AddReference();
00244    shared_->AddReference();
00245    if (shared_->NumReferences() <= 0)
00246    {
00247       delete shared_;
00248    }
00249    shared_ = other.shared_;
00250    abspos_ = other.abspos_;
00251    extpos_ = other.extpos_;
00252    extlast_ = other.extlast_;
00253    extbase_ = other.extbase_;
00254    curext_ = other.curext_;
00255    return(*this);
00256 };
00257 
00258 bool StrChunk::__iterator::isEqual(const __iterator &other) const
00259 {
00260    if ((shared_ == other.shared_) ||
00261        (shared_ && other.shared_ &&
00262         (shared_->root_.GetPtr() == other.shared_->root_.GetPtr())))
00263    {
00264       return(abspos_ == other.abspos_);
00265    }
00266    return(false);
00267 }
00268 /*!
00269  * Returns a comparison of position within a StrChunk if the two iterators
00270  * point at the same StrChunk.
00271  *
00272  * Punts and returns some silly value when the values aren't comparable
00273  * because the iterators don't point at the same StrChunk.
00274  */
00275 bool StrChunk::__iterator::isLessThan(const __iterator &other) const
00276 {
00277    if ((shared_ == other.shared_) ||
00278        (shared_ && other.shared_ &&
00279         (shared_->root_.GetPtr() == other.shared_->root_.GetPtr())))
00280    {
00281       return(abspos_ < other.abspos_);
00282    }
00283    else
00284    {
00285       return(memcmp(&shared_, &(other.shared_), sizeof(shared_)) < 0);
00286    }
00287 }
00288 
00289 /*!
00290  * @return The number of bytes between two iterators.
00291  *
00292  * Returns a silly value if the two iterators don't point at the same
00293  * StrChunk.  I believe that STL leaves this behavior undefined, so the silly
00294  * value is as good as anything else.
00295  */
00296 StrChunk::__iterator::difference_type
00297 StrChunk::__iterator::distance(const __iterator &other) const
00298 {
00299    if ((shared_ == other.shared_) ||
00300        (shared_ && other.shared_ &&
00301         (shared_->root_.GetPtr() == other.shared_->root_.GetPtr())))
00302    {
00303       return(abspos_ - other.abspos_);
00304    }
00305    else
00306    {
00307       // lamer speak for loselose.  What you did if you got this value
00308       // back.  :-) Maybe this ought to be an exception.
00309       return(0x10531053);
00310    }
00311 }
00312 
00313 static const unsigned char junk = 'G';
00314 
00315 void StrChunk::__iterator::moveToEnd()
00316 {
00317    if (!shared_)
00318    {
00319       return;
00320    }
00321    shared &shrd = *shared_;
00322    abspos_ = shrd.length_;
00323    curext_ = shrd.numexts_;
00324    extpos_ = 0;
00325    extbase_ = &junk;
00326    extlast_ = 0;
00327 }
00328 
00329 void StrChunk::__iterator::moveToBegin()
00330 {
00331    if (!shared_)
00332    {
00333       return;
00334    }
00335    shared &shrd = *shared_;
00336    abspos_ = 0;
00337    curext_ = 0;
00338    extpos_ = 0;
00339    if (shrd.length_ > 0)
00340    {
00341       assert(shrd.numexts_ > 0);
00342       extbase_ = static_cast<const unsigned char *>(shrd.dataexts_[0].base_);
00343       extlast_ = shrd.dataexts_[0].len_ - 1;
00344    }
00345    else
00346    {
00347       extbase_ = &junk;
00348       extlast_ = 0;
00349    }
00350 }
00351 
00352 void StrChunk::__iterator::move_forward_complex()
00353 {
00354    assert(extpos_ >= extlast_);
00355    if (extpos_ < extlast_)
00356    {
00357       ++extpos_;
00358       return;
00359    }
00360    if (shared_ == NULL)
00361    {
00362       return;
00363    }
00364    shared &shrd = *shared_;
00365    if ((shrd.length_ <= 0) || (abspos_ >= (shrd.length_ - 1)))
00366    {
00367       moveToEnd();
00368       return;
00369    }
00370    ++abspos_;
00371    assert(abspos_ < shrd.length_);
00372    ++curext_;
00373    assert(curext_ < shrd.numexts_);
00374    extpos_ = 0;
00375    extbase_ = static_cast<const unsigned char *>(shrd.dataexts_[curext_].base_);
00376    extlast_ = shrd.dataexts_[curext_].len_;
00377    assert(extlast_ > 0);
00378    // extlast was the length of the buffer, now it's the index of the last
00379    // element.
00380    --extlast_;
00381 }
00382 
00383 void StrChunk::__iterator::move_backward_complex()
00384 {
00385    assert(extpos_ == 0);
00386    if (extpos_ != 0)
00387    {
00388       --extpos_;
00389       return;
00390    }
00391    if (shared_ == NULL)
00392    {
00393       return;
00394    }
00395    shared &shrd = *shared_;
00396    if (abspos_ <= 1)
00397    {
00398       moveToBegin();
00399    }
00400    else
00401    {
00402       assert(curext_ > 0);
00403       --abspos_;
00404       --curext_;
00405       extbase_ = static_cast<const unsigned char *>(shrd.dataexts_[curext_].base_);
00406       extlast_ = shrd.dataexts_[curext_].len_;
00407       assert(extlast_ > 0);
00408       // extlast was the length of the buffer, now it's the index of the last
00409       // element.
00410       --extlast_;
00411       extpos_ = extlast_;
00412    }
00413 }
00414 
00415 //---
00416 
00417 StrChunk::__iterator StrChunk::begin()
00418 {
00419    return __iterator(this);
00420 }
00421 
00422 
00423 StrChunk::__iterator StrChunk::end()
00424 //#ifdef __GNUG__     // Stupid GNU extension that could be skipped if they
00425 //   return tmp(this) // decent optimization.
00426 //#endif
00427 {
00428 //#ifndef __GNUG__
00429    __iterator tmp(this);
00430 //#endif
00431    tmp.moveToEnd();
00432    return tmp;
00433 }
00434 
00435 };  // End namespace strmod
00436 };  // End namespace strmod

Generated on Wed Jan 29 00:32:43 2003 for libNet by doxygen1.3-rc1