source: trunk/libdjvu/DataPool.cpp @ 15

Last change on this file since 15 was 15, checked in by Eugene Romanenko, 15 years ago

needed libs update

File size: 50.7 KB
Line 
1//C-  -*- C++ -*-
2//C- -------------------------------------------------------------------
3//C- DjVuLibre-3.5
4//C- Copyright (c) 2002  Leon Bottou and Yann Le Cun.
5//C- Copyright (c) 2001  AT&T
6//C-
7//C- This software is subject to, and may be distributed under, the
8//C- GNU General Public License, Version 2. The license should have
9//C- accompanied the software or you may obtain a copy of the license
10//C- from the Free Software Foundation at http://www.fsf.org .
11//C-
12//C- This program is distributed in the hope that it will be useful,
13//C- but WITHOUT ANY WARRANTY; without even the implied warranty of
14//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15//C- GNU General Public License for more details.
16//C-
17//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library
18//C- distributed by Lizardtech Software.  On July 19th 2002, Lizardtech
19//C- Software authorized us to replace the original DjVu(r) Reference
20//C- Library notice by the following text (see doc/lizard2002.djvu):
21//C-
22//C-  ------------------------------------------------------------------
23//C- | DjVu (r) Reference Library (v. 3.5)
24//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved.
25//C- | The DjVu Reference Library is protected by U.S. Pat. No.
26//C- | 6,058,214 and patents pending.
27//C- |
28//C- | This software is subject to, and may be distributed under, the
29//C- | GNU General Public License, Version 2. The license should have
30//C- | accompanied the software or you may obtain a copy of the license
31//C- | from the Free Software Foundation at http://www.fsf.org .
32//C- |
33//C- | The computer code originally released by LizardTech under this
34//C- | license and unmodified by other parties is deemed "the LIZARDTECH
35//C- | ORIGINAL CODE."  Subject to any third party intellectual property
36//C- | claims, LizardTech grants recipient a worldwide, royalty-free,
37//C- | non-exclusive license to make, use, sell, or otherwise dispose of
38//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the
39//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU
40//C- | General Public License.   This grant only confers the right to
41//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to
42//C- | the extent such infringement is reasonably necessary to enable
43//C- | recipient to make, have made, practice, sell, or otherwise dispose
44//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to
45//C- | any greater extent that may be necessary to utilize further
46//C- | modifications or combinations.
47//C- |
48//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY
49//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
50//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF
51//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
52//C- +------------------------------------------------------------------
53//
54// $Id: DataPool.cpp,v 1.11 2004/08/06 15:11:29 leonb Exp $
55// $Name: release_3_5_16 $
56
57#ifdef HAVE_CONFIG_H
58# include "config.h"
59#endif
60#if NEED_GNUG_PRAGMAS
61# pragma implementation
62#endif
63
64#include "DataPool.h"
65#include "IFFByteStream.h"
66#include "GString.h"
67#include "GOS.h"
68#include "GURL.h"
69#include "debug.h"
70
71#ifndef macintosh
72# include <sys/types.h>
73#endif
74
75#ifdef HAVE_NAMESPACES
76namespace DJVU {
77# ifdef NOT_DEFINED // Just to fool emacs c++ mode
78}
79#endif
80#endif
81
82const char * DataPool::Stop = ERR_MSG("STOP");
83
84static void
85// call_callback(void (* callback)(GP<GPEnabled> &), GP<GPEnabled> cl_data)
86call_callback(void (* callback)(void *), void *cl_data)
87{
88   G_TRY
89   {
90      if (callback)
91        callback(cl_data);
92   } G_CATCH_ALL {} G_ENDCATCH;
93}
94
95
96//****************************************************************************
97//****************************** OpenFiles ***********************************
98//****************************************************************************
99
100#define MAX_OPEN_FILES  15
101
102/** The purpose of this class is to limit the number of files open by
103    connected DataPools. Now, when a DataPool is connected to a file, it
104    doesn't necessarily has it open. Every time it needs access to data
105    it's supposed to ask this file for the ByteStream. It should
106    also inform the class when it's going to die (so that the file can
107    be closed). OpenFiles makes sure, that the number of open files
108    doesn't exceed MAX_OPEN_FILES. When it does, it looks for the oldest
109    file, closes it and asks all DataPools working with it to ZERO
110    their GP<> pointers. */
111class DataPool::OpenFiles_File : public GPEnabled
112{
113public:
114  GURL                  url;
115  GP<ByteStream>                stream;         // Stream connected to 'url'
116  GCriticalSection              stream_lock;
117  GPList<DataPool>              pools_list;     // List of pools using this stream
118  GCriticalSection              pools_lock;
119  unsigned long         open_time;      // Time when stream was open
120 
121  int   add_pool(GP<DataPool> &pool);
122  int   del_pool(GP<DataPool> &pool);
123 
124  OpenFiles_File(const GURL &url, GP<DataPool> &pool);
125  virtual ~OpenFiles_File(void);
126  void clear_stream(void);
127};
128
129class DataPool::OpenFiles : public GPEnabled
130{
131private:
132   static OpenFiles     * global_ptr;
133
134   GPList<DataPool::OpenFiles_File>             files_list;
135   GCriticalSection     files_lock;
136public:
137   static OpenFiles     * get(void);
138
139      // Opend the specified file if necessary (or finds an already open one)
140      // and returns it. The caller (pool) is stored in the list associated
141      // with the stream. Whenever OpenFiles decides, that this stream
142      // had better be closed, it will order every pool from the list to
143      // ZERO their references to it
144   GP<DataPool::OpenFiles_File> request_stream(const GURL &url, GP<DataPool> pool);
145      // If there are more than MAX_STREAM_FILES open, close the oldest.
146   void         prune(void);
147      // Removes the pool from the list associated with the stream.
148      // If there is nobody else using this stream, the stream will
149      // be closed too.
150   void         stream_released(GP<ByteStream> &stream, GP<DataPool> pool);
151
152   void         close_all(void);
153};
154
155DataPool::OpenFiles * DataPool::OpenFiles::global_ptr;
156
157DataPool::OpenFiles_File::OpenFiles_File(const GURL &xurl, GP<DataPool> &pool) : url(xurl)
158{
159   DEBUG_MSG("DataPool::OpenFiles_File::OpenFiles_File(): Opening file '" << url << "'\n");
160   DEBUG_MAKE_INDENT(3);
161   
162   open_time=GOS::ticks();
163   stream=ByteStream::create(url,"rb");
164   add_pool(pool);
165}
166
167DataPool::OpenFiles_File::~OpenFiles_File(void)
168{
169   DEBUG_MSG("DataPool::OpenFiles_File::~OpenFiles_File(): Closing file '" << url << "'\n");
170   DEBUG_MAKE_INDENT(3);
171   clear_stream();
172}
173
174void
175DataPool::OpenFiles_File::clear_stream(void)
176{
177  GCriticalSectionLock lock(&pools_lock);
178  for(GPosition pos=pools_list;pos;++pos)
179    if(pools_list[pos])
180      pools_list[pos]->clear_stream(false);
181  pools_list.empty();
182}
183
184int
185DataPool::OpenFiles_File::add_pool(GP<DataPool> &pool)
186{
187   DEBUG_MSG("DataPool::OpenFiles_File::add_pool: pool=" << (void *) pool << "\n");
188   DEBUG_MAKE_INDENT(3);
189   GCriticalSectionLock lock(&pools_lock);
190   if (!pools_list.contains(pool))
191     pools_list.append(pool);
192   return pools_list.size();
193}
194
195int
196DataPool::OpenFiles_File::del_pool(GP<DataPool> &pool)
197{
198   DEBUG_MSG("DataPool::OpenFiles_File::del_pool: pool=" << (void *) pool << "\n");
199   DEBUG_MAKE_INDENT(3);
200   GCriticalSectionLock lock(&pools_lock);
201   GPosition pos;
202   if (pools_list.search(pool, pos))
203     pools_list.del(pos);
204   return pools_list.size();
205}
206
207inline DataPool::OpenFiles *
208DataPool::OpenFiles::get(void)
209{
210   DEBUG_MSG("DataPool::OpenFiles::get()\n");
211   DEBUG_MAKE_INDENT(3);
212   if (!global_ptr)
213     global_ptr=new OpenFiles();
214   return global_ptr;
215}
216
217void
218DataPool::OpenFiles::prune(void)
219{
220  DEBUG_MSG("DataPool::OpenFiles::prune(void): "<<files_list.size()<< "\n");
221  DEBUG_MAKE_INDENT(3);
222  while(files_list.size()>MAX_OPEN_FILES)
223    {
224      // Too many open files (streams). Get rid of the oldest one.
225      unsigned long oldest_time=GOS::ticks();
226      GPosition oldest_pos=files_list;
227      for(GPosition pos=files_list;pos;++pos)
228        {
229          if (files_list[pos]->open_time<oldest_time)
230            {
231              oldest_time=files_list[pos]->open_time;
232              oldest_pos=pos;
233            }
234        }
235      files_list[oldest_pos]->clear_stream();
236      files_list.del(oldest_pos);
237    }
238}
239
240//                        GP<ByteStream> & stream,
241//                        GCriticalSection ** stream_lock)
242GP<DataPool::OpenFiles_File>
243DataPool::OpenFiles::request_stream(const GURL &url, GP<DataPool> pool)
244{
245   DEBUG_MSG("DataPool::OpenFiles::request_stream(): url='" << url << "'\n");
246   DEBUG_MAKE_INDENT(3);
247
248   GP<DataPool::OpenFiles_File> file;
249
250      // Check: maybe the stream has already been open by request of
251      // another DataPool
252   GCriticalSectionLock lock(&files_lock);
253   for(GPosition pos=files_list;pos;++pos)
254   {
255      if (files_list[pos]->url==url)
256      {
257         DEBUG_MSG("found existing stream\n");
258         file=files_list[pos];
259         break;
260      }
261   }
262
263      // No? Open the stream, but check, that there are not
264      // too many streams open
265   if (!file)
266   {
267      file=new DataPool::OpenFiles_File(url, pool);
268      files_list.append(file);
269      prune();
270   }
271   
272   file->add_pool(pool);
273   return file;
274}
275
276void
277DataPool::OpenFiles::stream_released(GP<ByteStream> &stream, GP<DataPool> pool)
278{
279   DEBUG_MSG("DataPool::OpenFiles::stream_release: stream=" 
280             << (void *)stream << " pool=" << (void *)pool << "\n");
281   DEBUG_MAKE_INDENT(3);
282   GCriticalSectionLock lock(&files_lock);
283   for(GPosition pos=files_list;pos;)
284   {
285     GPosition dpos = pos;
286     ++pos;
287     GP<DataPool::OpenFiles_File> f=files_list[dpos];
288     if ((ByteStream *)(f->stream) == (ByteStream *)stream)
289       if (f->del_pool(pool)==0)
290         files_list.del(dpos);
291   }
292}
293
294// This isn't really an accurate url.  The files are not really
295// closed.  Instead they are dereferenced from the data pool.  If
296// a there is another reference to the respective bytestream, it
297// will remain open until dereferenced.
298void
299DataPool::OpenFiles::close_all(void)
300{
301  DEBUG_MSG("DataPool::OpenFiles::close_all\n");
302  DEBUG_MAKE_INDENT(3);
303  GCriticalSectionLock lock(&files_lock);
304  files_list.empty();
305}
306
307//****************************************************************************
308//******************************** FCPools ***********************************
309//****************************************************************************
310
311/** This class is used to maintain a list of DataPools connected to a file.
312    It's important to have this list if we want to do something with this file
313    like to modify it or just erase. Since any modifications of the file
314    will break DataPools directly connected to it, it would be nice to have
315    a mechanism for signaling all the related DataPools to read data into
316    memory. This is precisely the purpose of this class. */
317class FCPools
318{
319private:
320   GMap<GURL, GPList<DataPool> >        map;    // GMap<GUTF8String, GPList<DataPool>> in fact
321   GCriticalSection             map_lock;
322
323   static FCPools       * global_ptr;
324public:
325   static FCPools *     get(void);
326      // Adds the <furl, pool> pair into the list
327   void         add_pool(const GURL &furl, GP<DataPool> pool);
328      // Removes the <furl, pool> pair from the list
329   void         del_pool(const GURL &furl, GP<DataPool> pool);
330      // Looks for the list of DataPools connected to 'furl' and makes
331      // each of them load the contents of the file into memory
332   void         load_file(const GURL &url);
333      // Retrieve a local URL, if available.
334   GP<DataPool> get_pool(const GURL &url, int start, int length);
335   void clean(void);
336};
337
338void
339FCPools::clean(void)
340{
341  GCriticalSectionLock lock(&map_lock);
342  static int count=0;
343  if(! count++)
344  {
345    bool restart = true;
346    while (restart)
347      {
348        restart = false;
349        for (GPosition posmap = map; posmap; ++posmap)
350          {
351            GPList<DataPool> *lst;
352            lst = & map[posmap];
353            if (lst->isempty())
354              {
355                map.del(posmap);
356                restart = true;
357                break;
358              }
359            for (GPosition poslst = *lst; poslst; ++poslst)
360              if ((*lst)[poslst]->get_count() < 2) 
361                {
362                  lst->del(poslst);
363                  restart = true;
364                  break;
365                }
366            if (restart)
367              break;
368          }
369      }
370  }
371  --count;
372}
373
374void
375FCPools::add_pool(const GURL &url, GP<DataPool> pool)
376{
377  DEBUG_MSG("FCPools::add_pool: url='" << url << "' pool=" << (void *)pool << "\n");
378  DEBUG_MAKE_INDENT(3);
379  GCriticalSectionLock lock(&map_lock);
380
381   if (url.is_local_file_url())
382   {
383      GPList<DataPool> list;
384      GPosition pos(map.contains(url));
385      if (! pos)
386      {
387        map[url]=list;
388        pos=map.contains(url);
389      }
390      GPList<DataPool> &plist=map[pos];
391      if (!plist.contains(pool))
392        plist.append(pool);
393   }
394  clean();
395}
396
397GP<DataPool>
398FCPools::get_pool(const GURL &url, int start, int length)
399{
400  DEBUG_MSG("FCPools::get_pool: url='" << url << "\n");
401  DEBUG_MAKE_INDENT(3);
402  GP<DataPool> retval;
403  if (url.is_local_file_url())
404  {
405    GCriticalSectionLock lock(&map_lock);
406    GPosition pos(map.contains(url));
407    if (pos)
408    {
409      GPList<DataPool> &plist=map[pos];
410      for(pos=plist;pos;++pos)
411      {
412        DataPool &pool=*plist[pos];
413        if(start == pool.start && (length < 0 || (length == pool.length)))
414        {
415          retval=plist[pos];
416          break;
417        }
418      }
419    }
420    clean();
421  }
422  return retval;
423}
424
425void
426FCPools::del_pool(const GURL &url, GP<DataPool> pool)
427{
428  DEBUG_MSG("FCPools::del_pool: url='" << url << "' pool=" << (void *)pool << "\n");
429  DEBUG_MAKE_INDENT(3);
430  GCriticalSectionLock lock(&map_lock);
431
432  clean();
433   if (url.is_local_file_url())
434   {
435      GPosition pos;
436      if (map.contains(url, pos))
437      {
438         GPList<DataPool> &list=map[pos];
439         GPosition list_pos;
440         while(list.search(pool, list_pos))
441            list.del(list_pos);
442         if (list.isempty())
443         {
444            map.del(pos);
445         }
446      }
447   }
448}
449
450void
451FCPools::load_file(const GURL &url)
452{
453  DEBUG_MSG("FCPools::load_file: url='" << url << "'\n");
454  DEBUG_MAKE_INDENT(3);
455  GCriticalSectionLock lock(&map_lock);
456   
457  clean();
458   if (url.is_local_file_url())
459   {
460      GPosition pos;
461      if (map.contains(url, pos))
462      {
463            // We make here a copy of the list because DataPool::load_file()
464            // will call FCPools::del_pool(), which will modify the list
465         GPList<DataPool> list=map[pos];
466         for(GPosition list_pos=list;list_pos;++list_pos)
467            list[list_pos]->load_file();
468      }
469   }
470}
471
472FCPools * FCPools::global_ptr;
473
474inline FCPools *
475FCPools::get(void)
476{
477   if (!global_ptr)
478     global_ptr=new FCPools();
479   return global_ptr;
480}
481
482//****************************************************************************
483//****************************** BlockList ***********************************
484//****************************************************************************
485
486// Since data can be added to the DataPool at any offset now, there may
487// be white spots, which contain illegal data. This class is to contain
488// the list of valid and invalid regions.
489// The class is basically a list of integers. Abs(integer)=size of the
490// block. If the integer is positive, data for the block is known.
491// Otherwise it's unkown.
492
493class DataPool::BlockList
494{
495         // See comments in .cpp file.
496private:
497   GCriticalSection  lock;
498   GList<int>        list;
499public:
500   BlockList() {};
501   void              clear(void);
502   void              add_range(int start, int length);
503   int               get_bytes(int start, int length) const;
504   int               get_range(int start, int length) const;
505friend class DataPool;
506};
507
508void
509DataPool::BlockList::clear(void)
510{
511  DEBUG_MSG("DataPool::BlockList::clear()\n");
512  DEBUG_MAKE_INDENT(3);
513   GCriticalSectionLock lk(&lock);
514   list.empty();
515}
516
517void
518DataPool::BlockList::add_range(int start, int length)
519      // Adds range of known data.
520{
521  DEBUG_MSG("DataPool::BlockList::add_range: start=" << start << " length=" << length << "\n");
522  DEBUG_MAKE_INDENT(3);
523   if (start<0)
524     G_THROW( ERR_MSG("DataPool.neg_start") );
525   if (length<=0)
526     G_THROW( ERR_MSG("DataPool.bad_length") );
527   if (length>0)
528   {
529      GCriticalSectionLock lk(&lock);
530
531         // Look thru existing zones, change their sign and split if
532         // necessary.
533      GPosition pos=list;
534      int block_start=0, block_end=0;
535      while(pos && block_start<start+length)
536      {
537         int size=list[pos];
538         block_end=block_start+abs(size);
539         if (size<0)
540            if (block_start<start)
541            {
542               if (block_end>start && block_end<=start+length)
543               {
544                  list[pos]=-(start-block_start);
545                  list.insert_after(pos, block_end-start);
546                  ++pos;
547                  block_start=start;
548               } else if (block_end>start+length)
549               {
550                  list[pos]=-(start-block_start);
551                  list.insert_after(pos, length);
552                  ++pos;
553                  list.insert_after(pos, -(block_end-(start+length)));
554                  ++pos;
555                  block_start=start+length;
556               }
557            } else if (block_start>=start && block_start<start+length)
558            {
559               if (block_end<=start+length) list[pos]=abs(size);
560               else
561               {
562                  list[pos]=start+length-block_start;
563                  list.insert_after(pos, -(block_end-(start+length)));
564                  ++pos;
565                  block_start=start+length;
566               }
567            }
568         block_start=block_end;
569         ++pos;
570      }
571      if (block_end<start)
572      {
573         list.append(-(start-block_end));
574         list.append(length);
575      } else if (block_end<start+length) list.append(start+length-block_end);
576
577         // Now merge adjacent areas with the same sign
578      pos=list;
579      while(pos)
580      {
581         GPosition pos1=pos; ++pos1;
582         while(pos1)
583         {
584            if (list[pos]<0 && list[pos1]>0 ||
585                list[pos]>0 && list[pos1]<0)
586               break;
587            list[pos]+=list[pos1];
588            GPosition this_pos=pos1;
589            ++pos1;
590            list.del(this_pos);
591         }
592         pos=pos1;
593      }
594   } // if (length>0)
595}
596
597int
598DataPool::BlockList::get_bytes(int start, int length) const
599      // Returns the number of bytes of data available in the range
600      // [start, start+length[. There may be holes between data chunks
601{
602  DEBUG_MSG("DataPool::BlockList::get_bytes: start=" << start << " length=" << length << "\n");
603  DEBUG_MAKE_INDENT(3);
604
605   if (length<0)
606     G_THROW( ERR_MSG("DataPool.bad_length") );
607
608   GCriticalSectionLock lk((GCriticalSection *) &lock);
609   int bytes=0;
610   int block_start=0, block_end=0;
611   for(GPosition pos=list;pos && block_start<start+length;++pos)
612   {
613      int size=list[pos];
614      block_end=block_start+abs(size);
615      if (size>0)
616         if (block_start<start)
617         {
618            if (block_end>=start && block_end<start+length)
619               bytes+=block_end-start;
620            else if (block_end>=start+length)
621               bytes+=length;
622         } else
623         {
624            if (block_end<=start+length)
625               bytes+=block_end-block_start;
626            else bytes+=start+length-block_start;
627         }
628      block_start=block_end;
629   }
630   return bytes;
631}
632
633int
634DataPool::BlockList::get_range(int start, int length) const
635      // Finds a range covering offset=start and returns the length
636      // of intersection of this range with [start, start+length[
637      // 0 is returned if nothing can be found
638{
639  DEBUG_MSG("DataPool::BlockList::get_range: start=" << start << " length=" << length << "\n");
640  DEBUG_MAKE_INDENT(3);
641   if (start<0)
642     G_THROW( ERR_MSG("DataPool.neg_start") );
643   if (length<=0)
644      G_THROW( ERR_MSG("DataPool.bad_length") );
645
646   GCriticalSectionLock lk((GCriticalSection *) &lock);
647   int block_start=0, block_end=0;
648   for(GPosition pos=list;pos && block_start<start+length;++pos)
649   {
650      int size=list[pos];
651      block_end=block_start+abs(size);
652      if (block_start<=start && block_end>start)
653         if (size<0) return -1;
654         else
655            if (block_end>start+length) return length;
656            else return block_end-start;
657      block_start=block_end;
658   }
659   return 0;
660}
661
662//****************************************************************************
663//******************************* DataPool ***********************************
664//****************************************************************************
665
666class DataPool::Reader : public GPEnabled
667{
668public:
669   GEvent event;
670   bool reenter_flag;
671   int  offset;
672   int  size;
673   Reader() : reenter_flag(false), offset(0), size(-1){};
674   Reader(int offset_in, int size_in=-1) :
675   reenter_flag(false), offset(offset_in), size(size_in) {};
676   virtual ~Reader() {};
677};
678
679class DataPool::Trigger : public GPEnabled
680{
681public:
682   GSafeFlags disabled;
683   int  start, length;
684//   void (* callback)(GP<GPEnabled> &);
685   void (* callback)(void *);
686//   GP<GPEnabled> cl_data;
687   void *cl_data;
688
689   Trigger() : start(0), length(-1), callback(0), cl_data(0) {};
690   Trigger(int xstart, int xlength,
691//   void (* xcallback)(GP<GPEnabled> &), GP<GPEnabled> xcl_data) :
692   void (* xcallback)(void *), void *xcl_data) :
693      start(xstart), length(xlength), callback(xcallback), cl_data(xcl_data) {};
694   virtual ~Trigger() {};
695};
696
697class DataPool::Counter
698{
699private:
700   int               counter;
701   GCriticalSection  lock;
702public:
703   Counter() : counter(0) {};
704   operator int(void) const;
705   void              inc(void);
706   void              dec(void);
707};
708
709#define DATAPOOL_INIT eof_flag(false),stop_flag(false), \
710    stop_blocked_flag(false), \
711    add_at(0),start(0),length(-1)
712
713void
714DataPool::init(void)
715{
716  DEBUG_MSG("DataPool::init(): Initializing\n");
717  DEBUG_MAKE_INDENT(3);
718  start=0; length=-1; add_at=0;
719  eof_flag=false;
720  stop_flag=false;
721  stop_blocked_flag=false;
722
723  active_readers=new Counter;
724  block_list=0;
725  G_TRY
726  {   
727    block_list=new BlockList;
728    data=ByteStream::create();
729  }
730  G_CATCH_ALL
731  {
732    delete block_list;
733    block_list=0;
734    delete active_readers;
735    active_readers=0;
736    G_RETHROW;
737  }
738  G_ENDCATCH;
739}
740
741DataPool::DataPool(void) : DATAPOOL_INIT {}
742
743GP<DataPool>
744DataPool::create(void)
745{
746  DEBUG_MSG("DataPool::DataPool()\n");
747  DEBUG_MAKE_INDENT(3);
748  DataPool *pool=new DataPool();
749
750  GP<DataPool> retval=pool;
751  pool->init();
752
753      // If we maintain the data ourselves, we want to interpret its
754      // IFF structure to predict its length
755  pool->add_trigger(0, 32, static_trigger_cb, pool);
756  return retval;
757}
758
759GP<DataPool> 
760DataPool::create(const GP<ByteStream> &gstr)
761{
762  DEBUG_MSG("DataPool::create: str="<<(ByteStream *)gstr<<"\n");
763  DEBUG_MAKE_INDENT(3);
764  DataPool *pool=new DataPool();
765  GP<DataPool> retval=pool;
766  pool->init();
767
768      // It's nice to have IFF data analyzed in this case too.
769  pool->add_trigger(0, 32, static_trigger_cb, pool);
770
771  pool->data=gstr->duplicate();
772  pool->added_data(0,pool->data->size());   
773//  char buffer[1024];
774//  int length;
775//  while((length=str.read(buffer, 1024)))
776//     pool->add_data(buffer, length);
777  pool->set_eof();
778  return retval;
779}
780
781GP<DataPool>
782DataPool::create(const GP<DataPool> & pool, int start, int length)
783{
784  DEBUG_MSG("DataPool::DataPool: pool=" << (void *)((DataPool *)pool) << " start=" << start << " length= " << length << "\n");
785  DEBUG_MAKE_INDENT(3);
786
787  DataPool *xpool=new DataPool();
788  GP<DataPool> retval=xpool;
789  xpool->init();
790  xpool->connect(pool, start, length);
791  return retval;
792}
793
794GP<DataPool>
795DataPool::create(const GURL &furl, int start, int length)
796{
797  DEBUG_MSG("DataPool::DataPool: furl='" << furl << "' start=" << start << " length= " << length << "\n");
798  DEBUG_MAKE_INDENT(3);
799
800  GP<DataPool> retval=FCPools::get()->get_pool(furl,start,length);
801  if(! retval)
802  {
803    DataPool *pool=new DataPool();
804    retval=pool;
805    pool->init();
806    pool->connect(furl, start, length);
807  }
808  return retval;
809}
810
811void
812DataPool::clear_stream(const bool release)
813{
814  DEBUG_MSG("DataPool::clear_stream()\n");
815  DEBUG_MAKE_INDENT(3);
816  if(fstream)
817  {
818    GCriticalSectionLock lock1(&class_stream_lock);
819    GP<OpenFiles_File> f=fstream;
820    if(f)
821    {
822      GCriticalSectionLock lock2(&(f->stream_lock));
823      fstream=0;
824      if(release)
825        OpenFiles::get()->stream_released(f->stream, this);
826    }
827  }
828}
829
830DataPool::~DataPool(void)
831{
832  DEBUG_MSG("DataPool::~DataPool()\n");
833  DEBUG_MAKE_INDENT(3);
834
835  clear_stream(true);
836  if (furl.is_local_file_url())
837  {
838    FCPools::get()->del_pool(furl, this);
839  }
840   
841  {
842         // Wait until the static_trigger_cb() exits
843      GCriticalSectionLock lock(&trigger_lock);
844      if (pool)
845        pool->del_trigger(static_trigger_cb, this);
846      del_trigger(static_trigger_cb, this);
847  }
848
849  if (pool)
850  {
851      GCriticalSectionLock lock(&triggers_lock);
852      for(GPosition pos=triggers_list;pos;++pos)
853      {
854         GP<Trigger> trigger=triggers_list[pos];
855         pool->del_trigger(trigger->callback, trigger->cl_data);
856      }
857  }
858  delete block_list;
859  delete active_readers;
860}
861
862void
863DataPool::connect(const GP<DataPool> & pool_in, int start_in, int length_in)
864{
865   DEBUG_MSG("DataPool::connect(): connecting to another DataPool\n");
866   DEBUG_MAKE_INDENT(3);
867   
868   if (pool) G_THROW( ERR_MSG("DataPool.connected1") );
869   if (furl.is_local_file_url()) G_THROW( ERR_MSG("DataPool.connected2") );
870   if (start_in<0) G_THROW( ERR_MSG("DataPool.neg_start") );
871
872   pool=pool_in;
873   start=start_in;
874   length=length_in;
875
876      // The following will work for length<0 too
877   if (pool->has_data(start, length))
878     eof_flag=true;
879   else
880     pool->add_trigger(start, length, static_trigger_cb, this);
881
882   data=0;
883
884   wake_up_all_readers();
885   
886      // Pass registered trigger callbacks to the DataPool
887   GCriticalSectionLock lock(&triggers_lock);
888   for(GPosition pos=triggers_list;pos;++pos)
889   {
890      GP<Trigger> t=triggers_list[pos];
891      int tlength=t->length;
892      if (tlength<0 && length>0)
893        tlength=length-t->start;
894      pool->add_trigger(start+t->start, tlength, t->callback, t->cl_data);
895   }
896}
897
898void
899DataPool::connect(const GURL &furl_in, int start_in, int length_in)
900{
901   DEBUG_MSG("DataPool::connect(): connecting to a file\n");
902   DEBUG_MAKE_INDENT(3);
903   
904   if (pool)
905     G_THROW( ERR_MSG("DataPool.connected1") );
906   if (furl.is_local_file_url())
907     G_THROW( ERR_MSG("DataPool.connected2") );
908   if (start_in<0)
909     G_THROW( ERR_MSG("DataPool.neg_start") );
910
911
912   if (furl_in.name() == "-")
913   {
914      DEBUG_MSG("This is stdin => just read the data...\n");
915      DEBUG_MAKE_INDENT(3);
916      char buffer[1024];
917      int length;
918      GP<ByteStream> gstr=ByteStream::create(furl_in, "rb");
919      ByteStream &str=*gstr;
920      while((length=str.read(buffer, 1024)))
921         add_data(buffer, length);
922      set_eof();
923   } else if(furl_in.is_local_file_url())
924   {
925         // Open the stream (just in this function) too see if
926         // the file is accessible. In future we will be using 'OpenFiles'
927         // to request and release streams
928      GP<ByteStream> str=ByteStream::create(furl_in,"rb");
929      str->seek(0, SEEK_END);
930      int file_size=str->tell();
931
932      furl=furl_in;
933      start=start_in;
934      length=length_in;
935      if (start>=file_size)
936        length=0;
937      else if (length<0 || start+length>=file_size)
938        length=file_size-start;
939
940      eof_flag=true;
941
942      if(str->is_static())
943      {
944        data=str;
945        added_data(0,length);
946      }else 
947      {
948        data=0;
949      }
950
951      FCPools::get()->add_pool(furl, this);
952
953      wake_up_all_readers();
954   
955         // Call every trigger callback
956      GCriticalSectionLock lock(&triggers_lock);
957      for(GPosition pos=triggers_list;pos;++pos)
958      {
959         GP<Trigger> t=triggers_list[pos];
960         call_callback(t->callback, t->cl_data);
961      }
962      triggers_list.empty();
963   }
964}
965
966int
967DataPool::get_length(void) const
968{
969      // Not connected and length has been guessed
970      // Or connected to a file
971      // Or connected to a pool, but length was preset
972   int retval=(-1);
973   if (length>=0) 
974   {
975     retval=length;
976   }else if (pool)
977   {
978      int plength=pool->get_length();
979      if (plength>=0)
980        retval=plength-start;
981   }
982   return retval;
983}
984
985int
986DataPool::get_size(int dstart, int dlength) const
987{
988   if (dlength<0 && length>0)
989   {
990      dlength=length-dstart;
991      if (dlength<0) return 0;
992   }
993   
994   if (pool) return pool->get_size(start+dstart, dlength);
995   else if (furl.is_local_file_url())
996   {
997      if (start+dstart+dlength>length) return length-(start+dstart);
998      else return dlength;
999   } else
1000   {
1001      if (dlength<0)
1002      {
1003         GCriticalSectionLock lock((GCriticalSection *) &data_lock);
1004         dlength=data->size()-dstart;
1005      }
1006      return (dlength<0)?0:(block_list->get_bytes(dstart, dlength));
1007   }
1008}
1009
1010void
1011DataPool::add_data(const void * buffer, int size)
1012      // This function adds data sequentially at 'add_at' position
1013{
1014   DEBUG_MSG("DataPool::add_data(): adding " << size << " bytes of data...\n");
1015   DEBUG_MAKE_INDENT(3);
1016
1017   add_data(buffer, add_at, size);
1018   add_at+=size;
1019}
1020
1021void
1022DataPool::add_data(const void * buffer, int offset, int size)
1023{
1024   DEBUG_MSG("DataPool::add_data(): adding " << size << " bytes at pos=" <<
1025             offset << "...\n");
1026   DEBUG_MAKE_INDENT(3);
1027
1028   if (furl.is_local_file_url() || pool)
1029      G_THROW( ERR_MSG("DataPool.add_data") );
1030   
1031      // Add data to the data storage
1032   {
1033      GCriticalSectionLock lock(&data_lock);
1034      if (offset>data->size())
1035      {
1036         char ch=0;
1037         data->seek(0, SEEK_END);
1038         for(int i=data->size();i<offset;i++)
1039            data->write(&ch, 1);
1040      } else
1041      {
1042         data->seek(offset, SEEK_SET);
1043         data->writall(buffer, size);
1044      }
1045   }
1046
1047   added_data(offset, size);
1048}
1049
1050void
1051DataPool::added_data(const int offset, const int size)
1052{
1053     // Modify map of blocks
1054  block_list->add_range(offset, size);
1055   
1056     // Wake up all threads, which may be waiting for this data
1057  {
1058    GCriticalSectionLock lock(&readers_lock);
1059    for(GPosition pos=readers_list;pos;++pos)
1060    {
1061      GP<Reader> reader=readers_list[pos];
1062      if (block_list->get_bytes(reader->offset, 1))
1063      {
1064        DEBUG_MSG("waking up reader: offset=" << reader->offset <<
1065          ", size=" << reader->size << "\n");
1066        DEBUG_MAKE_INDENT(3);
1067        reader->event.set();
1068      }
1069    }
1070  }
1071
1072    // And call triggers
1073  check_triggers();
1074
1075      // Do not undo the following two lines. The reason why we need them
1076      // here is the connected DataPools, which use 'length' (more exactly
1077      // has_data()) to see if they have all data required. So, right after
1078      // all data has been added to the master DataPool, but before EOF
1079      // is set, the master and slave DataPools disagree regarding if
1080      // all data is there or not. These two lines solve the problem
1081  GCriticalSectionLock lock(&data_lock);
1082  if (length>=0 && data->size()>=length)
1083    set_eof();
1084}
1085
1086bool
1087DataPool::has_data(int dstart, int dlength)
1088{
1089   if (dlength<0 && length>0)
1090     dlength=length-dstart;
1091   return (pool?(pool->has_data(start+dstart, dlength))
1092     :((furl.is_local_file_url())?(start+dstart+dlength<=length)
1093       :((dlength<0)?is_eof()
1094         :(block_list->get_bytes(dstart, dlength)==dlength))));
1095}
1096
1097int
1098DataPool::get_data(void * buffer, int offset, int sz)
1099{
1100   return get_data(buffer, offset, sz, 0);
1101}
1102
1103class DataPool::Incrementor
1104{
1105private:
1106   Counter      & counter;
1107public:
1108   Incrementor(Counter & xcounter) : counter(xcounter) {counter.inc();}
1109   ~Incrementor() {counter.dec();}
1110};
1111
1112int
1113DataPool::get_data(void * buffer, int offset, int sz, int level)
1114{
1115   DEBUG_MSG("DataPool::get_data()\n");
1116   DEBUG_MAKE_INDENT(3);
1117   Incrementor inc(*active_readers);
1118   
1119   if (stop_flag)
1120     G_THROW( DataPool::Stop );
1121   if (stop_blocked_flag && !is_eof() &&
1122       !has_data(offset, sz))
1123     G_THROW( DataPool::Stop );
1124   
1125   if (sz < 0)
1126     G_THROW( ERR_MSG("DataPool.bad_size") );
1127   
1128   if (! sz)
1129     return 0;
1130   
1131   if (pool)
1132     {
1133       DEBUG_MSG("DataPool::get_data(): from pool\n");
1134       DEBUG_MAKE_INDENT(3);
1135       int retval=0;
1136       if (length>0 && offset+sz>length)
1137         sz=length-offset;
1138       if (sz<0)
1139        sz=0;
1140       for(;;)
1141         {
1142           // Ask the underlying (master) DataPool for data. Note, that
1143           // master DataPool may throw the "DATA_POOL_REENTER" exception
1144           // demanding all readers to restart. This happens when
1145           // a DataPool in the chain of DataPools stops. All readers
1146           // should return to the most upper level and then reenter the
1147           // DataPools hierarchy. Some of them will be stopped by
1148           // DataPool::Stop exception.
1149           G_TRY
1150             {
1151               if(stop_flag||stop_blocked_flag&&!is_eof()&&!has_data(offset, sz))
1152                 G_THROW( DataPool::Stop );
1153               retval=pool->get_data(buffer, start+offset, sz, level+1);
1154             } 
1155           G_CATCH(exc) 
1156             {
1157               pool->clear_stream(true);
1158               if ((exc.get_cause() != GUTF8String( ERR_MSG("DataPool.reenter") ) ) || level)
1159                 G_RETHROW;
1160             } G_ENDCATCH;
1161           pool->clear_stream(true);
1162           return retval;
1163         }
1164     }
1165   else if(data && data->is_static() && eof_flag)
1166     { 
1167       DEBUG_MSG("DataPool::get_data(): static\n");
1168       DEBUG_MAKE_INDENT(3);
1169       // We're not connected to anybody => handle the data
1170       int size=block_list->get_range(offset, sz);
1171       if (size>0)
1172         {
1173           // Hooray! Some data is there
1174           GCriticalSectionLock lock(&data_lock);
1175           data->seek(offset, SEEK_SET);
1176           return data->readall(buffer, size);
1177         }
1178       return 0;
1179     } 
1180   else if (furl.is_local_file_url())
1181     {
1182       DEBUG_MSG("DataPool::get_data(): from file\n");
1183       DEBUG_MAKE_INDENT(3);
1184       if (length>0 && offset+sz>length)
1185         sz=length-offset;
1186       if (sz<0)
1187         sz=0;
1188       
1189       GP<OpenFiles_File> f=fstream;
1190       if (!f)
1191         {
1192           GCriticalSectionLock lock(&class_stream_lock);
1193           f=fstream;
1194           if(!f)
1195             {
1196               fstream=f=OpenFiles::get()->request_stream(furl, this);
1197             }
1198         }
1199       GCriticalSectionLock lock2(&(f->stream_lock));
1200       f->stream->seek(start+offset, SEEK_SET); 
1201       return f->stream->readall(buffer, sz);
1202     } 
1203   else
1204     {
1205       DEBUG_MSG("DataPool::get_data(): direct\n");
1206       DEBUG_MAKE_INDENT(3);
1207       // We're not connected to anybody => handle the data
1208       int size=block_list->get_range(offset, sz);
1209       if (size>0)
1210         {
1211           // Hooray! Some data is there
1212           GCriticalSectionLock lock(&data_lock);
1213           data->seek(offset, SEEK_SET);
1214           return data->readall(buffer, size);
1215         }
1216       
1217       // No data available.
1218       
1219       // If there is no data and nothing else is expected, we can do
1220       // two things: throw ByteStream::EndOfFile exception or return ZERO bytes.
1221       // The exception is for the cases when the data flow has been
1222       // terminated in the middle. ZERO bytes is for regular read() beyond
1223       // the boundaries of legal data. The problem is to distinguish
1224       // these two cases. We do it here with the help of analysis of the
1225       // IFF structure of the data (which sets the 'length' variable).
1226       // If we attempt to read beyond the [0, length[, ZERO bytes will be
1227       // returned. Otherwise an ByteStream::EndOfFile exception will be thrown.
1228       if (eof_flag)
1229         {
1230           if (length>0 && offset<length) 
1231             {
1232               G_THROW( ByteStream::EndOfFile );
1233             }
1234           else 
1235             {
1236               return 0;
1237             }
1238         } 
1239       // Some data is still expected => add this reader to the
1240       // list of readers and call virtual wait_for_data()
1241       DEBUG_MSG("DataPool::get_data(): There is no data in the pool.\n");
1242       DEBUG_MSG("offset=" << offset << ", size=" << sz <<
1243                 ", data_size=" << data->size() << "\n");
1244       GP<Reader> reader=new Reader(offset, sz);
1245       G_TRY
1246         {
1247           {
1248             GCriticalSectionLock slock(&readers_lock);
1249             readers_list.append(reader);
1250           }
1251           wait_for_data(reader);
1252         } 
1253       G_CATCH_ALL
1254         {
1255           {
1256             GCriticalSectionLock slock(&readers_lock);
1257             GPosition pos;
1258             if (readers_list.search(reader, pos)) readers_list.del(pos);
1259           }
1260           G_RETHROW;
1261         } 
1262       G_ENDCATCH;
1263       
1264       {
1265         GCriticalSectionLock slock(&readers_lock);
1266         GPosition pos;
1267         if (readers_list.search(reader, pos)) readers_list.del(pos);
1268       }
1269       
1270       // This call to get_data() should return immediately as there MUST
1271       // be data in the buffer after wait_for_data(reader) returns
1272       // or eof_flag should be TRUE
1273       return get_data(buffer, reader->offset, reader->size, level);
1274     }
1275   return 0;
1276}
1277
1278void
1279DataPool::wait_for_data(const GP<Reader> & reader)
1280      // This function may NOT return until there is some data for the
1281      // given reader in the internal buffer
1282{
1283   DEBUG_MSG("DataPool::wait_for_data(): waiting for data at offset=" << reader->offset <<
1284             ", length=" << reader->size << "\n");
1285   DEBUG_MAKE_INDENT(3);
1286
1287#if THREADMODEL==NOTHREADS
1288   G_THROW( ERR_MSG("DataPool.no_threadless") );
1289#else
1290   for(;;)
1291   {
1292      if (stop_flag)
1293        G_THROW( DataPool::Stop );
1294      if (reader->reenter_flag)
1295        G_THROW( ERR_MSG("DataPool.reenter") );
1296      if (eof_flag || block_list->get_bytes(reader->offset, 1))
1297        return;
1298      if (pool || furl.is_local_file_url())
1299        return;
1300
1301      if (stop_blocked_flag)
1302        G_THROW( DataPool::Stop );
1303
1304      DEBUG_MSG("calling event.wait()...\n");
1305      reader->event.wait();
1306   }
1307#endif
1308   
1309   DEBUG_MSG("Got some data to read\n");
1310}
1311
1312void
1313DataPool::wake_up_all_readers(void)
1314{
1315   DEBUG_MSG("DataPool::wake_up_all_readers(): waking up all readers\n");
1316   DEBUG_MAKE_INDENT(3);
1317
1318   GCriticalSectionLock lock(&readers_lock);
1319   for(GPosition pos=readers_list;pos;++pos)
1320      readers_list[pos]->event.set();
1321}
1322
1323void
1324DataPool::set_eof(void)
1325      // Has no effect on connected DataPools
1326{
1327   if (!furl.is_local_file_url() && !pool)
1328   {
1329      eof_flag=true;
1330     
1331         // Can we set the length now?
1332      if (length<0)
1333      {
1334         GCriticalSectionLock lock(&data_lock);
1335         length=data->size();
1336      }
1337
1338         // Wake up all readers to let them rescan the flags
1339      wake_up_all_readers();
1340   
1341         // Activate all trigger callbacks with negative threshold
1342      check_triggers();
1343   }
1344}
1345
1346void
1347DataPool::stop(bool only_blocked)
1348{
1349   DEBUG_MSG("DataPool::stop(): Stopping this and dependent DataPools, only_blocked="
1350             << only_blocked << "\n");
1351   DEBUG_MAKE_INDENT(3);
1352
1353   if (only_blocked) stop_blocked_flag=true;
1354   else stop_flag=true;
1355   
1356
1357   wake_up_all_readers();
1358
1359      // Now let all readers, which already go thru to the master DataPool,
1360      // come back and reenter. While reentering some of them will go
1361      // thru this DataPool again and will be stopped (DataPool::Stop exception)
1362      // Others (which entered the master DataPool thru other slave DataPools)
1363      // will simply continue waiting for their data.
1364   if (pool)
1365   {
1366         // This loop is necessary because there may be another thread, which
1367         // is going down thru the DataPool chain and did not reach the
1368         // lowest "master" DataPool yet. Since it didn't reach it yet,
1369         // the "pool->restart_readers()" will not restart it. So we're going
1370         // to continue issuing this command until we get rid of all
1371         // "active_readers"
1372      while(*active_readers)
1373      {
1374#if (THREADMODEL==COTHREADS) || (THREADMODEL==MACTHREADS)
1375         GThread::yield();
1376#endif
1377         pool->restart_readers();
1378      }
1379   }
1380}
1381
1382void
1383DataPool::restart_readers(void)
1384{
1385   DEBUG_MSG("DataPool::restart_readers(): telling all readers to reenter\n");
1386   DEBUG_MAKE_INDENT(3);
1387   
1388   GCriticalSectionLock slock(&readers_lock);
1389   for(GPosition pos=readers_list;pos;++pos)
1390   {
1391      GP<Reader> reader=readers_list[pos];
1392      reader->reenter_flag=true;
1393      reader->event.set();
1394   }
1395     
1396   if (pool)
1397     pool->restart_readers();
1398}
1399
1400void
1401DataPool::load_file(void)
1402{
1403   DEBUG_MSG("DataPool::load_file() called\n");
1404   DEBUG_MAKE_INDENT(3);
1405
1406   if (pool)
1407   {
1408      DEBUG_MSG("passing the request down.\n");
1409      pool->load_file();
1410   } else if (furl.is_local_file_url())
1411   {
1412      DEBUG_MSG("loading the data from \""<<(const char *)furl<<"\".\n");
1413
1414      GCriticalSectionLock lock1(&class_stream_lock);
1415      GP<OpenFiles_File> f=fstream;
1416      if (!f)
1417      {
1418        fstream=f=OpenFiles::get()->request_stream(furl, this);
1419      }
1420      {  // Scope to de-allocate lock2 before stream gets released
1421         GCriticalSectionLock lock2(&(f->stream_lock));
1422
1423         data=ByteStream::create();
1424         block_list->clear();
1425         FCPools::get()->del_pool(furl, this);
1426         furl=GURL();
1427
1428         const GP<ByteStream> gbs=f->stream;
1429         gbs->seek(0, SEEK_SET);
1430         data=gbs->duplicate();
1431         added_data(0,data->size());   
1432         set_eof();
1433//         char buffer[1024];
1434//         int length;
1435//         while((length=f->stream->read(buffer, 1024)))
1436//               add_data(buffer, length);
1437              // No need to set EOF. It should already be set.
1438        OpenFiles::get()->stream_released(f->stream, this);
1439      }
1440      fstream=0;
1441   } else DEBUG_MSG("Not connected\n");
1442}
1443
1444void
1445DataPool::load_file(const GURL &url )
1446{
1447   FCPools::get()->load_file(url);
1448}
1449
1450void
1451DataPool::check_triggers(void)
1452      // This function is for not connected DataPools only
1453{
1454  DEBUG_MSG("DataPool::check_triggers(): calling activated trigger callbacks.\n");
1455  DEBUG_MAKE_INDENT(3);
1456 
1457  if (!pool && !furl.is_local_file_url())
1458    while(true)
1459    {
1460      GP<Trigger> trigger;
1461     
1462      // First find a candidate (trigger, which needs to be called)
1463      // Don't remove it from the list yet. del_trigger() should
1464      // be able to find it if necessary and disable.
1465      {
1466        GCriticalSectionLock list_lock(&triggers_lock);
1467        for(GPosition pos=triggers_list;pos;++pos)
1468        {
1469          GP<Trigger> t=triggers_list[pos];
1470          if (is_eof() || t->length>=0 &&
1471            block_list->get_bytes(t->start, t->length)==t->length)
1472          {
1473            trigger=t;
1474            break;
1475          }
1476        }
1477      }
1478     
1479      if (trigger)
1480      {
1481               // Now check that the trigger is not disabled
1482               // and lock the trigger->disabled lock for the duration
1483               // of the trigger. This will block the del_trigger() and
1484               // will postpone client's destruction (usually following
1485               // the call to del_trigger())
1486        {
1487          GMonitorLock lock(&trigger->disabled);
1488          if (!trigger->disabled)
1489            call_callback(trigger->callback, trigger->cl_data);
1490        }
1491       
1492               // Finally - remove the trigger from the list.
1493        GCriticalSectionLock list_lock(&triggers_lock);
1494        for(GPosition pos=triggers_list;pos;++pos)
1495          if (triggers_list[pos]==trigger)
1496          {
1497            triggers_list.del(pos);
1498            break;
1499          }
1500      } else break;
1501    }
1502}
1503
1504void
1505// DataPool::add_trigger(int thresh, void (* callback)(GP<GPEnabled> &), GP<GPEnabled> cl_data)
1506DataPool::add_trigger(int thresh, void (* callback)(void *), void * cl_data)
1507{
1508  if (thresh>=0)
1509    add_trigger(0, thresh+1, callback, cl_data);
1510  else
1511    add_trigger(0, -1, callback, cl_data);
1512}
1513
1514void
1515DataPool::add_trigger(int tstart, int tlength,
1516//                    void (* callback)(GP<GPEnabled> &), GP<GPEnabled> cl_data)
1517                      void (* callback)(void *), void * cl_data)
1518{
1519   DEBUG_MSG("DataPool::add_trigger(): start=" << tstart <<
1520             ", length=" << tlength << ", func=" << (void *) callback << "\n");
1521   DEBUG_MAKE_INDENT(3);
1522   
1523   if (callback)
1524   {
1525      if (is_eof())
1526      {
1527        call_callback(callback, cl_data);
1528      }else
1529      {
1530         if (pool)
1531         {
1532               // We're connected to a DataPool
1533               // Just pass the triggers down remembering it in the list
1534            if (tlength<0 && length>0) tlength=length-tstart;
1535            GP<Trigger> trigger=new Trigger(tstart, tlength, callback, cl_data);
1536            pool->add_trigger(start+tstart, tlength, callback, cl_data);
1537            GCriticalSectionLock lock(&triggers_lock);
1538            triggers_list.append(trigger);
1539         } else if (!furl.is_local_file_url())
1540         {
1541               // We're not connected to anything and maintain our own data
1542            if (tlength>=0 && block_list->get_bytes(tstart, tlength)==tlength)
1543               call_callback(callback, cl_data);
1544            else
1545            {
1546               GCriticalSectionLock lock(&triggers_lock);
1547               triggers_list.append(new Trigger(tstart, tlength, callback, cl_data));
1548            }
1549         }
1550      }
1551   }
1552}
1553
1554void
1555// DataPool::del_trigger(void (* callback)(GP<GPEnabled> &), GP<GPEnabled> cl_data)
1556DataPool::del_trigger(void (* callback)(void *), void * cl_data)
1557{
1558   DEBUG_MSG("DataPool::del_trigger(): func=" << (void *) callback << "\n");
1559   DEBUG_MAKE_INDENT(3);
1560
1561   for(;;)
1562   {
1563      GP<Trigger> trigger;
1564      {
1565         GCriticalSectionLock lock(&triggers_lock);
1566         for(GPosition pos=triggers_list;pos;)
1567         {
1568            GP<Trigger> t=triggers_list[pos];
1569            if (t->callback==callback && t->cl_data==cl_data)
1570            {
1571               trigger=t;
1572               GPosition this_pos=pos;
1573               ++pos;
1574               triggers_list.del(this_pos);
1575               break;
1576            } else
1577              ++pos;
1578         }
1579      }
1580
1581         // Above we removed the trigger from the list and unlocked the list
1582         // Now we will disable it and will wait if necessary (if the
1583         // trigger is currently being processed by check_triggers())
1584         // check_triggers() locks the trigger for the duration of the
1585         // trigger callback. Thus we will wait for the trigger callback
1586         // to finish and avoid client's destruction.
1587      if (trigger)
1588        trigger->disabled=1;
1589      else
1590        break;
1591   }
1592
1593   if (pool)
1594     pool->del_trigger(callback, cl_data);
1595}
1596
1597void
1598// DataPool::static_trigger_cb(GP<GPEnabled> &cl_data)
1599DataPool::static_trigger_cb(void *cl_data)
1600{
1601//  GP<DataPool> d=(DataPool *)(GPEnabled *)cl_data;
1602  GP<DataPool> d=(DataPool *)cl_data;
1603  d->trigger_cb();
1604}
1605
1606void
1607DataPool::trigger_cb(void)
1608      // This function may be triggered by the DataPool, which we're
1609      // connected to, or by ourselves, if we're connected to nothing
1610{
1611      // Don't want to be destroyed while I'm here. Can't use GP<> life saver
1612      // because it may be called from the constructor
1613   GCriticalSectionLock lock(&trigger_lock);
1614   
1615   DEBUG_MSG("DataPool::trigger_cb() called\n");
1616   DEBUG_MAKE_INDENT(3);
1617
1618   if (pool)
1619   {
1620      // Connected to a pool
1621      // We may be here when either EOF is set on the master DataPool
1622      // Or when it may have learnt its length (from IFF or whatever)
1623      if (pool->is_eof() || pool->has_data(start, length)) eof_flag=true;
1624   } else if (!furl.is_local_file_url())
1625   {
1626            // Not connected to anything => Try to guess the length
1627      if (length<0) analyze_iff();
1628     
1629            // Failed to analyze? Check, maybe it's EOF already
1630      if (length<0 && is_eof())
1631      {
1632               GCriticalSectionLock lock(&data_lock);
1633               length=data->size();
1634      }
1635   }
1636}
1637
1638void
1639DataPool::analyze_iff(void)
1640      // In order to display decode progress properly, we need to know
1641      // the size of the data. It's trivial to figure it out if is_eof()
1642      // is true. Otherwise we need to make a prediction. Luckily all
1643      // DjVuFiles have IFF structure, which makes it possible to do it.
1644      // If due to some reason we fail, the length will remain -1.
1645{
1646   DEBUG_MSG("DataPool::analyze_iff(): Trying to decode IFF structure of " << furl << ".\n");
1647   DEBUG_MSG("in order to predict the DataPool's size\n");
1648   DEBUG_MAKE_INDENT(3);
1649
1650   GP<ByteStream> str=get_stream();
1651   
1652   GP<IFFByteStream> giff=IFFByteStream::create(str);
1653   IFFByteStream &iff=*giff;
1654   GUTF8String chkid;
1655   int size;
1656   if ((size=iff.get_chunk(chkid)) && size>=0)
1657   {
1658      length=size+iff.tell()-4;
1659      DEBUG_MSG("Got size=" << size << ", length=" << length << "\n");
1660   }
1661}
1662
1663
1664//****************************************************************************
1665//****************************** PoolByteStream ******************************
1666//****************************************************************************
1667
1668// This is an internal ByteStream receiving data from the associated DataPool.
1669// It's just a sequential interface, nothing more. All the job for data
1670// retrieval, waiting and thread synchronization is done by DataPool
1671
1672class PoolByteStream : public ByteStream
1673{
1674public:
1675   PoolByteStream(GP<DataPool> data_pool);
1676   virtual ~PoolByteStream() {};
1677
1678   virtual size_t read(void *buffer, size_t size);
1679   virtual size_t write(const void *buffer, size_t size);
1680   virtual long tell(void) const ;
1681   virtual int seek(long offset, int whence = SEEK_SET, bool nothrow=false);
1682private:
1683      // Don't make data_pool GP<>. The problem is that DataPool creates
1684      // and soon destroys this ByteStream from the constructor. Since
1685      // there are no other pointers to the DataPool created yet, it becomes
1686      // destroyed immediately :(
1687   DataPool             * data_pool;
1688   GP<DataPool>         data_pool_lock;
1689   long                 position;
1690   
1691   char                 buffer[512];
1692   size_t               buffer_size;
1693   size_t               buffer_pos;
1694
1695      // Cancel C++ default stuff
1696   PoolByteStream & operator=(const PoolByteStream &);
1697};
1698
1699inline
1700PoolByteStream::PoolByteStream(GP<DataPool> xdata_pool) :
1701   data_pool(xdata_pool), position(0), buffer_size(0), buffer_pos(0)
1702{
1703   if (!data_pool) 
1704       G_THROW( ERR_MSG("DataPool.zero_DataPool") );
1705
1706      // Secure the DataPool if possible. If we're called from DataPool
1707      // constructor (get_count()==0) there is no need to secure at all.
1708   if (data_pool->get_count()) data_pool_lock=data_pool;
1709}
1710
1711size_t
1712PoolByteStream::read(void *data, size_t size)
1713{
1714  if (buffer_pos >= buffer_size) {
1715    if (size >= sizeof(buffer)) {
1716      // Direct read
1717      size = data_pool->get_data(data, position, size);
1718      position += size;
1719      return size;
1720    } else {
1721      // Refill buffer
1722      buffer_size = data_pool->get_data(buffer, position, sizeof(buffer));
1723      buffer_pos=0;
1724    }
1725  }
1726  if (buffer_pos + size >= buffer_size)
1727    size = buffer_size - buffer_pos;
1728  memcpy(data, buffer+buffer_pos, size);
1729  buffer_pos += size;
1730  position += size;
1731  return size;
1732}
1733
1734size_t
1735PoolByteStream::write(const void *buffer, size_t size)
1736{
1737   G_THROW( ERR_MSG("not_implemented_n") "\tPoolByteStream::write()");   //  PoolByteStream::write() is not implemented.
1738   return 0;    // For compiler not to bark
1739}
1740
1741long
1742PoolByteStream::tell(void) const
1743{
1744   return position;
1745}
1746
1747int
1748PoolByteStream::seek(long offset, int whence, bool nothrow)
1749{
1750  int retval=(-1);
1751  switch(whence)
1752  {
1753    case SEEK_CUR:
1754      offset+=position;
1755      // fallthrough;
1756    case SEEK_SET:
1757      if(offset<position)
1758      {
1759        if((int)(offset+buffer_pos)>=(int)position)
1760        {
1761          buffer_pos-=position-offset;
1762        }else
1763        {
1764          buffer_size=0;
1765        }
1766        position=offset;
1767      }else if(offset>position)
1768      {
1769        buffer_pos+=(offset-position)-1;
1770        position=offset-1;
1771        unsigned char c;
1772        if(read(&c,1)<1)
1773        {
1774          G_THROW( ByteStream::EndOfFile );
1775        }
1776      }
1777      retval=0;
1778      break;
1779    case SEEK_END:
1780      if(! nothrow)
1781        G_THROW( ERR_MSG("DataPool.seek_backward") );
1782      break;
1783   }
1784   return retval;
1785}
1786
1787void
1788DataPool::close_all(void)
1789{
1790  OpenFiles::get()->close_all();
1791}
1792
1793
1794GP<ByteStream>
1795DataPool::get_stream(void)
1796{
1797  if(data && data->is_static())
1798  {
1799    GCriticalSectionLock lock(&data_lock);
1800    data->seek(0, SEEK_SET);
1801    return data->duplicate(length);
1802  }else
1803  {
1804    return new PoolByteStream(this);
1805  }
1806}
1807
1808
1809inline
1810DataPool::Counter::operator int(void) const
1811{
1812   GCriticalSectionLock lk((GCriticalSection *) &lock);
1813   int cnt=counter;
1814   return cnt;
1815}
1816
1817inline void
1818DataPool::Counter::inc(void)
1819{
1820   GCriticalSectionLock lk(&lock);
1821   counter++;
1822}
1823
1824inline void
1825DataPool::Counter::dec(void)
1826{
1827   GCriticalSectionLock lk(&lock);
1828   counter--;
1829}
1830
1831
1832#ifdef HAVE_NAMESPACES
1833}
1834# ifndef NOT_USING_DJVU_NAMESPACE
1835using namespace DJVU;
1836# endif
1837#endif
Note: See TracBrowser for help on using the repository browser.