source: trunk/libdjvu/DjVuPort.cpp @ 17

Last change on this file since 17 was 17, checked in by Eugene Romanenko, 15 years ago

update makefiles, remove absolute paths, update djvulibre to version 3.5.17

File size: 20.4 KB
Line 
1//C-  -*- C++ -*-
2//C- -------------------------------------------------------------------
3//C- DjVuLibre-3.5
4//C- Copyright (c) 2002  Leon Bottou and Yann Le Cun.
5//C- Copyright (c) 2001  AT&T
6//C-
7//C- This software is subject to, and may be distributed under, the
8//C- GNU General Public License, Version 2. The license should have
9//C- accompanied the software or you may obtain a copy of the license
10//C- from the Free Software Foundation at http://www.fsf.org .
11//C-
12//C- This program is distributed in the hope that it will be useful,
13//C- but WITHOUT ANY WARRANTY; without even the implied warranty of
14//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15//C- GNU General Public License for more details.
16//C-
17//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library
18//C- distributed by Lizardtech Software.  On July 19th 2002, Lizardtech
19//C- Software authorized us to replace the original DjVu(r) Reference
20//C- Library notice by the following text (see doc/lizard2002.djvu):
21//C-
22//C-  ------------------------------------------------------------------
23//C- | DjVu (r) Reference Library (v. 3.5)
24//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved.
25//C- | The DjVu Reference Library is protected by U.S. Pat. No.
26//C- | 6,058,214 and patents pending.
27//C- |
28//C- | This software is subject to, and may be distributed under, the
29//C- | GNU General Public License, Version 2. The license should have
30//C- | accompanied the software or you may obtain a copy of the license
31//C- | from the Free Software Foundation at http://www.fsf.org .
32//C- |
33//C- | The computer code originally released by LizardTech under this
34//C- | license and unmodified by other parties is deemed "the LIZARDTECH
35//C- | ORIGINAL CODE."  Subject to any third party intellectual property
36//C- | claims, LizardTech grants recipient a worldwide, royalty-free,
37//C- | non-exclusive license to make, use, sell, or otherwise dispose of
38//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the
39//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU
40//C- | General Public License.   This grant only confers the right to
41//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to
42//C- | the extent such infringement is reasonably necessary to enable
43//C- | recipient to make, have made, practice, sell, or otherwise dispose
44//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to
45//C- | any greater extent that may be necessary to utilize further
46//C- | modifications or combinations.
47//C- |
48//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY
49//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
50//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF
51//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
52//C- +------------------------------------------------------------------
53//
54// $Id: DjVuPort.cpp,v 1.10 2003/11/07 22:08:21 leonb Exp $
55// $Name:  $
56
57#ifdef HAVE_CONFIG_H
58# include "config.h"
59#endif
60#if NEED_GNUG_PRAGMAS
61# pragma implementation
62#endif
63
64#include "DjVuPort.h"
65#include "GOS.h"
66#include "DjVuImage.h"
67#include "DjVuDocument.h"
68#include "DjVuFile.h"
69#include "DjVuMessageLite.h"
70#include "DataPool.h"
71
72
73#ifdef HAVE_NAMESPACES
74namespace DJVU {
75# ifdef NOT_DEFINED // Just to fool emacs c++ mode
76}
77#endif
78#endif
79
80
81//****************************************************************************
82//******************************* Globals ************************************
83//****************************************************************************
84
85static DjVuPortcaster *pcaster;
86
87DjVuPortcaster *
88DjVuPort::get_portcaster(void)
89{
90   if (!pcaster) pcaster = new DjVuPortcaster();
91   return pcaster;
92}
93
94class DjVuPort::DjVuPortCorpse
95{
96public:
97   DjVuPort             * port;
98   DjVuPortCorpse       * next;
99
100   DjVuPortCorpse(DjVuPort * _port) : port(_port), next(0) {}
101};
102
103//****************************************************************************
104//******************************* DjVuPort ***********************************
105//****************************************************************************
106
107#define MAX_CORPSE_NUM  128
108
109// Last MAX_CORPSE_NUM addresses of dead DjVuPorts. We want to maintain this
110// list because of the way DjVuPort::is_port_alive() works: it accepts an
111// address and runs it thru its internal maps. The problem will occur if
112// a new DjVuPort is created exactly on place of another one, which just
113// died. Here we attempt to remember the last MAX_CORPSE_NUM addresses
114// of dead DjVuPorts, and take them into account in DjVuPort::operator new();
115GCriticalSection * DjVuPort::corpse_lock;
116DjVuPort::DjVuPortCorpse        * DjVuPort::corpse_head;
117DjVuPort::DjVuPortCorpse        * DjVuPort::corpse_tail;
118int             DjVuPort::corpse_num;
119
120void *
121DjVuPort::operator new (size_t sz)
122{
123  if (!corpse_lock) corpse_lock=new GCriticalSection();
124 
125  // Loop until we manage to allocate smth, which is not mentioned in
126  // the 'corpse' list. Thus we will avoid allocating a new DjVuPort
127  // on place of a dead one. Not *absolutely* secure (only 64 items
128  // in the list) but is still better than nothing.
129  void * addr=0;
130  {
131    GCriticalSectionLock lock(corpse_lock);
132   
133    // Store here addresses, which were found in 'corpse' list.
134    // We will free then in the end
135    int addr_num=0;
136    static void * addr_arr[MAX_CORPSE_NUM];
137   
138    // Make at most MAX_CORPSE_NUM attempts. During each attempt
139    // we try to allocate a block of memory for DjVuPort. If
140    // the address of this block is not in the corpse list, we break
141    // All addresses will be recorder, so that we can delete them
142    // after we're done.
143    for(int attempt=0;attempt<MAX_CORPSE_NUM;attempt++)
144    {
145      void * test_addr=::operator new (sz);
146      addr_arr[addr_num++]=test_addr;
147     
148      // See if 'test_addr' is in the 'corpse' list (was recently used)
149      DjVuPortCorpse * corpse;
150      for(corpse=corpse_head;corpse;corpse=corpse->next)
151        if (test_addr==corpse->port) break;
152        if (!corpse)
153        {
154          addr=test_addr;
155          addr_num--;
156          break;
157        }
158    }
159    // If all attempts failed (all addresses generated are already
160    // in the list of corpses, allocate a new one and proceed
161    // w/o additional checks
162    if (!addr) addr=::operator new(sz);
163   
164    // Here 'addr_arr[0<=i<addr_num]' contains addresses, that we
165    // tried to allocate, and which need to be freed now
166    // 'addr' contains address we want to use.
167    addr_num--;
168    while(addr_num>=0) ::operator delete(addr_arr[addr_num--]);
169  }
170 
171  DjVuPortcaster * pcaster=get_portcaster();
172  GCriticalSectionLock lock(&pcaster->map_lock);
173  pcaster->cont_map[addr]=0;
174  return addr;
175}
176
177void
178DjVuPort::operator delete(void * addr)
179{
180  if (corpse_lock)
181  {
182    GCriticalSectionLock lock(corpse_lock);
183   
184    // Add 'addr' to the list of corpses
185    if (corpse_tail)
186    {
187      corpse_tail->next=new DjVuPortCorpse((DjVuPort *) addr);
188      corpse_tail=corpse_tail->next;
189      corpse_tail->next=0;
190    } else
191    {
192      corpse_head=corpse_tail=new DjVuPortCorpse((DjVuPort *) addr);
193      corpse_tail->next=0;
194    }
195    corpse_num++;
196    if (corpse_num>=MAX_CORPSE_NUM)
197    {
198      DjVuPortCorpse * corpse=corpse_head;
199      corpse_head=corpse_head->next;
200      delete corpse;
201      corpse_num--;
202    }
203  }
204  ::operator delete(addr);
205}
206
207DjVuPort::DjVuPort()
208{
209  DjVuPortcaster *pcaster = get_portcaster();
210  GCriticalSectionLock lock(& pcaster->map_lock );
211  GPosition p = pcaster->cont_map.contains(this);
212  if (!p) G_THROW( ERR_MSG("DjVuPort.not_alloc") );
213  pcaster->cont_map[p] = (void*)this;
214}
215
216DjVuPort::DjVuPort(const DjVuPort & port)
217{
218  DjVuPortcaster *pcaster = get_portcaster();
219  GCriticalSectionLock lock(& pcaster->map_lock );
220  GPosition p = pcaster->cont_map.contains(this);
221  if (!p) G_THROW( ERR_MSG("DjVuPort.not_alloc") );
222  pcaster->cont_map[p] = (void*)this;
223  pcaster->copy_routes(this, &port);
224}
225
226DjVuPort &
227DjVuPort::operator=(const DjVuPort & port)
228{
229   if (this != &port)
230      get_portcaster()->copy_routes(this, &port);
231   return *this;
232}
233
234DjVuPort::~DjVuPort(void)
235{
236  get_portcaster()->del_port(this);
237}
238
239
240//****************************************************************************
241//**************************** DjVuPortcaster ********************************
242//****************************************************************************
243
244
245
246DjVuPortcaster::DjVuPortcaster(void)
247{
248}
249
250DjVuPortcaster::~DjVuPortcaster(void)
251{
252   GCriticalSectionLock lock(&map_lock);
253   for(GPosition pos=route_map;pos;++pos)
254      delete (GList<void *> *) route_map[pos];
255}
256
257GP<DjVuPort>
258DjVuPortcaster::is_port_alive(DjVuPort *port)
259{
260   GP<DjVuPort> gp_port;
261   GCriticalSectionLock lock(&map_lock);
262   GPosition pos=cont_map.contains(port);
263   if (pos && cont_map[pos] && ((DjVuPort *) port)->get_count()>0)
264      gp_port=port;
265   return gp_port;
266}
267
268void
269DjVuPortcaster::add_alias(const DjVuPort * port, const GUTF8String &alias)
270{
271   GCriticalSectionLock lock(&map_lock);
272   a2p_map[alias]=port;
273}
274
275void
276DjVuPortcaster::clear_all_aliases(void)
277{
278  DjVuPortcaster *p=get_portcaster();
279  GCriticalSectionLock lock(&(p->map_lock));
280  GPosition pos;
281  while((pos=p->a2p_map))
282  {
283    p->a2p_map.del(pos);
284  }
285}
286
287void
288DjVuPortcaster::clear_aliases(const DjVuPort * port)
289{
290  GCriticalSectionLock lock(&map_lock);
291  for(GPosition pos=a2p_map;pos;)
292    if (a2p_map[pos]==port)
293    {
294      GPosition this_pos=pos;
295      ++pos;
296      a2p_map.del(this_pos);
297    } else ++pos;
298}
299
300GP<DjVuPort>
301DjVuPortcaster::alias_to_port(const GUTF8String &alias)
302{
303   GCriticalSectionLock lock(&map_lock);
304   GPosition pos;
305   if (a2p_map.contains(alias, pos))
306   {
307      DjVuPort * port=(DjVuPort *) a2p_map[pos];
308      GP<DjVuPort> gp_port=is_port_alive(port);
309      if (gp_port) return gp_port;
310      else a2p_map.del(pos);
311   }
312   return 0;
313}
314
315GPList<DjVuPort>
316DjVuPortcaster::prefix_to_ports(const GUTF8String &prefix)
317{
318  GPList<DjVuPort> list;
319  {
320    int length=prefix.length();
321    if (length)
322    {
323      GCriticalSectionLock lock(&map_lock);
324      for(GPosition pos=a2p_map;pos;++pos)
325        if (!prefix.cmp(a2p_map.key(pos), length))
326        {
327          DjVuPort * port=(DjVuPort *) a2p_map[pos];
328          GP<DjVuPort> gp_port=is_port_alive(port);
329          if (gp_port) list.append(gp_port);
330        }
331    }
332  }
333  return list;
334}
335
336void
337DjVuPortcaster::del_port(const DjVuPort * port)
338{
339  GCriticalSectionLock lock(&map_lock);
340 
341  GPosition pos;
342 
343  // Update the "aliases map"
344  clear_aliases(port);
345 
346  // Update "contents map"
347  if (cont_map.contains(port, pos)) cont_map.del(pos);
348 
349  // Update "route map"
350  if (route_map.contains(port, pos))
351  {
352    delete (GList<void *> *) route_map[pos];
353    route_map.del(pos);
354  }
355  for(pos=route_map;pos;)
356  {
357    GList<void *> & list=*(GList<void *> *) route_map[pos];
358    GPosition list_pos;
359    if (list.search((void *) port, list_pos)) list.del(list_pos);
360    if (!list.size())
361    {
362      delete &list;
363      GPosition tmp_pos=pos;
364      ++pos;
365      route_map.del(tmp_pos);
366    } else ++pos;
367  }
368}
369
370void
371DjVuPortcaster::add_route(const DjVuPort * src, DjVuPort * dst)
372      // Adds route src->dst
373{
374   GCriticalSectionLock lock(&map_lock);
375   if (cont_map.contains(src) && src->get_count()>0 &&
376       cont_map.contains(dst) && dst->get_count()>0)
377   {
378      if (!route_map.contains(src)) route_map[src]=new GList<void *>();
379      GList<void *> & list=*(GList<void *> *) route_map[src];
380      if (!list.contains(dst)) list.append(dst);
381   }
382}
383
384void
385DjVuPortcaster::del_route(const DjVuPort * src, DjVuPort * dst)
386// Deletes route src->dst
387{
388  GCriticalSectionLock lock(&map_lock);
389 
390  if (route_map.contains(src))
391  {
392    GList<void *> & list=*(GList<void *> *) route_map[src];
393    GPosition pos;
394    if (list.search(dst, pos)) list.del(pos);
395    if (!list.size())
396    {
397      delete &list;
398      route_map.del(src);
399    }
400  }
401}
402
403void
404DjVuPortcaster::copy_routes(DjVuPort * dst, const DjVuPort * src)
405      // For every route src->x or x->src, it creates a new one:
406      // dst->x or x->dst respectively. It's useful when you create a copy
407      // of a port and you want the copy to stay connected.
408{
409  GCriticalSectionLock lock(&map_lock);
410 
411  if (!cont_map.contains(src) || src->get_count()<=0 ||
412    !cont_map.contains(dst) || dst->get_count()<=0) return;
413 
414  for(GPosition pos=route_map;pos;++pos)
415  {
416    GList<void *> & list=*(GList<void *> *) route_map[pos];
417    if (route_map.key(pos) == src)
418      for(GPosition pos=list;pos;++pos)
419        add_route(dst, (DjVuPort *) list[pos]);
420    for(GPosition pos=list;pos;++pos)
421      if ((DjVuPort*)(list[pos]) == src)
422        add_route((DjVuPort *) route_map.key(pos), dst);
423  }
424}
425
426void
427DjVuPortcaster::add_to_closure(GMap<const void *, void *> & set,
428                               const DjVuPort * dst, int distance)
429{
430  // Assuming that the map's already locked
431  // GCriticalSectionLock lock(&map_lock);
432  set[dst]= (void*) (unsigned long) distance;
433  if (route_map.contains(dst))
434    {
435      GList<void *> & list=*(GList<void *> *) route_map[dst];
436      for(GPosition pos=list;pos;++pos)
437        {
438          DjVuPort * new_dst=(DjVuPort *) list[pos];
439          if (!set.contains(new_dst)) 
440            add_to_closure(set, new_dst, distance+1);
441        }
442   }
443}
444
445void
446DjVuPortcaster::compute_closure(const DjVuPort * src, GPList<DjVuPort> &list, bool sorted)
447{
448   GCriticalSectionLock lock(&map_lock);
449   GMap<const void*, void*> set;
450   if (route_map.contains(src))
451   {
452      GList<void *> & list=*(GList<void *> *) route_map[src];
453      for(GPosition pos=list;pos;++pos)
454      {
455               DjVuPort * dst=(DjVuPort *) list[pos];
456               if (dst==src) add_to_closure(set, src, 0);
457               else add_to_closure(set, dst, 1);
458      }
459   }
460
461   // Compute list
462   GPosition pos;
463   if (sorted)
464     {
465       // Sort in depth order
466       int max_dist=0;
467       for(pos=set;pos;++pos)
468         if (max_dist < (int)(long)set[pos])
469           max_dist = (int)(long)set[pos];
470       GArray<GList<const void*> > lists(0,max_dist);
471       for(pos=set;pos;++pos)
472         lists[(int)(long)set[pos]].append(set.key(pos));
473       for(int dist=0;dist<=max_dist;dist++)
474         for(pos=lists[dist];pos;++pos)
475           {
476             GP<DjVuPort> p = is_port_alive((DjVuPort*) lists[dist][pos]);
477             if (p) list.append(p);
478           }
479     }
480   else
481     {
482       // Gather ports without order
483       for(pos=set;pos;++pos)
484         {
485           GP<DjVuPort> p = is_port_alive((DjVuPort*) set.key(pos));
486           if (p) list.append(p);
487         }
488     }
489}
490
491GURL
492DjVuPortcaster::id_to_url(const DjVuPort * source, const GUTF8String &id)
493{
494   GPList<DjVuPort> list;
495   compute_closure(source, list, true);
496   GURL url;
497   for(GPosition pos=list;pos;++pos)
498   {
499      url=list[pos]->id_to_url(source, id);
500      if (!url.is_empty()) break;
501   }
502   return url;
503}
504
505GP<DjVuFile>
506DjVuPortcaster::id_to_file(const DjVuPort * source, const GUTF8String &id)
507{
508   GPList<DjVuPort> list;
509   compute_closure(source, list, true);
510   GP<DjVuFile> file;
511   for(GPosition pos=list;pos;++pos)
512      if ((file=list[pos]->id_to_file(source, id))) break;
513   return file;
514}
515
516GP<DataPool>
517DjVuPortcaster::request_data(const DjVuPort * source, const GURL & url)
518{
519   GPList<DjVuPort> list;
520   compute_closure(source, list, true);
521   GP<DataPool> data;
522   for(GPosition pos=list;pos;++pos)
523     if ((data = list[pos]->request_data(source, url)))
524       break;
525   return data;
526}
527
528bool
529DjVuPortcaster::notify_error(const DjVuPort * source, const GUTF8String &msg)
530{
531   GPList<DjVuPort> list;
532   compute_closure(source, list, true);
533   for(GPosition pos=list;pos;++pos)
534     if (list[pos]->notify_error(source, msg))
535       return 1;
536   return 0;
537}
538
539bool
540DjVuPortcaster::notify_status(const DjVuPort * source, const GUTF8String &msg)
541{
542   GPList<DjVuPort> list;
543   compute_closure(source, list, true);
544   for(GPosition pos=list;pos;++pos)
545     if (list[pos]->notify_status(source, msg))
546       return 1;
547   return 0;
548}
549
550void
551DjVuPortcaster::notify_redisplay(const DjVuImage * source)
552{
553   GPList<DjVuPort> list;
554   compute_closure(source, list);
555   for(GPosition pos=list; pos; ++pos)
556     list[pos]->notify_redisplay(source);
557}
558
559void
560DjVuPortcaster::notify_relayout(const DjVuImage * source)
561{
562   GPList<DjVuPort> list;
563   compute_closure(source, list);
564   for(GPosition pos=list; pos; ++pos)
565     list[pos]->notify_relayout(source);
566}
567
568void
569DjVuPortcaster::notify_chunk_done(const DjVuPort * source, const GUTF8String &name)
570{
571   GPList<DjVuPort> list;
572   compute_closure(source, list);
573   for(GPosition pos=list; pos; ++pos)
574     list[pos]->notify_chunk_done(source, name);
575}
576
577void
578DjVuPortcaster::notify_file_flags_changed(const DjVuFile * source,
579                                          long set_mask, long clr_mask)
580{
581   GPList<DjVuPort> list;
582   compute_closure(source, list);
583   for(GPosition pos=list; pos; ++pos)
584     list[pos]->notify_file_flags_changed(source, set_mask, clr_mask);
585}
586
587void
588DjVuPortcaster::notify_doc_flags_changed(const DjVuDocument * source,
589                                         long set_mask, long clr_mask)
590{
591   GPList<DjVuPort> list;
592   compute_closure(source, list);
593   for(GPosition pos=list; pos; ++pos)
594     list[pos]->notify_doc_flags_changed(source, set_mask, clr_mask);
595}
596
597void
598DjVuPortcaster::notify_decode_progress(const DjVuPort * source, float done)
599{
600   GPList<DjVuPort> list;
601   compute_closure(source, list);
602   for(GPosition pos=list; pos; ++pos)
603     list[pos]->notify_decode_progress(source, done);
604}
605
606//****************************************************************************
607//******************************* DjVuPort ***********************************
608//****************************************************************************
609
610GURL
611DjVuPort::id_to_url(const DjVuPort *, const GUTF8String &) { return GURL(); }
612
613GP<DjVuFile>
614DjVuPort::id_to_file(const DjVuPort *, const GUTF8String &) { return 0; }
615
616GP<DataPool>
617DjVuPort::request_data(const DjVuPort *, const GURL &) { return 0; }
618
619bool
620DjVuPort::notify_error(const DjVuPort *, const GUTF8String &) { return 0; }
621
622bool
623DjVuPort::notify_status(const DjVuPort *, const GUTF8String &) { return 0; }
624
625void
626DjVuPort::notify_redisplay(const DjVuImage *) {}
627
628void
629DjVuPort::notify_relayout(const DjVuImage *) {}
630
631void
632DjVuPort::notify_chunk_done(const DjVuPort *, const GUTF8String &) {}
633
634void
635DjVuPort::notify_file_flags_changed(const DjVuFile *, long, long) {}
636
637void
638DjVuPort::notify_doc_flags_changed(const DjVuDocument *, long, long) {}
639
640void
641DjVuPort::notify_decode_progress(const DjVuPort *, float) {}
642
643//****************************************************************************
644//*************************** DjVuSimplePort *********************************
645//****************************************************************************
646
647GP<DataPool>
648DjVuSimplePort::request_data(const DjVuPort * source, const GURL & url)
649{
650  G_TRY {
651    if (url.is_local_file_url())
652    {
653//      GUTF8String fname=GOS::url_to_filename(url);
654//      if (GOS::basename(fname)=="-") fname="-";
655      return DataPool::create(url);
656    }
657  } G_CATCH_ALL {} G_ENDCATCH;
658  return 0;
659}
660
661bool
662DjVuSimplePort::notify_error(const DjVuPort * source, const GUTF8String &msg)
663{
664   DjVuMessageLite::perror(msg);
665   return 1;
666}
667
668bool
669DjVuSimplePort::notify_status(const DjVuPort * source, const GUTF8String &msg)
670{
671   DjVuMessageLite::perror(msg);
672   return 1;
673}
674
675
676
677
678
679//****************************************************************************
680//*************************** DjVuMemoryPort *********************************
681//****************************************************************************
682
683
684
685GP<DataPool>
686DjVuMemoryPort::request_data(const DjVuPort * source, const GURL & url)
687{
688   GCriticalSectionLock lk(&lock);
689   GP<DataPool> pool;
690   GPosition pos;
691   if (map.contains(url, pos))
692      pool=map[pos];
693   return pool;
694}
695
696void
697DjVuMemoryPort::add_data(const GURL & url, const GP<DataPool> & pool)
698{
699   GCriticalSectionLock lk(&lock);
700   map[url]=pool;
701}
702
703
704#ifdef HAVE_NAMESPACES
705}
706# ifndef NOT_USING_DJVU_NAMESPACE
707using namespace DJVU;
708# endif
709#endif
710
Note: See TracBrowser for help on using the repository browser.