Blame libdjvu/DataPool.cpp

Packit df99a1
//C-  -*- C++ -*-
Packit df99a1
//C- -------------------------------------------------------------------
Packit df99a1
//C- DjVuLibre-3.5
Packit df99a1
//C- Copyright (c) 2002  Leon Bottou and Yann Le Cun.
Packit df99a1
//C- Copyright (c) 2001  AT&T
Packit df99a1
//C-
Packit df99a1
//C- This software is subject to, and may be distributed under, the
Packit df99a1
//C- GNU General Public License, either Version 2 of the license,
Packit df99a1
//C- or (at your option) any later version. The license should have
Packit df99a1
//C- accompanied the software or you may obtain a copy of the license
Packit df99a1
//C- from the Free Software Foundation at http://www.fsf.org .
Packit df99a1
//C-
Packit df99a1
//C- This program is distributed in the hope that it will be useful,
Packit df99a1
//C- but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit df99a1
//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit df99a1
//C- GNU General Public License for more details.
Packit df99a1
//C- 
Packit df99a1
//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library from
Packit df99a1
//C- Lizardtech Software.  Lizardtech Software has authorized us to
Packit df99a1
//C- replace the original DjVu(r) Reference Library notice by the following
Packit df99a1
//C- text (see doc/lizard2002.djvu and doc/lizardtech2007.djvu):
Packit df99a1
//C-
Packit df99a1
//C-  ------------------------------------------------------------------
Packit df99a1
//C- | DjVu (r) Reference Library (v. 3.5)
Packit df99a1
//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved.
Packit df99a1
//C- | The DjVu Reference Library is protected by U.S. Pat. No.
Packit df99a1
//C- | 6,058,214 and patents pending.
Packit df99a1
//C- |
Packit df99a1
//C- | This software is subject to, and may be distributed under, the
Packit df99a1
//C- | GNU General Public License, either Version 2 of the license,
Packit df99a1
//C- | or (at your option) any later version. The license should have
Packit df99a1
//C- | accompanied the software or you may obtain a copy of the license
Packit df99a1
//C- | from the Free Software Foundation at http://www.fsf.org .
Packit df99a1
//C- |
Packit df99a1
//C- | The computer code originally released by LizardTech under this
Packit df99a1
//C- | license and unmodified by other parties is deemed "the LIZARDTECH
Packit df99a1
//C- | ORIGINAL CODE."  Subject to any third party intellectual property
Packit df99a1
//C- | claims, LizardTech grants recipient a worldwide, royalty-free, 
Packit df99a1
//C- | non-exclusive license to make, use, sell, or otherwise dispose of 
Packit df99a1
//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the 
Packit df99a1
//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU 
Packit df99a1
//C- | General Public License.   This grant only confers the right to 
Packit df99a1
//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to 
Packit df99a1
//C- | the extent such infringement is reasonably necessary to enable 
Packit df99a1
//C- | recipient to make, have made, practice, sell, or otherwise dispose 
Packit df99a1
//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to 
Packit df99a1
//C- | any greater extent that may be necessary to utilize further 
Packit df99a1
//C- | modifications or combinations.
Packit df99a1
//C- |
Packit df99a1
//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY
Packit df99a1
//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
Packit df99a1
//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF
Packit df99a1
//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
Packit df99a1
//C- +------------------------------------------------------------------
Packit df99a1
Packit df99a1
#ifdef HAVE_CONFIG_H
Packit df99a1
# include "config.h"
Packit df99a1
#endif
Packit df99a1
#if NEED_GNUG_PRAGMAS
Packit df99a1
# pragma implementation
Packit df99a1
#endif
Packit df99a1
Packit df99a1
#include "DataPool.h"
Packit df99a1
#include "IFFByteStream.h"
Packit df99a1
#include "GString.h"
Packit df99a1
#include "GOS.h"
Packit df99a1
#include "GURL.h"
Packit df99a1
#include "debug.h"
Packit df99a1
Packit df99a1
#ifndef macintosh
Packit df99a1
# include <sys/types.h>
Packit df99a1
#endif
Packit df99a1
Packit df99a1
#ifdef HAVE_NAMESPACES
Packit df99a1
namespace DJVU {
Packit df99a1
# ifdef NOT_DEFINED // Just to fool emacs c++ mode
Packit df99a1
}
Packit df99a1
#endif
Packit df99a1
#endif
Packit df99a1
Packit df99a1
const char * DataPool::Stop = ERR_MSG("STOP");
Packit df99a1
Packit df99a1
static void
Packit df99a1
// call_callback(void (* callback)(GP<GPEnabled> &), GP<GPEnabled> cl_data)
Packit df99a1
call_callback(void (* callback)(void *), void *cl_data)
Packit df99a1
{
Packit df99a1
   G_TRY
Packit df99a1
   {
Packit df99a1
      if (callback)
Packit df99a1
        callback(cl_data);
Packit df99a1
   } G_CATCH_ALL {} G_ENDCATCH;
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
//****************************************************************************
Packit df99a1
//****************************** OpenFiles ***********************************
Packit df99a1
//****************************************************************************
Packit df99a1
Packit df99a1
#define MAX_OPEN_FILES	15
Packit df99a1
Packit df99a1
/** The purpose of this class is to limit the number of files open by
Packit df99a1
    connected DataPools. Now, when a DataPool is connected to a file, it
Packit df99a1
    doesn't necessarily has it open. Every time it needs access to data
Packit df99a1
    it's supposed to ask this file for the ByteStream. It should
Packit df99a1
    also inform the class when it's going to die (so that the file can
Packit df99a1
    be closed). OpenFiles makes sure, that the number of open files
Packit df99a1
    doesn't exceed MAX_OPEN_FILES. When it does, it looks for the oldest
Packit df99a1
    file, closes it and asks all DataPools working with it to ZERO
Packit df99a1
    their GP<> pointers. */
Packit df99a1
class DataPool::OpenFiles_File : public GPEnabled
Packit df99a1
{
Packit df99a1
public:
Packit df99a1
  GURL			url;
Packit df99a1
  GP<ByteStream>	        stream;		// Stream connected to 'url'
Packit df99a1
  GCriticalSection		stream_lock;
Packit df99a1
  GPList<DataPool>		pools_list;	// List of pools using this stream
Packit df99a1
  GCriticalSection		pools_lock;
Packit df99a1
  unsigned long		open_time;	// Time when stream was open
Packit df99a1
  
Packit df99a1
  int	add_pool(GP<DataPool> &pool);
Packit df99a1
  int	del_pool(GP<DataPool> &pool);
Packit df99a1
  
Packit df99a1
  OpenFiles_File(const GURL &url, GP<DataPool> &pool);
Packit df99a1
  virtual ~OpenFiles_File(void);
Packit df99a1
  void clear_stream(void);
Packit df99a1
};
Packit df99a1
Packit df99a1
class DataPool::OpenFiles : public GPEnabled
Packit df99a1
{
Packit df99a1
private:
Packit df99a1
   static OpenFiles	* global_ptr;
Packit df99a1
Packit df99a1
   GPList<DataPool::OpenFiles_File>		files_list;
Packit df99a1
   GCriticalSection	files_lock;
Packit df99a1
public:
Packit df99a1
   static OpenFiles	* get(void);
Packit df99a1
Packit df99a1
      // Opend the specified file if necessary (or finds an already open one)
Packit df99a1
      // and returns it. The caller (pool) is stored in the list associated
Packit df99a1
      // with the stream. Whenever OpenFiles decides, that this stream
Packit df99a1
      // had better be closed, it will order every pool from the list to
Packit df99a1
      // ZERO their references to it
Packit df99a1
   GP<DataPool::OpenFiles_File> request_stream(const GURL &url, GP<DataPool> pool);
Packit df99a1
      // If there are more than MAX_STREAM_FILES open, close the oldest.
Packit df99a1
   void		prune(void);
Packit df99a1
      // Removes the pool from the list associated with the stream.
Packit df99a1
      // If there is nobody else using this stream, the stream will
Packit df99a1
      // be closed too.
Packit df99a1
   void		stream_released(GP<ByteStream> &stream, GP<DataPool> pool);
Packit df99a1
Packit df99a1
   void 	close_all(void);
Packit df99a1
};
Packit df99a1
Packit df99a1
DataPool::OpenFiles * DataPool::OpenFiles::global_ptr;
Packit df99a1
Packit df99a1
DataPool::OpenFiles_File::OpenFiles_File(const GURL &xurl, GP<DataPool> &pool) : url(xurl)
Packit df99a1
{
Packit df99a1
   DEBUG_MSG("DataPool::OpenFiles_File::OpenFiles_File(): Opening file '" << url << "'\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
   
Packit df99a1
   open_time=GOS::ticks();
Packit df99a1
   stream=ByteStream::create(url,"rb");
Packit df99a1
   add_pool(pool);
Packit df99a1
}
Packit df99a1
Packit df99a1
DataPool::OpenFiles_File::~OpenFiles_File(void)
Packit df99a1
{
Packit df99a1
   DEBUG_MSG("DataPool::OpenFiles_File::~OpenFiles_File(): Closing file '" << url << "'\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
   clear_stream();
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DataPool::OpenFiles_File::clear_stream(void)
Packit df99a1
{
Packit df99a1
  GCriticalSectionLock lock(&pools_lock);
Packit df99a1
  for(GPosition pos=pools_list;pos;++pos)
Packit df99a1
    if(pools_list[pos])
Packit df99a1
      pools_list[pos]->clear_stream(false);
Packit df99a1
  pools_list.empty();
Packit df99a1
}
Packit df99a1
Packit df99a1
int
Packit df99a1
DataPool::OpenFiles_File::add_pool(GP<DataPool> &pool)
Packit df99a1
{
Packit df99a1
   DEBUG_MSG("DataPool::OpenFiles_File::add_pool: pool=" << (void *) pool << "\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
   GCriticalSectionLock lock(&pools_lock);
Packit df99a1
   if (!pools_list.contains(pool))
Packit df99a1
     pools_list.append(pool);
Packit df99a1
   return pools_list.size();
Packit df99a1
}
Packit df99a1
Packit df99a1
int
Packit df99a1
DataPool::OpenFiles_File::del_pool(GP<DataPool> &pool)
Packit df99a1
{
Packit df99a1
   DEBUG_MSG("DataPool::OpenFiles_File::del_pool: pool=" << (void *) pool << "\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
   GCriticalSectionLock lock(&pools_lock);
Packit df99a1
   GPosition pos;
Packit df99a1
   if (pools_list.search(pool, pos))
Packit df99a1
     pools_list.del(pos);
Packit df99a1
   return pools_list.size();
Packit df99a1
}
Packit df99a1
Packit df99a1
inline DataPool::OpenFiles *
Packit df99a1
DataPool::OpenFiles::get(void)
Packit df99a1
{
Packit df99a1
   DEBUG_MSG("DataPool::OpenFiles::get()\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
   if (!global_ptr)
Packit df99a1
     global_ptr=new OpenFiles();
Packit df99a1
   return global_ptr;
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DataPool::OpenFiles::prune(void)
Packit df99a1
{
Packit df99a1
  DEBUG_MSG("DataPool::OpenFiles::prune(void): "<
Packit df99a1
  DEBUG_MAKE_INDENT(3);
Packit df99a1
  while(files_list.size()>MAX_OPEN_FILES)
Packit df99a1
    {
Packit df99a1
      // Too many open files (streams). Get rid of the oldest one.
Packit df99a1
      unsigned long oldest_time=GOS::ticks();
Packit df99a1
      GPosition oldest_pos=files_list;
Packit df99a1
      for(GPosition pos=files_list;pos;++pos)
Packit df99a1
        {
Packit df99a1
          if (files_list[pos]->open_time
Packit df99a1
            {
Packit df99a1
              oldest_time=files_list[pos]->open_time;
Packit df99a1
              oldest_pos=pos;
Packit df99a1
            }
Packit df99a1
        }
Packit df99a1
      files_list[oldest_pos]->clear_stream();
Packit df99a1
      files_list.del(oldest_pos);
Packit df99a1
    }
Packit df99a1
}
Packit df99a1
Packit df99a1
//			  GP<ByteStream> & stream,
Packit df99a1
//			  GCriticalSection ** stream_lock)
Packit df99a1
GP<DataPool::OpenFiles_File>
Packit df99a1
DataPool::OpenFiles::request_stream(const GURL &url, GP<DataPool> pool)
Packit df99a1
{
Packit df99a1
   DEBUG_MSG("DataPool::OpenFiles::request_stream(): url='" << url << "'\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
Packit df99a1
   GP<DataPool::OpenFiles_File> file;
Packit df99a1
Packit df99a1
      // Check: maybe the stream has already been open by request of
Packit df99a1
      // another DataPool
Packit df99a1
   GCriticalSectionLock lock(&files_lock);
Packit df99a1
   for(GPosition pos=files_list;pos;++pos)
Packit df99a1
   {
Packit df99a1
      if (files_list[pos]->url==url)
Packit df99a1
      {
Packit df99a1
	 DEBUG_MSG("found existing stream\n");
Packit df99a1
	 file=files_list[pos];
Packit df99a1
	 break;
Packit df99a1
      }
Packit df99a1
   }
Packit df99a1
Packit df99a1
      // No? Open the stream, but check, that there are not
Packit df99a1
      // too many streams open
Packit df99a1
   if (!file)
Packit df99a1
   {
Packit df99a1
      file=new DataPool::OpenFiles_File(url, pool);
Packit df99a1
      files_list.append(file);
Packit df99a1
      prune();
Packit df99a1
   }
Packit df99a1
   
Packit df99a1
   file->add_pool(pool);
Packit df99a1
   return file;
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DataPool::OpenFiles::stream_released(GP<ByteStream> &stream, GP<DataPool> pool)
Packit df99a1
{
Packit df99a1
   DEBUG_MSG("DataPool::OpenFiles::stream_release: stream=" 
Packit df99a1
             << (void *)stream << " pool=" << (void *)pool << "\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
   GCriticalSectionLock lock(&files_lock);
Packit df99a1
   for(GPosition pos=files_list;pos;)
Packit df99a1
   {
Packit df99a1
     GPosition dpos = pos;
Packit df99a1
     ++pos;
Packit df99a1
     GP<DataPool::OpenFiles_File> f=files_list[dpos];
Packit df99a1
     if ((ByteStream *)(f->stream) == (ByteStream *)stream)
Packit df99a1
       if (f->del_pool(pool)==0)
Packit df99a1
         files_list.del(dpos);
Packit df99a1
   }
Packit df99a1
}
Packit df99a1
Packit df99a1
// This isn't really an accurate url.  The files are not really
Packit df99a1
// closed.  Instead they are dereferenced from the data pool.  If
Packit df99a1
// a there is another reference to the respective bytestream, it
Packit df99a1
// will remain open until dereferenced.
Packit df99a1
void
Packit df99a1
DataPool::OpenFiles::close_all(void)
Packit df99a1
{
Packit df99a1
  DEBUG_MSG("DataPool::OpenFiles::close_all\n");
Packit df99a1
  DEBUG_MAKE_INDENT(3);
Packit df99a1
  GCriticalSectionLock lock(&files_lock);
Packit df99a1
  files_list.empty();
Packit df99a1
}
Packit df99a1
Packit df99a1
//****************************************************************************
Packit df99a1
//******************************** FCPools ***********************************
Packit df99a1
//****************************************************************************
Packit df99a1
Packit df99a1
/** This class is used to maintain a list of DataPools connected to a file.
Packit df99a1
    It's important to have this list if we want to do something with this file
Packit df99a1
    like to modify it or just erase. Since any modifications of the file
Packit df99a1
    will break DataPools directly connected to it, it would be nice to have
Packit df99a1
    a mechanism for signaling all the related DataPools to read data into
Packit df99a1
    memory. This is precisely the purpose of this class. */
Packit df99a1
class FCPools
Packit df99a1
{
Packit df99a1
private:
Packit df99a1
   GMap<GURL, GPList<DataPool> >	map;	// GMap<GUTF8String, GPList<DataPool>> in fact
Packit df99a1
   GCriticalSection		map_lock;
Packit df99a1
Packit df99a1
   static FCPools	* global_ptr;
Packit df99a1
public:
Packit df99a1
   static FCPools *	get(void);
Packit df99a1
      // Adds the <furl, pool> pair into the list
Packit df99a1
   void		add_pool(const GURL &furl, GP<DataPool> pool);
Packit df99a1
      // Removes the <furl, pool> pair from the list
Packit df99a1
   void		del_pool(const GURL &furl, GP<DataPool> pool);
Packit df99a1
      // Looks for the list of DataPools connected to 'furl' and makes
Packit df99a1
      // each of them load the contents of the file into memory
Packit df99a1
   void		load_file(const GURL &url;;
Packit df99a1
      // Retrieve a local URL, if available.
Packit df99a1
   GP<DataPool> get_pool(const GURL &url, int start, int length);
Packit df99a1
   void clean(void);
Packit df99a1
};
Packit df99a1
Packit df99a1
void
Packit df99a1
FCPools::clean(void)
Packit df99a1
{
Packit df99a1
  GCriticalSectionLock lock(&map_lock);
Packit df99a1
  static int count=0;
Packit df99a1
  if(! count++)
Packit df99a1
  {
Packit df99a1
    bool restart = true;
Packit df99a1
    while (restart)
Packit df99a1
      {
Packit df99a1
        restart = false;
Packit df99a1
        for (GPosition posmap = map; posmap; ++posmap)
Packit df99a1
          {
Packit df99a1
            GPList<DataPool> *lst;
Packit df99a1
            lst = & map[posmap];
Packit df99a1
            if (lst->isempty())
Packit df99a1
              {
Packit df99a1
                map.del(posmap);
Packit df99a1
                restart = true;
Packit df99a1
                break;
Packit df99a1
              }
Packit df99a1
            for (GPosition poslst = *lst; poslst; ++poslst)
Packit df99a1
              if ((*lst)[poslst]->get_count() < 2) 
Packit df99a1
                {
Packit df99a1
                  lst->del(poslst);
Packit df99a1
                  restart = true;
Packit df99a1
                  break;
Packit df99a1
                }
Packit df99a1
            if (restart)
Packit df99a1
              break;
Packit df99a1
          }
Packit df99a1
      }
Packit df99a1
  }
Packit df99a1
  --count;
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
FCPools::add_pool(const GURL &url, GP<DataPool> pool)
Packit df99a1
{
Packit df99a1
  DEBUG_MSG("FCPools::add_pool: url='" << url << "' pool=" << (void *)pool << "\n");
Packit df99a1
  DEBUG_MAKE_INDENT(3);
Packit df99a1
  GCriticalSectionLock lock(&map_lock);
Packit df99a1
Packit df99a1
   if (url.is_local_file_url())
Packit df99a1
   {
Packit df99a1
      GPList<DataPool> list;
Packit df99a1
      GPosition pos(map.contains(url));
Packit df99a1
      if (! pos)
Packit df99a1
      {
Packit df99a1
        map[url]=list;
Packit df99a1
        pos=map.contains(url);
Packit df99a1
      }
Packit df99a1
      GPList<DataPool> &plist=map[pos];
Packit df99a1
      if (!plist.contains(pool))
Packit df99a1
        plist.append(pool);
Packit df99a1
   }
Packit df99a1
  clean();
Packit df99a1
}
Packit df99a1
Packit df99a1
GP<DataPool>
Packit df99a1
FCPools::get_pool(const GURL &url, int start, int length)
Packit df99a1
{
Packit df99a1
  DEBUG_MSG("FCPools::get_pool: url='" << url << "\n");
Packit df99a1
  DEBUG_MAKE_INDENT(3);
Packit df99a1
  GP<DataPool> retval;
Packit df99a1
  if (url.is_local_file_url())
Packit df99a1
  {
Packit df99a1
    GCriticalSectionLock lock(&map_lock);
Packit df99a1
    GPosition pos(map.contains(url));
Packit df99a1
    if (pos)
Packit df99a1
    {
Packit df99a1
      GPList<DataPool> &plist=map[pos];
Packit df99a1
      for(pos=plist;pos;++pos)
Packit df99a1
      {
Packit df99a1
        DataPool &pool=*plist[pos];
Packit df99a1
        if(start == pool.start && (length < 0 || (length == pool.length)))
Packit df99a1
        {
Packit df99a1
          retval=plist[pos];
Packit df99a1
          break;
Packit df99a1
        }
Packit df99a1
      }
Packit df99a1
    }
Packit df99a1
    clean();
Packit df99a1
  }
Packit df99a1
  return retval;
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
FCPools::del_pool(const GURL &url, GP<DataPool> pool)
Packit df99a1
{
Packit df99a1
  DEBUG_MSG("FCPools::del_pool: url='" << url << "' pool=" << (void *)pool << "\n");
Packit df99a1
  DEBUG_MAKE_INDENT(3);
Packit df99a1
  GCriticalSectionLock lock(&map_lock);
Packit df99a1
Packit df99a1
  clean();
Packit df99a1
   if (url.is_local_file_url())
Packit df99a1
   {
Packit df99a1
      GPosition pos;
Packit df99a1
      if (map.contains(url, pos))
Packit df99a1
      {
Packit df99a1
	 GPList<DataPool> &list=map[pos];
Packit df99a1
	 GPosition list_pos;
Packit df99a1
	 while(list.search(pool, list_pos))
Packit df99a1
	    list.del(list_pos);
Packit df99a1
	 if (list.isempty())
Packit df99a1
	 {
Packit df99a1
	    map.del(pos);
Packit df99a1
	 }
Packit df99a1
      }
Packit df99a1
   }
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
FCPools::load_file(const GURL &url)
Packit df99a1
{
Packit df99a1
  DEBUG_MSG("FCPools::load_file: url='" << url << "'\n");
Packit df99a1
  DEBUG_MAKE_INDENT(3);
Packit df99a1
  GCriticalSectionLock lock(&map_lock);
Packit df99a1
   
Packit df99a1
  clean();
Packit df99a1
   if (url.is_local_file_url())
Packit df99a1
   {
Packit df99a1
      GPosition pos;
Packit df99a1
      if (map.contains(url, pos))
Packit df99a1
      {
Packit df99a1
	    // We make here a copy of the list because DataPool::load_file()
Packit df99a1
	    // will call FCPools::del_pool(), which will modify the list
Packit df99a1
	 GPList<DataPool> list=map[pos];
Packit df99a1
	 for(GPosition list_pos=list;list_pos;++list_pos)
Packit df99a1
	    list[list_pos]->load_file();
Packit df99a1
      }
Packit df99a1
   }
Packit df99a1
}
Packit df99a1
Packit df99a1
FCPools	* FCPools::global_ptr;
Packit df99a1
Packit df99a1
inline FCPools *
Packit df99a1
FCPools::get(void)
Packit df99a1
{
Packit df99a1
   if (!global_ptr)
Packit df99a1
     global_ptr=new FCPools();
Packit df99a1
   return global_ptr;
Packit df99a1
}
Packit df99a1
Packit df99a1
//****************************************************************************
Packit df99a1
//****************************** BlockList ***********************************
Packit df99a1
//****************************************************************************
Packit df99a1
Packit df99a1
// Since data can be added to the DataPool at any offset now, there may
Packit df99a1
// be white spots, which contain illegal data. This class is to contain
Packit df99a1
// the list of valid and invalid regions.
Packit df99a1
// The class is basically a list of integers. Abs(integer)=size of the
Packit df99a1
// block. If the integer is positive, data for the block is known.
Packit df99a1
// Otherwise it's unkown.
Packit df99a1
Packit df99a1
class DataPool::BlockList
Packit df99a1
{
Packit df99a1
         // See comments in .cpp file.
Packit df99a1
private:
Packit df99a1
   GCriticalSection  lock;
Packit df99a1
   GList<int>        list;
Packit df99a1
public:
Packit df99a1
   BlockList() {};
Packit df99a1
   void              clear(void);
Packit df99a1
   void              add_range(int start, int length);
Packit df99a1
   int               get_bytes(int start, int length) const;
Packit df99a1
   int               get_range(int start, int length) const;
Packit df99a1
friend class DataPool;
Packit df99a1
};
Packit df99a1
Packit df99a1
void
Packit df99a1
DataPool::BlockList::clear(void)
Packit df99a1
{
Packit df99a1
  DEBUG_MSG("DataPool::BlockList::clear()\n");
Packit df99a1
  DEBUG_MAKE_INDENT(3);
Packit df99a1
   GCriticalSectionLock lk(&lock);
Packit df99a1
   list.empty();
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DataPool::BlockList::add_range(int start, int length)
Packit df99a1
      // Adds range of known data.
Packit df99a1
{
Packit df99a1
  DEBUG_MSG("DataPool::BlockList::add_range: start=" << start << " length=" << length << "\n");
Packit df99a1
  DEBUG_MAKE_INDENT(3);
Packit df99a1
   if (start<0)
Packit df99a1
     G_THROW( ERR_MSG("DataPool.neg_start") );
Packit df99a1
   if (length<=0)
Packit df99a1
     G_THROW( ERR_MSG("DataPool.bad_length") );
Packit df99a1
   if (length>0)
Packit df99a1
   {
Packit df99a1
      GCriticalSectionLock lk(&lock);
Packit df99a1
Packit df99a1
	 // Look thru existing zones, change their sign and split if
Packit df99a1
	 // necessary.
Packit df99a1
      GPosition pos=list;
Packit df99a1
      int block_start=0, block_end=0;
Packit df99a1
      while(pos && block_start
Packit df99a1
      {
Packit df99a1
	 int size=list[pos];
Packit df99a1
	 block_end=block_start+abs(size);
Packit df99a1
	 if (size<0)
Packit df99a1
	   {
Packit df99a1
	     if (block_start
Packit df99a1
	       {
Packit df99a1
		 if (block_end>start && block_end<=start+length)
Packit df99a1
		   {
Packit df99a1
		     list[pos]=-(start-block_start);
Packit df99a1
		     list.insert_after(pos, block_end-start);
Packit df99a1
		     ++pos;
Packit df99a1
		     block_start=start;
Packit df99a1
		   } else if (block_end>start+length)
Packit df99a1
		   {
Packit df99a1
		     list[pos]=-(start-block_start);
Packit df99a1
		     list.insert_after(pos, length);
Packit df99a1
		     ++pos;
Packit df99a1
		     list.insert_after(pos, -(block_end-(start+length)));
Packit df99a1
		     ++pos;
Packit df99a1
		     block_start=start+length;
Packit df99a1
		   }
Packit df99a1
	       } else if (block_start>=start && block_start
Packit df99a1
	       {
Packit df99a1
		 if (block_end<=start+length) list[pos]=abs(size);
Packit df99a1
		 else
Packit df99a1
		   {
Packit df99a1
		     list[pos]=start+length-block_start;
Packit df99a1
		     list.insert_after(pos, -(block_end-(start+length)));
Packit df99a1
		     ++pos;
Packit df99a1
		     block_start=start+length;
Packit df99a1
		   }
Packit df99a1
	       }
Packit df99a1
	   }
Packit df99a1
	 block_start=block_end;
Packit df99a1
	 ++pos;
Packit df99a1
      }
Packit df99a1
      if (block_end
Packit df99a1
      {
Packit df99a1
	 list.append(-(start-block_end));
Packit df99a1
	 list.append(length);
Packit df99a1
      } else if (block_end
Packit df99a1
Packit df99a1
	 // Now merge adjacent areas with the same sign
Packit df99a1
      pos=list;
Packit df99a1
      while(pos)
Packit df99a1
      {
Packit df99a1
	 GPosition pos1=pos; ++pos1;
Packit df99a1
	 while(pos1)
Packit df99a1
	   {
Packit df99a1
	     if ( (list[pos]<0 && list[pos1]>0) ||
Packit df99a1
		  (list[pos]>0 && list[pos1]<0) )
Packit df99a1
	       break;
Packit df99a1
	     list[pos]+=list[pos1];
Packit df99a1
	     GPosition this_pos=pos1;
Packit df99a1
	     ++pos1;
Packit df99a1
	     list.del(this_pos);
Packit df99a1
	   }
Packit df99a1
	 pos=pos1;
Packit df99a1
      }
Packit df99a1
   } // if (length>0)
Packit df99a1
}
Packit df99a1
Packit df99a1
int
Packit df99a1
DataPool::BlockList::get_bytes(int start, int length) const
Packit df99a1
      // Returns the number of bytes of data available in the range
Packit df99a1
      // [start, start+length[. There may be holes between data chunks
Packit df99a1
{
Packit df99a1
  DEBUG_MSG("DataPool::BlockList::get_bytes: start=" << start << " length=" << length << "\n");
Packit df99a1
  DEBUG_MAKE_INDENT(3);
Packit df99a1
Packit df99a1
   if (length<0)
Packit df99a1
     G_THROW( ERR_MSG("DataPool.bad_length") );
Packit df99a1
Packit df99a1
   GCriticalSectionLock lk((GCriticalSection *) &lock);
Packit df99a1
   int bytes=0;
Packit df99a1
   int block_start=0, block_end=0;
Packit df99a1
   for(GPosition pos=list;pos && block_start
Packit df99a1
   {
Packit df99a1
      int size=list[pos];
Packit df99a1
      block_end=block_start+abs(size);
Packit df99a1
      if (size>0)
Packit df99a1
	{
Packit df99a1
	  if (block_start
Packit df99a1
	    {
Packit df99a1
	      if (block_end>=start && block_end
Packit df99a1
		bytes+=block_end-start;
Packit df99a1
	      else if (block_end>=start+length)
Packit df99a1
		bytes+=length;
Packit df99a1
	    }
Packit df99a1
	  else
Packit df99a1
	    {
Packit df99a1
	      if (block_end<=start+length)
Packit df99a1
		bytes+=block_end-block_start;
Packit df99a1
	      else bytes+=start+length-block_start;
Packit df99a1
	    }
Packit df99a1
	}
Packit df99a1
      block_start=block_end;
Packit df99a1
   }
Packit df99a1
   return bytes;
Packit df99a1
}
Packit df99a1
Packit df99a1
int
Packit df99a1
DataPool::BlockList::get_range(int start, int length) const
Packit df99a1
      // Finds a range covering offset=start and returns the length
Packit df99a1
      // of intersection of this range with [start, start+length[
Packit df99a1
      // 0 is returned if nothing can be found
Packit df99a1
{
Packit df99a1
  DEBUG_MSG("DataPool::BlockList::get_range: start=" << start << " length=" << length << "\n");
Packit df99a1
  DEBUG_MAKE_INDENT(3);
Packit df99a1
   if (start<0)
Packit df99a1
     G_THROW( ERR_MSG("DataPool.neg_start") );
Packit df99a1
   if (length<=0)
Packit df99a1
      G_THROW( ERR_MSG("DataPool.bad_length") );
Packit df99a1
Packit df99a1
   GCriticalSectionLock lk((GCriticalSection *) &lock);
Packit df99a1
   int block_start=0, block_end=0;
Packit df99a1
   for(GPosition pos=list;pos && block_start
Packit df99a1
     {
Packit df99a1
       int size=list[pos];
Packit df99a1
       block_end=block_start+abs(size);
Packit df99a1
       if (block_start<=start && block_end>start)
Packit df99a1
	 {
Packit df99a1
	   if (size<0)
Packit df99a1
	     return -1;
Packit df99a1
	   else if (block_end>start+length)
Packit df99a1
	     return length;
Packit df99a1
	   else
Packit df99a1
	     return block_end-start;
Packit df99a1
	 }
Packit df99a1
       block_start=block_end;
Packit df99a1
     }
Packit df99a1
   return 0;
Packit df99a1
}
Packit df99a1
Packit df99a1
//****************************************************************************
Packit df99a1
//******************************* DataPool ***********************************
Packit df99a1
//****************************************************************************
Packit df99a1
Packit df99a1
class DataPool::Reader : public GPEnabled
Packit df99a1
{
Packit df99a1
public:
Packit df99a1
   GEvent event;
Packit df99a1
   bool reenter_flag;
Packit df99a1
   int  offset;
Packit df99a1
   int  size;
Packit df99a1
   Reader() : reenter_flag(false), offset(0), size(-1){};
Packit df99a1
   Reader(int offset_in, int size_in=-1) :
Packit df99a1
   reenter_flag(false), offset(offset_in), size(size_in) {};
Packit df99a1
   virtual ~Reader() {};
Packit df99a1
};
Packit df99a1
Packit df99a1
class DataPool::Trigger : public GPEnabled
Packit df99a1
{
Packit df99a1
public:
Packit df99a1
   GSafeFlags disabled;
Packit df99a1
   int  start, length;
Packit df99a1
//   void (* callback)(GP<GPEnabled> &);
Packit df99a1
   void (* callback)(void *);
Packit df99a1
//   GP<GPEnabled> cl_data;
Packit df99a1
   void *cl_data;
Packit df99a1
Packit df99a1
   Trigger() : start(0), length(-1), callback(0), cl_data(0) {};
Packit df99a1
   Trigger(int xstart, int xlength,
Packit df99a1
//   void (* xcallback)(GP<GPEnabled> &), GP<GPEnabled> xcl_data) :
Packit df99a1
   void (* xcallback)(void *), void *xcl_data) :
Packit df99a1
      start(xstart), length(xlength), callback(xcallback), cl_data(xcl_data) {};
Packit df99a1
   virtual ~Trigger() {};
Packit df99a1
};
Packit df99a1
Packit df99a1
class DataPool::Counter
Packit df99a1
{
Packit df99a1
private:
Packit df99a1
   int               counter;
Packit df99a1
   GCriticalSection  lock;
Packit df99a1
public:
Packit df99a1
   Counter() : counter(0) {};
Packit df99a1
   operator int(void) const;
Packit df99a1
   void              inc(void);
Packit df99a1
   void              dec(void);
Packit df99a1
};
Packit df99a1
Packit df99a1
#define DATAPOOL_INIT eof_flag(false),stop_flag(false), \
Packit df99a1
    stop_blocked_flag(false), \
Packit df99a1
    add_at(0),start(0),length(-1)
Packit df99a1
Packit df99a1
void
Packit df99a1
DataPool::init(void)
Packit df99a1
{
Packit df99a1
  DEBUG_MSG("DataPool::init(): Initializing\n");
Packit df99a1
  DEBUG_MAKE_INDENT(3);
Packit df99a1
  start=0; length=-1; add_at=0;
Packit df99a1
  eof_flag=false;
Packit df99a1
  stop_flag=false;
Packit df99a1
  stop_blocked_flag=false;
Packit df99a1
Packit df99a1
  active_readers=new Counter;
Packit df99a1
  block_list=0;
Packit df99a1
  G_TRY
Packit df99a1
  {   
Packit df99a1
    block_list=new BlockList;
Packit df99a1
    data=ByteStream::create();
Packit df99a1
  }
Packit df99a1
  G_CATCH_ALL
Packit df99a1
  {
Packit df99a1
    delete block_list;
Packit df99a1
    block_list=0;
Packit df99a1
    delete active_readers;
Packit df99a1
    active_readers=0;
Packit df99a1
    G_RETHROW;
Packit df99a1
  }
Packit df99a1
  G_ENDCATCH;
Packit df99a1
}
Packit df99a1
Packit df99a1
DataPool::DataPool(void) : DATAPOOL_INIT {}
Packit df99a1
Packit df99a1
GP<DataPool>
Packit df99a1
DataPool::create(void)
Packit df99a1
{
Packit df99a1
  DEBUG_MSG("DataPool::DataPool()\n");
Packit df99a1
  DEBUG_MAKE_INDENT(3);
Packit df99a1
  DataPool *pool=new DataPool();
Packit df99a1
Packit df99a1
  GP<DataPool> retval=pool;
Packit df99a1
  pool->init();
Packit df99a1
Packit df99a1
      // If we maintain the data ourselves, we want to interpret its
Packit df99a1
      // IFF structure to predict its length
Packit df99a1
  pool->add_trigger(0, 32, static_trigger_cb, pool);
Packit df99a1
  return retval;
Packit df99a1
}
Packit df99a1
Packit df99a1
GP<DataPool> 
Packit df99a1
DataPool::create(const GP<ByteStream> &gstr)
Packit df99a1
{
Packit df99a1
  DEBUG_MSG("DataPool::create: str="<<(ByteStream *)gstr<<"\n");
Packit df99a1
  DEBUG_MAKE_INDENT(3);
Packit df99a1
  DataPool *pool=new DataPool();
Packit df99a1
  GP<DataPool> retval=pool;
Packit df99a1
  pool->init();
Packit df99a1
Packit df99a1
  // It's nice to have IFF data analyzed in this case too.
Packit df99a1
  pool->add_trigger(0, 32, static_trigger_cb, pool);
Packit df99a1
Packit df99a1
  char buffer[1024];
Packit df99a1
  int length;
Packit df99a1
  while((length=gstr->read(buffer, 1024)))
Packit df99a1
    pool->add_data(buffer, length);
Packit df99a1
  pool->set_eof();
Packit df99a1
Packit df99a1
  return retval;
Packit df99a1
}
Packit df99a1
Packit df99a1
GP<DataPool>
Packit df99a1
DataPool::create(const GP<DataPool> & pool, int start, int length)
Packit df99a1
{
Packit df99a1
  DEBUG_MSG("DataPool::DataPool: pool=" << (void *)((DataPool *)pool) << " start=" << start << " length= " << length << "\n");
Packit df99a1
  DEBUG_MAKE_INDENT(3);
Packit df99a1
Packit df99a1
  DataPool *xpool=new DataPool();
Packit df99a1
  GP<DataPool> retval=xpool;
Packit df99a1
  xpool->init();
Packit df99a1
  xpool->connect(pool, start, length);
Packit df99a1
  return retval;
Packit df99a1
}
Packit df99a1
Packit df99a1
GP<DataPool>
Packit df99a1
DataPool::create(const GURL &furl, int start, int length)
Packit df99a1
{
Packit df99a1
  DEBUG_MSG("DataPool::DataPool: furl='" << furl << "' start=" << start << " length= " << length << "\n");
Packit df99a1
  DEBUG_MAKE_INDENT(3);
Packit df99a1
Packit df99a1
  GP<DataPool> retval=FCPools::get()->get_pool(furl,start,length);
Packit df99a1
  if(! retval)
Packit df99a1
  {
Packit df99a1
    DataPool *pool=new DataPool();
Packit df99a1
    retval=pool;
Packit df99a1
    pool->init();
Packit df99a1
    pool->connect(furl, start, length);
Packit df99a1
  }
Packit df99a1
  return retval;
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DataPool::clear_stream(const bool release)
Packit df99a1
{
Packit df99a1
  DEBUG_MSG("DataPool::clear_stream()\n");
Packit df99a1
  DEBUG_MAKE_INDENT(3);
Packit df99a1
  if(fstream)
Packit df99a1
  {
Packit df99a1
    GCriticalSectionLock lock1(&class_stream_lock);
Packit df99a1
    GP<OpenFiles_File> f=fstream;
Packit df99a1
    if(f)
Packit df99a1
    {
Packit df99a1
      GCriticalSectionLock lock2(&(f->stream_lock));
Packit df99a1
      fstream=0;
Packit df99a1
      if(release)
Packit df99a1
        OpenFiles::get()->stream_released(f->stream, this);
Packit df99a1
    }
Packit df99a1
  }
Packit df99a1
}
Packit df99a1
Packit df99a1
DataPool::~DataPool(void)
Packit df99a1
{
Packit df99a1
  DEBUG_MSG("DataPool::~DataPool()\n");
Packit df99a1
  DEBUG_MAKE_INDENT(3);
Packit df99a1
Packit df99a1
  clear_stream(true);
Packit df99a1
  if (furl.is_local_file_url())
Packit df99a1
    if (this->get_count() > 1)
Packit df99a1
      FCPools::get()->del_pool(furl, this);
Packit df99a1
  
Packit df99a1
  GP<DataPool> pool = this->pool;
Packit df99a1
  {
Packit df99a1
	 // Wait until the static_trigger_cb() exits
Packit df99a1
      GCriticalSectionLock lock(&trigger_lock);
Packit df99a1
      if (pool)
Packit df99a1
        pool->del_trigger(static_trigger_cb, this);
Packit df99a1
      del_trigger(static_trigger_cb, this);
Packit df99a1
  }
Packit df99a1
  if (pool)
Packit df99a1
  {
Packit df99a1
      GCriticalSectionLock lock(&triggers_lock);
Packit df99a1
      for(GPosition pos=triggers_list;pos;++pos)
Packit df99a1
      {
Packit df99a1
	 GP<Trigger> trigger=triggers_list[pos];
Packit df99a1
	 pool->del_trigger(trigger->callback, trigger->cl_data);
Packit df99a1
      }
Packit df99a1
  }
Packit df99a1
  delete block_list;
Packit df99a1
  delete active_readers;
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DataPool::connect(const GP<DataPool> & pool_in, int start_in, int length_in)
Packit df99a1
{
Packit df99a1
   DEBUG_MSG("DataPool::connect(): connecting to another DataPool\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
   
Packit df99a1
   if (pool) G_THROW( ERR_MSG("DataPool.connected1") );
Packit df99a1
   if (furl.is_local_file_url()) G_THROW( ERR_MSG("DataPool.connected2") );
Packit df99a1
   if (start_in<0) G_THROW( ERR_MSG("DataPool.neg_start") );
Packit df99a1
Packit df99a1
   pool=pool_in;
Packit df99a1
   start=start_in;
Packit df99a1
   length=length_in;
Packit df99a1
Packit df99a1
      // The following will work for length<0 too
Packit df99a1
   if (pool->has_data(start, length))
Packit df99a1
     eof_flag=true;
Packit df99a1
   else
Packit df99a1
     pool->add_trigger(start, length, static_trigger_cb, this);
Packit df99a1
Packit df99a1
   data=0;
Packit df99a1
Packit df99a1
   wake_up_all_readers();
Packit df99a1
   
Packit df99a1
      // Pass registered trigger callbacks to the DataPool
Packit df99a1
   GCriticalSectionLock lock(&triggers_lock);
Packit df99a1
   for(GPosition pos=triggers_list;pos;++pos)
Packit df99a1
   {
Packit df99a1
      GP<Trigger> t=triggers_list[pos];
Packit df99a1
      int tlength=t->length;
Packit df99a1
      if (tlength<0 && length>0)
Packit df99a1
        tlength=length-t->start;
Packit df99a1
      pool->add_trigger(start+t->start, tlength, t->callback, t->cl_data);
Packit df99a1
   }
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DataPool::connect(const GURL &furl_in, int start_in, int length_in)
Packit df99a1
{
Packit df99a1
   DEBUG_MSG("DataPool::connect(): connecting to a file\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
   
Packit df99a1
   if (pool)
Packit df99a1
     G_THROW( ERR_MSG("DataPool.connected1") );
Packit df99a1
   if (furl.is_local_file_url())
Packit df99a1
     G_THROW( ERR_MSG("DataPool.connected2") );
Packit df99a1
   if (start_in<0)
Packit df99a1
     G_THROW( ERR_MSG("DataPool.neg_start") );
Packit df99a1
Packit df99a1
Packit df99a1
   if (furl_in.name() == "-")
Packit df99a1
   {
Packit df99a1
      DEBUG_MSG("This is stdin => just read the data...\n");
Packit df99a1
      DEBUG_MAKE_INDENT(3);
Packit df99a1
      char buffer[1024];
Packit df99a1
      int length;
Packit df99a1
      GP<ByteStream> gstr=ByteStream::create(furl_in, "rb");
Packit df99a1
      ByteStream &str=*gstr;
Packit df99a1
      while((length=str.read(buffer, 1024)))
Packit df99a1
	 add_data(buffer, length);
Packit df99a1
      set_eof();
Packit df99a1
   } else if(furl_in.is_local_file_url())
Packit df99a1
   {
Packit df99a1
	 // Open the stream (just in this function) too see if
Packit df99a1
	 // the file is accessible. In future we will be using 'OpenFiles'
Packit df99a1
	 // to request and release streams
Packit df99a1
      GP<ByteStream> str=ByteStream::create(furl_in,"rb");
Packit df99a1
      str->seek(0, SEEK_END);
Packit df99a1
      int file_size=str->tell();
Packit df99a1
Packit df99a1
      furl=furl_in;
Packit df99a1
      start=start_in;
Packit df99a1
      length=length_in;
Packit df99a1
      if (start>=file_size)
Packit df99a1
        length=0;
Packit df99a1
      else if (length<0 || start+length>=file_size)
Packit df99a1
        length=file_size-start;
Packit df99a1
      
Packit df99a1
      eof_flag=true;
Packit df99a1
      data=0;
Packit df99a1
      
Packit df99a1
      FCPools::get()->add_pool(furl, this);
Packit df99a1
Packit df99a1
      wake_up_all_readers();
Packit df99a1
   
Packit df99a1
	 // Call every trigger callback
Packit df99a1
      GCriticalSectionLock lock(&triggers_lock);
Packit df99a1
      for(GPosition pos=triggers_list;pos;++pos)
Packit df99a1
      {
Packit df99a1
	 GP<Trigger> t=triggers_list[pos];
Packit df99a1
	 call_callback(t->callback, t->cl_data);
Packit df99a1
      }
Packit df99a1
      triggers_list.empty();
Packit df99a1
   }
Packit df99a1
}
Packit df99a1
Packit df99a1
int
Packit df99a1
DataPool::get_length(void) const
Packit df99a1
{
Packit df99a1
      // Not connected and length has been guessed
Packit df99a1
      // Or connected to a file
Packit df99a1
      // Or connected to a pool, but length was preset
Packit df99a1
   int retval=(-1);
Packit df99a1
   if (length>=0) 
Packit df99a1
   {
Packit df99a1
     retval=length;
Packit df99a1
   }else if (pool)
Packit df99a1
   {
Packit df99a1
      int plength=pool->get_length();
Packit df99a1
      if (plength>=0)
Packit df99a1
        retval=plength-start;
Packit df99a1
   }
Packit df99a1
   return retval;
Packit df99a1
}
Packit df99a1
Packit df99a1
int
Packit df99a1
DataPool::get_size(int dstart, int dlength) const
Packit df99a1
{
Packit df99a1
   if (dlength<0 && length>0)
Packit df99a1
   {
Packit df99a1
      dlength=length-dstart;
Packit df99a1
      if (dlength<0) return 0;
Packit df99a1
   }
Packit df99a1
   
Packit df99a1
   GP<DataPool> pool = this->pool;
Packit df99a1
   if (pool) 
Packit df99a1
     return pool->get_size(start+dstart, dlength);
Packit df99a1
   else if (furl.is_local_file_url())
Packit df99a1
   {
Packit df99a1
      if (start+dstart+dlength>length) return length-(start+dstart);
Packit df99a1
      else return dlength;
Packit df99a1
   } else
Packit df99a1
   {
Packit df99a1
      if (dlength<0)
Packit df99a1
      {
Packit df99a1
	 GCriticalSectionLock lock((GCriticalSection *) &data_lock);
Packit df99a1
	 dlength=data->size()-dstart;
Packit df99a1
      }
Packit df99a1
      return (dlength<0)?0:(block_list->get_bytes(dstart, dlength));
Packit df99a1
   }
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DataPool::add_data(const void * buffer, int size)
Packit df99a1
      // This function adds data sequentially at 'add_at' position
Packit df99a1
{
Packit df99a1
   DEBUG_MSG("DataPool::add_data(): adding " << size << " bytes of data...\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
Packit df99a1
   add_data(buffer, add_at, size);
Packit df99a1
   add_at+=size;
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DataPool::add_data(const void * buffer, int offset, int size)
Packit df99a1
{
Packit df99a1
   DEBUG_MSG("DataPool::add_data(): adding " << size << " bytes at pos=" <<
Packit df99a1
	     offset << "...\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
Packit df99a1
   if (furl.is_local_file_url() || pool)
Packit df99a1
      G_THROW( ERR_MSG("DataPool.add_data") );
Packit df99a1
   
Packit df99a1
      // Add data to the data storage
Packit df99a1
   {
Packit df99a1
      GCriticalSectionLock lock(&data_lock);
Packit df99a1
      if (offset>data->size())
Packit df99a1
      {
Packit df99a1
	 char ch=0;
Packit df99a1
	 data->seek(0, SEEK_END);
Packit df99a1
	 for(int i=data->size();i
Packit df99a1
	    data->write(&ch, 1);
Packit df99a1
      } else
Packit df99a1
      {
Packit df99a1
	 data->seek(offset, SEEK_SET);
Packit df99a1
	 data->writall(buffer, size);
Packit df99a1
      }
Packit df99a1
   }
Packit df99a1
Packit df99a1
   added_data(offset, size);
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DataPool::added_data(const int offset, const int size)
Packit df99a1
{
Packit df99a1
     // Modify map of blocks
Packit df99a1
  block_list->add_range(offset, size);
Packit df99a1
   
Packit df99a1
     // Wake up all threads, which may be waiting for this data
Packit df99a1
  {
Packit df99a1
    GCriticalSectionLock lock(&readers_lock);
Packit df99a1
    for(GPosition pos=readers_list;pos;++pos)
Packit df99a1
    {
Packit df99a1
      GP<Reader> reader=readers_list[pos];
Packit df99a1
      if (block_list->get_bytes(reader->offset, 1))
Packit df99a1
      {
Packit df99a1
        DEBUG_MSG("waking up reader: offset=" << reader->offset <<
Packit df99a1
          ", size=" << reader->size << "\n");
Packit df99a1
        DEBUG_MAKE_INDENT(3);
Packit df99a1
        reader->event.set();
Packit df99a1
      }
Packit df99a1
    }
Packit df99a1
  }
Packit df99a1
Packit df99a1
    // And call triggers
Packit df99a1
  check_triggers();
Packit df99a1
Packit df99a1
      // Do not undo the following two lines. The reason why we need them
Packit df99a1
      // here is the connected DataPools, which use 'length' (more exactly
Packit df99a1
      // has_data()) to see if they have all data required. So, right after
Packit df99a1
      // all data has been added to the master DataPool, but before EOF
Packit df99a1
      // is set, the master and slave DataPools disagree regarding if
Packit df99a1
      // all data is there or not. These two lines solve the problem
Packit df99a1
  GCriticalSectionLock lock(&data_lock);
Packit df99a1
  if (length>=0 && data->size()>=length)
Packit df99a1
    set_eof();
Packit df99a1
}
Packit df99a1
Packit df99a1
bool
Packit df99a1
DataPool::has_data(int dstart, int dlength)
Packit df99a1
{
Packit df99a1
   if (dlength<0 && length>0)
Packit df99a1
     dlength=length-dstart;
Packit df99a1
   return (pool?(pool->has_data(start+dstart, dlength))
Packit df99a1
     :((furl.is_local_file_url())?(start+dstart+dlength<=length)
Packit df99a1
       :((dlength<0)?is_eof()
Packit df99a1
         :(block_list->get_bytes(dstart, dlength)==dlength))));
Packit df99a1
}
Packit df99a1
Packit df99a1
int
Packit df99a1
DataPool::get_data(void * buffer, int offset, int sz)
Packit df99a1
{
Packit df99a1
   return get_data(buffer, offset, sz, 0);
Packit df99a1
}
Packit df99a1
Packit df99a1
class DataPool::Incrementor
Packit df99a1
{
Packit df99a1
private:
Packit df99a1
   Counter      & counter;
Packit df99a1
public:
Packit df99a1
   Incrementor(Counter & xcounter) : counter(xcounter) {counter.inc();}
Packit df99a1
   ~Incrementor() {counter.dec();}
Packit df99a1
};
Packit df99a1
Packit df99a1
int
Packit df99a1
DataPool::get_data(void * buffer, int offset, int sz, int level)
Packit df99a1
{
Packit df99a1
   DEBUG_MSG("DataPool::get_data()\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
   Incrementor inc(*active_readers);
Packit df99a1
   
Packit df99a1
   if (stop_flag)
Packit df99a1
     G_THROW( DataPool::Stop );
Packit df99a1
   if (stop_blocked_flag && !is_eof() &&
Packit df99a1
       !has_data(offset, sz))
Packit df99a1
     G_THROW( DataPool::Stop );
Packit df99a1
   
Packit df99a1
   if (sz < 0)
Packit df99a1
     G_THROW( ERR_MSG("DataPool.bad_size") );
Packit df99a1
   
Packit df99a1
   if (! sz)
Packit df99a1
     return 0;
Packit df99a1
Packit df99a1
   GP<DataPool> pool = this->pool;
Packit df99a1
   if (pool)
Packit df99a1
     {
Packit df99a1
       DEBUG_MSG("DataPool::get_data(): from pool\n");
Packit df99a1
       DEBUG_MAKE_INDENT(3);
Packit df99a1
       int retval=0;
Packit df99a1
       if (length>0 && offset+sz>length)
Packit df99a1
	 sz=length-offset;
Packit df99a1
       if (sz<0)
Packit df99a1
	 sz=0;
Packit df99a1
       for(;;)
Packit df99a1
	 {
Packit df99a1
	   // Ask the underlying (master) DataPool for data. Note, that
Packit df99a1
	   // master DataPool may throw the "DATA_POOL_REENTER" exception
Packit df99a1
	   // demanding all readers to restart. This happens when
Packit df99a1
	   // a DataPool in the chain of DataPools stops. All readers
Packit df99a1
	   // should return to the most upper level and then reenter the
Packit df99a1
	   // DataPools hierarchy. Some of them will be stopped by
Packit df99a1
	   // DataPool::Stop exception.
Packit df99a1
	   G_TRY
Packit df99a1
	     {
Packit df99a1
	       if(stop_flag
Packit df99a1
		  || (stop_blocked_flag && !is_eof() && !has_data(offset, sz)))
Packit df99a1
		 G_THROW( DataPool::Stop );
Packit df99a1
	       retval=pool->get_data(buffer, start+offset, sz, level+1);
Packit df99a1
	     }
Packit df99a1
	   G_CATCH(exc)
Packit df99a1
	   {
Packit df99a1
	     pool->clear_stream(true);
Packit df99a1
	     if ((exc.get_cause() != GUTF8String(ERR_MSG("DataPool.reenter")))
Packit df99a1
		 || level)
Packit df99a1
	       G_RETHROW;
Packit df99a1
	   } G_ENDCATCH;
Packit df99a1
	   pool->clear_stream(true);
Packit df99a1
	   return retval;
Packit df99a1
	 }
Packit df99a1
     }
Packit df99a1
   else if (furl.is_local_file_url())
Packit df99a1
     {
Packit df99a1
       DEBUG_MSG("DataPool::get_data(): from file\n");
Packit df99a1
       DEBUG_MAKE_INDENT(3);
Packit df99a1
       if (length>0 && offset+sz>length)
Packit df99a1
	 sz=length-offset;
Packit df99a1
       if (sz<0)
Packit df99a1
         sz=0;
Packit df99a1
       
Packit df99a1
       GP<OpenFiles_File> f=fstream;
Packit df99a1
       if (!f)
Packit df99a1
         {
Packit df99a1
           GCriticalSectionLock lock(&class_stream_lock);
Packit df99a1
           f=fstream;
Packit df99a1
           if(!f)
Packit df99a1
             {
Packit df99a1
               fstream=f=OpenFiles::get()->request_stream(furl, this);
Packit df99a1
             }
Packit df99a1
         }
Packit df99a1
       GCriticalSectionLock lock2(&(f->stream_lock));
Packit df99a1
       f->stream->seek(start+offset, SEEK_SET); 
Packit df99a1
       return f->stream->readall(buffer, sz);
Packit df99a1
     } 
Packit df99a1
   else
Packit df99a1
     {
Packit df99a1
       DEBUG_MSG("DataPool::get_data(): direct\n");
Packit df99a1
       DEBUG_MAKE_INDENT(3);
Packit df99a1
       // We're not connected to anybody => handle the data
Packit df99a1
       int size=block_list->get_range(offset, sz);
Packit df99a1
       if (size>0)
Packit df99a1
         {
Packit df99a1
           // Hooray! Some data is there
Packit df99a1
           GCriticalSectionLock lock(&data_lock);
Packit df99a1
           data->seek(offset, SEEK_SET);
Packit df99a1
           return data->readall(buffer, size);
Packit df99a1
         }
Packit df99a1
       
Packit df99a1
       // No data available.
Packit df99a1
       
Packit df99a1
       // If there is no data and nothing else is expected, we can do
Packit df99a1
       // two things: throw ByteStream::EndOfFile exception or return ZERO bytes.
Packit df99a1
       // The exception is for the cases when the data flow has been
Packit df99a1
       // terminated in the middle. ZERO bytes is for regular read() beyond
Packit df99a1
       // the boundaries of legal data. The problem is to distinguish
Packit df99a1
       // these two cases. We do it here with the help of analysis of the
Packit df99a1
       // IFF structure of the data (which sets the 'length' variable).
Packit df99a1
       // If we attempt to read beyond the [0, length[, ZERO bytes will be
Packit df99a1
       // returned. Otherwise an ByteStream::EndOfFile exception will be thrown.
Packit df99a1
       if (eof_flag)
Packit df99a1
         {
Packit df99a1
           if (length>0 && offset
Packit df99a1
             {
Packit df99a1
               G_THROW( ByteStream::EndOfFile );
Packit df99a1
             }
Packit df99a1
           else 
Packit df99a1
             {
Packit df99a1
               return 0;
Packit df99a1
             }
Packit df99a1
         } 
Packit df99a1
       // Some data is still expected => add this reader to the
Packit df99a1
       // list of readers and call virtual wait_for_data()
Packit df99a1
       DEBUG_MSG("DataPool::get_data(): There is no data in the pool.\n");
Packit df99a1
       DEBUG_MSG("offset=" << offset << ", size=" << sz <<
Packit df99a1
                 ", data_size=" << data->size() << "\n");
Packit df99a1
       GP<Reader> reader=new Reader(offset, sz);
Packit df99a1
       G_TRY 
Packit df99a1
         {
Packit df99a1
           {
Packit df99a1
             GCriticalSectionLock slock(&readers_lock);
Packit df99a1
             readers_list.append(reader);
Packit df99a1
           }
Packit df99a1
           wait_for_data(reader);
Packit df99a1
         } 
Packit df99a1
       G_CATCH_ALL 
Packit df99a1
         {
Packit df99a1
           {
Packit df99a1
             GCriticalSectionLock slock(&readers_lock);
Packit df99a1
             GPosition pos;
Packit df99a1
             if (readers_list.search(reader, pos)) readers_list.del(pos);
Packit df99a1
           }
Packit df99a1
           G_RETHROW;
Packit df99a1
         } 
Packit df99a1
       G_ENDCATCH;
Packit df99a1
       
Packit df99a1
       {
Packit df99a1
         GCriticalSectionLock slock(&readers_lock);
Packit df99a1
         GPosition pos;
Packit df99a1
         if (readers_list.search(reader, pos)) readers_list.del(pos);
Packit df99a1
       }
Packit df99a1
       
Packit df99a1
       // This call to get_data() should return immediately as there MUST
Packit df99a1
       // be data in the buffer after wait_for_data(reader) returns
Packit df99a1
       // or eof_flag should be TRUE
Packit df99a1
       return get_data(buffer, reader->offset, reader->size, level);
Packit df99a1
     }
Packit df99a1
   return 0;
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DataPool::wait_for_data(const GP<Reader> & reader)
Packit df99a1
      // This function may NOT return until there is some data for the
Packit df99a1
      // given reader in the internal buffer
Packit df99a1
{
Packit df99a1
   DEBUG_MSG("DataPool::wait_for_data(): waiting for data at offset=" << reader->offset <<
Packit df99a1
	     ", length=" << reader->size << "\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
Packit df99a1
   for(;;)
Packit df99a1
   {
Packit df99a1
      if (stop_flag)
Packit df99a1
        G_THROW( DataPool::Stop );
Packit df99a1
      if (reader->reenter_flag)
Packit df99a1
        G_THROW( ERR_MSG("DataPool.reenter") );
Packit df99a1
      if (eof_flag || block_list->get_bytes(reader->offset, 1))
Packit df99a1
        return;
Packit df99a1
      if (pool || furl.is_local_file_url())
Packit df99a1
        return;
Packit df99a1
Packit df99a1
      if (stop_blocked_flag)
Packit df99a1
        G_THROW( DataPool::Stop );
Packit df99a1
Packit df99a1
      DEBUG_MSG("calling event.wait()...\n");
Packit df99a1
      reader->event.wait();
Packit df99a1
   }
Packit df99a1
   
Packit df99a1
   DEBUG_MSG("Got some data to read\n");
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DataPool::wake_up_all_readers(void)
Packit df99a1
{
Packit df99a1
   DEBUG_MSG("DataPool::wake_up_all_readers(): waking up all readers\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
Packit df99a1
   GCriticalSectionLock lock(&readers_lock);
Packit df99a1
   for(GPosition pos=readers_list;pos;++pos)
Packit df99a1
      readers_list[pos]->event.set();
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DataPool::set_eof(void)
Packit df99a1
      // Has no effect on connected DataPools
Packit df99a1
{
Packit df99a1
   if (!furl.is_local_file_url() && !pool)
Packit df99a1
   {
Packit df99a1
      eof_flag=true;
Packit df99a1
      
Packit df99a1
	 // Can we set the length now?
Packit df99a1
      if (length<0)
Packit df99a1
      {
Packit df99a1
	 GCriticalSectionLock lock(&data_lock);
Packit df99a1
	 length=data->size();
Packit df99a1
      }
Packit df99a1
Packit df99a1
	 // Wake up all readers to let them rescan the flags
Packit df99a1
      wake_up_all_readers();
Packit df99a1
   
Packit df99a1
	 // Activate all trigger callbacks with negative threshold
Packit df99a1
      check_triggers();
Packit df99a1
   }
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DataPool::stop(bool only_blocked)
Packit df99a1
{
Packit df99a1
   DEBUG_MSG("DataPool::stop(): Stopping this and dependent DataPools, only_blocked="
Packit df99a1
	     << only_blocked << "\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
Packit df99a1
   if (only_blocked) stop_blocked_flag=true;
Packit df99a1
   else stop_flag=true;
Packit df99a1
   
Packit df99a1
Packit df99a1
   wake_up_all_readers();
Packit df99a1
Packit df99a1
      // Now let all readers, which already go thru to the master DataPool,
Packit df99a1
      // come back and reenter. While reentering some of them will go
Packit df99a1
      // thru this DataPool again and will be stopped (DataPool::Stop exception)
Packit df99a1
      // Others (which entered the master DataPool thru other slave DataPools)
Packit df99a1
      // will simply continue waiting for their data.
Packit df99a1
   GP<DataPool> pool = this->pool;
Packit df99a1
   if (pool)
Packit df99a1
   {
Packit df99a1
	 // This loop is necessary because there may be another thread, which
Packit df99a1
	 // is going down thru the DataPool chain and did not reach the
Packit df99a1
	 // lowest "master" DataPool yet. Since it didn't reach it yet,
Packit df99a1
	 // the "pool->restart_readers()" will not restart it. So we're going
Packit df99a1
	 // to continue issuing this command until we get rid of all
Packit df99a1
	 // "active_readers"
Packit df99a1
      while(*active_readers)
Packit df99a1
        pool->restart_readers();
Packit df99a1
   }
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DataPool::restart_readers(void)
Packit df99a1
{
Packit df99a1
   DEBUG_MSG("DataPool::restart_readers(): telling all readers to reenter\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
   
Packit df99a1
   GCriticalSectionLock slock(&readers_lock);
Packit df99a1
   for(GPosition pos=readers_list;pos;++pos)
Packit df99a1
   {
Packit df99a1
      GP<Reader> reader=readers_list[pos];
Packit df99a1
      reader->reenter_flag=true;
Packit df99a1
      reader->event.set();
Packit df99a1
   }
Packit df99a1
      
Packit df99a1
   if (pool)
Packit df99a1
     pool->restart_readers();
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DataPool::load_file(void)
Packit df99a1
{
Packit df99a1
   DEBUG_MSG("DataPool::load_file() called\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
Packit df99a1
   if (pool)
Packit df99a1
   {
Packit df99a1
      DEBUG_MSG("passing the request down.\n");
Packit df99a1
      pool->load_file();
Packit df99a1
   } else if (furl.is_local_file_url())
Packit df99a1
   {
Packit df99a1
      DEBUG_MSG("loading the data from \""<<(const char *)furl<<"\".\n");
Packit df99a1
Packit df99a1
      GCriticalSectionLock lock1(&class_stream_lock);
Packit df99a1
      GP<OpenFiles_File> f=fstream;
Packit df99a1
      if (!f)
Packit df99a1
      {
Packit df99a1
        fstream=f=OpenFiles::get()->request_stream(furl, this);
Packit df99a1
      }
Packit df99a1
      {  // Scope to de-allocate lock2 before stream gets released
Packit df99a1
         GCriticalSectionLock lock2(&(f->stream_lock));
Packit df99a1
Packit df99a1
         data=ByteStream::create();
Packit df99a1
         block_list->clear();
Packit df99a1
         FCPools::get()->del_pool(furl, this);
Packit df99a1
         furl=GURL();
Packit df99a1
Packit df99a1
         const GP<ByteStream> gbs = f->stream;
Packit df99a1
         gbs->seek(0, SEEK_SET);
Packit df99a1
         
Packit df99a1
         char buffer[1024];
Packit df99a1
         int length;
Packit df99a1
         while((length = f->stream->read(buffer, 1024)))
Packit df99a1
           add_data(buffer, length);
Packit df99a1
         set_eof();
Packit df99a1
         
Packit df99a1
         OpenFiles::get()->stream_released(f->stream, this);
Packit df99a1
      }
Packit df99a1
      fstream=0;
Packit df99a1
   } else DEBUG_MSG("Not connected\n");
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DataPool::load_file(const GURL &url )
Packit df99a1
{
Packit df99a1
   FCPools::get()->load_file(url);
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DataPool::check_triggers(void)
Packit df99a1
      // This function is for not connected DataPools only
Packit df99a1
{
Packit df99a1
  DEBUG_MSG("DataPool::check_triggers(): calling activated trigger callbacks.\n");
Packit df99a1
  DEBUG_MAKE_INDENT(3);
Packit df99a1
Packit df99a1
  if (!pool && !furl.is_local_file_url())
Packit df99a1
    while(true)
Packit df99a1
      {
Packit df99a1
	GP<Trigger> trigger;
Packit df99a1
Packit df99a1
	// First find a candidate (trigger, which needs to be called)
Packit df99a1
	// Don't remove it from the list yet. del_trigger() should
Packit df99a1
	// be able to find it if necessary and disable.
Packit df99a1
	{
Packit df99a1
	  GCriticalSectionLock list_lock(&triggers_lock);
Packit df99a1
	  for(GPosition pos=triggers_list;pos;++pos)
Packit df99a1
	    {
Packit df99a1
	      GP<Trigger> t=triggers_list[pos];
Packit df99a1
	      if (is_eof()
Packit df99a1
		  || (t->length>=0 &&
Packit df99a1
		      block_list->get_bytes(t->start, t->length)==t->length))
Packit df99a1
		{
Packit df99a1
		  trigger=t;
Packit df99a1
		  break;
Packit df99a1
		}
Packit df99a1
	    }
Packit df99a1
	}
Packit df99a1
Packit df99a1
	if (trigger)
Packit df99a1
	  {
Packit df99a1
	    // Now check that the trigger is not disabled
Packit df99a1
	    // and lock the trigger->disabled lock for the duration
Packit df99a1
	    // of the trigger. This will block the del_trigger() and
Packit df99a1
	    // will postpone client's destruction (usually following
Packit df99a1
	    // the call to del_trigger())
Packit df99a1
	    {
Packit df99a1
	      GMonitorLock lock(&trigger->disabled);
Packit df99a1
	      if (!trigger->disabled)
Packit df99a1
		call_callback(trigger->callback, trigger->cl_data);
Packit df99a1
	    }
Packit df99a1
Packit df99a1
	    // Finally - remove the trigger from the list.
Packit df99a1
	    GCriticalSectionLock list_lock(&triggers_lock);
Packit df99a1
	    for(GPosition pos=triggers_list;pos;++pos)
Packit df99a1
	      if (triggers_list[pos]==trigger)
Packit df99a1
		{
Packit df99a1
		  triggers_list.del(pos);
Packit df99a1
		  break;
Packit df99a1
		}
Packit df99a1
	  } else break;
Packit df99a1
      }
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DataPool::add_trigger(int thresh, void (* callback)(void *), void * cl_data)
Packit df99a1
{
Packit df99a1
  if (thresh>=0)
Packit df99a1
    add_trigger(0, thresh+1, callback, cl_data);
Packit df99a1
  else
Packit df99a1
    add_trigger(0, -1, callback, cl_data);
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DataPool::add_trigger(int tstart, int tlength,
Packit df99a1
		      void (* callback)(void *), void * cl_data)
Packit df99a1
{
Packit df99a1
   DEBUG_MSG("DataPool::add_trigger(): start=" << tstart <<
Packit df99a1
	     ", length=" << tlength << ", func=" << (void *) callback << "\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
   
Packit df99a1
   if (callback)
Packit df99a1
   {
Packit df99a1
      if (is_eof())
Packit df99a1
      {
Packit df99a1
        call_callback(callback, cl_data);
Packit df99a1
      }else
Packit df99a1
      {
Packit df99a1
         GP<DataPool> pool = this->pool;
Packit df99a1
	 if (pool)
Packit df99a1
	 {
Packit df99a1
	       // We're connected to a DataPool
Packit df99a1
	       // Just pass the triggers down remembering it in the list
Packit df99a1
	    if (tlength<0 && length>0) tlength=length-tstart;
Packit df99a1
	    GP<Trigger> trigger=new Trigger(tstart, tlength, callback, cl_data);
Packit df99a1
	    pool->add_trigger(start+tstart, tlength, callback, cl_data);
Packit df99a1
	    GCriticalSectionLock lock(&triggers_lock);
Packit df99a1
	    triggers_list.append(trigger);
Packit df99a1
	 } 
Packit df99a1
         else if (!furl.is_local_file_url())
Packit df99a1
	 {
Packit df99a1
	       // We're not connected to anything and maintain our own data
Packit df99a1
	    if (tlength>=0 && block_list->get_bytes(tstart, tlength)==tlength)
Packit df99a1
	       call_callback(callback, cl_data);
Packit df99a1
	    else
Packit df99a1
	    {
Packit df99a1
              GP<Trigger> trigger=new Trigger(tstart, tlength, callback, cl_data);
Packit df99a1
              GCriticalSectionLock lock(&triggers_lock);
Packit df99a1
              triggers_list.append(trigger);
Packit df99a1
	    }
Packit df99a1
	 }
Packit df99a1
      }
Packit df99a1
   }
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DataPool::del_trigger(void (* callback)(void *), void * cl_data)
Packit df99a1
{
Packit df99a1
   DEBUG_MSG("DataPool::del_trigger(): func=" << (void *) callback << "\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
Packit df99a1
   for(;;)
Packit df99a1
   {
Packit df99a1
      GP<Trigger> trigger;
Packit df99a1
      {
Packit df99a1
	 GCriticalSectionLock lock(&triggers_lock);
Packit df99a1
	 for(GPosition pos=triggers_list;pos;)
Packit df99a1
	 {
Packit df99a1
	    GP<Trigger> t=triggers_list[pos];
Packit df99a1
	    if (t->callback==callback && t->cl_data==cl_data)
Packit df99a1
	    {
Packit df99a1
	       trigger=t;
Packit df99a1
	       GPosition this_pos=pos;
Packit df99a1
	       ++pos;
Packit df99a1
	       triggers_list.del(this_pos);
Packit df99a1
	       break;
Packit df99a1
	    } else
Packit df99a1
              ++pos;
Packit df99a1
	 }
Packit df99a1
      }
Packit df99a1
Packit df99a1
	 // Above we removed the trigger from the list and unlocked the list
Packit df99a1
	 // Now we will disable it and will wait if necessary (if the
Packit df99a1
	 // trigger is currently being processed by check_triggers())
Packit df99a1
	 // check_triggers() locks the trigger for the duration of the
Packit df99a1
	 // trigger callback. Thus we will wait for the trigger callback
Packit df99a1
	 // to finish and avoid client's destruction.
Packit df99a1
      if (trigger)
Packit df99a1
        trigger->disabled=1;
Packit df99a1
      else
Packit df99a1
        break;
Packit df99a1
   }
Packit df99a1
Packit df99a1
   GP<DataPool> pool = this->pool;
Packit df99a1
   if (pool)
Packit df99a1
     pool->del_trigger(callback, cl_data);
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
// DataPool::static_trigger_cb(GP<GPEnabled> &cl_data)
Packit df99a1
DataPool::static_trigger_cb(void *cl_data)
Packit df99a1
{
Packit df99a1
//  GP<DataPool> d=(DataPool *)(GPEnabled *)cl_data;
Packit df99a1
  GP<DataPool> d=(DataPool *)cl_data;
Packit df99a1
  d->trigger_cb();
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DataPool::trigger_cb(void)
Packit df99a1
      // This function may be triggered by the DataPool, which we're
Packit df99a1
      // connected to, or by ourselves, if we're connected to nothing
Packit df99a1
{
Packit df99a1
      // Don't want to be destroyed while I'm here. Can't use GP<> life saver
Packit df99a1
      // because it may be called from the constructor
Packit df99a1
   GCriticalSectionLock lock(&trigger_lock);
Packit df99a1
   
Packit df99a1
   DEBUG_MSG("DataPool::trigger_cb() called\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
Packit df99a1
   GP<DataPool> pool = this->pool;
Packit df99a1
   if (pool)
Packit df99a1
   {
Packit df99a1
      // Connected to a pool
Packit df99a1
      // We may be here when either EOF is set on the master DataPool
Packit df99a1
      // Or when it may have learnt its length (from IFF or whatever)
Packit df99a1
      if (pool->is_eof() || pool->has_data(start, length)) eof_flag=true;
Packit df99a1
   } else if (!furl.is_local_file_url())
Packit df99a1
   {
Packit df99a1
	    // Not connected to anything => Try to guess the length
Packit df99a1
      if (length<0) analyze_iff();
Packit df99a1
      
Packit df99a1
	    // Failed to analyze? Check, maybe it's EOF already
Packit df99a1
      if (length<0 && is_eof())
Packit df99a1
      {
Packit df99a1
	       GCriticalSectionLock lock(&data_lock);
Packit df99a1
	       length=data->size();
Packit df99a1
      }
Packit df99a1
   }
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DataPool::analyze_iff(void)
Packit df99a1
      // In order to display decode progress properly, we need to know
Packit df99a1
      // the size of the data. It's trivial to figure it out if is_eof()
Packit df99a1
      // is true. Otherwise we need to make a prediction. Luckily all
Packit df99a1
      // DjVuFiles have IFF structure, which makes it possible to do it.
Packit df99a1
      // If due to some reason we fail, the length will remain -1.
Packit df99a1
{
Packit df99a1
   DEBUG_MSG("DataPool::analyze_iff(): Trying to decode IFF structure of " << furl << ".\n");
Packit df99a1
   DEBUG_MSG("in order to predict the DataPool's size\n");
Packit df99a1
   DEBUG_MAKE_INDENT(3);
Packit df99a1
Packit df99a1
   GP<ByteStream> str=get_stream();
Packit df99a1
   
Packit df99a1
   GP<IFFByteStream> giff=IFFByteStream::create(str);
Packit df99a1
   IFFByteStream &iff=*giff;
Packit df99a1
   GUTF8String chkid;
Packit df99a1
   int size;
Packit df99a1
   if ((size=iff.get_chunk(chkid)) && size>=0)
Packit df99a1
   {
Packit df99a1
      length=size+iff.tell()-4;
Packit df99a1
      DEBUG_MSG("Got size=" << size << ", length=" << length << "\n");
Packit df99a1
   }
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
Packit df99a1
Packit df99a1
Packit df99a1
//****************************************************************************
Packit df99a1
//****************************** PoolByteStream ******************************
Packit df99a1
//****************************************************************************
Packit df99a1
Packit df99a1
// This is an internal ByteStream receiving data from the associated DataPool.
Packit df99a1
// It's just a sequential interface, nothing more. All the job for data
Packit df99a1
// retrieval, waiting and thread synchronization is done by DataPool
Packit df99a1
Packit df99a1
class PoolByteStream : public ByteStream
Packit df99a1
{
Packit df99a1
public:
Packit df99a1
   PoolByteStream(GP<DataPool> data_pool);
Packit df99a1
   virtual ~PoolByteStream() {};
Packit df99a1
Packit df99a1
   virtual size_t read(void *buffer, size_t size);
Packit df99a1
   virtual size_t write(const void *buffer, size_t size);
Packit df99a1
   virtual long tell(void) const ;
Packit df99a1
   virtual int seek(long offset, int whence = SEEK_SET, bool nothrow=false);
Packit df99a1
private:
Packit df99a1
      // Don't make data_pool GP<>. The problem is that DataPool creates
Packit df99a1
      // and soon destroys this ByteStream from the constructor. Since
Packit df99a1
      // there are no other pointers to the DataPool created yet, it becomes
Packit df99a1
      // destroyed immediately :(
Packit df99a1
   DataPool		* data_pool;
Packit df99a1
   GP<DataPool>		data_pool_lock;
Packit df99a1
   long			position;
Packit df99a1
   
Packit df99a1
   char			buffer[512];
Packit df99a1
   size_t		buffer_size;
Packit df99a1
   size_t		buffer_pos;
Packit df99a1
Packit df99a1
      // Cancel C++ default stuff
Packit df99a1
   PoolByteStream & operator=(const PoolByteStream &);
Packit df99a1
};
Packit df99a1
Packit df99a1
inline
Packit df99a1
PoolByteStream::PoolByteStream(GP<DataPool> xdata_pool) :
Packit df99a1
   data_pool(xdata_pool), position(0), buffer_size(0), buffer_pos(0)
Packit df99a1
{
Packit df99a1
   if (!data_pool) 
Packit df99a1
       G_THROW( ERR_MSG("DataPool.zero_DataPool") );
Packit df99a1
Packit df99a1
      // Secure the DataPool if possible. If we're called from DataPool
Packit df99a1
      // constructor (get_count()==0) there is no need to secure at all.
Packit df99a1
   if (data_pool->get_count()) data_pool_lock=data_pool;
Packit df99a1
}
Packit df99a1
Packit df99a1
size_t
Packit df99a1
PoolByteStream::read(void *data, size_t size)
Packit df99a1
{
Packit df99a1
  if (buffer_pos >= buffer_size) {
Packit df99a1
    if (size >= sizeof(buffer)) {
Packit df99a1
      // Direct read
Packit df99a1
      size = data_pool->get_data(data, position, size);
Packit df99a1
      position += size;
Packit df99a1
      return size;
Packit df99a1
    } else {
Packit df99a1
      // Refill buffer
Packit df99a1
      buffer_size = data_pool->get_data(buffer, position, sizeof(buffer));
Packit df99a1
      buffer_pos=0;
Packit df99a1
    }
Packit df99a1
  }
Packit df99a1
  if (buffer_pos + size >= buffer_size)
Packit df99a1
    size = buffer_size - buffer_pos;
Packit df99a1
  memcpy(data, buffer+buffer_pos, size);
Packit df99a1
  buffer_pos += size;
Packit df99a1
  position += size;
Packit df99a1
  return size;
Packit df99a1
}
Packit df99a1
Packit df99a1
size_t
Packit df99a1
PoolByteStream::write(const void *buffer, size_t size)
Packit df99a1
{
Packit df99a1
   G_THROW( ERR_MSG("not_implemented_n") "\tPoolByteStream::write()");   //  PoolByteStream::write() is not implemented.
Packit df99a1
   return 0;	// For compiler not to bark
Packit df99a1
}
Packit df99a1
Packit df99a1
long
Packit df99a1
PoolByteStream::tell(void) const
Packit df99a1
{
Packit df99a1
   return position;
Packit df99a1
}
Packit df99a1
Packit df99a1
int
Packit df99a1
PoolByteStream::seek(long offset, int whence, bool nothrow)
Packit df99a1
{
Packit df99a1
  int retval=(-1);
Packit df99a1
  switch(whence)
Packit df99a1
  {
Packit df99a1
    case SEEK_CUR:
Packit df99a1
      offset+=position;
Packit df99a1
      // fallthrough;
Packit df99a1
    case SEEK_SET:
Packit df99a1
      if(offset
Packit df99a1
      {
Packit df99a1
        if((int)(offset+buffer_pos)>=(int)position)
Packit df99a1
        {
Packit df99a1
          buffer_pos-=position-offset;
Packit df99a1
        }else
Packit df99a1
        {
Packit df99a1
          buffer_size=0;
Packit df99a1
        }
Packit df99a1
        position=offset;
Packit df99a1
      }else if(offset>position)
Packit df99a1
      {
Packit df99a1
        buffer_pos+=(offset-position)-1;
Packit df99a1
        position=offset-1;
Packit df99a1
        unsigned char c;
Packit df99a1
        if(read(&c,1)<1)
Packit df99a1
        {
Packit df99a1
          G_THROW( ByteStream::EndOfFile );
Packit df99a1
        }
Packit df99a1
      }
Packit df99a1
      retval=0;
Packit df99a1
      break;
Packit df99a1
    case SEEK_END:
Packit df99a1
      if(! nothrow)
Packit df99a1
        G_THROW( ERR_MSG("DataPool.seek_backward") );
Packit df99a1
      break;
Packit df99a1
   }
Packit df99a1
   return retval;
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
DataPool::close_all(void)
Packit df99a1
{
Packit df99a1
  OpenFiles::get()->close_all();
Packit df99a1
  FCPools::get()->clean();
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
GP<ByteStream>
Packit df99a1
DataPool::get_stream(void)
Packit df99a1
{
Packit df99a1
  return new PoolByteStream(this);
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
inline
Packit df99a1
DataPool::Counter::operator int(void) const
Packit df99a1
{
Packit df99a1
   GCriticalSectionLock lk((GCriticalSection *) &lock);
Packit df99a1
   int cnt=counter;
Packit df99a1
   return cnt;
Packit df99a1
}
Packit df99a1
Packit df99a1
inline void
Packit df99a1
DataPool::Counter::inc(void)
Packit df99a1
{
Packit df99a1
   GCriticalSectionLock lk(&lock);
Packit df99a1
   counter++;
Packit df99a1
}
Packit df99a1
Packit df99a1
inline void
Packit df99a1
DataPool::Counter::dec(void)
Packit df99a1
{
Packit df99a1
   GCriticalSectionLock lk(&lock);
Packit df99a1
   counter--;
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
#ifdef HAVE_NAMESPACES
Packit df99a1
}
Packit df99a1
# ifndef NOT_USING_DJVU_NAMESPACE
Packit df99a1
using namespace DJVU;
Packit df99a1
# endif
Packit df99a1
#endif