source: trunk/libdjvu/DataPool.cpp @ 426

Last change on this file since 426 was 280, checked in by rbri, 12 years ago

DJVU plugin: djvulibre updated to version 3.5.22

File size: 49.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, 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.17 2008/08/05 20:50:35 bpearlmutter Exp $
57// $Name: release_3_5_22 $
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           {
543             if (block_start<start)
544               {
545                 if (block_end>start && block_end<=start+length)
546                   {
547                     list[pos]=-(start-block_start);
548                     list.insert_after(pos, block_end-start);
549                     ++pos;
550                     block_start=start;
551                   } else if (block_end>start+length)
552                   {
553                     list[pos]=-(start-block_start);
554                     list.insert_after(pos, length);
555                     ++pos;
556                     list.insert_after(pos, -(block_end-(start+length)));
557                     ++pos;
558                     block_start=start+length;
559                   }
560               } else if (block_start>=start && block_start<start+length)
561               {
562                 if (block_end<=start+length) list[pos]=abs(size);
563                 else
564                   {
565                     list[pos]=start+length-block_start;
566                     list.insert_after(pos, -(block_end-(start+length)));
567                     ++pos;
568                     block_start=start+length;
569                   }
570               }
571           }
572         block_start=block_end;
573         ++pos;
574      }
575      if (block_end<start)
576      {
577         list.append(-(start-block_end));
578         list.append(length);
579      } else if (block_end<start+length) list.append(start+length-block_end);
580
581         // Now merge adjacent areas with the same sign
582      pos=list;
583      while(pos)
584      {
585         GPosition pos1=pos; ++pos1;
586         while(pos1)
587           {
588             if ( (list[pos]<0 && list[pos1]>0) ||
589                  (list[pos]>0 && list[pos1]<0) )
590               break;
591             list[pos]+=list[pos1];
592             GPosition this_pos=pos1;
593             ++pos1;
594             list.del(this_pos);
595           }
596         pos=pos1;
597      }
598   } // if (length>0)
599}
600
601int
602DataPool::BlockList::get_bytes(int start, int length) const
603      // Returns the number of bytes of data available in the range
604      // [start, start+length[. There may be holes between data chunks
605{
606  DEBUG_MSG("DataPool::BlockList::get_bytes: start=" << start << " length=" << length << "\n");
607  DEBUG_MAKE_INDENT(3);
608
609   if (length<0)
610     G_THROW( ERR_MSG("DataPool.bad_length") );
611
612   GCriticalSectionLock lk((GCriticalSection *) &lock);
613   int bytes=0;
614   int block_start=0, block_end=0;
615   for(GPosition pos=list;pos && block_start<start+length;++pos)
616   {
617      int size=list[pos];
618      block_end=block_start+abs(size);
619      if (size>0)
620        {
621          if (block_start<start)
622            {
623              if (block_end>=start && block_end<start+length)
624                bytes+=block_end-start;
625              else if (block_end>=start+length)
626                bytes+=length;
627            }
628          else
629            {
630              if (block_end<=start+length)
631                bytes+=block_end-block_start;
632              else bytes+=start+length-block_start;
633            }
634        }
635      block_start=block_end;
636   }
637   return bytes;
638}
639
640int
641DataPool::BlockList::get_range(int start, int length) const
642      // Finds a range covering offset=start and returns the length
643      // of intersection of this range with [start, start+length[
644      // 0 is returned if nothing can be found
645{
646  DEBUG_MSG("DataPool::BlockList::get_range: start=" << start << " length=" << length << "\n");
647  DEBUG_MAKE_INDENT(3);
648   if (start<0)
649     G_THROW( ERR_MSG("DataPool.neg_start") );
650   if (length<=0)
651      G_THROW( ERR_MSG("DataPool.bad_length") );
652
653   GCriticalSectionLock lk((GCriticalSection *) &lock);
654   int block_start=0, block_end=0;
655   for(GPosition pos=list;pos && block_start<start+length;++pos)
656     {
657       int size=list[pos];
658       block_end=block_start+abs(size);
659       if (block_start<=start && block_end>start)
660         {
661           if (size<0)
662             return -1;
663           else if (block_end>start+length)
664             return length;
665           else
666             return block_end-start;
667         }
668       block_start=block_end;
669     }
670   return 0;
671}
672
673//****************************************************************************
674//******************************* DataPool ***********************************
675//****************************************************************************
676
677class DataPool::Reader : public GPEnabled
678{
679public:
680   GEvent event;
681   bool reenter_flag;
682   int  offset;
683   int  size;
684   Reader() : reenter_flag(false), offset(0), size(-1){};
685   Reader(int offset_in, int size_in=-1) :
686   reenter_flag(false), offset(offset_in), size(size_in) {};
687   virtual ~Reader() {};
688};
689
690class DataPool::Trigger : public GPEnabled
691{
692public:
693   GSafeFlags disabled;
694   int  start, length;
695//   void (* callback)(GP<GPEnabled> &);
696   void (* callback)(void *);
697//   GP<GPEnabled> cl_data;
698   void *cl_data;
699
700   Trigger() : start(0), length(-1), callback(0), cl_data(0) {};
701   Trigger(int xstart, int xlength,
702//   void (* xcallback)(GP<GPEnabled> &), GP<GPEnabled> xcl_data) :
703   void (* xcallback)(void *), void *xcl_data) :
704      start(xstart), length(xlength), callback(xcallback), cl_data(xcl_data) {};
705   virtual ~Trigger() {};
706};
707
708class DataPool::Counter
709{
710private:
711   int               counter;
712   GCriticalSection  lock;
713public:
714   Counter() : counter(0) {};
715   operator int(void) const;
716   void              inc(void);
717   void              dec(void);
718};
719
720#define DATAPOOL_INIT eof_flag(false),stop_flag(false), \
721    stop_blocked_flag(false), \
722    add_at(0),start(0),length(-1)
723
724void
725DataPool::init(void)
726{
727  DEBUG_MSG("DataPool::init(): Initializing\n");
728  DEBUG_MAKE_INDENT(3);
729  start=0; length=-1; add_at=0;
730  eof_flag=false;
731  stop_flag=false;
732  stop_blocked_flag=false;
733
734  active_readers=new Counter;
735  block_list=0;
736  G_TRY
737  {   
738    block_list=new BlockList;
739    data=ByteStream::create();
740  }
741  G_CATCH_ALL
742  {
743    delete block_list;
744    block_list=0;
745    delete active_readers;
746    active_readers=0;
747    G_RETHROW;
748  }
749  G_ENDCATCH;
750}
751
752DataPool::DataPool(void) : DATAPOOL_INIT {}
753
754GP<DataPool>
755DataPool::create(void)
756{
757  DEBUG_MSG("DataPool::DataPool()\n");
758  DEBUG_MAKE_INDENT(3);
759  DataPool *pool=new DataPool();
760
761  GP<DataPool> retval=pool;
762  pool->init();
763
764      // If we maintain the data ourselves, we want to interpret its
765      // IFF structure to predict its length
766  pool->add_trigger(0, 32, static_trigger_cb, pool);
767  return retval;
768}
769
770GP<DataPool> 
771DataPool::create(const GP<ByteStream> &gstr)
772{
773  DEBUG_MSG("DataPool::create: str="<<(ByteStream *)gstr<<"\n");
774  DEBUG_MAKE_INDENT(3);
775  DataPool *pool=new DataPool();
776  GP<DataPool> retval=pool;
777  pool->init();
778
779  // It's nice to have IFF data analyzed in this case too.
780  pool->add_trigger(0, 32, static_trigger_cb, pool);
781
782  char buffer[1024];
783  int length;
784  while((length=gstr->read(buffer, 1024)))
785    pool->add_data(buffer, length);
786  pool->set_eof();
787
788  return retval;
789}
790
791GP<DataPool>
792DataPool::create(const GP<DataPool> & pool, int start, int length)
793{
794  DEBUG_MSG("DataPool::DataPool: pool=" << (void *)((DataPool *)pool) << " start=" << start << " length= " << length << "\n");
795  DEBUG_MAKE_INDENT(3);
796
797  DataPool *xpool=new DataPool();
798  GP<DataPool> retval=xpool;
799  xpool->init();
800  xpool->connect(pool, start, length);
801  return retval;
802}
803
804GP<DataPool>
805DataPool::create(const GURL &furl, int start, int length)
806{
807  DEBUG_MSG("DataPool::DataPool: furl='" << furl << "' start=" << start << " length= " << length << "\n");
808  DEBUG_MAKE_INDENT(3);
809
810  GP<DataPool> retval=FCPools::get()->get_pool(furl,start,length);
811  if(! retval)
812  {
813    DataPool *pool=new DataPool();
814    retval=pool;
815    pool->init();
816    pool->connect(furl, start, length);
817  }
818  return retval;
819}
820
821void
822DataPool::clear_stream(const bool release)
823{
824  DEBUG_MSG("DataPool::clear_stream()\n");
825  DEBUG_MAKE_INDENT(3);
826  if(fstream)
827  {
828    GCriticalSectionLock lock1(&class_stream_lock);
829    GP<OpenFiles_File> f=fstream;
830    if(f)
831    {
832      GCriticalSectionLock lock2(&(f->stream_lock));
833      fstream=0;
834      if(release)
835        OpenFiles::get()->stream_released(f->stream, this);
836    }
837  }
838}
839
840DataPool::~DataPool(void)
841{
842  DEBUG_MSG("DataPool::~DataPool()\n");
843  DEBUG_MAKE_INDENT(3);
844
845  clear_stream(true);
846  if (furl.is_local_file_url())
847    if (this->get_count() > 1)
848      FCPools::get()->del_pool(furl, this);
849 
850  GP<DataPool> pool = this->pool;
851  {
852         // Wait until the static_trigger_cb() exits
853      GCriticalSectionLock lock(&trigger_lock);
854      if (pool)
855        pool->del_trigger(static_trigger_cb, this);
856      del_trigger(static_trigger_cb, this);
857  }
858  if (pool)
859  {
860      GCriticalSectionLock lock(&triggers_lock);
861      for(GPosition pos=triggers_list;pos;++pos)
862      {
863         GP<Trigger> trigger=triggers_list[pos];
864         pool->del_trigger(trigger->callback, trigger->cl_data);
865      }
866  }
867  delete block_list;
868  delete active_readers;
869}
870
871void
872DataPool::connect(const GP<DataPool> & pool_in, int start_in, int length_in)
873{
874   DEBUG_MSG("DataPool::connect(): connecting to another DataPool\n");
875   DEBUG_MAKE_INDENT(3);
876   
877   if (pool) G_THROW( ERR_MSG("DataPool.connected1") );
878   if (furl.is_local_file_url()) G_THROW( ERR_MSG("DataPool.connected2") );
879   if (start_in<0) G_THROW( ERR_MSG("DataPool.neg_start") );
880
881   pool=pool_in;
882   start=start_in;
883   length=length_in;
884
885      // The following will work for length<0 too
886   if (pool->has_data(start, length))
887     eof_flag=true;
888   else
889     pool->add_trigger(start, length, static_trigger_cb, this);
890
891   data=0;
892
893   wake_up_all_readers();
894   
895      // Pass registered trigger callbacks to the DataPool
896   GCriticalSectionLock lock(&triggers_lock);
897   for(GPosition pos=triggers_list;pos;++pos)
898   {
899      GP<Trigger> t=triggers_list[pos];
900      int tlength=t->length;
901      if (tlength<0 && length>0)
902        tlength=length-t->start;
903      pool->add_trigger(start+t->start, tlength, t->callback, t->cl_data);
904   }
905}
906
907void
908DataPool::connect(const GURL &furl_in, int start_in, int length_in)
909{
910   DEBUG_MSG("DataPool::connect(): connecting to a file\n");
911   DEBUG_MAKE_INDENT(3);
912   
913   if (pool)
914     G_THROW( ERR_MSG("DataPool.connected1") );
915   if (furl.is_local_file_url())
916     G_THROW( ERR_MSG("DataPool.connected2") );
917   if (start_in<0)
918     G_THROW( ERR_MSG("DataPool.neg_start") );
919
920
921   if (furl_in.name() == "-")
922   {
923      DEBUG_MSG("This is stdin => just read the data...\n");
924      DEBUG_MAKE_INDENT(3);
925      char buffer[1024];
926      int length;
927      GP<ByteStream> gstr=ByteStream::create(furl_in, "rb");
928      ByteStream &str=*gstr;
929      while((length=str.read(buffer, 1024)))
930         add_data(buffer, length);
931      set_eof();
932   } else if(furl_in.is_local_file_url())
933   {
934         // Open the stream (just in this function) too see if
935         // the file is accessible. In future we will be using 'OpenFiles'
936         // to request and release streams
937      GP<ByteStream> str=ByteStream::create(furl_in,"rb");
938      str->seek(0, SEEK_END);
939      int file_size=str->tell();
940
941      furl=furl_in;
942      start=start_in;
943      length=length_in;
944      if (start>=file_size)
945        length=0;
946      else if (length<0 || start+length>=file_size)
947        length=file_size-start;
948     
949      eof_flag=true;
950      data=0;
951     
952      FCPools::get()->add_pool(furl, this);
953
954      wake_up_all_readers();
955   
956         // Call every trigger callback
957      GCriticalSectionLock lock(&triggers_lock);
958      for(GPosition pos=triggers_list;pos;++pos)
959      {
960         GP<Trigger> t=triggers_list[pos];
961         call_callback(t->callback, t->cl_data);
962      }
963      triggers_list.empty();
964   }
965}
966
967int
968DataPool::get_length(void) const
969{
970      // Not connected and length has been guessed
971      // Or connected to a file
972      // Or connected to a pool, but length was preset
973   int retval=(-1);
974   if (length>=0) 
975   {
976     retval=length;
977   }else if (pool)
978   {
979      int plength=pool->get_length();
980      if (plength>=0)
981        retval=plength-start;
982   }
983   return retval;
984}
985
986int
987DataPool::get_size(int dstart, int dlength) const
988{
989   if (dlength<0 && length>0)
990   {
991      dlength=length-dstart;
992      if (dlength<0) return 0;
993   }
994   
995   GP<DataPool> pool = this->pool;
996   if (pool) 
997     return pool->get_size(start+dstart, dlength);
998   else if (furl.is_local_file_url())
999   {
1000      if (start+dstart+dlength>length) return length-(start+dstart);
1001      else return dlength;
1002   } else
1003   {
1004      if (dlength<0)
1005      {
1006         GCriticalSectionLock lock((GCriticalSection *) &data_lock);
1007         dlength=data->size()-dstart;
1008      }
1009      return (dlength<0)?0:(block_list->get_bytes(dstart, dlength));
1010   }
1011}
1012
1013void
1014DataPool::add_data(const void * buffer, int size)
1015      // This function adds data sequentially at 'add_at' position
1016{
1017   DEBUG_MSG("DataPool::add_data(): adding " << size << " bytes of data...\n");
1018   DEBUG_MAKE_INDENT(3);
1019
1020   add_data(buffer, add_at, size);
1021   add_at+=size;
1022}
1023
1024void
1025DataPool::add_data(const void * buffer, int offset, int size)
1026{
1027   DEBUG_MSG("DataPool::add_data(): adding " << size << " bytes at pos=" <<
1028             offset << "...\n");
1029   DEBUG_MAKE_INDENT(3);
1030
1031   if (furl.is_local_file_url() || pool)
1032      G_THROW( ERR_MSG("DataPool.add_data") );
1033   
1034      // Add data to the data storage
1035   {
1036      GCriticalSectionLock lock(&data_lock);
1037      if (offset>data->size())
1038      {
1039         char ch=0;
1040         data->seek(0, SEEK_END);
1041         for(int i=data->size();i<offset;i++)
1042            data->write(&ch, 1);
1043      } else
1044      {
1045         data->seek(offset, SEEK_SET);
1046         data->writall(buffer, size);
1047      }
1048   }
1049
1050   added_data(offset, size);
1051}
1052
1053void
1054DataPool::added_data(const int offset, const int size)
1055{
1056     // Modify map of blocks
1057  block_list->add_range(offset, size);
1058   
1059     // Wake up all threads, which may be waiting for this data
1060  {
1061    GCriticalSectionLock lock(&readers_lock);
1062    for(GPosition pos=readers_list;pos;++pos)
1063    {
1064      GP<Reader> reader=readers_list[pos];
1065      if (block_list->get_bytes(reader->offset, 1))
1066      {
1067        DEBUG_MSG("waking up reader: offset=" << reader->offset <<
1068          ", size=" << reader->size << "\n");
1069        DEBUG_MAKE_INDENT(3);
1070        reader->event.set();
1071      }
1072    }
1073  }
1074
1075    // And call triggers
1076  check_triggers();
1077
1078      // Do not undo the following two lines. The reason why we need them
1079      // here is the connected DataPools, which use 'length' (more exactly
1080      // has_data()) to see if they have all data required. So, right after
1081      // all data has been added to the master DataPool, but before EOF
1082      // is set, the master and slave DataPools disagree regarding if
1083      // all data is there or not. These two lines solve the problem
1084  GCriticalSectionLock lock(&data_lock);
1085  if (length>=0 && data->size()>=length)
1086    set_eof();
1087}
1088
1089bool
1090DataPool::has_data(int dstart, int dlength)
1091{
1092   if (dlength<0 && length>0)
1093     dlength=length-dstart;
1094   return (pool?(pool->has_data(start+dstart, dlength))
1095     :((furl.is_local_file_url())?(start+dstart+dlength<=length)
1096       :((dlength<0)?is_eof()
1097         :(block_list->get_bytes(dstart, dlength)==dlength))));
1098}
1099
1100int
1101DataPool::get_data(void * buffer, int offset, int sz)
1102{
1103   return get_data(buffer, offset, sz, 0);
1104}
1105
1106class DataPool::Incrementor
1107{
1108private:
1109   Counter      & counter;
1110public:
1111   Incrementor(Counter & xcounter) : counter(xcounter) {counter.inc();}
1112   ~Incrementor() {counter.dec();}
1113};
1114
1115int
1116DataPool::get_data(void * buffer, int offset, int sz, int level)
1117{
1118   DEBUG_MSG("DataPool::get_data()\n");
1119   DEBUG_MAKE_INDENT(3);
1120   Incrementor inc(*active_readers);
1121   
1122   if (stop_flag)
1123     G_THROW( DataPool::Stop );
1124   if (stop_blocked_flag && !is_eof() &&
1125       !has_data(offset, sz))
1126     G_THROW( DataPool::Stop );
1127   
1128   if (sz < 0)
1129     G_THROW( ERR_MSG("DataPool.bad_size") );
1130   
1131   if (! sz)
1132     return 0;
1133
1134   GP<DataPool> pool = this->pool;
1135   if (pool)
1136     {
1137       DEBUG_MSG("DataPool::get_data(): from pool\n");
1138       DEBUG_MAKE_INDENT(3);
1139       int retval=0;
1140       if (length>0 && offset+sz>length)
1141         sz=length-offset;
1142       if (sz<0)
1143         sz=0;
1144       for(;;)
1145         {
1146           // Ask the underlying (master) DataPool for data. Note, that
1147           // master DataPool may throw the "DATA_POOL_REENTER" exception
1148           // demanding all readers to restart. This happens when
1149           // a DataPool in the chain of DataPools stops. All readers
1150           // should return to the most upper level and then reenter the
1151           // DataPools hierarchy. Some of them will be stopped by
1152           // DataPool::Stop exception.
1153           G_TRY
1154             {
1155               if(stop_flag
1156                  || (stop_blocked_flag && !is_eof() && !has_data(offset, sz)))
1157                 G_THROW( DataPool::Stop );
1158               retval=pool->get_data(buffer, start+offset, sz, level+1);
1159             }
1160           G_CATCH(exc)
1161           {
1162             pool->clear_stream(true);
1163             if ((exc.get_cause() != GUTF8String(ERR_MSG("DataPool.reenter")))
1164                 || level)
1165               G_RETHROW;
1166           } G_ENDCATCH;
1167           pool->clear_stream(true);
1168           return retval;
1169         }
1170     }
1171   else if (furl.is_local_file_url())
1172     {
1173       DEBUG_MSG("DataPool::get_data(): from file\n");
1174       DEBUG_MAKE_INDENT(3);
1175       if (length>0 && offset+sz>length)
1176         sz=length-offset;
1177       if (sz<0)
1178         sz=0;
1179       
1180       GP<OpenFiles_File> f=fstream;
1181       if (!f)
1182         {
1183           GCriticalSectionLock lock(&class_stream_lock);
1184           f=fstream;
1185           if(!f)
1186             {
1187               fstream=f=OpenFiles::get()->request_stream(furl, this);
1188             }
1189         }
1190       GCriticalSectionLock lock2(&(f->stream_lock));
1191       f->stream->seek(start+offset, SEEK_SET); 
1192       return f->stream->readall(buffer, sz);
1193     } 
1194   else
1195     {
1196       DEBUG_MSG("DataPool::get_data(): direct\n");
1197       DEBUG_MAKE_INDENT(3);
1198       // We're not connected to anybody => handle the data
1199       int size=block_list->get_range(offset, sz);
1200       if (size>0)
1201         {
1202           // Hooray! Some data is there
1203           GCriticalSectionLock lock(&data_lock);
1204           data->seek(offset, SEEK_SET);
1205           return data->readall(buffer, size);
1206         }
1207       
1208       // No data available.
1209       
1210       // If there is no data and nothing else is expected, we can do
1211       // two things: throw ByteStream::EndOfFile exception or return ZERO bytes.
1212       // The exception is for the cases when the data flow has been
1213       // terminated in the middle. ZERO bytes is for regular read() beyond
1214       // the boundaries of legal data. The problem is to distinguish
1215       // these two cases. We do it here with the help of analysis of the
1216       // IFF structure of the data (which sets the 'length' variable).
1217       // If we attempt to read beyond the [0, length[, ZERO bytes will be
1218       // returned. Otherwise an ByteStream::EndOfFile exception will be thrown.
1219       if (eof_flag)
1220         {
1221           if (length>0 && offset<length) 
1222             {
1223               G_THROW( ByteStream::EndOfFile );
1224             }
1225           else 
1226             {
1227               return 0;
1228             }
1229         } 
1230       // Some data is still expected => add this reader to the
1231       // list of readers and call virtual wait_for_data()
1232       DEBUG_MSG("DataPool::get_data(): There is no data in the pool.\n");
1233       DEBUG_MSG("offset=" << offset << ", size=" << sz <<
1234                 ", data_size=" << data->size() << "\n");
1235       GP<Reader> reader=new Reader(offset, sz);
1236       G_TRY
1237         {
1238           {
1239             GCriticalSectionLock slock(&readers_lock);
1240             readers_list.append(reader);
1241           }
1242           wait_for_data(reader);
1243         } 
1244       G_CATCH_ALL
1245         {
1246           {
1247             GCriticalSectionLock slock(&readers_lock);
1248             GPosition pos;
1249             if (readers_list.search(reader, pos)) readers_list.del(pos);
1250           }
1251           G_RETHROW;
1252         } 
1253       G_ENDCATCH;
1254       
1255       {
1256         GCriticalSectionLock slock(&readers_lock);
1257         GPosition pos;
1258         if (readers_list.search(reader, pos)) readers_list.del(pos);
1259       }
1260       
1261       // This call to get_data() should return immediately as there MUST
1262       // be data in the buffer after wait_for_data(reader) returns
1263       // or eof_flag should be TRUE
1264       return get_data(buffer, reader->offset, reader->size, level);
1265     }
1266   return 0;
1267}
1268
1269void
1270DataPool::wait_for_data(const GP<Reader> & reader)
1271      // This function may NOT return until there is some data for the
1272      // given reader in the internal buffer
1273{
1274   DEBUG_MSG("DataPool::wait_for_data(): waiting for data at offset=" << reader->offset <<
1275             ", length=" << reader->size << "\n");
1276   DEBUG_MAKE_INDENT(3);
1277
1278#if THREADMODEL==NOTHREADS
1279   G_THROW( ERR_MSG("DataPool.no_threadless") );
1280#else
1281   for(;;)
1282   {
1283      if (stop_flag)
1284        G_THROW( DataPool::Stop );
1285      if (reader->reenter_flag)
1286        G_THROW( ERR_MSG("DataPool.reenter") );
1287      if (eof_flag || block_list->get_bytes(reader->offset, 1))
1288        return;
1289      if (pool || furl.is_local_file_url())
1290        return;
1291
1292      if (stop_blocked_flag)
1293        G_THROW( DataPool::Stop );
1294
1295      DEBUG_MSG("calling event.wait()...\n");
1296      reader->event.wait();
1297   }
1298#endif
1299   
1300   DEBUG_MSG("Got some data to read\n");
1301}
1302
1303void
1304DataPool::wake_up_all_readers(void)
1305{
1306   DEBUG_MSG("DataPool::wake_up_all_readers(): waking up all readers\n");
1307   DEBUG_MAKE_INDENT(3);
1308
1309   GCriticalSectionLock lock(&readers_lock);
1310   for(GPosition pos=readers_list;pos;++pos)
1311      readers_list[pos]->event.set();
1312}
1313
1314void
1315DataPool::set_eof(void)
1316      // Has no effect on connected DataPools
1317{
1318   if (!furl.is_local_file_url() && !pool)
1319   {
1320      eof_flag=true;
1321     
1322         // Can we set the length now?
1323      if (length<0)
1324      {
1325         GCriticalSectionLock lock(&data_lock);
1326         length=data->size();
1327      }
1328
1329         // Wake up all readers to let them rescan the flags
1330      wake_up_all_readers();
1331   
1332         // Activate all trigger callbacks with negative threshold
1333      check_triggers();
1334   }
1335}
1336
1337void
1338DataPool::stop(bool only_blocked)
1339{
1340   DEBUG_MSG("DataPool::stop(): Stopping this and dependent DataPools, only_blocked="
1341             << only_blocked << "\n");
1342   DEBUG_MAKE_INDENT(3);
1343
1344   if (only_blocked) stop_blocked_flag=true;
1345   else stop_flag=true;
1346   
1347
1348   wake_up_all_readers();
1349
1350      // Now let all readers, which already go thru to the master DataPool,
1351      // come back and reenter. While reentering some of them will go
1352      // thru this DataPool again and will be stopped (DataPool::Stop exception)
1353      // Others (which entered the master DataPool thru other slave DataPools)
1354      // will simply continue waiting for their data.
1355   GP<DataPool> pool = this->pool;
1356   if (pool)
1357   {
1358         // This loop is necessary because there may be another thread, which
1359         // is going down thru the DataPool chain and did not reach the
1360         // lowest "master" DataPool yet. Since it didn't reach it yet,
1361         // the "pool->restart_readers()" will not restart it. So we're going
1362         // to continue issuing this command until we get rid of all
1363         // "active_readers"
1364      while(*active_readers)
1365      {
1366#if (THREADMODEL==COTHREADS) || (THREADMODEL==MACTHREADS)
1367         GThread::yield();
1368#endif
1369         pool->restart_readers();
1370      }
1371   }
1372}
1373
1374void
1375DataPool::restart_readers(void)
1376{
1377   DEBUG_MSG("DataPool::restart_readers(): telling all readers to reenter\n");
1378   DEBUG_MAKE_INDENT(3);
1379   
1380   GCriticalSectionLock slock(&readers_lock);
1381   for(GPosition pos=readers_list;pos;++pos)
1382   {
1383      GP<Reader> reader=readers_list[pos];
1384      reader->reenter_flag=true;
1385      reader->event.set();
1386   }
1387     
1388   if (pool)
1389     pool->restart_readers();
1390}
1391
1392void
1393DataPool::load_file(void)
1394{
1395   DEBUG_MSG("DataPool::load_file() called\n");
1396   DEBUG_MAKE_INDENT(3);
1397
1398   if (pool)
1399   {
1400      DEBUG_MSG("passing the request down.\n");
1401      pool->load_file();
1402   } else if (furl.is_local_file_url())
1403   {
1404      DEBUG_MSG("loading the data from \""<<(const char *)furl<<"\".\n");
1405
1406      GCriticalSectionLock lock1(&class_stream_lock);
1407      GP<OpenFiles_File> f=fstream;
1408      if (!f)
1409      {
1410        fstream=f=OpenFiles::get()->request_stream(furl, this);
1411      }
1412      {  // Scope to de-allocate lock2 before stream gets released
1413         GCriticalSectionLock lock2(&(f->stream_lock));
1414
1415         data=ByteStream::create();
1416         block_list->clear();
1417         FCPools::get()->del_pool(furl, this);
1418         furl=GURL();
1419
1420         const GP<ByteStream> gbs = f->stream;
1421         gbs->seek(0, SEEK_SET);
1422         
1423         char buffer[1024];
1424         int length;
1425         while((length = f->stream->read(buffer, 1024)))
1426           add_data(buffer, length);
1427         set_eof();
1428         
1429         OpenFiles::get()->stream_released(f->stream, this);
1430      }
1431      fstream=0;
1432   } else DEBUG_MSG("Not connected\n");
1433}
1434
1435void
1436DataPool::load_file(const GURL &url )
1437{
1438   FCPools::get()->load_file(url);
1439}
1440
1441void
1442DataPool::check_triggers(void)
1443      // This function is for not connected DataPools only
1444{
1445  DEBUG_MSG("DataPool::check_triggers(): calling activated trigger callbacks.\n");
1446  DEBUG_MAKE_INDENT(3);
1447
1448  if (!pool && !furl.is_local_file_url())
1449    while(true)
1450      {
1451        GP<Trigger> trigger;
1452
1453        // First find a candidate (trigger, which needs to be called)
1454        // Don't remove it from the list yet. del_trigger() should
1455        // be able to find it if necessary and disable.
1456        {
1457          GCriticalSectionLock list_lock(&triggers_lock);
1458          for(GPosition pos=triggers_list;pos;++pos)
1459            {
1460              GP<Trigger> t=triggers_list[pos];
1461              if (is_eof()
1462                  || (t->length>=0 &&
1463                      block_list->get_bytes(t->start, t->length)==t->length))
1464                {
1465                  trigger=t;
1466                  break;
1467                }
1468            }
1469        }
1470
1471        if (trigger)
1472          {
1473            // Now check that the trigger is not disabled
1474            // and lock the trigger->disabled lock for the duration
1475            // of the trigger. This will block the del_trigger() and
1476            // will postpone client's destruction (usually following
1477            // the call to del_trigger())
1478            {
1479              GMonitorLock lock(&trigger->disabled);
1480              if (!trigger->disabled)
1481                call_callback(trigger->callback, trigger->cl_data);
1482            }
1483
1484            // Finally - remove the trigger from the list.
1485            GCriticalSectionLock list_lock(&triggers_lock);
1486            for(GPosition pos=triggers_list;pos;++pos)
1487              if (triggers_list[pos]==trigger)
1488                {
1489                  triggers_list.del(pos);
1490                  break;
1491                }
1492          } else break;
1493      }
1494}
1495
1496void
1497DataPool::add_trigger(int thresh, void (* callback)(void *), void * cl_data)
1498{
1499  if (thresh>=0)
1500    add_trigger(0, thresh+1, callback, cl_data);
1501  else
1502    add_trigger(0, -1, callback, cl_data);
1503}
1504
1505void
1506DataPool::add_trigger(int tstart, int tlength,
1507                      void (* callback)(void *), void * cl_data)
1508{
1509   DEBUG_MSG("DataPool::add_trigger(): start=" << tstart <<
1510             ", length=" << tlength << ", func=" << (void *) callback << "\n");
1511   DEBUG_MAKE_INDENT(3);
1512   
1513   if (callback)
1514   {
1515      if (is_eof())
1516      {
1517        call_callback(callback, cl_data);
1518      }else
1519      {
1520         GP<DataPool> pool = this->pool;
1521         if (pool)
1522         {
1523               // We're connected to a DataPool
1524               // Just pass the triggers down remembering it in the list
1525            if (tlength<0 && length>0) tlength=length-tstart;
1526            GP<Trigger> trigger=new Trigger(tstart, tlength, callback, cl_data);
1527            pool->add_trigger(start+tstart, tlength, callback, cl_data);
1528            GCriticalSectionLock lock(&triggers_lock);
1529            triggers_list.append(trigger);
1530         } 
1531         else if (!furl.is_local_file_url())
1532         {
1533               // We're not connected to anything and maintain our own data
1534            if (tlength>=0 && block_list->get_bytes(tstart, tlength)==tlength)
1535               call_callback(callback, cl_data);
1536            else
1537            {
1538              GP<Trigger> trigger=new Trigger(tstart, tlength, callback, cl_data);
1539              GCriticalSectionLock lock(&triggers_lock);
1540              triggers_list.append(trigger);
1541            }
1542         }
1543      }
1544   }
1545}
1546
1547void
1548DataPool::del_trigger(void (* callback)(void *), void * cl_data)
1549{
1550   DEBUG_MSG("DataPool::del_trigger(): func=" << (void *) callback << "\n");
1551   DEBUG_MAKE_INDENT(3);
1552
1553   for(;;)
1554   {
1555      GP<Trigger> trigger;
1556      {
1557         GCriticalSectionLock lock(&triggers_lock);
1558         for(GPosition pos=triggers_list;pos;)
1559         {
1560            GP<Trigger> t=triggers_list[pos];
1561            if (t->callback==callback && t->cl_data==cl_data)
1562            {
1563               trigger=t;
1564               GPosition this_pos=pos;
1565               ++pos;
1566               triggers_list.del(this_pos);
1567               break;
1568            } else
1569              ++pos;
1570         }
1571      }
1572
1573         // Above we removed the trigger from the list and unlocked the list
1574         // Now we will disable it and will wait if necessary (if the
1575         // trigger is currently being processed by check_triggers())
1576         // check_triggers() locks the trigger for the duration of the
1577         // trigger callback. Thus we will wait for the trigger callback
1578         // to finish and avoid client's destruction.
1579      if (trigger)
1580        trigger->disabled=1;
1581      else
1582        break;
1583   }
1584
1585   GP<DataPool> pool = this->pool;
1586   if (pool)
1587     pool->del_trigger(callback, cl_data);
1588}
1589
1590void
1591// DataPool::static_trigger_cb(GP<GPEnabled> &cl_data)
1592DataPool::static_trigger_cb(void *cl_data)
1593{
1594//  GP<DataPool> d=(DataPool *)(GPEnabled *)cl_data;
1595  GP<DataPool> d=(DataPool *)cl_data;
1596  d->trigger_cb();
1597}
1598
1599void
1600DataPool::trigger_cb(void)
1601      // This function may be triggered by the DataPool, which we're
1602      // connected to, or by ourselves, if we're connected to nothing
1603{
1604      // Don't want to be destroyed while I'm here. Can't use GP<> life saver
1605      // because it may be called from the constructor
1606   GCriticalSectionLock lock(&trigger_lock);
1607   
1608   DEBUG_MSG("DataPool::trigger_cb() called\n");
1609   DEBUG_MAKE_INDENT(3);
1610
1611   GP<DataPool> pool = this->pool;
1612   if (pool)
1613   {
1614      // Connected to a pool
1615      // We may be here when either EOF is set on the master DataPool
1616      // Or when it may have learnt its length (from IFF or whatever)
1617      if (pool->is_eof() || pool->has_data(start, length)) eof_flag=true;
1618   } else if (!furl.is_local_file_url())
1619   {
1620            // Not connected to anything => Try to guess the length
1621      if (length<0) analyze_iff();
1622     
1623            // Failed to analyze? Check, maybe it's EOF already
1624      if (length<0 && is_eof())
1625      {
1626               GCriticalSectionLock lock(&data_lock);
1627               length=data->size();
1628      }
1629   }
1630}
1631
1632void
1633DataPool::analyze_iff(void)
1634      // In order to display decode progress properly, we need to know
1635      // the size of the data. It's trivial to figure it out if is_eof()
1636      // is true. Otherwise we need to make a prediction. Luckily all
1637      // DjVuFiles have IFF structure, which makes it possible to do it.
1638      // If due to some reason we fail, the length will remain -1.
1639{
1640   DEBUG_MSG("DataPool::analyze_iff(): Trying to decode IFF structure of " << furl << ".\n");
1641   DEBUG_MSG("in order to predict the DataPool's size\n");
1642   DEBUG_MAKE_INDENT(3);
1643
1644   GP<ByteStream> str=get_stream();
1645   
1646   GP<IFFByteStream> giff=IFFByteStream::create(str);
1647   IFFByteStream &iff=*giff;
1648   GUTF8String chkid;
1649   int size;
1650   if ((size=iff.get_chunk(chkid)) && size>=0)
1651   {
1652      length=size+iff.tell()-4;
1653      DEBUG_MSG("Got size=" << size << ", length=" << length << "\n");
1654   }
1655}
1656
1657
1658
1659
1660
1661//****************************************************************************
1662//****************************** PoolByteStream ******************************
1663//****************************************************************************
1664
1665// This is an internal ByteStream receiving data from the associated DataPool.
1666// It's just a sequential interface, nothing more. All the job for data
1667// retrieval, waiting and thread synchronization is done by DataPool
1668
1669class PoolByteStream : public ByteStream
1670{
1671public:
1672   PoolByteStream(GP<DataPool> data_pool);
1673   virtual ~PoolByteStream() {};
1674
1675   virtual size_t read(void *buffer, size_t size);
1676   virtual size_t write(const void *buffer, size_t size);
1677   virtual long tell(void) const ;
1678   virtual int seek(long offset, int whence = SEEK_SET, bool nothrow=false);
1679private:
1680      // Don't make data_pool GP<>. The problem is that DataPool creates
1681      // and soon destroys this ByteStream from the constructor. Since
1682      // there are no other pointers to the DataPool created yet, it becomes
1683      // destroyed immediately :(
1684   DataPool             * data_pool;
1685   GP<DataPool>         data_pool_lock;
1686   long                 position;
1687   
1688   char                 buffer[512];
1689   size_t               buffer_size;
1690   size_t               buffer_pos;
1691
1692      // Cancel C++ default stuff
1693   PoolByteStream & operator=(const PoolByteStream &);
1694};
1695
1696inline
1697PoolByteStream::PoolByteStream(GP<DataPool> xdata_pool) :
1698   data_pool(xdata_pool), position(0), buffer_size(0), buffer_pos(0)
1699{
1700   if (!data_pool) 
1701       G_THROW( ERR_MSG("DataPool.zero_DataPool") );
1702
1703      // Secure the DataPool if possible. If we're called from DataPool
1704      // constructor (get_count()==0) there is no need to secure at all.
1705   if (data_pool->get_count()) data_pool_lock=data_pool;
1706}
1707
1708size_t
1709PoolByteStream::read(void *data, size_t size)
1710{
1711  if (buffer_pos >= buffer_size) {
1712    if (size >= sizeof(buffer)) {
1713      // Direct read
1714      size = data_pool->get_data(data, position, size);
1715      position += size;
1716      return size;
1717    } else {
1718      // Refill buffer
1719      buffer_size = data_pool->get_data(buffer, position, sizeof(buffer));
1720      buffer_pos=0;
1721    }
1722  }
1723  if (buffer_pos + size >= buffer_size)
1724    size = buffer_size - buffer_pos;
1725  memcpy(data, buffer+buffer_pos, size);
1726  buffer_pos += size;
1727  position += size;
1728  return size;
1729}
1730
1731size_t
1732PoolByteStream::write(const void *buffer, size_t size)
1733{
1734   G_THROW( ERR_MSG("not_implemented_n") "\tPoolByteStream::write()");   //  PoolByteStream::write() is not implemented.
1735   return 0;    // For compiler not to bark
1736}
1737
1738long
1739PoolByteStream::tell(void) const
1740{
1741   return position;
1742}
1743
1744int
1745PoolByteStream::seek(long offset, int whence, bool nothrow)
1746{
1747  int retval=(-1);
1748  switch(whence)
1749  {
1750    case SEEK_CUR:
1751      offset+=position;
1752      // fallthrough;
1753    case SEEK_SET:
1754      if(offset<position)
1755      {
1756        if((int)(offset+buffer_pos)>=(int)position)
1757        {
1758          buffer_pos-=position-offset;
1759        }else
1760        {
1761          buffer_size=0;
1762        }
1763        position=offset;
1764      }else if(offset>position)
1765      {
1766        buffer_pos+=(offset-position)-1;
1767        position=offset-1;
1768        unsigned char c;
1769        if(read(&c,1)<1)
1770        {
1771          G_THROW( ByteStream::EndOfFile );
1772        }
1773      }
1774      retval=0;
1775      break;
1776    case SEEK_END:
1777      if(! nothrow)
1778        G_THROW( ERR_MSG("DataPool.seek_backward") );
1779      break;
1780   }
1781   return retval;
1782}
1783
1784void
1785DataPool::close_all(void)
1786{
1787  OpenFiles::get()->close_all();
1788}
1789
1790
1791GP<ByteStream>
1792DataPool::get_stream(void)
1793{
1794  return new PoolByteStream(this);
1795}
1796
1797
1798inline
1799DataPool::Counter::operator int(void) const
1800{
1801   GCriticalSectionLock lk((GCriticalSection *) &lock);
1802   int cnt=counter;
1803   return cnt;
1804}
1805
1806inline void
1807DataPool::Counter::inc(void)
1808{
1809   GCriticalSectionLock lk(&lock);
1810   counter++;
1811}
1812
1813inline void
1814DataPool::Counter::dec(void)
1815{
1816   GCriticalSectionLock lk(&lock);
1817   counter--;
1818}
1819
1820
1821#ifdef HAVE_NAMESPACES
1822}
1823# ifndef NOT_USING_DJVU_NAMESPACE
1824using namespace DJVU;
1825# endif
1826#endif
Note: See TracBrowser for help on using the repository browser.