source: trunk/libdjvu/DjVuPort.cpp @ 209

Last change on this file since 209 was 206, checked in by Eugene Romanenko, 14 years ago

DJVU plugin: djvulibre updated to version 3.5.19

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