You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdegraphics/kviewshell/plugins/djvu/libdjvu/ByteStream.cpp

1445 lines
34 KiB

//C- -------------------------------------------------------------------
//C- DjVuLibre-3.5
//C- Copyright (c) 2002 Leon Bottou and Yann Le Cun.
//C- Copyright (c) 2001 AT&T
//C-
//C- This software is subject to, and may be distributed under, the
//C- GNU General Public License, Version 2. The license should have
//C- accompanied the software or you may obtain a copy of the license
//C- from the Free Software Foundation at http://www.fsf.org .
//C-
//C- This program is distributed in the hope that it will be useful,
//C- but WITHOUT ANY WARRANTY; without even the implied warranty of
//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
//C- GNU General Public License for more details.
//C-
//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library
//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech
//C- Software authorized us to replace the original DjVu(r) Reference
//C- Library notice by the following text (see doc/lizard2002.djvu):
//C-
//C- ------------------------------------------------------------------
//C- | DjVu (r) Reference Library (v. 3.5)
//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved.
//C- | The DjVu Reference Library is protected by U.S. Pat. No.
//C- | 6,058,214 and patents pending.
//C- |
//C- | This software is subject to, and may be distributed under, the
//C- | GNU General Public License, Version 2. The license should have
//C- | accompanied the software or you may obtain a copy of the license
//C- | from the Free Software Foundation at http://www.fsf.org .
//C- |
//C- | The computer code originally released by LizardTech under this
//C- | license and unmodified by other parties is deemed "the LIZARDTECH
//C- | ORIGINAL CODE." Subject to any third party intellectual property
//C- | claims, LizardTech grants recipient a worldwide, royalty-free,
//C- | non-exclusive license to make, use, sell, or otherwise dispose of
//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the
//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU
//C- | General Public License. This grant only confers the right to
//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to
//C- | the extent such infringement is reasonably necessary to enable
//C- | recipient to make, have made, practice, sell, or otherwise dispose
//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to
//C- | any greater extent that may be necessary to utilize further
//C- | modifications or combinations.
//C- |
//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY
//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF
//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
//C- +------------------------------------------------------------------
//
// $Id: ByteStream.cpp,v 1.18 2004/08/06 14:50:05 leonb Exp $
// $Name: release_3_5_15 $
// From: Leon Bottou, 1/31/2002
// This file has very little to do with my initial implementation.
// It has been practically rewritten by Lizardtech for i18n changes.
// Our original implementation consisted of multiple classes.
// <http://prdownloads.sourceforge.net/djvu/DjVu2_2b-src.tgz>.
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#if NEED_GNUG_PRAGMAS
# pragma implementation
#endif
// - Author: Leon Bottou, 04/1997
#include "DjVuGlobal.h"
#include "ByteStream.h"
#include "GOS.h"
#include "GURL.h"
#include "DjVuMessage.h"
#include <fcntl.h>
#if defined(WIN32) || defined(__CYGWIN32__)
# include <io.h>
#endif
#ifdef UNIX
# ifndef HAS_MEMMAP
# define HAS_MEMMAP 1
# endif
#endif
#if defined(UNIX)
# include <sys/types.h>
# include <sys/stat.h>
# include <unistd.h>
# include <errno.h>
# ifdef HAS_MEMMAP
# include <sys/mman.h>
# endif
#elif defined(macintosh)
# include <unistd.h>
_MSL_IMP_EXP_C int _dup(int);
_MSL_IMP_EXP_C int _dup2(int,int);
_MSL_IMP_EXP_C int _close(int);
__inline int dup(int _a ) { return _dup(_a);}
__inline int dup2(int _a, int _b ) { return _dup2(_a, _b);}
#endif
#ifdef HAVE_NAMESPACES
namespace DJVU {
# ifdef NOT_DEFINED // Just to fool emacs c++ mode
}
#endif
#endif
const char *ByteStream::EndOfFile=ERR_MSG("EOF");
/** ByteStream interface for stdio files.
The virtual member functions #read#, #write#, #tell# and #seek# are mapped
to the well known stdio functions #fread#, #fwrite#, #ftell# and #fseek#.
@see Unix man page fopen(3), fread(3), fwrite(3), ftell(3), fseek(3) */
class ByteStream::Stdio : public ByteStream {
public:
Stdio(void);
/** Constructs a ByteStream for accessing the file named #url#.
Arguments #url# and #mode# are similar to the arguments of the well
known stdio function #fopen#. In addition a url of #-# will be
interpreted as the standard output or the standard input according to
#mode#. This constructor will open a stdio file and construct a
ByteStream object accessing this file. Destroying the ByteStream object
will flush and close the associated stdio file. Returns an error code
if the stdio file cannot be opened. */
GUTF8String init(const GURL &url, const char * const mode);
/** Constructs a ByteStream for accessing the stdio file #f#.
Argument #mode# indicates the type of the stdio file, as in the
well known stdio function #fopen#. Destroying the ByteStream
object will not close the stdio file #f# unless closeme is true. */
GUTF8String init(FILE * const f, const char * const mode="rb", const bool closeme=false);
/** Initializes from stdio */
GUTF8String init(const char mode[]);
// Virtual functions
~Stdio();
virtual size_t read(void *buffer, size_t size);
virtual size_t write(const void *buffer, size_t size);
virtual void flush(void);
virtual int seek(long offset, int whence = SEEK_SET, bool nothrow=false);
virtual long tell(void) const;
private:
// Cancel C++ default stuff
Stdio(const Stdio &);
Stdio & operator=(const Stdio &);
private:
// Implementation
bool can_read;
bool can_write;
bool must_close;
protected:
FILE *fp;
long pos;
};
inline GUTF8String
ByteStream::Stdio::init(FILE * const f,const char mode[],const bool closeme)
{
fp=f;
must_close=closeme;
return init(mode);
}
/** ByteStream interface managing a memory buffer.
Class #ByteStream::Memory# manages a dynamically resizable buffer from
which data can be read or written. The buffer itself is organized as an
array of blocks of 4096 bytes. */
class ByteStream::Memory : public ByteStream
{
public:
/** Constructs an empty ByteStream::Memory.
The buffer is initially empty. You must first use function #write#
to store data into the buffer, use function #seek# to rewind the
current position, and function #read# to read the data back. */
Memory();
/** Constructs a Memory by copying initial data. The
Memory buffer is initialized with #size# bytes copied from the
memory area pointed to by #buffer#. */
GUTF8String init(const void * const buffer, const size_t size);
// Virtual functions
~Memory();
virtual size_t read(void *buffer, size_t size);
virtual size_t write(const void *buffer, size_t size);
virtual int seek(long offset, int whence=SEEK_SET, bool nothrow=false);
virtual long tell(void) const;
/** Erases everything in the Memory.
The current location is reset to zero. */
void empty();
/** Returns the total number of bytes contained in the buffer. Valid
offsets for function #seek# range from 0 to the value returned by this
function. */
virtual int size(void) const;
/** Returns a reference to the byte at offset #n#. This reference can be
used to read (as in #mbs[n]#) or modify (as in #mbs[n]=c#) the contents
of the buffer. */
char &operator[] (int n);
/** Copies all internal data into \Ref{TArray} and returns it */
private:
// Cancel C++ default stuff
Memory(const Memory &);
Memory & operator=(const Memory &);
// Current position
int where;
protected:
/** Reads data from a random position. This function reads at most #sz#
bytes at position #pos# into #buffer# and returns the actual number of
bytes read. The current position is unchanged. */
virtual size_t readat(void *buffer, size_t sz, int pos);
/** Number of bytes in internal buffer. */
int bsize;
/** Number of 4096 bytes blocks. */
int nblocks;
/** Pointers (possibly null) to 4096 bytes blocks. */
char **blocks;
/** Pointers (possibly null) to 4096 bytes blocks. */
GPBuffer<char *> gblocks;
};
inline int
ByteStream::Memory::size(void) const
{
return bsize;
}
inline char &
ByteStream::Memory::operator[] (int n)
{
return blocks[n>>12][n&0xfff];
}
/** Read-only ByteStream interface to a memory area.
Class #ByteStream::Static# implements a read-only ByteStream interface for a
memory area specified by the user at construction time. Calls to function
#read# directly access this memory area. The user must therefore make
sure that its content remain valid long enough. */
class ByteStream::Static : public ByteStream
{
public:
class Allocate;
class Duplicate;
friend class Duplicate;
/** Creates a Static object for allocating the memory area of
length #sz# starting at address #buffer#. */
Static(const void * const buffer, const size_t sz);
~Static();
// Virtual functions
virtual size_t read(void *buffer, size_t sz);
virtual int seek(long offset, int whence = SEEK_SET, bool nothrow=false);
virtual long tell(void) const;
/** Returns the total number of bytes contained in the buffer, file, etc.
Valid offsets for function #seek# range from 0 to the value returned
by this function. */
virtual int size(void) const;
virtual GP<ByteStream> duplicate(const size_t xsize) const;
/// Returns false, unless a subclass of ByteStream::Static
virtual bool is_static(void) const { return true; }
protected:
const char *data;
int bsize;
private:
int where;
};
ByteStream::Static::~Static() {}
class ByteStream::Static::Allocate : public ByteStream::Static
{
public:
friend class ByteStream;
protected:
char *buf;
GPBuffer<char> gbuf;
public:
Allocate(const size_t size) : Static(0,size), gbuf(buf,size) { data=buf; }
virtual ~Allocate();
};
ByteStream::Static::Allocate::~Allocate() {}
inline int
ByteStream::Static::size(void) const
{
return bsize;
}
class ByteStream::Static::Duplicate : public ByteStream::Static
{
protected:
GP<ByteStream> gbs;
public:
Duplicate(const ByteStream::Static &bs, const size_t size);
};
ByteStream::Static::Duplicate::Duplicate(
const ByteStream::Static &bs, const size_t xsize)
: ByteStream::Static(0,0)
{
if(xsize&&(bs.bsize<bs.where))
{
const size_t bssize=(size_t)bs.bsize-(size_t)bs.where;
bsize=(size_t)((xsize>bssize)?bssize:xsize);
gbs=const_cast<ByteStream::Static *>(&bs);
data=bs.data+bs.where;
}
}
GP<ByteStream>
ByteStream::Static::duplicate(const size_t xsize) const
{
return new ByteStream::Static::Duplicate(*this,xsize);
}
#if HAS_MEMMAP
/** Read-only ByteStream interface to a memmap area.
Class #MemoryMapByteStream# implements a read-only ByteStream interface
for a memory map to a file. */
class MemoryMapByteStream : public ByteStream::Static
{
public:
MemoryMapByteStream(void);
virtual ~MemoryMapByteStream();
private:
GUTF8String init(const int fd, const bool closeme);
GUTF8String init(FILE *const f,const bool closeme);
friend class ByteStream;
};
#endif
//// CLASS BYTESTREAM
ByteStream::~ByteStream()
{
}
int
ByteStream::scanf(const char *fmt, ...)
{
G_THROW( ERR_MSG("ByteStream.not_implemented") ); // This is a place holder function.
return 0;
}
size_t
ByteStream::read(void *buffer, size_t sz)
{
G_THROW( ERR_MSG("ByteStream.cant_read") ); // Cannot read from a ByteStream created for writing
return 0;
}
size_t
ByteStream::write(const void *buffer, size_t sz)
{
G_THROW( ERR_MSG("ByteStream.cant_write") ); // Cannot write from a ByteStream created for reading
return 0;
}
void
ByteStream::flush()
{
}
int
ByteStream::seek(long offset, int whence, bool nothrow)
{
int nwhere = 0;
int ncurrent = tell();
switch (whence)
{
case SEEK_SET:
nwhere=0; break;
case SEEK_CUR:
nwhere=ncurrent; break;
case SEEK_END:
{
if(offset)
{
if (nothrow)
return -1;
G_THROW( ERR_MSG("ByteStream.backward") );
}
char buffer[1024];
int bytes;
while((bytes=read(buffer, sizeof(buffer))))
EMPTY_LOOP;
return 0;
}
default:
G_THROW( ERR_MSG("ByteStream.bad_arg") ); // Illegal argument in seek
}
nwhere += offset;
if (nwhere < ncurrent)
{
// Seeking backwards is not supported by this ByteStream
if (nothrow)
return -1;
G_THROW( ERR_MSG("ByteStream.backward") );
}
while (nwhere>ncurrent)
{
char buffer[1024];
const int xbytes=(ncurrent+(int)sizeof(buffer)>nwhere)
?(nwhere - ncurrent):(int)sizeof(buffer);
const int bytes = read(buffer, xbytes);
ncurrent += bytes;
if (!bytes)
G_THROW( ByteStream::EndOfFile );
// Seeking works funny on this ByteStream (ftell() acts strange)
if (ncurrent!=tell())
G_THROW( ERR_MSG("ByteStream.seek") );
}
return 0;
}
size_t
ByteStream::readall(void *buffer, size_t size)
{
size_t total = 0;
while (size > 0)
{
int nitems = read(buffer, size);
// Replaced perror() below with G_THROW(). It still makes little sense
// as there is no guarantee, that errno is right. Still, throwing
// exception instead of continuing to loop is better.
// - eaf
if(nitems < 0)
G_THROW(strerror(errno)); // (No error in the DjVuMessageFile)
if (nitems == 0)
break;
total += nitems;
size -= nitems;
buffer = (void*)((char*)buffer + nitems);
}
return total;
}
size_t
ByteStream::format(const char *fmt, ... )
{
va_list args;
va_start(args, fmt);
const GUTF8String message(fmt,args);
return writestring(message);
}
size_t
ByteStream::writestring(const GNativeString &s)
{
int retval;
if(cp != UTF8)
{
retval=writall((const char *)s,s.length());
if(cp == AUTO)
cp=NATIVE; // Avoid mixing string types.
}else
{
const GUTF8String msg(s.getNative2UTF8());
retval=writall((const char *)msg,msg.length());
}
return retval;
}
size_t
ByteStream::writestring(const GUTF8String &s)
{
int retval;
if(cp != NATIVE)
{
retval=writall((const char *)s,s.length());
if(cp == AUTO)
cp=UTF8; // Avoid mixing string types.
}else
{
const GNativeString msg(s.getUTF82Native());
retval=writall((const char *)msg,msg.length());
}
return retval;
}
size_t
ByteStream::writall(const void *buffer, size_t size)
{
size_t total = 0;
while (size > 0)
{
size_t nitems = write(buffer, size);
if (nitems == 0)
G_THROW( ERR_MSG("ByteStream.write_error") ); // Unknown error in write
total += nitems;
size -= nitems;
buffer = (void*)((char*)buffer + nitems);
}
return total;
}
size_t
ByteStream::copy(ByteStream &bsfrom, size_t size)
{
size_t total = 0;
const size_t max_buffer_size=200*1024;
const size_t buffer_size=(size>0 && size<max_buffer_size)
?size:max_buffer_size;
char *buffer;
GPBuffer<char> gbuf(buffer,buffer_size);
for(;;)
{
size_t bytes = buffer_size;
if (size>0 && bytes+total>size)
bytes = size - total;
if (bytes == 0)
break;
bytes = bsfrom.read((void*)buffer, bytes);
if (bytes == 0)
break;
writall((void*)buffer, bytes);
total += bytes;
}
return total;
}
void
ByteStream::write8 (unsigned int card)
{
unsigned char c[1];
c[0] = (card) & 0xff;
if (write((void*)c, sizeof(c)) != sizeof(c))
G_THROW(strerror(errno)); // (No error in the DjVuMessageFile)
}
void
ByteStream::write16(unsigned int card)
{
unsigned char c[2];
c[0] = (card>>8) & 0xff;
c[1] = (card) & 0xff;
if (writall((void*)c, sizeof(c)) != sizeof(c))
G_THROW(strerror(errno)); // (No error in the DjVuMessageFile)
}
void
ByteStream::write24(unsigned int card)
{
unsigned char c[3];
c[0] = (card>>16) & 0xff;
c[1] = (card>>8) & 0xff;
c[2] = (card) & 0xff;
if (writall((void*)c, sizeof(c)) != sizeof(c))
G_THROW(strerror(errno)); // (No error in the DjVuMessageFile)
}
void
ByteStream::write32(unsigned int card)
{
unsigned char c[4];
c[0] = (card>>24) & 0xff;
c[1] = (card>>16) & 0xff;
c[2] = (card>>8) & 0xff;
c[3] = (card) & 0xff;
if (writall((void*)c, sizeof(c)) != sizeof(c))
G_THROW(strerror(errno)); // (No error in the DjVuMessageFile)
}
unsigned int
ByteStream::read8 ()
{
unsigned char c[1];
if (readall((void*)c, sizeof(c)) != sizeof(c))
G_THROW( ByteStream::EndOfFile );
return c[0];
}
unsigned int
ByteStream::read16()
{
unsigned char c[2];
if (readall((void*)c, sizeof(c)) != sizeof(c))
G_THROW( ByteStream::EndOfFile );
return (c[0]<<8)+c[1];
}
unsigned int
ByteStream::read24()
{
unsigned char c[3];
if (readall((void*)c, sizeof(c)) != sizeof(c))
G_THROW( ByteStream::EndOfFile );
return (((c[0]<<8)+c[1])<<8)+c[2];
}
unsigned int
ByteStream::read32()
{
unsigned char c[4];
if (readall((void*)c, sizeof(c)) != sizeof(c))
G_THROW( ByteStream::EndOfFile );
return (((((c[0]<<8)+c[1])<<8)+c[2])<<8)+c[3];
}
//// CLASS ByteStream::Stdio
ByteStream::Stdio::Stdio(void)
: can_read(false),can_write(false),must_close(true),fp(0),pos(0)
{}
ByteStream::Stdio::~Stdio()
{
if (fp && must_close)
fclose(fp);
}
GUTF8String
ByteStream::Stdio::init(const char mode[])
{
char const *mesg=0;
bool binary=false;
if(!fp)
must_close=false;
for (const char *s=mode; s && *s; s++)
{
switch(*s)
{
case 'r':
can_read=true;
if(!fp) fp=stdin;
break;
case 'w':
case 'a':
can_write=true;
if(!fp) fp=stdout;
break;
case '+':
can_read=can_write=true;
break;
case 'b':
binary=true;
break;
default:
mesg= ERR_MSG("ByteStream.bad_mode"); // Illegal mode in Stdio
}
}
if(binary && fp) {
#if defined(__CYGWIN32__)
setmode(fileno(fp), O_BINARY);
#elif defined(WIN32)
_setmode(_fileno(fp), _O_BINARY);
#endif
}
GUTF8String retval;
if(!mesg)
{
tell();
}else
{
retval=mesg;
}
if(mesg &&(fp && must_close))
{
fclose(fp);
fp=0;
must_close=false;
}
return retval;
}
static FILE *
urlfopen(const GURL &url,const char mode[])
{
#ifdef WIN32
FILE *retval=0;
const GUTF8String filename(url.UTF8Filename());
wchar_t *wfilename;
const size_t wfilename_size=filename.length()+1;
GPBuffer<wchar_t> gwfilename(wfilename,wfilename_size);
if(filename.ncopy(wfilename,wfilename_size) > 0)
{
const GUTF8String gmode(mode);
wchar_t *wmode;
const size_t wmode_size=gmode.length()+1;
GPBuffer<wchar_t> gwmode(wmode,wmode_size);
if(gmode.ncopy(wmode,wmode_size) > 0)
{
retval=_wfopen(wfilename,wmode);
}
}
return retval?retval:fopen((const char *)url.NativeFilename(),mode);
#else
return fopen((const char *)url.NativeFilename(),mode);
#endif
}
#ifdef UNIX
static int
urlopen(const GURL &url, const int mode, const int perm)
{
return open((const char *)url.NativeFilename(),mode,perm);
}
#endif /* UNIX */
GUTF8String
ByteStream::Stdio::init(const GURL &url, const char mode[])
{
GUTF8String retval;
if (url.fname() != "-")
{
fp = urlfopen(url,mode);
if (!fp)
{
// Failed to open '%s': %s
G_THROW( ERR_MSG("ByteStream.open_fail") "\t" + url.name()
+"\t"+GNativeString(strerror(errno)).getNative2UTF8());
}
}
return retval.length()?retval:init(mode);
}
size_t
ByteStream::Stdio::read(void *buffer, size_t size)
{
if (!can_read)
G_THROW( ERR_MSG("ByteStream.no_read") ); // Stdio not opened for reading
size_t nitems;
do
{
clearerr(fp);
nitems = fread(buffer, 1, size, fp);
if (nitems<=0 && ferror(fp))
{
#ifdef EINTR
if (errno!=EINTR)
#endif
G_THROW(strerror(errno)); // (No error in the DjVuMessageFile)
}
else
break;
} while(true);
pos += nitems;
return nitems;
}
size_t
ByteStream::Stdio::write(const void *buffer, size_t size)
{
if (!can_write)
G_THROW( ERR_MSG("ByteStream.no_write") ); // Stdio not opened for writing
size_t nitems;
do
{
clearerr(fp);
nitems = fwrite(buffer, 1, size, fp);
if (nitems<=0 && ferror(fp))
{
#ifdef EINTR
if (errno!=EINTR)
#endif
G_THROW(strerror(errno)); // (No error in the DjVuMessageFile)
}
else
break;
} while(true);
pos += nitems;
return nitems;
}
void
ByteStream::Stdio::flush()
{
if (fflush(fp) < 0)
G_THROW(strerror(errno)); // (No error in the DjVuMessageFile)
}
long
ByteStream::Stdio::tell(void) const
{
long x = ftell(fp);
if (x >= 0)
{
Stdio *sbs=const_cast<Stdio *>(this);
(sbs->pos) = x;
}else
{
x=pos;
}
return x;
}
int
ByteStream::Stdio::seek(long offset, int whence, bool nothrow)
{
if (whence==SEEK_SET && offset>=0 && offset==ftell(fp))
return 0;
clearerr(fp);
if (fseek(fp, offset, whence))
{
if (nothrow)
return -1;
G_THROW(strerror(errno)); // (No error in the DjVuMessageFile)
}
return tell();
}
///////// ByteStream::Memory
ByteStream::Memory::Memory()
: where(0), bsize(0), nblocks(0), gblocks(blocks,0)
{
}
GUTF8String
ByteStream::Memory::init(void const * const buffer, const size_t sz)
{
GUTF8String retval;
G_TRY
{
writall(buffer, sz);
where = 0;
}
G_CATCH(ex) // The only error that should be thrown is out of memory...
{
retval=ex.get_cause();
}
G_ENDCATCH;
return retval;
}
void
ByteStream::Memory::empty()
{
for (int b=0; b<nblocks; b++)
{
delete [] blocks[b];
blocks[b]=0;
}
bsize = 0;
where = 0;
nblocks = 0;
}
ByteStream::Memory::~Memory()
{
empty();
}
size_t
ByteStream::Memory::write(const void *buffer, size_t sz)
{
int nsz = (int)sz;
if (nsz <= 0)
return 0;
// check memory
if ( (where+nsz) > ((bsize+0xfff)&~0xfff) )
{
// reallocate pointer array
if ( (where+nsz) > (nblocks<<12) )
{
const int old_nblocks=nblocks;
nblocks = (((where+nsz)+0xffff)&~0xffff) >> 12;
gblocks.resize(nblocks);
char const ** eblocks=(char const **)(blocks+old_nblocks);
for(char const * const * const new_eblocks=blocks+nblocks;
eblocks <new_eblocks; eblocks++)
{
*eblocks = 0;
}
}
// allocate blocks
for (int b=(where>>12); (b<<12)<(where+nsz); b++)
{
if (! blocks[b])
blocks[b] = new char[0x1000];
}
}
// write data to buffer
while (nsz > 0)
{
int n = (where|0xfff) + 1 - where;
n = ((nsz < n) ? nsz : n);
memcpy( (void*)&blocks[where>>12][where&0xfff], buffer, n);
buffer = (void*) ((char*)buffer + n);
where += n;
nsz -= n;
}
// adjust size
if (where > bsize)
bsize = where;
return sz;
}
size_t
ByteStream::Memory::readat(void *buffer, size_t sz, int pos)
{
if ((int) sz > bsize - pos)
sz = bsize - pos;
int nsz = (int)sz;
if (nsz <= 0)
return 0;
// read data from buffer
while (nsz > 0)
{
int n = (pos|0xfff) + 1 - pos;
n = ((nsz < n) ? nsz : n);
memcpy(buffer, (void*)&blocks[pos>>12][pos&0xfff], n);
buffer = (void*) ((char*)buffer + n);
pos += n;
nsz -= n;
}
return sz;
}
size_t
ByteStream::Memory::read(void *buffer, size_t sz)
{
sz = readat(buffer,sz,where);
where += sz;
return sz;
}
long
ByteStream::Memory::tell(void) const
{
return where;
}
int
ByteStream::Memory::seek(long offset, int whence, bool nothrow)
{
int nwhere = 0;
switch (whence)
{
case SEEK_SET: nwhere = 0; break;
case SEEK_CUR: nwhere = where; break;
case SEEK_END: nwhere = bsize; break;
default: G_THROW( ERR_MSG("bad_arg") "\tByteStream::Memory::seek()"); // Illegal argument in ByteStream::Memory::seek()
}
nwhere += offset;
if (nwhere<0)
G_THROW( ERR_MSG("ByteStream.seek_error2") ); // Attempt to seek before the beginning of the file
where = nwhere;
return 0;
}
/** This function has been moved into Arrays.cpp
In order to avoid dependencies from ByteStream.o
to Arrays.o */
#ifdef DO_NOT_MOVE_GET_DATA_TO_ARRAYS_CPP
TArray<char>
ByteStream::get_data(void)
{
TArray<char> data(0, size()-1);
readat((char*)data, size(), 0);
return data;
}
#endif
///////// ByteStream::Static
ByteStream::Static::Static(const void * const buffer, const size_t sz)
: data((const char *)buffer), bsize(sz), where(0)
{
}
size_t
ByteStream::Static::read(void *buffer, size_t sz)
{
int nsz = (int)sz;
if (nsz > bsize - where)
nsz = bsize - where;
if (nsz <= 0)
return 0;
memcpy(buffer, data+where, nsz);
where += nsz;
return nsz;
}
int
ByteStream::Static::seek(long offset, int whence, bool nothrow)
{
int nwhere = 0;
switch (whence)
{
case SEEK_SET: nwhere = 0; break;
case SEEK_CUR: nwhere = where; break;
case SEEK_END: nwhere = bsize; break;
default: G_THROW("bad_arg\tByteStream::Static::seek()"); // Illegal argument to ByteStream::Static::seek()
}
nwhere += offset;
if (nwhere<0)
G_THROW( ERR_MSG("ByteStream.seek_error2") ); // Attempt to seek before the beginning of the file
where = nwhere;
return 0;
}
long
ByteStream::Static::tell(void) const
{
return where;
}
GP<ByteStream>
ByteStream::create(void)
{
return new Memory();
}
GP<ByteStream>
ByteStream::create(void const * const buffer, const size_t size)
{
Memory *mbs=new Memory();
GP<ByteStream> retval=mbs;
mbs->init(buffer,size);
return retval;
}
GP<ByteStream>
ByteStream::create(const GURL &url,char const * const xmode)
{
GP<ByteStream> retval;
const char *mode = ((xmode) ? xmode : "rb");
#ifdef UNIX
if (!strcmp(mode,"rb"))
{
int fd = urlopen(url,O_RDONLY,0777);
if (fd >= 0)
{
#if HAS_MEMMAP && defined(S_IFREG)
struct stat buf;
if ( (fstat(fd, &buf) >= 0) && (buf.st_mode & S_IFREG) )
{
MemoryMapByteStream *rb = new MemoryMapByteStream();
retval = rb;
GUTF8String errmessage = rb->init(fd,true);
if(errmessage.length())
retval=0;
}
#endif
if (! retval)
{
FILE *f = fdopen(fd, mode);
if (f)
{
Stdio *sbs=new Stdio();
retval=sbs;
GUTF8String errmessage=sbs->init(f, mode, true);
if(errmessage.length())
retval=0;
}
}
if (! retval)
close(fd);
}
}
#endif
if (! retval)
{
Stdio *sbs=new Stdio();
retval=sbs;
GUTF8String errmessage=sbs->init(url, mode);
if(errmessage.length())
G_THROW(errmessage);
}
return retval;
}
GP<ByteStream>
ByteStream::create(char const * const mode)
{
GP<ByteStream> retval;
Stdio *sbs=new Stdio();
retval=sbs;
GUTF8String errmessage=sbs->init(mode?mode:"rb");
if(errmessage.length())
{
G_THROW(errmessage);
}
return retval;
}
GP<ByteStream>
ByteStream::create(const int fd,char const * const mode,const bool closeme)
{
GP<ByteStream> retval;
const char *default_mode="rb";
#if HAS_MEMMAP
if ( (!mode&&(fd!=0)&&(fd!=1)&&(fd!=2))
|| (mode&&(GUTF8String("rb") == mode)))
{
MemoryMapByteStream *rb=new MemoryMapByteStream();
retval=rb;
GUTF8String errmessage=rb->init(fd,closeme);
if(errmessage.length())
{
retval=0;
}
}
if(!retval)
#endif
{
int fd2 = fd;
FILE *f = 0;
if (fd == 0 && !closeme
&& (!mode || mode[0]=='r') )
{
f=stdin;
default_mode = "r";
fd2=(-1);
}
else if (fd == 1 && !closeme
&& (!mode || mode[0]=='a' || mode[0]=='w') )
{
default_mode = "a";
f=stdout;
fd2 = -1;
}
else if (fd == 2 && !closeme
&& (!mode || mode[0]=='a' || mode[0]=='w') )
{
default_mode = "a";
f=stderr;
fd2 = -1;
}
else
{
if (! closeme)
fd2 = dup(fd);
f = fdopen(fd2,(char*)(mode?mode:default_mode));
}
if(!f)
{
if ( fd2 >= 0)
close(fd2);
G_THROW( ERR_MSG("ByteStream.open_fail2") );
}
Stdio *sbs=new Stdio();
retval=sbs;
GUTF8String errmessage=sbs->init(f,mode?mode:default_mode,(fd2>=0));
if(errmessage.length())
G_THROW(errmessage);
}
return retval;
}
GP<ByteStream>
ByteStream::create(FILE * const f,char const * const mode,const bool closeme)
{
GP<ByteStream> retval;
#if HAS_MEMMAP
if (!mode || (GUTF8String("rb") == mode))
{
MemoryMapByteStream *rb=new MemoryMapByteStream();
retval=rb;
GUTF8String errmessage=rb->init(fileno(f),false);
if(errmessage.length())
{
retval=0;
}else
{
fclose(f);
}
}
if(!retval)
#endif
{
Stdio *sbs=new Stdio();
retval=sbs;
GUTF8String errmessage=sbs->init(f,mode?mode:"rb",closeme);
if(errmessage.length())
{
G_THROW(errmessage);
}
}
return retval;
}
GP<ByteStream>
ByteStream::create_static(const void * const buffer, size_t sz)
{
return new Static(buffer, sz);
}
GP<ByteStream>
ByteStream::duplicate(const size_t xsize) const
{
GP<ByteStream> retval;
const long int pos=tell();
const int tsize=size();
ByteStream &self=*(const_cast<ByteStream *>(this));
if(tsize < 0 || pos < 0 || (unsigned int)tsize < 1+(unsigned int)pos)
{
retval=ByteStream::create();
retval->copy(self,xsize);
retval->seek(0L);
}else
{
const size_t s=(size_t)tsize-(size_t)pos;
const int size=(!xsize||(s<xsize))?s:xsize;
ByteStream::Static::Allocate *bs=new ByteStream::Static::Allocate(size);
retval=bs;
self.readall(bs->buf,size);
}
self.seek(pos,SEEK_SET,true);
return retval;
}
#if HAS_MEMMAP
MemoryMapByteStream::MemoryMapByteStream(void)
: ByteStream::Static(0,0)
{}
GUTF8String
MemoryMapByteStream::init(FILE *const f,const bool closeme)
{
GUTF8String retval;
retval=init(fileno(f),false);
if(closeme)
{
fclose(f);
}
return retval;
}
GUTF8String
MemoryMapByteStream::init(const int fd,const bool closeme)
{
GUTF8String retval;
#if defined(PROT_READ) && defined(MAP_SHARED)
struct stat statbuf;
if(!fstat(fd,&statbuf))
{
if(statbuf.st_size)
{
bsize=statbuf.st_size;
data=(char *)mmap(0,statbuf.st_size,PROT_READ,MAP_SHARED,fd,0);
}
}else
{
if(closeme)
{
close(fd);
}
retval= ERR_MSG("ByteStream.open_fail2");
}
#else
retval= ERR_MSG("ByteStream.open_fail2");
#endif
if(closeme)
{
close(fd);
}
return retval;
}
MemoryMapByteStream::~MemoryMapByteStream()
{
if(data)
{
munmap(const_cast<char *>(data),bsize);
}
}
#endif
ByteStream::Wrapper::~Wrapper() {}
GP<ByteStream>
ByteStream::get_stdin(char const * const mode)
{
static GP<ByteStream> gp = ByteStream::create(0,mode,false);
return gp;
}
GP<ByteStream>
ByteStream::get_stdout(char const * const mode)
{
static GP<ByteStream> gp = ByteStream::create(1,mode,false);
return gp;
}
GP<ByteStream>
ByteStream::get_stderr(char const * const mode)
{
static GP<ByteStream> gp = ByteStream::create(2,mode,false);
return gp;
}
/** Looks up the message and writes it to the specified stream. */
void ByteStream::formatmessage( const char *fmt, ... )
{
va_list args;
va_start(args, fmt);
const GUTF8String message(fmt,args);
writemessage( message );
}
/** Looks up the message and writes it to the specified stream. */
void ByteStream::writemessage( const char *message )
{
writestring( DjVuMessage::LookUpUTF8( message ) );
}
static void
read_file(ByteStream &bs,char *&buffer,GPBuffer<char> &gbuffer)
{
const int size=bs.size();
int pos=0;
if(size>0)
{
size_t readsize=size+1;
gbuffer.resize(readsize);
for(int i;readsize&&(i=bs.read(buffer+pos,readsize))>0;pos+=i,readsize-=i)
EMPTY_LOOP;
}else
{
const size_t readsize=32768;
gbuffer.resize(readsize);
for(int i;((i=bs.read(buffer+pos,readsize))>0);
gbuffer.resize((pos+=i)+readsize))
EMPTY_LOOP;
}
buffer[pos]=0;
}
GNativeString
ByteStream::getAsNative(void)
{
char *buffer;
GPBuffer<char> gbuffer(buffer);
read_file(*this,buffer,gbuffer);
return GNativeString(buffer);
}
GUTF8String
ByteStream::getAsUTF8(void)
{
char *buffer;
GPBuffer<char> gbuffer(buffer);
read_file(*this,buffer,gbuffer);
return GUTF8String(buffer);
}
#ifdef HAVE_NAMESPACES
}
# ifndef NOT_USING_DJVU_NAMESPACE
using namespace DJVU;
# endif
#endif
void
DjVuPrintErrorUTF8(const char *fmt, ... )
{
G_TRY {
GP<ByteStream> errout = ByteStream::get_stderr();
if (errout)
{
errout->cp=ByteStream::NATIVE;
va_list args;
va_start(args, fmt);
const GUTF8String message(fmt,args);
errout->writestring(message);
}
// Need to catch all exceptions because these might be
// called from an outer exception handler (with prejudice)
} G_CATCH_ALL { } G_ENDCATCH;
}
void
DjVuPrintErrorNative(const char *fmt, ... )
{
G_TRY {
GP<ByteStream> errout = ByteStream::get_stderr();
if (errout)
{
errout->cp=ByteStream::NATIVE;
va_list args;
va_start(args, fmt);
const GNativeString message(fmt,args);
errout->writestring(message);
}
// Need to catch all exceptions because these might be
// called from an outer exception handler (with prejudice)
} G_CATCH_ALL { } G_ENDCATCH;
}
void
DjVuPrintMessageUTF8(const char *fmt, ... )
{
G_TRY {
GP<ByteStream> strout = ByteStream::get_stdout();
if (strout)
{
strout->cp=ByteStream::NATIVE;
va_list args;
va_start(args, fmt);
const GUTF8String message(fmt,args);
strout->writestring(message);
}
// Need to catch all exceptions because these might be
// called from an outer exception handler (with prejudice)
} G_CATCH_ALL { } G_ENDCATCH;
}
void
DjVuPrintMessageNative(const char *fmt, ... )
{
G_TRY {
GP<ByteStream> strout = ByteStream::get_stdout();
if (strout)
{
strout->cp=ByteStream::NATIVE;
va_list args;
va_start(args, fmt);
const GNativeString message(fmt,args);
strout->writestring(message);
}
// Need to catch all exceptions because these might be
// called from an outer exception handler (with prejudice)
} G_CATCH_ALL { } G_ENDCATCH;
}