1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
|
//C- -*- C++ -*-
//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: DataPool.h,v 1.10 2003/11/07 22:08:20 leonb Exp $
// $Name: release_3_5_15 $
#ifndef _DATAPOOL_H
#define _DATAPOOL_H
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#if NEED_GNUG_PRAGMAS
# pragma interface
#endif
#include "GThreads.h"
#include "GString.h"
#include "GURL.h"
#ifdef HAVE_NAMESPACES
namespace DJVU {
# ifdef NOT_DEFINED // Just to fool emacs c++ mode
}
#endif
#endif
class ByteStream;
/** @name DataPool.h
Files #"DataPool.h"# and #"DataPool.cpp"# implement classes \Ref{DataPool}
and \Ref{DataRange} used by DjVu decoder to access data.
The main goal of class \Ref{DataPool} is to provide concurrent access
to the same data from many threads with a possibility to add data
from yet another thread. It is especially important in the case of the
Netscape plugin when data is not immediately available, but decoding
should be started as soon as possible. In this situation it is vital
to provide transparent access to the data from many threads possibly
blocking readers that try to access information that has not been
received yet.
When the data is local though, it can be accessed directly using
standard IO mechanism. To provide a uniform interface for decoding
routines, \Ref{DataPool} supports file mode as well.
@memo Thread safe data storage
@author Andrei Erofeev <eaf@geocities.com>
@version #$Id: DataPool.h,v 1.10 2003/11/07 22:08:20 leonb Exp $#
*/
//@{
/** Thread safe data storage.
The purpose of #DataPool# is to provide a uniform interface for
accessing data from decoding routines running in a multi-threaded
environment. Depending on the mode of operation it may contain the
actual data, may be connected to another #DataPool# or may be mapped
to a file. Regardless of the mode, the class returns data in a
thread-safe way, blocking reading threads if there is no data of
interest available. This blocking is especially useful in the
networking environment (plugin) when there is a running decoding thread,
which wants to start decoding as soon as there is just one byte available
blocking if necessary.
Access to data in a #DataPool# may be direct (Using \Ref{get_data}()
function) or sequential (See \Ref{get_stream}() function).
If the #DataPool# is not connected to anything, that is it contains
some real data, this data can be added to it by means of two
\Ref{add_data}() functions. One of them adds data sequentially maintaining
the offset of the last block of data added by it. The other can store
data anywhere. Thus it's important to realize, that there may be "white
spots" in the data storage.
There is also a way to test if data is available for some given data
range (See \Ref{has_data}()). In addition to this mechanism, there are
so-called {\em trigger callbacks}, which are called, when there is
all data available for a given data range.
Let us consider all modes of operation in details:
\begin{enumerate}
\item {\bf Not connected #DataPool#}. In this mode the #DataPool#
contains some real data. As mentioned above, it may be added
by means of two functions \Ref{add_data}() operating independent
of each other and allowing to add data sequentially and
directly to any place of data storage. It's important to call
function \Ref{set_eof}() after all data has been added.
Functions like \Ref{get_data}() or \Ref{get_stream}() can
be used to obtain direct or sequential access to the data. As
long as \Ref{is_eof}() is #FALSE#, #DataPool# will block every
reader, which is trying to read unavailable data until it
really becomes available. But as soon as \Ref{is_eof}() is
#TRUE#, any attempt to read non-existing data will read #0# bytes.
Taking into account the fact, that #DataPool# was designed to
store DjVu files, which are in IFF formats, it becomes possible
to predict the size of the #DataPool# as soon as the first
#32# bytes have been added. This is invaluable for estimating
download progress. See function \Ref{get_length}() for details.
If this estimate fails (which means, that stored data is not
in IFF format), \Ref{get_length}() returns #-1#.
Triggers may be added and removed by means of \Ref{add_trigger}()
and \Ref{del_trigger}() functions. \Ref{add_trigger}() takes
a data range. As soon as all data in that data range is
available, the trigger callback will be called.
All trigger callbacks will be called when #EOF# condition
has been set.
\item {\bf #DataPool# connected to another #DataPool#}. In this
{\em slave} mode you can map a given #DataPool# to any offsets
range inside another #DataPool#. You can connect the slave
#DataPool# even if there is no data in the master #DataPool#.
Any \Ref{get_data}() request will be forwarded to the master
#DataPool#, and it will be responsible for blocking readers
trying to access unavailable data.
The usage of \Ref{add_data}() functions is prohibited for
connected #DataPool#s.
The offsets range used to map a slave #DataPool# can be fully
specified (both start offset and length are positive numbers)
or partially specified (the length is negative). In this mode
the slave #DataPool# is assumed to extend up to the end
of the master #DataPool#.
Triggers may be used with slave #DataPool#s as well as with
the master ones.
Calling \Ref{stop}() function of a slave will stop only the slave
(and any other slave connected to it), but not the master.
\Ref{set_eof}() function is meaningless for slaves. They obtain
the #ByteStream::EndOfFile# status from their master.
Depending on the offsets range passed to the constructor,
\Ref{get_length}() returns different values. If the length
passed to the constructor was positive, then it is returned
by \Ref{get_length}() all the time. Otherwise the value returned
is either #-1# if master's length is still unknown (it didn't
manage to parse IFF data yet) or it is calculated as
#masters_length-slave_start#.
\item {\bf #DataPool# connected to a file}. This mode is quite similar
to the case, when the #DataPool# is connected to another
#DataPool#. Similarly, the #DataPool# stores no data inside.
It just forwards all \Ref{get_data}() requests to the underlying
source (a file in this case). Thus these requests will never
block the reader. But they may return #0# if there is no data
available at the requested offset.
The usage of \Ref{add_data}() functions is meaningless and
is prohibited.
\Ref{is_eof}() function always returns #TRUE#. Thus \Ref{set_eof}()
us meaningless and does nothing.
\Ref{get_length}() function always returns the file size.
Calling \Ref{stop}() function will stop this #DataPool# and
any other slave connected to it.
Trigger callbacks passed through \Ref{add_trigger}() function
are called immediately.
This mode is useful to read and decode DjVu files without reading
and storing them in full in memory.
\end{enumerate}
*/
class DataPool : public GPEnabled
{
public: // Classes used internally by DataPool
// These are declared public to support buggy C++ compilers.
class Incrementor;
class Reader;
class Trigger;
class OpenFiles;
class OpenFiles_File;
class BlockList;
class Counter;
protected:
DataPool(void);
public:
/** @name Initialization */
//@{
/** Default creator. Will prepare #DataPool# for accepting data
added through functions \Ref{add_data}(). Use \Ref{connect}()
functions if you want to map this #DataPool# to another or
to a file. */
static GP<DataPool> create(void);
/** Creates and initialized the #DataPool# with data from stream #str#.
The constructor will read the stream's contents and add them
to the pool using the \Ref{add_data}() function. Afterwards it
will call \Ref{set_eof}() function, and no other data will be
allowed to be added to the pool. */
static GP<DataPool> create(const GP<ByteStream> & str);
/** Initializes the #DataPool# in slave mode and connects it
to the specified offsets range of the specified master #DataPool#.
It is equivalent to calling default constructor and function
\Ref{connect}().
@param master_pool Master #DataPool# providing data for this slave
@param start Beginning of the offsets range which the slave is
mapped into
@param length Length of the offsets range. If negative, the range
is assumed to extend up to the end of the master #DataPool#.
*/
static GP<DataPool> create(const GP<DataPool> & master_pool, int start=0, int length=-1);
/** Initializes the #DataPool# in slave mode and connects it
to the specified offsets range of the specified file.
It is equivalent to calling default constructor and function
\Ref{connect}().
@param url Name of the file to connect to.
@param start Beginning of the offsets range which the #DataPool# is
mapped into
@param length Length of the offsets range. If negative, the range
is assumed to extend up to the end of the file.
*/
static GP<DataPool> create(const GURL &url, int start=0, int length=-1);
virtual ~DataPool();
/** Switches the #DataPool# to slave mode and connects it to the
specified offsets range of the master #DataPool#.
@param master_pool Master #DataPool# providing data for this slave
@param start Beginning of the offsets range which the slave is
mapped into
@param length Length of the offsets range. If negative, the range
is assumed to extend up to the end of the master #DataPool#.
*/
void connect(const GP<DataPool> & master_pool, int start=0, int length=-1);
/** Connects the #DataPool# to the specified offsets range of
the named #url#.
@param url Name of the file to connect to.
@param start Beginning of the offsets range which the #DataPool# is
mapped into
@param length Length of the offsets range. If negative, the range
is assumed to extend up to the end of the file.
*/
void connect(const GURL &url, int start=0, int length=-1);
//@}
/** Tells the #DataPool# to stop serving readers.
If #only_blocked# flag is #TRUE# then only those requests will
be processed, which would not block. Any attempt to get non-existing
data would result in a #STOP# exception (instead of blocking until
data is available).
If #only_blocked# flag is #FALSE# then any further attempt to read
from this #DataPool# (as well as from any #DataPool# connected
to this one) will result in a #STOP# exception. */
void stop(bool only_blocked=false);
/** @name Adding data.
Please note, that these functions are for not connected #DataPool#s
only. You can not add data to a #DataPool#, which is connected
to another #DataPool# or to a file.
*/
//@{
/** Appends the new block of data to the #DataPool#. There are two
\Ref{add_data}() functions available. One is for adding data
sequentially. It keeps track of the last byte position, which has
been stored {\bf by it} and always appends the next block after
this position. The other \Ref{add_data}() can store data anywhere.
The function will unblock readers waiting for data if this data
arrives with this block. It may also trigger some {\em trigger
callbacks}, which may have been added by means of \Ref{add_trigger}()
function.
{\bf Note:} After all the data has been added, it's necessary
to call \Ref{set_eof}() to tell the #DataPool# that nothing else
is expected.
{\bf Note:} This function may not be called if the #DataPool#
has been connected to something.
@param buffer data to append
@param size length of the {\em buffer}
*/
void add_data(const void * buffer, int size);
/** Stores the specified block of data at the specified offset.
Like the function above this one can also unblock readers
waiting for data and engage trigger callbacks. The difference
is that {\bf this} function can store data anywhere.
{\bf Note:} After all the data has been added, it's necessary
to call \Ref{set_eof}() to tell the #DataPool# that nothing else
is expected.
{\bf Note:} This function may not be called if the #DataPool#
has been connected to something.
@param buffer data to store
@param offset where to store the data
@param size length of the {\em buffer} */
void add_data(const void * buffer, int offset, int size);
/** Tells the #DataPool# that all data has been added and nothing else
is anticipated. When #EOF# is true, any reader attempting to read
non existing data will not be blocked. It will either read #ZERO#
bytes or will get an #ByteStream::EndOfFile# exception (see \Ref{get_data}()).
Calling this function will also activate all registered trigger
callbacks.
{\bf Note:} This function is meaningless and does nothing
when the #DataPool# is connected to another #DataPool# or to
a file. */
void set_eof(void);
//@}
/** @name Accessing data.
These functions provide direct and sequential access to the
data of the #DataPool#. If the #DataPool# is not connected
(contains some real data) then it handles the requests itself.
Otherwise they are forwarded to the master #DataPool# or the file.
*/
//@{
/** Attempts to return a block of data at the given #offset#
of the given #size#.
\begin{enumerate}
\item If the #DataPool# is connected to another #DataPool# or
to a file, the request will just be forwarded to them.
\item If the #DataPool# is not connected to anything and
some of the data requested is in the internal buffer,
the function copies available data to #buffer# and returns
immediately.
If there is no data available, and \Ref{is_eof}() returns
#FALSE#, the reader (and the thread) will be {\bf blocked}
until the data actually arrives. Please note, that since
the reader is blocked, it should run in a separate thread
so that other threads have a chance to call \Ref{add_data}().
If there is no data available, but \Ref{is_eof}() is #TRUE#
the behavior is different and depends on the #DataPool#'s
estimate of the file size:
\begin{itemize}
\item If #DataPool# learns from the IFF structure of the
data, that its size should be greater than it
really is, then any attempt to read non-existing
data in the range of {\em valid} offsets will
result in an #ByteStream::EndOfFile# exception. This is done to
indicate, that there was an error in adding data,
and the data requested is {\bf supposed} to be
there, but has actually not been added.
\item If #DataPool#'s expectations about the data size
coincide with the reality then any attempt to
read data beyond the legal range of offsets will
result in #ZERO# bytes returned.
\end{itemize}.
\end{enumerate}.
@param buffer Buffer to be filled with data
@param offset Offset in the #DataPool# to read data at
@param size Size of the {\em buffer}
@return The number of bytes actually read
@exception STOP The stream has been stopped
@exception EOF The requested data is not there and will not be added,
although it should have been.
*/
int get_data(void * buffer, int offset, int size);
/** Returns a \Ref{ByteStream} to access contents of the #DataPool#
sequentially. By reading from the returned stream you basically
call \Ref{get_data}() function. Thus, everything said for it
remains true for the stream too. */
GP<ByteStream> get_stream(void);
//@}
/** @name State querying functions. */
//@{
/** Returns #TRUE# if this #DataPool# is connected to another #DataPool#
or to a file. */
bool is_connected(void) const;
/** Returns #TRUE# if all data available for offsets from
#start# till #start+length-1#. If #length# is negative, the
range is assumed to extend up to the end of the #DataPool#.
This function works both for connected and not connected #DataPool#s.
Once it returned #TRUE# for some offsets range, you can be
sure that the subsequent \Ref{get_data}() request will not block.
*/
bool has_data(int start, int length);
/* Returns #TRUE# if no more data is planned to be added.
{\bf Note:} This function always returns #TRUE# when the #DataPool#
has been initialized with a file name. */
bool is_eof(void) const {return eof_flag;}
/** Returns the {\em length} of data in the #DataPool#. The value
returned depends on the mode of operation:
\begin{itemize}
\item If the #DataPool# is not connected to anything then
the length returned is either calculated by interpreting
the IFF structure of stored data (if successful) or
by calculating the real size of data after \Ref{set_eof}()
has been called. Otherwise it is #-1#.
\item If the #DataPool# is connected to a file, the length
is calculated basing on the length passed to the
\Ref{connect}() function and the file size.
\item If the #DataPool# is connected to a master #DataPool#,
the length is calculated basing on the value returned
by the master's #get_length()# function and the length
passed to the \Ref{connect}() function.
\end{itemize}. */
int get_length(void) const;
/** Returns the number of bytes of data available in this #DataPool#.
Contrary to the \Ref{get_length}() function, this one doesn't try
to interpret the IFF structure and predict the file length.
It just returns the number of bytes of data really available inside
the #DataPool#, if it contains data, or inside its range, if it's
connected to another #DataPool# or a file. */
int get_size(void) const {return get_size(0, -1);}
//@}
/** @name Trigger callbacks.
{\em Trigger callbacks} are special callbacks called when
all data for the given range of offsets has been made available.
Since reading unavailable data may result in a thread block,
which may be bad, the usage of {\em trigger callbacks} appears
to be a convenient way to signal availability of data.
You can add a trigger callback in two ways:
\begin{enumerate}
\item By specifying a range. This is the most general case
\item By providing just one {\em threshold}. In this case
the range is assumed to start from offset #ZERO# and
last for {\em threshold}+1 bytes.
\end{enumerate}
*/
//@{
/** Associates the specified {\em trigger callback} with the
given data range.
{\bf Note:} The callback may be called immediately if all
data for the given range is already available or #EOF# is #TRUE#.
@param start The beginning of the range for which all data
should be available
@param length If the {\em length} is not negative then the callback
will be called when there is data available for every
offset from {\em start} to {\em start+length-1}.
If {\em thresh} is negative, the callback is called after
#EOF# condition has been set.
@param callback Function to call
@param cl_data Argument to pass to the callback when it's called. */
void add_trigger(int start, int length,
// void (* callback)(GP<GPEnabled> &), GP<GPEnabled> cl_data);
void (* callback)(void *), void * cl_data);
/** Associates the specified {\em trigger callback} with the
specified threshold.
This function is a simplified version of the function above.
The callback will be called when there is data available for
every offset from #0# to #thresh#, if #thresh# is positive, or
when #EOF# condition has been set otherwise. */
// void add_trigger(int thresh, void (* callback)(GP<GPEnabled> &), GP<GPEnabled> cl_data);
void add_trigger(int thresh, void (* callback)(void *), void * cl_data);
/** Use this function to unregister callbacks, which are no longer
needed. {\bf Note!} It's important to do it when the client
is about to be destroyed. */
void del_trigger(void (* callback)(void *), void * cl_data);
// void del_trigger(void (* callback)(GP<GPEnabled> &), GP<GPEnabled> cl_data);
//@}
/** Loads data from the file into memory. This function is only useful
for #DataPool#s getting data from a file. It descends the #DataPool#s
hierarchy until it either reaches a file-connected #DataPool#
or #DataPool# containing the real data. In the latter case it
does nothing, in the first case it makes the #DataPool# read all
data from the file into memory and stop using the file.
This may be useful when you want to overwrite the file and leave
existing #DataPool#s with valid data. */
void load_file(void);
/** This function will make every #DataPool# in the program, which
is connected to a file, to load the file contents to the main
memory and close the file. This feature is important when you
want to do something with the file like remove or overwrite it
not affecting the rest of the program. */
static void load_file(const GURL &url);
/** This function will remove OpenFiles filelist. */
static void close_all(void);
// Internal. Used by 'OpenFiles'
void clear_stream(const bool release = true);
/** Useful in comparing data pools. Returns true if dirived from
same URL or bytestream. */
bool simple_compare(DataPool &pool) const;
private:
bool eof_flag;
bool stop_flag;
bool stop_blocked_flag;
Counter *active_readers;
// Source or storage of data
GP<DataPool> pool;
GURL furl;
GP<OpenFiles_File> fstream;
GCriticalSection class_stream_lock;
GP<ByteStream> data;
GCriticalSection data_lock;
BlockList *block_list;
int add_at;
int start, length;
// List of readers waiting for data
GPList<Reader> readers_list;
GCriticalSection readers_lock;
// Triggers
GPList<Trigger> triggers_list; // List of passed or our triggers
GCriticalSection triggers_lock; // Lock for the list above
GCriticalSection trigger_lock; // Lock for static_trigger_cb()
void init(void);
void wait_for_data(const GP<Reader> & reader);
void wake_up_all_readers(void);
void check_triggers(void);
int get_data(void * buffer, int offset, int size, int level);
int get_size(int start, int length) const;
void restart_readers(void);
// static void static_trigger_cb(GP<GPEnabled> &);
static void static_trigger_cb(void *);
void trigger_cb(void);
void analyze_iff(void);
void added_data(const int offset, const int size);
public:
static const char *Stop;
friend class FCPools;
};
inline bool
DataPool::simple_compare(DataPool &pool) const
{
// return true if these pools are identical. False means they may or may
// not be identical.
return (this == &pool)
||(furl.is_valid()&&!furl.is_empty()&&pool.furl.is_valid()&&(furl == pool.furl))
||(data && (data == pool.data));
}
inline bool
DataPool::is_connected(void) const
{
return furl.is_local_file_url() || pool!=0;
}
//@}
#ifdef HAVE_NAMESPACES
}
# ifndef NOT_USING_DJVU_NAMESPACE
using namespace DJVU;
# endif
#endif
#endif
|