source: trunk/libdjvu/DataPool.cpp @ 206

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

DJVU plugin: djvulibre updated to version 3.5.19

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