• Skip to content
  • Skip to link menu
KDE 3.5 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

kviewshell

DataPool.cpp

Go to the documentation of this file.
00001 //C-  -*- C++ -*-
00002 //C- -------------------------------------------------------------------
00003 //C- DjVuLibre-3.5
00004 //C- Copyright (c) 2002  Leon Bottou and Yann Le Cun.
00005 //C- Copyright (c) 2001  AT&T
00006 //C-
00007 //C- This software is subject to, and may be distributed under, the
00008 //C- GNU General Public License, Version 2. The license should have
00009 //C- accompanied the software or you may obtain a copy of the license
00010 //C- from the Free Software Foundation at http://www.fsf.org .
00011 //C-
00012 //C- This program is distributed in the hope that it will be useful,
00013 //C- but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 //C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 //C- GNU General Public License for more details.
00016 //C- 
00017 //C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library
00018 //C- distributed by Lizardtech Software.  On July 19th 2002, Lizardtech 
00019 //C- Software authorized us to replace the original DjVu(r) Reference 
00020 //C- Library notice by the following text (see doc/lizard2002.djvu):
00021 //C-
00022 //C-  ------------------------------------------------------------------
00023 //C- | DjVu (r) Reference Library (v. 3.5)
00024 //C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved.
00025 //C- | The DjVu Reference Library is protected by U.S. Pat. No.
00026 //C- | 6,058,214 and patents pending.
00027 //C- |
00028 //C- | This software is subject to, and may be distributed under, the
00029 //C- | GNU General Public License, Version 2. The license should have
00030 //C- | accompanied the software or you may obtain a copy of the license
00031 //C- | from the Free Software Foundation at http://www.fsf.org .
00032 //C- |
00033 //C- | The computer code originally released by LizardTech under this
00034 //C- | license and unmodified by other parties is deemed "the LIZARDTECH
00035 //C- | ORIGINAL CODE."  Subject to any third party intellectual property
00036 //C- | claims, LizardTech grants recipient a worldwide, royalty-free, 
00037 //C- | non-exclusive license to make, use, sell, or otherwise dispose of 
00038 //C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the 
00039 //C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU 
00040 //C- | General Public License.   This grant only confers the right to 
00041 //C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to 
00042 //C- | the extent such infringement is reasonably necessary to enable 
00043 //C- | recipient to make, have made, practice, sell, or otherwise dispose 
00044 //C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to 
00045 //C- | any greater extent that may be necessary to utilize further 
00046 //C- | modifications or combinations.
00047 //C- |
00048 //C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY
00049 //C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
00050 //C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF
00051 //C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
00052 //C- +------------------------------------------------------------------
00053 // 
00054 // $Id: DataPool.cpp,v 1.11 2004/08/06 15:11:29 leonb Exp $
00055 // $Name: release_3_5_15 $
00056 
00057 #ifdef HAVE_CONFIG_H
00058 # include "config.h"
00059 #endif
00060 #if NEED_GNUG_PRAGMAS
00061 # pragma implementation
00062 #endif
00063 
00064 #include "DataPool.h"
00065 #include "IFFByteStream.h"
00066 #include "GString.h"
00067 #include "GOS.h"
00068 #include "GURL.h"
00069 #include "debug.h"
00070 
00071 #ifndef macintosh
00072 # include <sys/types.h>
00073 #endif
00074 
00075 #ifdef HAVE_NAMESPACES
00076 namespace DJVU {
00077 # ifdef NOT_DEFINED // Just to fool emacs c++ mode
00078 }
00079 #endif
00080 #endif
00081 
00082 const char * DataPool::Stop = ERR_MSG("STOP");
00083 
00084 static void
00085 // call_callback(void (* callback)(GP<GPEnabled> &), GP<GPEnabled> cl_data)
00086 call_callback(void (* callback)(void *), void *cl_data)
00087 {
00088    G_TRY
00089    {
00090       if (callback)
00091         callback(cl_data);
00092    } G_CATCH_ALL {} G_ENDCATCH;
00093 }
00094 
00095 
00096 //****************************************************************************
00097 //****************************** OpenFiles ***********************************
00098 //****************************************************************************
00099 
00100 #define MAX_OPEN_FILES  15
00101 
00111 class DataPool::OpenFiles_File : public GPEnabled
00112 {
00113 public:
00114   GURL          url;
00115   GP<ByteStream>            stream;     // Stream connected to 'url'
00116   GCriticalSection      stream_lock;
00117   GPList<DataPool>      pools_list; // List of pools using this stream
00118   GCriticalSection      pools_lock;
00119   unsigned long     open_time;  // Time when stream was open
00120   
00121   int   add_pool(GP<DataPool> &pool);
00122   int   del_pool(GP<DataPool> &pool);
00123   
00124   OpenFiles_File(const GURL &url, GP<DataPool> &pool);
00125   virtual ~OpenFiles_File(void);
00126   void clear_stream(void);
00127 };
00128 
00129 class DataPool::OpenFiles : public GPEnabled
00130 {
00131 private:
00132    static OpenFiles * global_ptr;
00133 
00134    GPList<DataPool::OpenFiles_File>     files_list;
00135    GCriticalSection files_lock;
00136 public:
00137    static OpenFiles * get(void);
00138 
00139       // Opend the specified file if necessary (or finds an already open one)
00140       // and returns it. The caller (pool) is stored in the list associated
00141       // with the stream. Whenever OpenFiles decides, that this stream
00142       // had better be closed, it will order every pool from the list to
00143       // ZERO their references to it
00144    GP<DataPool::OpenFiles_File> request_stream(const GURL &url, GP<DataPool> pool);
00145       // If there are more than MAX_STREAM_FILES open, close the oldest.
00146    void     prune(void);
00147       // Removes the pool from the list associated with the stream.
00148       // If there is nobody else using this stream, the stream will
00149       // be closed too.
00150    void     stream_released(GP<ByteStream> &stream, GP<DataPool> pool);
00151 
00152    void     close_all(void);
00153 };
00154 
00155 DataPool::OpenFiles * DataPool::OpenFiles::global_ptr;
00156 
00157 DataPool::OpenFiles_File::OpenFiles_File(const GURL &xurl, GP<DataPool> &pool) : url(xurl)
00158 {
00159    DEBUG_MSG("DataPool::OpenFiles_File::OpenFiles_File(): Opening file '" << url << "'\n");
00160    DEBUG_MAKE_INDENT(3);
00161    
00162    open_time=GOS::ticks();
00163    stream=ByteStream::create(url,"rb");
00164    add_pool(pool);
00165 }
00166 
00167 DataPool::OpenFiles_File::~OpenFiles_File(void)
00168 {
00169    DEBUG_MSG("DataPool::OpenFiles_File::~OpenFiles_File(): Closing file '" << url << "'\n");
00170    DEBUG_MAKE_INDENT(3);
00171    clear_stream();
00172 }
00173 
00174 void
00175 DataPool::OpenFiles_File::clear_stream(void)
00176 {
00177   GCriticalSectionLock lock(&pools_lock);
00178   for(GPosition pos=pools_list;pos;++pos)
00179     if(pools_list[pos])
00180       pools_list[pos]->clear_stream(false);
00181   pools_list.empty();
00182 }
00183 
00184 int
00185 DataPool::OpenFiles_File::add_pool(GP<DataPool> &pool)
00186 {
00187    DEBUG_MSG("DataPool::OpenFiles_File::add_pool: pool=" << (void *) pool << "\n");
00188    DEBUG_MAKE_INDENT(3);
00189    GCriticalSectionLock lock(&pools_lock);
00190    if (!pools_list.contains(pool))
00191      pools_list.append(pool);
00192    return pools_list.size();
00193 }
00194 
00195 int
00196 DataPool::OpenFiles_File::del_pool(GP<DataPool> &pool)
00197 {
00198    DEBUG_MSG("DataPool::OpenFiles_File::del_pool: pool=" << (void *) pool << "\n");
00199    DEBUG_MAKE_INDENT(3);
00200    GCriticalSectionLock lock(&pools_lock);
00201    GPosition pos;
00202    if (pools_list.search(pool, pos))
00203      pools_list.del(pos);
00204    return pools_list.size();
00205 }
00206 
00207 inline DataPool::OpenFiles *
00208 DataPool::OpenFiles::get(void)
00209 {
00210    DEBUG_MSG("DataPool::OpenFiles::get()\n");
00211    DEBUG_MAKE_INDENT(3);
00212    if (!global_ptr)
00213      global_ptr=new OpenFiles();
00214    return global_ptr;
00215 }
00216 
00217 void
00218 DataPool::OpenFiles::prune(void)
00219 {
00220   DEBUG_MSG("DataPool::OpenFiles::prune(void): "<<files_list.size()<< "\n");
00221   DEBUG_MAKE_INDENT(3);
00222   while(files_list.size()>MAX_OPEN_FILES)
00223     {
00224       // Too many open files (streams). Get rid of the oldest one.
00225       unsigned long oldest_time=GOS::ticks();
00226       GPosition oldest_pos=files_list;
00227       for(GPosition pos=files_list;pos;++pos)
00228         {
00229           if (files_list[pos]->open_time<oldest_time)
00230             {
00231               oldest_time=files_list[pos]->open_time;
00232               oldest_pos=pos;
00233             }
00234         }
00235       files_list[oldest_pos]->clear_stream();
00236       files_list.del(oldest_pos);
00237     }
00238 }
00239 
00240 //            GP<ByteStream> & stream,
00241 //            GCriticalSection ** stream_lock)
00242 GP<DataPool::OpenFiles_File>
00243 DataPool::OpenFiles::request_stream(const GURL &url, GP<DataPool> pool)
00244 {
00245    DEBUG_MSG("DataPool::OpenFiles::request_stream(): url='" << url << "'\n");
00246    DEBUG_MAKE_INDENT(3);
00247 
00248    GP<DataPool::OpenFiles_File> file;
00249 
00250       // Check: maybe the stream has already been open by request of
00251       // another DataPool
00252    GCriticalSectionLock lock(&files_lock);
00253    for(GPosition pos=files_list;pos;++pos)
00254    {
00255       if (files_list[pos]->url==url)
00256       {
00257      DEBUG_MSG("found existing stream\n");
00258      file=files_list[pos];
00259      break;
00260       }
00261    }
00262 
00263       // No? Open the stream, but check, that there are not
00264       // too many streams open
00265    if (!file)
00266    {
00267       file=new DataPool::OpenFiles_File(url, pool);
00268       files_list.append(file);
00269       prune();
00270    }
00271    
00272    file->add_pool(pool);
00273    return file;
00274 }
00275 
00276 void
00277 DataPool::OpenFiles::stream_released(GP<ByteStream> &stream, GP<DataPool> pool)
00278 {
00279    DEBUG_MSG("DataPool::OpenFiles::stream_release: stream=" 
00280              << (void *)stream << " pool=" << (void *)pool << "\n");
00281    DEBUG_MAKE_INDENT(3);
00282    GCriticalSectionLock lock(&files_lock);
00283    for(GPosition pos=files_list;pos;)
00284    {
00285      GPosition dpos = pos;
00286      ++pos;
00287      GP<DataPool::OpenFiles_File> f=files_list[dpos];
00288      if ((ByteStream *)(f->stream) == (ByteStream *)stream)
00289        if (f->del_pool(pool)==0)
00290          files_list.del(dpos);
00291    }
00292 }
00293 
00294 // This isn't really an accurate url.  The files are not really
00295 // closed.  Instead they are dereferenced from the data pool.  If
00296 // a there is another reference to the respective bytestream, it
00297 // will remain open until dereferenced.
00298 void
00299 DataPool::OpenFiles::close_all(void)
00300 {
00301   DEBUG_MSG("DataPool::OpenFiles::close_all\n");
00302   DEBUG_MAKE_INDENT(3);
00303   GCriticalSectionLock lock(&files_lock);
00304   files_list.empty();
00305 }
00306 
00307 //****************************************************************************
00308 //******************************** FCPools ***********************************
00309 //****************************************************************************
00310 
00317 class FCPools
00318 {
00319 private:
00320    GMap<GURL, GPList<DataPool> >    map;    // GMap<GUTF8String, GPList<DataPool>> in fact
00321    GCriticalSection     map_lock;
00322 
00323    static FCPools   * global_ptr;
00324 public:
00325    static FCPools * get(void);
00326       // Adds the <furl, pool> pair into the list
00327    void     add_pool(const GURL &furl, GP<DataPool> pool);
00328       // Removes the <furl, pool> pair from the list
00329    void     del_pool(const GURL &furl, GP<DataPool> pool);
00330       // Looks for the list of DataPools connected to 'furl' and makes
00331       // each of them load the contents of the file into memory
00332    void     load_file(const GURL &url);
00333       // Retrieve a local URL, if available.
00334    GP<DataPool> get_pool(const GURL &url, int start, int length);
00335    void clean(void);
00336 };
00337 
00338 void
00339 FCPools::clean(void)
00340 {
00341   GCriticalSectionLock lock(&map_lock);
00342   static int count=0;
00343   if(! count++)
00344   {
00345     bool restart = true;
00346     while (restart)
00347       {
00348         restart = false;
00349         for (GPosition posmap = map; posmap; ++posmap)
00350           {
00351             GPList<DataPool> *lst;
00352             lst = & map[posmap];
00353             if (lst->isempty())
00354               {
00355                 map.del(posmap);
00356                 restart = true;
00357                 break;
00358               }
00359             for (GPosition poslst = *lst; poslst; ++poslst)
00360               if ((*lst)[poslst]->get_count() < 2) 
00361                 {
00362                   lst->del(poslst);
00363                   restart = true;
00364                   break;
00365                 }
00366             if (restart)
00367               break;
00368           }
00369       }
00370   }
00371   --count;
00372 }
00373 
00374 void
00375 FCPools::add_pool(const GURL &url, GP<DataPool> pool)
00376 {
00377   DEBUG_MSG("FCPools::add_pool: url='" << url << "' pool=" << (void *)pool << "\n");
00378   DEBUG_MAKE_INDENT(3);
00379   GCriticalSectionLock lock(&map_lock);
00380 
00381    if (url.is_local_file_url())
00382    {
00383       GPList<DataPool> list;
00384       GPosition pos(map.contains(url));
00385       if (! pos)
00386       {
00387         map[url]=list;
00388         pos=map.contains(url);
00389       }
00390       GPList<DataPool> &plist=map[pos];
00391       if (!plist.contains(pool))
00392         plist.append(pool);
00393    }
00394   clean();
00395 }
00396 
00397 GP<DataPool>
00398 FCPools::get_pool(const GURL &url, int start, int length)
00399 {
00400   DEBUG_MSG("FCPools::get_pool: url='" << url << "\n");
00401   DEBUG_MAKE_INDENT(3);
00402   GP<DataPool> retval;
00403   if (url.is_local_file_url())
00404   {
00405     GCriticalSectionLock lock(&map_lock);
00406     GPosition pos(map.contains(url));
00407     if (pos)
00408     {
00409       GPList<DataPool> &plist=map[pos];
00410       for(pos=plist;pos;++pos)
00411       {
00412         DataPool &pool=*plist[pos];
00413         if(start == pool.start && (length < 0 || (length == pool.length)))
00414         {
00415           retval=plist[pos];
00416           break;
00417         }
00418       }
00419     }
00420     clean();
00421   }
00422   return retval;
00423 }
00424 
00425 void
00426 FCPools::del_pool(const GURL &url, GP<DataPool> pool)
00427 {
00428   DEBUG_MSG("FCPools::del_pool: url='" << url << "' pool=" << (void *)pool << "\n");
00429   DEBUG_MAKE_INDENT(3);
00430   GCriticalSectionLock lock(&map_lock);
00431 
00432   clean();
00433    if (url.is_local_file_url())
00434    {
00435       GPosition pos;
00436       if (map.contains(url, pos))
00437       {
00438      GPList<DataPool> &list=map[pos];
00439      GPosition list_pos;
00440      while(list.search(pool, list_pos))
00441         list.del(list_pos);
00442      if (list.isempty())
00443      {
00444         map.del(pos);
00445      }
00446       }
00447    }
00448 }
00449 
00450 void
00451 FCPools::load_file(const GURL &url)
00452 {
00453   DEBUG_MSG("FCPools::load_file: url='" << url << "'\n");
00454   DEBUG_MAKE_INDENT(3);
00455   GCriticalSectionLock lock(&map_lock);
00456    
00457   clean();
00458    if (url.is_local_file_url())
00459    {
00460       GPosition pos;
00461       if (map.contains(url, pos))
00462       {
00463         // We make here a copy of the list because DataPool::load_file()
00464         // will call FCPools::del_pool(), which will modify the list
00465      GPList<DataPool> list=map[pos];
00466      for(GPosition list_pos=list;list_pos;++list_pos)
00467         list[list_pos]->load_file();
00468       }
00469    }
00470 }
00471 
00472 FCPools * FCPools::global_ptr;
00473 
00474 inline FCPools *
00475 FCPools::get(void)
00476 {
00477    if (!global_ptr)
00478      global_ptr=new FCPools();
00479    return global_ptr;
00480 }
00481 
00482 //****************************************************************************
00483 //****************************** BlockList ***********************************
00484 //****************************************************************************
00485 
00486 // Since data can be added to the DataPool at any offset now, there may
00487 // be white spots, which contain illegal data. This class is to contain
00488 // the list of valid and invalid regions.
00489 // The class is basically a list of integers. Abs(integer)=size of the
00490 // block. If the integer is positive, data for the block is known.
00491 // Otherwise it's unkown.
00492 
00493 class DataPool::BlockList
00494 {
00495          // See comments in .cpp file.
00496 private:
00497    GCriticalSection  lock;
00498    GList<int>        list;
00499 public:
00500    BlockList() {};
00501    void              clear(void);
00502    void              add_range(int start, int length);
00503    int               get_bytes(int start, int length) const;
00504    int               get_range(int start, int length) const;
00505 friend class DataPool;
00506 };
00507 
00508 void
00509 DataPool::BlockList::clear(void)
00510 {
00511   DEBUG_MSG("DataPool::BlockList::clear()\n");
00512   DEBUG_MAKE_INDENT(3);
00513    GCriticalSectionLock lk(&lock);
00514    list.empty();
00515 }
00516 
00517 void
00518 DataPool::BlockList::add_range(int start, int length)
00519       // Adds range of known data.
00520 {
00521   DEBUG_MSG("DataPool::BlockList::add_range: start=" << start << " length=" << length << "\n");
00522   DEBUG_MAKE_INDENT(3);
00523    if (start<0)
00524      G_THROW( ERR_MSG("DataPool.neg_start") );
00525    if (length<=0)
00526      G_THROW( ERR_MSG("DataPool.bad_length") );
00527    if (length>0)
00528    {
00529       GCriticalSectionLock lk(&lock);
00530 
00531      // Look thru existing zones, change their sign and split if
00532      // necessary.
00533       GPosition pos=list;
00534       int block_start=0, block_end=0;
00535       while(pos && block_start<start+length)
00536       {
00537      int size=list[pos];
00538      block_end=block_start+abs(size);
00539      if (size<0)
00540         if (block_start<start)
00541         {
00542            if (block_end>start && block_end<=start+length)
00543            {
00544           list[pos]=-(start-block_start);
00545           list.insert_after(pos, block_end-start);
00546           ++pos;
00547           block_start=start;
00548            } else if (block_end>start+length)
00549            {
00550           list[pos]=-(start-block_start);
00551           list.insert_after(pos, length);
00552           ++pos;
00553           list.insert_after(pos, -(block_end-(start+length)));
00554           ++pos;
00555           block_start=start+length;
00556            }
00557         } else if (block_start>=start && block_start<start+length)
00558         {
00559            if (block_end<=start+length) list[pos]=abs(size);
00560            else
00561            {
00562           list[pos]=start+length-block_start;
00563           list.insert_after(pos, -(block_end-(start+length)));
00564           ++pos;
00565           block_start=start+length;
00566            }
00567         }
00568      block_start=block_end;
00569      ++pos;
00570       }
00571       if (block_end<start)
00572       {
00573      list.append(-(start-block_end));
00574      list.append(length);
00575       } else if (block_end<start+length) list.append(start+length-block_end);
00576 
00577      // Now merge adjacent areas with the same sign
00578       pos=list;
00579       while(pos)
00580       {
00581      GPosition pos1=pos; ++pos1;
00582      while(pos1)
00583      {
00584         if (list[pos]<0 && list[pos1]>0 ||
00585         list[pos]>0 && list[pos1]<0)
00586            break;
00587         list[pos]+=list[pos1];
00588         GPosition this_pos=pos1;
00589         ++pos1;
00590         list.del(this_pos);
00591      }
00592      pos=pos1;
00593       }
00594    } // if (length>0)
00595 }
00596 
00597 int
00598 DataPool::BlockList::get_bytes(int start, int length) const
00599       // Returns the number of bytes of data available in the range
00600       // [start, start+length[. There may be holes between data chunks
00601 {
00602   DEBUG_MSG("DataPool::BlockList::get_bytes: start=" << start << " length=" << length << "\n");
00603   DEBUG_MAKE_INDENT(3);
00604 
00605    if (length<0)
00606      G_THROW( ERR_MSG("DataPool.bad_length") );
00607 
00608    GCriticalSectionLock lk((GCriticalSection *) &lock);
00609    int bytes=0;
00610    int block_start=0, block_end=0;
00611    for(GPosition pos=list;pos && block_start<start+length;++pos)
00612    {
00613       int size=list[pos];
00614       block_end=block_start+abs(size);
00615       if (size>0)
00616      if (block_start<start)
00617      {
00618         if (block_end>=start && block_end<start+length)
00619            bytes+=block_end-start;
00620             else if (block_end>=start+length)
00621            bytes+=length;
00622      } else
00623      {
00624         if (block_end<=start+length)
00625            bytes+=block_end-block_start;
00626         else bytes+=start+length-block_start;
00627      }
00628       block_start=block_end;
00629    }
00630    return bytes;
00631 }
00632 
00633 int
00634 DataPool::BlockList::get_range(int start, int length) const
00635       // Finds a range covering offset=start and returns the length
00636       // of intersection of this range with [start, start+length[
00637       // 0 is returned if nothing can be found
00638 {
00639   DEBUG_MSG("DataPool::BlockList::get_range: start=" << start << " length=" << length << "\n");
00640   DEBUG_MAKE_INDENT(3);
00641    if (start<0)
00642      G_THROW( ERR_MSG("DataPool.neg_start") );
00643    if (length<=0)
00644       G_THROW( ERR_MSG("DataPool.bad_length") );
00645 
00646    GCriticalSectionLock lk((GCriticalSection *) &lock);
00647    int block_start=0, block_end=0;
00648    for(GPosition pos=list;pos && block_start<start+length;++pos)
00649    {
00650       int size=list[pos];
00651       block_end=block_start+abs(size);
00652       if (block_start<=start && block_end>start)
00653      if (size<0) return -1;
00654          else
00655         if (block_end>start+length) return length;
00656             else return block_end-start;
00657       block_start=block_end;
00658    }
00659    return 0;
00660 }
00661 
00662 //****************************************************************************
00663 //******************************* DataPool ***********************************
00664 //****************************************************************************
00665 
00666 class DataPool::Reader : public GPEnabled
00667 {
00668 public:
00669    GEvent event;
00670    bool reenter_flag;
00671    int  offset;
00672    int  size;
00673    Reader() : reenter_flag(false), offset(0), size(-1){};
00674    Reader(int offset_in, int size_in=-1) :
00675    reenter_flag(false), offset(offset_in), size(size_in) {};
00676    virtual ~Reader() {};
00677 };
00678 
00679 class DataPool::Trigger : public GPEnabled
00680 {
00681 public:
00682    GSafeFlags disabled;
00683    int  start, length;
00684 //   void (* callback)(GP<GPEnabled> &);
00685    void (* callback)(void *);
00686 //   GP<GPEnabled> cl_data;
00687    void *cl_data;
00688 
00689    Trigger() : start(0), length(-1), callback(0), cl_data(0) {};
00690    Trigger(int xstart, int xlength,
00691 //   void (* xcallback)(GP<GPEnabled> &), GP<GPEnabled> xcl_data) :
00692    void (* xcallback)(void *), void *xcl_data) :
00693       start(xstart), length(xlength), callback(xcallback), cl_data(xcl_data) {};
00694    virtual ~Trigger() {};
00695 };
00696 
00697 class DataPool::Counter
00698 {
00699 private:
00700    int               counter;
00701    GCriticalSection  lock;
00702 public:
00703    Counter() : counter(0) {};
00704    operator int(void) const;
00705    void              inc(void);
00706    void              dec(void);
00707 };
00708 
00709 #define DATAPOOL_INIT eof_flag(false),stop_flag(false), \
00710     stop_blocked_flag(false), \
00711     add_at(0),start(0),length(-1)
00712 
00713 void
00714 DataPool::init(void)
00715 {
00716   DEBUG_MSG("DataPool::init(): Initializing\n");
00717   DEBUG_MAKE_INDENT(3);
00718   start=0; length=-1; add_at=0;
00719   eof_flag=false;
00720   stop_flag=false;
00721   stop_blocked_flag=false;
00722 
00723   active_readers=new Counter;
00724   block_list=0;
00725   G_TRY
00726   {   
00727     block_list=new BlockList;
00728     data=ByteStream::create();
00729   }
00730   G_CATCH_ALL
00731   {
00732     delete block_list;
00733     block_list=0;
00734     delete active_readers;
00735     active_readers=0;
00736     G_RETHROW;
00737   }
00738   G_ENDCATCH;
00739 }
00740 
00741 DataPool::DataPool(void) : DATAPOOL_INIT {}
00742 
00743 GP<DataPool>
00744 DataPool::create(void)
00745 {
00746   DEBUG_MSG("DataPool::DataPool()\n");
00747   DEBUG_MAKE_INDENT(3);
00748   DataPool *pool=new DataPool();
00749 
00750   GP<DataPool> retval=pool;
00751   pool->init();
00752 
00753       // If we maintain the data ourselves, we want to interpret its
00754       // IFF structure to predict its length
00755   pool->add_trigger(0, 32, static_trigger_cb, pool);
00756   return retval;
00757 }
00758 
00759 GP<DataPool> 
00760 DataPool::create(const GP<ByteStream> &gstr)
00761 {
00762   DEBUG_MSG("DataPool::create: str="<<(ByteStream *)gstr<<"\n");
00763   DEBUG_MAKE_INDENT(3);
00764   DataPool *pool=new DataPool();
00765   GP<DataPool> retval=pool;
00766   pool->init();
00767 
00768       // It's nice to have IFF data analyzed in this case too.
00769   pool->add_trigger(0, 32, static_trigger_cb, pool);
00770 
00771   pool->data=gstr->duplicate();
00772   pool->added_data(0,pool->data->size());   
00773 //  char buffer[1024];
00774 //  int length;
00775 //  while((length=str.read(buffer, 1024)))
00776 //     pool->add_data(buffer, length);
00777   pool->set_eof();
00778   return retval;
00779 }
00780 
00781 GP<DataPool>
00782 DataPool::create(const GP<DataPool> & pool, int start, int length)
00783 {
00784   DEBUG_MSG("DataPool::DataPool: pool=" << (void *)((DataPool *)pool) << " start=" << start << " length= " << length << "\n");
00785   DEBUG_MAKE_INDENT(3);
00786 
00787   DataPool *xpool=new DataPool();
00788   GP<DataPool> retval=xpool;
00789   xpool->init();
00790   xpool->connect(pool, start, length);
00791   return retval;
00792 }
00793 
00794 GP<DataPool>
00795 DataPool::create(const GURL &furl, int start, int length)
00796 {
00797   DEBUG_MSG("DataPool::DataPool: furl='" << furl << "' start=" << start << " length= " << length << "\n");
00798   DEBUG_MAKE_INDENT(3);
00799 
00800   GP<DataPool> retval=FCPools::get()->get_pool(furl,start,length);
00801   if(! retval)
00802   {
00803     DataPool *pool=new DataPool();
00804     retval=pool;
00805     pool->init();
00806     pool->connect(furl, start, length);
00807   }
00808   return retval;
00809 }
00810 
00811 void
00812 DataPool::clear_stream(const bool release)
00813 {
00814   DEBUG_MSG("DataPool::clear_stream()\n");
00815   DEBUG_MAKE_INDENT(3);
00816   if(fstream)
00817   {
00818     GCriticalSectionLock lock1(&class_stream_lock);
00819     GP<OpenFiles_File> f=fstream;
00820     if(f)
00821     {
00822       GCriticalSectionLock lock2(&(f->stream_lock));
00823       fstream=0;
00824       if(release)
00825         OpenFiles::get()->stream_released(f->stream, this);
00826     }
00827   }
00828 }
00829 
00830 DataPool::~DataPool(void)
00831 {
00832   DEBUG_MSG("DataPool::~DataPool()\n");
00833   DEBUG_MAKE_INDENT(3);
00834 
00835   clear_stream(true);
00836   if (furl.is_local_file_url())
00837   {
00838     FCPools::get()->del_pool(furl, this);
00839   }
00840    
00841   {
00842      // Wait until the static_trigger_cb() exits
00843       GCriticalSectionLock lock(&trigger_lock);
00844       if (pool)
00845         pool->del_trigger(static_trigger_cb, this);
00846       del_trigger(static_trigger_cb, this);
00847   }
00848 
00849   if (pool)
00850   {
00851       GCriticalSectionLock lock(&triggers_lock);
00852       for(GPosition pos=triggers_list;pos;++pos)
00853       {
00854      GP<Trigger> trigger=triggers_list[pos];
00855      pool->del_trigger(trigger->callback, trigger->cl_data);
00856       }
00857   }
00858   delete block_list;
00859   delete active_readers;
00860 }
00861 
00862 void
00863 DataPool::connect(const GP<DataPool> & pool_in, int start_in, int length_in)
00864 {
00865    DEBUG_MSG("DataPool::connect(): connecting to another DataPool\n");
00866    DEBUG_MAKE_INDENT(3);
00867    
00868    if (pool) G_THROW( ERR_MSG("DataPool.connected1") );
00869    if (furl.is_local_file_url()) G_THROW( ERR_MSG("DataPool.connected2") );
00870    if (start_in<0) G_THROW( ERR_MSG("DataPool.neg_start") );
00871 
00872    pool=pool_in;
00873    start=start_in;
00874    length=length_in;
00875 
00876       // The following will work for length<0 too
00877    if (pool->has_data(start, length))
00878      eof_flag=true;
00879    else
00880      pool->add_trigger(start, length, static_trigger_cb, this);
00881 
00882    data=0;
00883 
00884    wake_up_all_readers();
00885    
00886       // Pass registered trigger callbacks to the DataPool
00887    GCriticalSectionLock lock(&triggers_lock);
00888    for(GPosition pos=triggers_list;pos;++pos)
00889    {
00890       GP<Trigger> t=triggers_list[pos];
00891       int tlength=t->length;
00892       if (tlength<0 && length>0)
00893         tlength=length-t->start;
00894       pool->add_trigger(start+t->start, tlength, t->callback, t->cl_data);
00895    }
00896 }
00897 
00898 void
00899 DataPool::connect(const GURL &furl_in, int start_in, int length_in)
00900 {
00901    DEBUG_MSG("DataPool::connect(): connecting to a file\n");
00902    DEBUG_MAKE_INDENT(3);
00903    
00904    if (pool)
00905      G_THROW( ERR_MSG("DataPool.connected1") );
00906    if (furl.is_local_file_url())
00907      G_THROW( ERR_MSG("DataPool.connected2") );
00908    if (start_in<0)
00909      G_THROW( ERR_MSG("DataPool.neg_start") );
00910 
00911 
00912    if (furl_in.name() == "-")
00913    {
00914       DEBUG_MSG("This is stdin => just read the data...\n");
00915       DEBUG_MAKE_INDENT(3);
00916       char buffer[1024];
00917       int length;
00918       GP<ByteStream> gstr=ByteStream::create(furl_in, "rb");
00919       ByteStream &str=*gstr;
00920       while((length=str.read(buffer, 1024)))
00921      add_data(buffer, length);
00922       set_eof();
00923    } else if(furl_in.is_local_file_url())
00924    {
00925      // Open the stream (just in this function) too see if
00926      // the file is accessible. In future we will be using 'OpenFiles'
00927      // to request and release streams
00928       GP<ByteStream> str=ByteStream::create(furl_in,"rb");
00929       str->seek(0, SEEK_END);
00930       int file_size=str->tell();
00931 
00932       furl=furl_in;
00933       start=start_in;
00934       length=length_in;
00935       if (start>=file_size)
00936         length=0;
00937       else if (length<0 || start+length>=file_size)
00938         length=file_size-start;
00939 
00940       eof_flag=true;
00941 
00942       if(str->is_static())
00943       {
00944         data=str;
00945         added_data(0,length);
00946       }else 
00947       {
00948         data=0;
00949       }
00950 
00951       FCPools::get()->add_pool(furl, this);
00952 
00953       wake_up_all_readers();
00954    
00955      // Call every trigger callback
00956       GCriticalSectionLock lock(&triggers_lock);
00957       for(GPosition pos=triggers_list;pos;++pos)
00958       {
00959      GP<Trigger> t=triggers_list[pos];
00960      call_callback(t->callback, t->cl_data);
00961       }
00962       triggers_list.empty();
00963    }
00964 }
00965 
00966 int
00967 DataPool::get_length(void) const
00968 {
00969       // Not connected and length has been guessed
00970       // Or connected to a file
00971       // Or connected to a pool, but length was preset
00972    int retval=(-1);
00973    if (length>=0) 
00974    {
00975      retval=length;
00976    }else if (pool)
00977    {
00978       int plength=pool->get_length();
00979       if (plength>=0)
00980         retval=plength-start;
00981    }
00982    return retval;
00983 }
00984 
00985 int
00986 DataPool::get_size(int dstart, int dlength) const
00987 {
00988    if (dlength<0 && length>0)
00989    {
00990       dlength=length-dstart;
00991       if (dlength<0) return 0;
00992    }
00993    
00994    if (pool) return pool->get_size(start+dstart, dlength);
00995    else if (furl.is_local_file_url())
00996    {
00997       if (start+dstart+dlength>length) return length-(start+dstart);
00998       else return dlength;
00999    } else
01000    {
01001       if (dlength<0)
01002       {
01003      GCriticalSectionLock lock((GCriticalSection *) &data_lock);
01004      dlength=data->size()-dstart;
01005       }
01006       return (dlength<0)?0:(block_list->get_bytes(dstart, dlength));
01007    }
01008 }
01009 
01010 void
01011 DataPool::add_data(const void * buffer, int size)
01012       // This function adds data sequentially at 'add_at' position
01013 {
01014    DEBUG_MSG("DataPool::add_data(): adding " << size << " bytes of data...\n");
01015    DEBUG_MAKE_INDENT(3);
01016 
01017    add_data(buffer, add_at, size);
01018    add_at+=size;
01019 }
01020 
01021 void
01022 DataPool::add_data(const void * buffer, int offset, int size)
01023 {
01024    DEBUG_MSG("DataPool::add_data(): adding " << size << " bytes at pos=" <<
01025          offset << "...\n");
01026    DEBUG_MAKE_INDENT(3);
01027 
01028    if (furl.is_local_file_url() || pool)
01029       G_THROW( ERR_MSG("DataPool.add_data") );
01030    
01031       // Add data to the data storage
01032    {
01033       GCriticalSectionLock lock(&data_lock);
01034       if (offset>data->size())
01035       {
01036      char ch=0;
01037      data->seek(0, SEEK_END);
01038      for(int i=data->size();i<offset;i++)
01039         data->write(&ch, 1);
01040       } else
01041       {
01042      data->seek(offset, SEEK_SET);
01043      data->writall(buffer, size);
01044       }
01045    }
01046 
01047    added_data(offset, size);
01048 }
01049 
01050 void
01051 DataPool::added_data(const int offset, const int size)
01052 {
01053      // Modify map of blocks
01054   block_list->add_range(offset, size);
01055    
01056      // Wake up all threads, which may be waiting for this data
01057   {
01058     GCriticalSectionLock lock(&readers_lock);
01059     for(GPosition pos=readers_list;pos;++pos)
01060     {
01061       GP<Reader> reader=readers_list[pos];
01062       if (block_list->get_bytes(reader->offset, 1))
01063       {
01064         DEBUG_MSG("waking up reader: offset=" << reader->offset <<
01065           ", size=" << reader->size << "\n");
01066         DEBUG_MAKE_INDENT(3);
01067         reader->event.set();
01068       }
01069     }
01070   }
01071 
01072     // And call triggers
01073   check_triggers();
01074 
01075       // Do not undo the following two lines. The reason why we need them
01076       // here is the connected DataPools, which use 'length' (more exactly
01077       // has_data()) to see if they have all data required. So, right after
01078       // all data has been added to the master DataPool, but before EOF
01079       // is set, the master and slave DataPools disagree regarding if
01080       // all data is there or not. These two lines solve the problem
01081   GCriticalSectionLock lock(&data_lock);
01082   if (length>=0 && data->size()>=length)
01083     set_eof();
01084 }
01085 
01086 bool
01087 DataPool::has_data(int dstart, int dlength)
01088 {
01089    if (dlength<0 && length>0)
01090      dlength=length-dstart;
01091    return (pool?(pool->has_data(start+dstart, dlength))
01092      :((furl.is_local_file_url())?(start+dstart+dlength<=length)
01093        :((dlength<0)?is_eof()
01094          :(block_list->get_bytes(dstart, dlength)==dlength))));
01095 }
01096 
01097 int
01098 DataPool::get_data(void * buffer, int offset, int sz)
01099 {
01100    return get_data(buffer, offset, sz, 0);
01101 }
01102 
01103 class DataPool::Incrementor
01104 {
01105 private:
01106    Counter      & counter;
01107 public:
01108    Incrementor(Counter & xcounter) : counter(xcounter) {counter.inc();}
01109    ~Incrementor() {counter.dec();}
01110 };
01111 
01112 int
01113 DataPool::get_data(void * buffer, int offset, int sz, int level)
01114 {
01115    DEBUG_MSG("DataPool::get_data()\n");
01116    DEBUG_MAKE_INDENT(3);
01117    Incrementor inc(*active_readers);
01118    
01119    if (stop_flag)
01120      G_THROW( DataPool::Stop );
01121    if (stop_blocked_flag && !is_eof() &&
01122        !has_data(offset, sz))
01123      G_THROW( DataPool::Stop );
01124    
01125    if (sz < 0)
01126      G_THROW( ERR_MSG("DataPool.bad_size") );
01127    
01128    if (! sz)
01129      return 0;
01130    
01131    if (pool)
01132      {
01133        DEBUG_MSG("DataPool::get_data(): from pool\n");
01134        DEBUG_MAKE_INDENT(3);
01135        int retval=0;
01136        if (length>0 && offset+sz>length)
01137          sz=length-offset;
01138        if (sz<0)
01139         sz=0;
01140        for(;;)
01141          {
01142            // Ask the underlying (master) DataPool for data. Note, that
01143            // master DataPool may throw the "DATA_POOL_REENTER" exception
01144            // demanding all readers to restart. This happens when
01145            // a DataPool in the chain of DataPools stops. All readers
01146            // should return to the most upper level and then reenter the
01147            // DataPools hierarchy. Some of them will be stopped by
01148            // DataPool::Stop exception.
01149            G_TRY
01150              {
01151                if(stop_flag||stop_blocked_flag&&!is_eof()&&!has_data(offset, sz))
01152                  G_THROW( DataPool::Stop );
01153                retval=pool->get_data(buffer, start+offset, sz, level+1);
01154              } 
01155            G_CATCH(exc) 
01156              {
01157                pool->clear_stream(true);
01158                if ((exc.get_cause() != GUTF8String( ERR_MSG("DataPool.reenter") ) ) || level)
01159                  G_RETHROW;
01160              } G_ENDCATCH;
01161            pool->clear_stream(true);
01162            return retval;
01163          }
01164      }
01165    else if(data && data->is_static() && eof_flag)
01166      { 
01167        DEBUG_MSG("DataPool::get_data(): static\n");
01168        DEBUG_MAKE_INDENT(3);
01169        // We're not connected to anybody => handle the data
01170        int size=block_list->get_range(offset, sz);
01171        if (size>0)
01172          {
01173            // Hooray! Some data is there
01174            GCriticalSectionLock lock(&data_lock);
01175            data->seek(offset, SEEK_SET);
01176            return data->readall(buffer, size);
01177          }
01178        return 0;
01179      } 
01180    else if (furl.is_local_file_url())
01181      {
01182        DEBUG_MSG("DataPool::get_data(): from file\n");
01183        DEBUG_MAKE_INDENT(3);
01184        if (length>0 && offset+sz>length)
01185          sz=length-offset;
01186        if (sz<0)
01187          sz=0;
01188        
01189        GP<OpenFiles_File> f=fstream;
01190        if (!f)
01191          {
01192            GCriticalSectionLock lock(&class_stream_lock);
01193            f=fstream;
01194            if(!f)
01195              {
01196                fstream=f=OpenFiles::get()->request_stream(furl, this);
01197              }
01198          }
01199        GCriticalSectionLock lock2(&(f->stream_lock));
01200        f->stream->seek(start+offset, SEEK_SET); 
01201        return f->stream->readall(buffer, sz);
01202      } 
01203    else
01204      {
01205        DEBUG_MSG("DataPool::get_data(): direct\n");
01206        DEBUG_MAKE_INDENT(3);
01207        // We're not connected to anybody => handle the data
01208        int size=block_list->get_range(offset, sz);
01209        if (size>0)
01210          {
01211            // Hooray! Some data is there
01212            GCriticalSectionLock lock(&data_lock);
01213            data->seek(offset, SEEK_SET);
01214            return data->readall(buffer, size);
01215          }
01216        
01217        // No data available.
01218        
01219        // If there is no data and nothing else is expected, we can do
01220        // two things: throw ByteStream::EndOfFile exception or return ZERO bytes.
01221        // The exception is for the cases when the data flow has been
01222        // terminated in the middle. ZERO bytes is for regular read() beyond
01223        // the boundaries of legal data. The problem is to distinguish
01224        // these two cases. We do it here with the help of analysis of the
01225        // IFF structure of the data (which sets the 'length' variable).
01226        // If we attempt to read beyond the [0, length[, ZERO bytes will be
01227        // returned. Otherwise an ByteStream::EndOfFile exception will be thrown.
01228        if (eof_flag)
01229          {
01230            if (length>0 && offset<length) 
01231              {
01232                G_THROW( ByteStream::EndOfFile );
01233              }
01234            else 
01235              {
01236                return 0;
01237              }
01238          } 
01239        // Some data is still expected => add this reader to the
01240        // list of readers and call virtual wait_for_data()
01241        DEBUG_MSG("DataPool::get_data(): There is no data in the pool.\n");
01242        DEBUG_MSG("offset=" << offset << ", size=" << sz <<
01243                  ", data_size=" << data->size() << "\n");
01244        GP<Reader> reader=new Reader(offset, sz);
01245        G_TRY 
01246          {
01247            {
01248              GCriticalSectionLock slock(&readers_lock);
01249              readers_list.append(reader);
01250            }
01251            wait_for_data(reader);
01252          } 
01253        G_CATCH_ALL 
01254          {
01255            {
01256              GCriticalSectionLock slock(&readers_lock);
01257              GPosition pos;
01258              if (readers_list.search(reader, pos)) readers_list.del(pos);
01259            }
01260            G_RETHROW;
01261          } 
01262        G_ENDCATCH;
01263        
01264        {
01265          GCriticalSectionLock slock(&readers_lock);
01266          GPosition pos;
01267          if (readers_list.search(reader, pos)) readers_list.del(pos);
01268        }
01269        
01270        // This call to get_data() should return immediately as there MUST
01271        // be data in the buffer after wait_for_data(reader) returns
01272        // or eof_flag should be TRUE
01273        return get_data(buffer, reader->offset, reader->size, level);
01274      }
01275    return 0;
01276 }
01277 
01278 void
01279 DataPool::wait_for_data(const GP<Reader> & reader)
01280       // This function may NOT return until there is some data for the
01281       // given reader in the internal buffer
01282 {
01283    DEBUG_MSG("DataPool::wait_for_data(): waiting for data at offset=" << reader->offset <<
01284          ", length=" << reader->size << "\n");
01285    DEBUG_MAKE_INDENT(3);
01286 
01287 #if THREADMODEL==NOTHREADS
01288    G_THROW( ERR_MSG("DataPool.no_threadless") );
01289 #else
01290    for(;;)
01291    {
01292       if (stop_flag)
01293         G_THROW( DataPool::Stop );
01294       if (reader->reenter_flag)
01295         G_THROW( ERR_MSG("DataPool.reenter") );
01296       if (eof_flag || block_list->get_bytes(reader->offset, 1))
01297         return;
01298       if (pool || furl.is_local_file_url())
01299         return;
01300 
01301       if (stop_blocked_flag)
01302         G_THROW( DataPool::Stop );
01303 
01304       DEBUG_MSG("calling event.wait()...\n");
01305       reader->event.wait();
01306    }
01307 #endif
01308    
01309    DEBUG_MSG("Got some data to read\n");
01310 }
01311 
01312 void
01313 DataPool::wake_up_all_readers(void)
01314 {
01315    DEBUG_MSG("DataPool::wake_up_all_readers(): waking up all readers\n");
01316    DEBUG_MAKE_INDENT(3);
01317 
01318    GCriticalSectionLock lock(&readers_lock);
01319    for(GPosition pos=readers_list;pos;++pos)
01320       readers_list[pos]->event.set();
01321 }
01322 
01323 void
01324 DataPool::set_eof(void)
01325       // Has no effect on connected DataPools
01326 {
01327    if (!furl.is_local_file_url() && !pool)
01328    {
01329       eof_flag=true;
01330       
01331      // Can we set the length now?
01332       if (length<0)
01333       {
01334      GCriticalSectionLock lock(&data_lock);
01335      length=data->size();
01336       }
01337 
01338      // Wake up all readers to let them rescan the flags
01339       wake_up_all_readers();
01340    
01341      // Activate all trigger callbacks with negative threshold
01342       check_triggers();
01343    }
01344 }
01345 
01346 void
01347 DataPool::stop(bool only_blocked)
01348 {
01349    DEBUG_MSG("DataPool::stop(): Stopping this and dependent DataPools, only_blocked="
01350          << only_blocked << "\n");
01351    DEBUG_MAKE_INDENT(3);
01352 
01353    if (only_blocked) stop_blocked_flag=true;
01354    else stop_flag=true;
01355    
01356 
01357    wake_up_all_readers();
01358 
01359       // Now let all readers, which already go thru to the master DataPool,
01360       // come back and reenter. While reentering some of them will go
01361       // thru this DataPool again and will be stopped (DataPool::Stop exception)
01362       // Others (which entered the master DataPool thru other slave DataPools)
01363       // will simply continue waiting for their data.
01364    if (pool)
01365    {
01366      // This loop is necessary because there may be another thread, which
01367      // is going down thru the DataPool chain and did not reach the
01368      // lowest "master" DataPool yet. Since it didn't reach it yet,
01369      // the "pool->restart_readers()" will not restart it. So we're going
01370      // to continue issuing this command until we get rid of all
01371      // "active_readers"
01372       while(*active_readers)
01373       {
01374 #if (THREADMODEL==COTHREADS) || (THREADMODEL==MACTHREADS)
01375      GThread::yield();
01376 #endif
01377      pool->restart_readers();
01378       }
01379    }
01380 }
01381 
01382 void
01383 DataPool::restart_readers(void)
01384 {
01385    DEBUG_MSG("DataPool::restart_readers(): telling all readers to reenter\n");
01386    DEBUG_MAKE_INDENT(3);
01387    
01388    GCriticalSectionLock slock(&readers_lock);
01389    for(GPosition pos=readers_list;pos;++pos)
01390    {
01391       GP<Reader> reader=readers_list[pos];
01392       reader->reenter_flag=true;
01393       reader->event.set();
01394    }
01395       
01396    if (pool)
01397      pool->restart_readers();
01398 }
01399 
01400 void
01401 DataPool::load_file(void)
01402 {
01403    DEBUG_MSG("DataPool::load_file() called\n");
01404    DEBUG_MAKE_INDENT(3);
01405 
01406    if (pool)
01407    {
01408       DEBUG_MSG("passing the request down.\n");
01409       pool->load_file();
01410    } else if (furl.is_local_file_url())
01411    {
01412       DEBUG_MSG("loading the data from \""<<(const char *)furl<<"\".\n");
01413 
01414       GCriticalSectionLock lock1(&class_stream_lock);
01415       GP<OpenFiles_File> f=fstream;
01416       if (!f)
01417       {
01418         fstream=f=OpenFiles::get()->request_stream(furl, this);
01419       }
01420       {  // Scope to de-allocate lock2 before stream gets released
01421          GCriticalSectionLock lock2(&(f->stream_lock));
01422 
01423          data=ByteStream::create();
01424          block_list->clear();
01425          FCPools::get()->del_pool(furl, this);
01426          furl=GURL();
01427 
01428          const GP<ByteStream> gbs=f->stream;
01429          gbs->seek(0, SEEK_SET);
01430          data=gbs->duplicate();
01431          added_data(0,data->size());   
01432          set_eof();
01433 //         char buffer[1024];
01434 //         int length;
01435 //         while((length=f->stream->read(buffer, 1024)))
01436 //           add_data(buffer, length);
01437           // No need to set EOF. It should already be set.
01438         OpenFiles::get()->stream_released(f->stream, this);
01439       }
01440       fstream=0;
01441    } else { DEBUG_MSG("Not connected\n"); }
01442 }
01443 
01444 void
01445 DataPool::load_file(const GURL &url )
01446 {
01447    FCPools::get()->load_file(url);
01448 }
01449 
01450 void
01451 DataPool::check_triggers(void)
01452       // This function is for not connected DataPools only
01453 {
01454   DEBUG_MSG("DataPool::check_triggers(): calling activated trigger callbacks.\n");
01455   DEBUG_MAKE_INDENT(3);
01456   
01457   if (!pool && !furl.is_local_file_url())
01458     while(true)
01459     {
01460       GP<Trigger> trigger;
01461       
01462       // First find a candidate (trigger, which needs to be called)
01463       // Don't remove it from the list yet. del_trigger() should
01464       // be able to find it if necessary and disable.
01465       {
01466         GCriticalSectionLock list_lock(&triggers_lock);
01467         for(GPosition pos=triggers_list;pos;++pos)
01468         {
01469           GP<Trigger> t=triggers_list[pos];
01470           if (is_eof() || t->length>=0 &&
01471             block_list->get_bytes(t->start, t->length)==t->length)
01472           {
01473             trigger=t;
01474             break;
01475           }
01476         }
01477       }
01478       
01479       if (trigger)
01480       {
01481            // Now check that the trigger is not disabled
01482            // and lock the trigger->disabled lock for the duration
01483            // of the trigger. This will block the del_trigger() and
01484            // will postpone client's destruction (usually following
01485            // the call to del_trigger())
01486         {
01487           GMonitorLock lock(&trigger->disabled);
01488           if (!trigger->disabled)
01489             call_callback(trigger->callback, trigger->cl_data);
01490         }
01491         
01492            // Finally - remove the trigger from the list.
01493         GCriticalSectionLock list_lock(&triggers_lock);
01494         for(GPosition pos=triggers_list;pos;++pos)
01495           if (triggers_list[pos]==trigger)
01496           {
01497             triggers_list.del(pos);
01498             break;
01499           }
01500       } else break;
01501     }
01502 }
01503 
01504 void
01505 // DataPool::add_trigger(int thresh, void (* callback)(GP<GPEnabled> &), GP<GPEnabled> cl_data)
01506 DataPool::add_trigger(int thresh, void (* callback)(void *), void * cl_data)
01507 {
01508   if (thresh>=0)
01509     add_trigger(0, thresh+1, callback, cl_data);
01510   else
01511     add_trigger(0, -1, callback, cl_data);
01512 }
01513 
01514 void
01515 DataPool::add_trigger(int tstart, int tlength,
01516 //            void (* callback)(GP<GPEnabled> &), GP<GPEnabled> cl_data)
01517               void (* callback)(void *), void * cl_data)
01518 {
01519    DEBUG_MSG("DataPool::add_trigger(): start=" << tstart <<
01520          ", length=" << tlength << ", func=" << (void *) callback << "\n");
01521    DEBUG_MAKE_INDENT(3);
01522    
01523    if (callback)
01524    {
01525       if (is_eof())
01526       {
01527         call_callback(callback, cl_data);
01528       }else
01529       {
01530      if (pool)
01531      {
01532            // We're connected to a DataPool
01533            // Just pass the triggers down remembering it in the list
01534         if (tlength<0 && length>0) tlength=length-tstart;
01535         GP<Trigger> trigger=new Trigger(tstart, tlength, callback, cl_data);
01536         pool->add_trigger(start+tstart, tlength, callback, cl_data);
01537         GCriticalSectionLock lock(&triggers_lock);
01538         triggers_list.append(trigger);
01539      } else if (!furl.is_local_file_url())
01540      {
01541            // We're not connected to anything and maintain our own data
01542         if (tlength>=0 && block_list->get_bytes(tstart, tlength)==tlength)
01543            call_callback(callback, cl_data);
01544         else
01545         {
01546            GCriticalSectionLock lock(&triggers_lock);
01547            triggers_list.append(new Trigger(tstart, tlength, callback, cl_data));
01548         }
01549      }
01550       }
01551    }
01552 }
01553 
01554 void
01555 // DataPool::del_trigger(void (* callback)(GP<GPEnabled> &), GP<GPEnabled> cl_data)
01556 DataPool::del_trigger(void (* callback)(void *), void * cl_data)
01557 {
01558    DEBUG_MSG("DataPool::del_trigger(): func=" << (void *) callback << "\n");
01559    DEBUG_MAKE_INDENT(3);
01560 
01561    for(;;)
01562    {
01563       GP<Trigger> trigger;
01564       {
01565      GCriticalSectionLock lock(&triggers_lock);
01566      for(GPosition pos=triggers_list;pos;)
01567      {
01568         GP<Trigger> t=triggers_list[pos];
01569         if (t->callback==callback && t->cl_data==cl_data)
01570         {
01571            trigger=t;
01572            GPosition this_pos=pos;
01573            ++pos;
01574            triggers_list.del(this_pos);
01575            break;
01576         } else
01577               ++pos;
01578      }
01579       }
01580 
01581      // Above we removed the trigger from the list and unlocked the list
01582      // Now we will disable it and will wait if necessary (if the
01583      // trigger is currently being processed by check_triggers())
01584      // check_triggers() locks the trigger for the duration of the
01585      // trigger callback. Thus we will wait for the trigger callback
01586      // to finish and avoid client's destruction.
01587       if (trigger)
01588         trigger->disabled=1;
01589       else
01590         break;
01591    }
01592 
01593    if (pool)
01594      pool->del_trigger(callback, cl_data);
01595 }
01596 
01597 void
01598 // DataPool::static_trigger_cb(GP<GPEnabled> &cl_data)
01599 DataPool::static_trigger_cb(void *cl_data)
01600 {
01601 //  GP<DataPool> d=(DataPool *)(GPEnabled *)cl_data;
01602   GP<DataPool> d=(DataPool *)cl_data;
01603   d->trigger_cb();
01604 }
01605 
01606 void
01607 DataPool::trigger_cb(void)
01608       // This function may be triggered by the DataPool, which we're
01609       // connected to, or by ourselves, if we're connected to nothing
01610 {
01611       // Don't want to be destroyed while I'm here. Can't use GP<> life saver
01612       // because it may be called from the constructor
01613    GCriticalSectionLock lock(&trigger_lock);
01614    
01615    DEBUG_MSG("DataPool::trigger_cb() called\n");
01616    DEBUG_MAKE_INDENT(3);
01617 
01618    if (pool)
01619    {
01620       // Connected to a pool
01621       // We may be here when either EOF is set on the master DataPool
01622       // Or when it may have learnt its length (from IFF or whatever)
01623       if (pool->is_eof() || pool->has_data(start, length)) eof_flag=true;
01624    } else if (!furl.is_local_file_url())
01625    {
01626         // Not connected to anything => Try to guess the length
01627       if (length<0) analyze_iff();
01628       
01629         // Failed to analyze? Check, maybe it's EOF already
01630       if (length<0 && is_eof())
01631       {
01632            GCriticalSectionLock lock(&data_lock);
01633            length=data->size();
01634       }
01635    }
01636 }
01637 
01638 void
01639 DataPool::analyze_iff(void)
01640       // In order to display decode progress properly, we need to know
01641       // the size of the data. It's trivial to figure it out if is_eof()
01642       // is true. Otherwise we need to make a prediction. Luckily all
01643       // DjVuFiles have IFF structure, which makes it possible to do it.
01644       // If due to some reason we fail, the length will remain -1.
01645 {
01646    DEBUG_MSG("DataPool::analyze_iff(): Trying to decode IFF structure of " << furl << ".\n");
01647    DEBUG_MSG("in order to predict the DataPool's size\n");
01648    DEBUG_MAKE_INDENT(3);
01649 
01650    GP<ByteStream> str=get_stream();
01651    
01652    GP<IFFByteStream> giff=IFFByteStream::create(str);
01653    IFFByteStream &iff=*giff;
01654    GUTF8String chkid;
01655    int size;
01656    if ((size=iff.get_chunk(chkid)) && size>=0)
01657    {
01658       length=size+iff.tell()-4;
01659       DEBUG_MSG("Got size=" << size << ", length=" << length << "\n");
01660    }
01661 }
01662 
01663 
01664 //****************************************************************************
01665 //****************************** PoolByteStream ******************************
01666 //****************************************************************************
01667 
01668 // This is an internal ByteStream receiving data from the associated DataPool.
01669 // It's just a sequential interface, nothing more. All the job for data
01670 // retrieval, waiting and thread synchronization is done by DataPool
01671 
01672 class PoolByteStream : public ByteStream
01673 {
01674 public:
01675    PoolByteStream(GP<DataPool> data_pool);
01676    virtual ~PoolByteStream() {};
01677 
01678    virtual size_t read(void *buffer, size_t size);
01679    virtual size_t write(const void *buffer, size_t size);
01680    virtual long tell(void) const ;
01681    virtual int seek(long offset, int whence = SEEK_SET, bool nothrow=false);
01682 private:
01683       // Don't make data_pool GP<>. The problem is that DataPool creates
01684       // and soon destroys this ByteStream from the constructor. Since
01685       // there are no other pointers to the DataPool created yet, it becomes
01686       // destroyed immediately :(
01687    DataPool     * data_pool;
01688    GP<DataPool>     data_pool_lock;
01689    long         position;
01690    
01691    char         buffer[512];
01692    size_t       buffer_size;
01693    size_t       buffer_pos;
01694 
01695       // Cancel C++ default stuff
01696    PoolByteStream & operator=(const PoolByteStream &);
01697 };
01698 
01699 inline
01700 PoolByteStream::PoolByteStream(GP<DataPool> xdata_pool) :
01701    data_pool(xdata_pool), position(0), buffer_size(0), buffer_pos(0)
01702 {
01703    if (!data_pool) 
01704        G_THROW( ERR_MSG("DataPool.zero_DataPool") );
01705 
01706       // Secure the DataPool if possible. If we're called from DataPool
01707       // constructor (get_count()==0) there is no need to secure at all.
01708    if (data_pool->get_count()) data_pool_lock=data_pool;
01709 }
01710 
01711 size_t
01712 PoolByteStream::read(void *data, size_t size)
01713 {
01714   if (buffer_pos >= buffer_size) {
01715     if (size >= sizeof(buffer)) {
01716       // Direct read
01717       size = data_pool->get_data(data, position, size);
01718       position += size;
01719       return size;
01720     } else {
01721       // Refill buffer
01722       buffer_size = data_pool->get_data(buffer, position, sizeof(buffer));
01723       buffer_pos=0;
01724     }
01725   }
01726   if (buffer_pos + size >= buffer_size)
01727     size = buffer_size - buffer_pos;
01728   memcpy(data, buffer+buffer_pos, size);
01729   buffer_pos += size;
01730   position += size;
01731   return size;
01732 }
01733 
01734 size_t
01735 PoolByteStream::write(const void *buffer, size_t size)
01736 {
01737    G_THROW( ERR_MSG("not_implemented_n") "\tPoolByteStream::write()");   //  PoolByteStream::write() is not implemented.
01738    return 0;    // For compiler not to bark
01739 }
01740 
01741 long
01742 PoolByteStream::tell(void) const
01743 {
01744    return position;
01745 }
01746 
01747 int
01748 PoolByteStream::seek(long offset, int whence, bool nothrow)
01749 {
01750   int retval=(-1);
01751   switch(whence)
01752   {
01753     case SEEK_CUR:
01754       offset+=position;
01755       // fallthrough;
01756     case SEEK_SET:
01757       if(offset<position)
01758       {
01759         if((int)(offset+buffer_pos)>=(int)position)
01760         {
01761           buffer_pos-=position-offset;
01762         }else
01763         {
01764           buffer_size=0;
01765         }
01766         position=offset;
01767       }else if(offset>position)
01768       {
01769         buffer_pos+=(offset-position)-1;
01770         position=offset-1;
01771         unsigned char c;
01772         if(read(&c,1)<1)
01773         {
01774           G_THROW( ByteStream::EndOfFile );
01775         }
01776       }
01777       retval=0;
01778       break;
01779     case SEEK_END:
01780       if(! nothrow)
01781         G_THROW( ERR_MSG("DataPool.seek_backward") );
01782       break;
01783    }
01784    return retval;
01785 }
01786 
01787 void
01788 DataPool::close_all(void)
01789 {
01790   OpenFiles::get()->close_all();
01791 }
01792 
01793 
01794 GP<ByteStream>
01795 DataPool::get_stream(void)
01796 {
01797   if(data && data->is_static())
01798   {
01799     GCriticalSectionLock lock(&data_lock);
01800     data->seek(0, SEEK_SET);
01801     return data->duplicate(length);
01802   }else
01803   {
01804     return new PoolByteStream(this);
01805   }
01806 }
01807 
01808 
01809 inline
01810 DataPool::Counter::operator int(void) const
01811 {
01812    GCriticalSectionLock lk((GCriticalSection *) &lock);
01813    int cnt=counter;
01814    return cnt;
01815 }
01816 
01817 inline void
01818 DataPool::Counter::inc(void)
01819 {
01820    GCriticalSectionLock lk(&lock);
01821    counter++;
01822 }
01823 
01824 inline void
01825 DataPool::Counter::dec(void)
01826 {
01827    GCriticalSectionLock lk(&lock);
01828    counter--;
01829 }
01830 
01831 
01832 #ifdef HAVE_NAMESPACES
01833 }
01834 # ifndef NOT_USING_DJVU_NAMESPACE
01835 using namespace DJVU;
01836 # endif
01837 #endif

kviewshell

Skip menu "kviewshell"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members

API Reference

Skip menu "API Reference"
  • kviewshell
Generated for API Reference by doxygen 1.5.9
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal