1 #ifndef OBJTOOLS_READERS_SEQDB__SEQDBATLAS_HPP
2 #define OBJTOOLS_READERS_SEQDB__SEQDBATLAS_HPP
3 
4 /*  $Id: seqdbatlas.hpp 631567 2021-05-19 13:53:46Z ivanov $
5  * ===========================================================================
6  *
7  *                            PUBLIC DOMAIN NOTICE
8  *               National Center for Biotechnology Information
9  *
10  *  This software/database is a "United States Government Work" under the
11  *  terms of the United States Copyright Act.  It was written as part of
12  *  the author's official duties as a United States Government employee and
13  *  thus cannot be copyrighted.  This software/database is freely available
14  *  to the public for use. The National Library of Medicine and the U.S.
15  *  Government have not placed any restriction on its use or reproduction.
16  *
17  *  Although all reasonable efforts have been taken to ensure the accuracy
18  *  and reliability of the software and data, the NLM and the U.S.
19  *  Government do not and cannot warrant the performance or results that
20  *  may be obtained by using this software or data. The NLM and the U.S.
21  *  Government disclaim all warranties, express or implied, including
22  *  warranties of performance, merchantability or fitness for any particular
23  *  purpose.
24  *
25  *  Please cite the author in any work or product based on this material.
26  *
27  * ===========================================================================
28  *
29  * Author:  Kevin Bealer
30  *
31  */
32 
33 /// @file seqdbatlas.hpp
34 /// The SeqDB memory management layer.
35 ///
36 /// Defines classes:
37 ///     CSeqDBLockHold
38 ///     CSeqDBMemReg
39 ///     CSeqDB_AtlasRegionHolder
40 ///     CSeqDBAtlas
41 ///     CSeqDBAtlasHolder
42 ///
43 /// Implemented for: UNIX, MS-Windows
44 
45 
46 #include <corelib/ncbimtx.hpp>
47 #include <corelib/ncbiatomic.hpp>
48 #include <corelib/ncbifile.hpp>
49 #include <corelib/ncbi_system.hpp>
50 #include <corelib/ncbiapp_api.hpp>
51 
52 #include <objtools/blast/seqdb_reader/impl/seqdbgeneral.hpp>
53 
54 #include <vector>
55 #include <map>
56 #include <set>
57 #include <mutex>
58 
59 BEGIN_NCBI_SCOPE
60 
61 #ifdef SEQDB_TRACE_LOGFILE
62 
63 #define SEQDB_LOGCLASS_DEFAULT 1
64 #define SEQDB_LOGCLASS_MMAP    2
65 #define SEQDB_LOGCLASS_MFILE   4
66 #define SEQDB_LOGCLASS_GET     8
67 
68 extern ofstream * seqdb_logfile;
69 extern int        seqdb_logclass;
70 
71 void seqdb_log(const char * s);
72 void seqdb_log(const char * s1, const string & s2);
73 
74 void seqdb_log(int cl, const char * s);
75 void seqdb_log(int cl, const char * s1, const string & s2);
76 void seqdb_log(int cl, const char * s1, int s2);
77 #endif // SEQDB_TRACE_LOGFILE
78 
79 #ifdef _DEBUG
80 
81 #include <iostream>
82 
83 // These defines implement a system for testing memory corruption,
84 // especially overwritten pointers and clobbered objects.  They are
85 // only enabled in debug mode.
86 
87 // If this is required for release mode as well, be sure to replace
88 // _ASSERT() with something useful for release mode.
89 
90 /// Define memory marker for class (4+ bytes of uppercase ascii).
91 
92 // Note: this is now strictly 4 bytes and little-endian-centric (on
93 // big endian architectures the bytes will be in the other order than
94 // the string would imply.  This should not matter to the code.
95 
96 #define INT4IFY_STRING(a)    \
97     (((a[3] & 0xFF) << 24) | \
98      ((a[2] & 0xFF) << 16) | \
99      ((a[1] & 0xFF) <<  8) | \
100      ((a[0] & 0xFF)))
101 
102 #define CLASS_MARKER_FIELD(a) \
103     static int    x_GetClassMark()  { return INT4IFY_STRING(a);  } \
104     static string x_GetMarkString() { return string((a a), 4); } \
105     int m_ClassMark;
106 
107 /// Marker initializer for constructor
108 #define INIT_CLASS_MARK() m_ClassMark = x_GetClassMark()
109 
110 /// Assertion to verify the marker
111 #define CHECK_MARKER() \
112    if (m_ClassMark != x_GetClassMark()) { \
113        cout << "Marker=" << m_ClassMark << endl; \
114        cout << "GetMrk=" << x_GetClassMark() << endl; \
115        cout << "\n!! Broken  [" << x_GetMarkString() << "] mark detected.\n" \
116             << "!! Mark is [" << hex << m_ClassMark << "], should be [" \
117             << hex << x_GetClassMark() << "]." << endl; \
118        _ASSERT(m_ClassMark == x_GetClassMark()); \
119    }
120 
121 /// Make the marker of this class invalid.
122 #define BREAK_MARKER() m_ClassMark |= 0x20202020;
123 
124 #else
125 
126 /// Define memory marker for class  (release mode code elision)
127 #define CLASS_MARKER_FIELD(a)
128 
129 /// Initializer for constructor (release mode code elision)
130 #define INIT_CLASS_MARK()
131 
132 /// Assertion to verify the marker (release mode code elision)
133 #define CHECK_MARKER()
134 
135 /// Make the marker of this class invalid (release mode code elision)
136 #define BREAK_MARKER()
137 
138 #endif
139 
140 /// CSeqDBAtlas class - a collection of memory maps.
141 class CSeqDBAtlas; // WorkShop needs this forward declaration.
142 class CSeqDBFileMemMap;
143 
144 /// Return path with delimiters changed to platform preferred kind.
145 ///
146 /// The path is modified and returned.  The 'Make' interface is more
147 /// convenient for cases where the input path and output path are
148 /// different objects.  Delimiter conversion should be called by SeqDB
149 /// at least once on any path received from the user, or via
150 /// filesystem sources such as alias files.
151 ///
152 /// @param dbs This is the input path.
153 /// @return The modified path is returned.
154 string SeqDB_MakeOSPath(const string & dbs);
155 
156 
157 /// CSeqDBLockHold
158 ///
159 /// This class is used to keep track of whether this thread holds the
160 /// atlas lock.  The atlas code will skip subsequent Lock() operations
161 /// during the same transaction if the lock is already held.  This
162 /// allows code that needs locking to get the lock without worrying
163 /// about whether the calling function has already done so.  The
164 /// destructor of this object will call Unlock() on the atlas if this
165 /// thread has it locked.
166 
167 class NCBI_XOBJREAD_EXPORT CSeqDBLockHold {
168 public:
169     /// Constructor
170     ///
171     /// This object constructs to an unlocked state, which means that
172     /// the thread owning the stack frame where this object is stored
173     /// does not hold the atlas lock.  This object keeps a reference
174     /// to the atlas object so that it can release the lock on
175     /// destruction, making it easier to write exception safe code.
176     /// @param atlas
177     ///   A reference to the atlas object.
CSeqDBLockHold(class CSeqDBAtlas & atlas)178     CSeqDBLockHold(class CSeqDBAtlas & atlas)
179         : m_Atlas(atlas),
180           m_Locked(false)
181     {
182         INIT_CLASS_MARK();
183     }
184 
185 
186     /// Destructor
187     ///
188     /// The class will unlock the atlas's lock on destruction (if it
189     /// is the owner of that lock).
190     ~CSeqDBLockHold();
191 
192 private:
193     CLASS_MARKER_FIELD("LHLD")
194 
195     /// Private method to prevent copy construction.
196     CSeqDBLockHold(CSeqDBLockHold & oth);
197     CSeqDBLockHold& operator=(CSeqDBLockHold & oth);
198 
199     /// Only the atlas code is permitted to modify this object - it
200     /// does so simply by editing the m_Locked member as needed.
201     friend class CSeqDBAtlas;
202 
203     /// This reference allows unlock on exit.
204     class CSeqDBAtlas & m_Atlas;
205 
206     /// If this is true, this thread owns the atlas lock.
207     volatile bool m_Locked;
208 };
209 
210 
211 /// CSeqDBMemReg
212 ///
213 /// This class is used to keep track of bytes allocated externally to
214 /// the atlas, but included under its memory bound.
215 
216 class CSeqDBMemReg {
217 public:
218     /// Constructor
219     ///
220     /// This object constructs to an empty state, which means that the
221     /// atlas does not consider this object to "own" any bytes.
222     ///
223     /// @param atlas
224     ///   A reference to the atlas object.
CSeqDBMemReg(class CSeqDBAtlas & atlas)225     CSeqDBMemReg(class CSeqDBAtlas & atlas)
226         : m_Atlas(atlas),
227           m_Bytes(0)
228     {
229     }
230 
231     /// Destructor
232     ///
233     /// The class will unlock the atlas's lock on destruction (if it
234     /// is the owner of that lock).
235     inline ~CSeqDBMemReg();
236 
237 private:
238     /// Private method to prevent copy construction.
239     CSeqDBMemReg(CSeqDBMemReg & oth);
240 
241     /// Only the atlas code is permitted to modify this object - it
242     /// does so simply by editing the m_Locked member as needed.
243     friend class CSeqDBAtlas;
244 
245     /// This reference allows unlock on exit.
246     class CSeqDBAtlas & m_Atlas;
247 
248     /// This object "owns" this many bytes of the atlas memory bound.
249     size_t m_Bytes;
250 };
251 
252 
253 
254 
255 /// Hold a memory region refcount, return to atlas when destroyed.
256 ///
257 /// This object `owns' a reference to a region of atlas owned memory,
258 /// releasing that reference when destructed.  This can be used to
259 /// return a hold on a region of memory to the user.  Care should be
260 /// taken when managing these objects.  In particular there should
261 /// never be a case where a "live" object of this type could be
262 /// destroyed in a thread that already holds the atlas lock.  A simple
263 /// technique is to keep an extra CRef<> to this object until the
264 /// thread releasees the lock.
265 
266 class CSeqDB_AtlasRegionHolder : public CObject {
267 public:
268     /// Constructor.
269     CSeqDB_AtlasRegionHolder(CSeqDBAtlas & atlas, const char * ptr);
270 
271     /// Destructor.
272     ~CSeqDB_AtlasRegionHolder();
273 
274 private:
275     /// Reference to the memory management layer.
276     CSeqDBAtlas & m_Atlas;
277 
278     /// Pointer to this object.
279     const char  * m_Ptr;
280 };
281 
282 
283 /// CSeqDBAtlas class
284 ///
285 /// This object manages a collection of (memory) maps.  It mmaps or
286 /// reads data from files on demand, to allow a set of files to be
287 /// accessed efficiently by SeqDB.  The total size of the files used
288 /// by a multivolume database may exceed the usable address space of
289 /// the system by several times.  SeqDB also registers certain large,
290 /// dynamically allocated (via new[]) memory blocks with this object,
291 /// in an effort to limit the total memory usage.  This class also
292 /// contains the primary mutex used to sequentialize access to the
293 /// various SeqDB critical regions, some of which are outside of this
294 /// class.  ["Atlas: n. 1. A book or bound collection of maps..."; The
295 /// American Heritage Dictionary of the English Language, 4th Edition.
296 /// Copyright (c) 2000 by Houghton Mifflin Company.]
297 
298 class NCBI_XOBJREAD_EXPORT CSeqDBAtlas {
299 public:
300     /// The type used for file offsets.
301     //typedef CRegionMap::TIndx TIndx;
302     typedef CNcbiStreamoff TIndx;
303 
304     /// Constructor
305     ///
306     /// Initializes the atlas object.
307     /// @param use_atlas_lock If true, the atlas lock will be used to protect
308     /// critical regions, otherwise the Lock() and Unlock() functions will be
309     /// noops. Setting the parameter to false improves CPU utilization when
310     /// each thread access a different database volume. It should be set to
311     /// true in other cases.
312     CSeqDBAtlas(bool use_atlas_lock);
313 
314     /// The destructor unmaps and frees all associated memory.
315     ~CSeqDBAtlas();
316 
317 
318     enum {e_MaxFileDescritors = 950};
319 
320     /// Check if file exists.
321     ///
322     /// If the file exists, this method will return true, otherwise
323     /// false.  Also, if the file exists, the first slice of it will
324     /// be mapped.  This method exists for efficiency reasons; it is
325     /// cheaper to ask the atlas whether it already knows about a file
326     /// than to search the filesystem for the file, particularly in
327     /// the case of network file systems.  If a slice of the file has
328     /// already been mapped, this will return true without consulting
329     /// the file system, so this method will not detect files that
330     /// have been deleted since the mapping occurred.
331     ///
332     /// @param fname
333     ///   The filename of the file to get.
334     /// @param locked
335     ///   The lock hold object for this thread.
336     /// @return
337     ///   True if the file exists.
338     bool DoesFileExist(const string & fname);
339 
340     /// Check if file exists.
341     ///
342     /// This is like the previous but accepts CSeqDB_Path.
343     ///
344     /// @param fname
345     ///   The filename of the file to get.
346     /// @param locked
347     ///   The lock hold object for this thread.
348     /// @return
349     ///   True if the file exists.
DoesFileExist(const CSeqDB_Path & fname)350     bool DoesFileExist(const CSeqDB_Path & fname)
351     {
352         return DoesFileExist(fname.GetPathS());
353     }
354 
355     /// Get size of a file.
356     ///
357     /// Check whether a file exists and get the file's size.
358     ///
359     /// @param fname
360     ///   The filename of the file to get the size of.
361     /// @param length
362     ///   The length of the file is returned here.
363     /// @param locked
364     ///   The lock hold object for this thread.
365     /// @return
366     ///   true if the file exists.
367     bool GetFileSize(const string   & fname,
368                      TIndx          & length);
369 
370     /// Get size of a file.
371     ///
372     /// Check whether a file exists and get the file's size.  This
373     /// version assumes the atlas lock is held.
374     ///
375     /// @param fname
376     ///   The filename of the file to get the size of.
377     /// @param length
378     ///   The length of the file is returned here.
379     /// @return
380     ///   true if the file exists.
381     bool GetFileSizeL(const string & fname, TIndx & length);
382 
383     /// Free allocated memory.
384     ///
385     /// This method releases the memory aquired
386     /// by the Alloc() method.  With
387     /// data known to have originated in Alloc(), it is faster to call
388     /// the Free() method.  This method assumes the lock is held.
389     ///
390     /// @param datap
391     ///   Pointer to the data to release or deallocate.
392     static void RetRegion(const char * datap);
393 
394     /// Allocate memory that atlas will keep track of.
395     ///
396     /// This method allocates memory for the calling code's use.
397     /// There are three reasons to do this.  First, the allocated
398     /// memory is guaranteed to be deleted when the atlas destructor
399     /// runs, so using this method ties the lifetime of the allocated
400     /// memory to that of the atlas, which may prevent memory leaks.
401     /// Secondly, allocating memory in this way brings the allocated
402     /// memory under the total memory bound the atlas imposes on
403     /// itself.  The amount of memory assumed to be available for
404     /// slice allocation will be reduced by the size of these
405     /// allocations during garbage collection.  Thirdly, the memory
406     /// allocated this way can be freed by the RetRegion(char*)
407     /// method, so the RetSequence code in the volume layers (and
408     /// thereabouts) does not need to can return allocated memory to
409     /// the user as "sequence data", and does not have to track
410     /// whether the data was allocated or mapped.
411     ///
412     /// @param length
413     ///   Amount of memory to allocate in bytes.
414     /// @param locked
415     ///   The lock hold object for this thread.
416     /// @param clear
417     ///   Specify true to zero out memory contents.
418     /// @return
419     ///   A pointer to the allocation region of memory.
420     static char * Alloc(size_t length, bool clear = true);
421 
422 
423     /// Register externally allocated memory.
424     ///
425     /// This method tells the atlas code that memory was allocated
426     /// external to the atlas code, and should be included under the
427     /// memory bound enforced by the atlas.  These areas of memory
428     /// will not be managed by the atlas, but may influence the atlas
429     /// by causing database volume files or auxiliary files to be
430     /// unmapped earlier or more often.  This method may trigger atlas
431     /// garbage collection.  RegisterFree() should be called when the
432     /// memory is freed.
433     ///
434     /// @param memreg
435     ///   Memory registration tracking object.
436     /// @param bytes
437     ///   Amount of memory externally allocated.
438     /// @param locked
439     ///   The lock hold object for this thread.
440     void RegisterExternal(CSeqDBMemReg   & memreg,
441                           size_t           bytes,
442                           CSeqDBLockHold & locked);
443 
444     /// Unregister externally allocated memory.
445     ///
446     /// This method tells the atlas that external memory registered
447     /// with RegisterExternal() has been freed.  The atlas lock is
448     /// assumed to be held.
449     ///
450     /// @param memreg
451     ///   Memory registration tracking object.
452     void UnregisterExternal(CSeqDBMemReg & memreg);
453 
454     /// Lock the atlas.
455     ///
456     /// If the lock hold object passed to
457     /// this method is already in a "locked" state, this call is a
458     /// noop.  Otherwise, the lock hold object is put in a locked
459     /// state and the lock is acquired.
460     ///
461     /// @param locked
462     ///   This object tracks whether this thread owns the mutex.
Lock(CSeqDBLockHold & locked)463     void Lock(CSeqDBLockHold & locked)
464     {
465         if (m_UseLock && !locked.m_Locked) {
466             m_Lock.Lock();
467             locked.m_Locked = true;
468         }
469     }
470 
471     /// Unlock the atlas.
472     ///
473     /// If the lock hold object passed to
474     /// this method is already in an "unlocked" state, this call is a
475     /// noop.  Otherwise, the lock hold object is put in an unlocked
476     /// state and the lock is released.
477     ///
478     /// @param locked
479     ///   This object tracks whether this thread owns the mutex.
Unlock(CSeqDBLockHold & locked)480     void Unlock(CSeqDBLockHold & locked)
481     {
482         if (m_UseLock && locked.m_Locked) {
483             locked.m_Locked = false;
484             m_Lock.Unlock();
485         }
486     }
487 
488 
489     /// Get the current slice size.
490     ///
491     /// This returns the current slice size used for mmap() style
492     /// memory allocations.
493     ///
494     /// @return
495     ///   Atlas will try to map this much data at a time.
496 
GetSliceSize()497     Uint8 GetSliceSize()
498     {
499         Uint8 max_slice = e_MaxSlice64;
500         Uint8 sliceSize = min(max_slice,m_MaxFileSize);
501         return sliceSize;
502     }
503 
504     /// Get BlastDB search path.
GetSearchPath() const505     const string GetSearchPath() const
506     {
507         return m_SearchPath;
508     }
509 
510     /// Generate search path
GenerateSearchPath()511     static const string GenerateSearchPath() {
512         string splitter;
513         string path;
514 #if defined(NCBI_OS_UNIX)
515         splitter = ":";
516 #else
517         splitter = ";";
518 #endif
519         // Local directory first;
520         path  = CDirEntry::NormalizePath(CDir::GetCwd(),eFollowLinks);
521         path += splitter;
522         // Then, BLASTDB;
523         CNcbiEnvironment env;
524         path += CDirEntry::NormalizePath(env.Get("BLASTDB"),eFollowLinks);
525         path += splitter;
526         // Finally, the config file.
527         CNcbiApplicationAPI* app = CNcbiApplicationAPI::Instance();
528         if (app) {
529             const CNcbiRegistry& registry = app->GetConfig();
530             if (registry.HasEntry("BLAST", "BLASTDB")) {
531                 path += CDirEntry::NormalizePath(registry.Get("BLAST", "BLASTDB"),eFollowLinks);
532                 path += splitter;
533             }
534         }
535         return path;
536     }
537 
538     CMemoryFile* GetMemoryFile(const string& fileName);
539 
540     enum EFilesCount{
541         eFileCounterNoChange,
542         eFileCounterIncrement,
543         eFileCounterDecrement
544     };
545 
546     CMemoryFile* ReturnMemoryFile(const string& fileName);
547 
ChangeOpenedFilseCount(EFilesCount fc)548     int ChangeOpenedFilseCount(EFilesCount fc)
549     {
550         switch(fc) {
551         case eFileCounterIncrement:
552             m_OpenedFilesCount++;
553             break;
554 
555         case eFileCounterDecrement:
556             m_OpenedFilesCount--;
557             break;
558 
559         default:
560             break;
561         }
562         m_MaxOpenedFilesCount = max(m_MaxOpenedFilesCount,m_OpenedFilesCount);
563         return m_OpenedFilesCount;
564     }
565 
GetOpenedFilseCount(void)566     int GetOpenedFilseCount(void) { return m_OpenedFilesCount;}
567 
568 private:
569 
570     class CAtlasMappedFile : public CMemoryFile {
571     public:
CAtlasMappedFile(const string & filename)572     	CAtlasMappedFile(const string & filename): CMemoryFile(filename),m_Count(1){
573     		const string exts="hd|hi|nd|ni|pd|pi|si|sd|ti|td";
574     		string ext = filename.substr(filename.length()-2);
575     		if (exts.find(ext) != NPOS) {
576     			m_isIsam = true;
577     		}
578     		else {
579     			m_isIsam = false;
580     		}
581     	}
~CAtlasMappedFile()582     	~CAtlasMappedFile() {
583     		_ASSERT(m_Count == 0);
584     	}
585     	int m_Count;
586     	bool m_isIsam;
587     };
588     /// Private method to prevent copy construction.
589     CSeqDBAtlas(const CSeqDBAtlas &);
590 
591     /// Protects most of the critical regions of the SeqDB library.
592     CMutex m_Lock;
593 
594     /// Use single atlas lock to protect critical regions. The single lock is
595     /// not needed if each thread access different database volume.
596     bool m_UseLock;
597 
598     enum {e_MaxSlice64 = 1 << 30};
599 
600     /// Cache of file existence and length.
601     std::mutex m_FileSizeMutex;
602     map< string, pair<bool, TIndx> > m_FileSize;
603     /// Maxium file size.
604     Uint8 m_MaxFileSize;
605 
606     std::mutex m_FileMemMapMutex;
607     map<string, unique_ptr<CAtlasMappedFile> > m_FileMemMap;
608     int m_OpenedFilesCount;
609     int m_MaxOpenedFilesCount;
610 
611     /// BlastDB search path.
612     const string m_SearchPath;
613 };
614 
615 
~CSeqDBMemReg()616 inline CSeqDBMemReg::~CSeqDBMemReg()
617 {
618     m_Atlas.UnregisterExternal(*this);
619 }
620 
621 
622 /// Guard object for the SeqDBAtlas singleton.
623 ///
624 /// The CSeqDBAtlas object is a singleton - only one exists at any
625 /// given time, and only if a CSeqDB object exists.  This object
626 /// implements that policy.  When no CSeqDBAtlas object exists, the
627 /// first CSeqDB object to be created will decide whether memory
628 /// mapping is enabled.  One of these objects exists in every
629 /// CSeqDBImpl and CSeqDBColumn object, and in the frames of a few
630 /// static functions.
631 
632 class CSeqDBAtlasHolder {
633 public:
634     /// Constructor.
635     /// @param locked The lock hold object for this thread (or NULL).
636     /// @param use_atlas_lock If true, the atlas lock will be used to protect
637     /// critical regions, otherwise the Lock() and Unlock() functions will be
638     /// noops. Setting the parameter to false improves CPU utilization when
639     /// each thread access a different database volume. It should be set to
640     /// true in other cases.
641     CSeqDBAtlasHolder(CSeqDBLockHold * lockedp, bool use_atlas_lock);
642 
643     NCBI_DEPRECATED
644     CSeqDBAtlasHolder(bool user_atlas_lock, CSeqDBLockHold* lockdep);
645 
646     /// Destructor.
647     ~CSeqDBAtlasHolder();
648 
649     /// Get the CSeqDBAtlas object.
650     CSeqDBAtlas & Get();
651 
652 private:
653 
654     /// Lock protecting this object's fields
655     DECLARE_CLASS_STATIC_FAST_MUTEX(m_Lock);
656 
657     /// Count of users of the CSeqDBAtlas object.
658     static int m_Count;
659 
660     /// The CSeqDBAtlas object itself.
661     static CSeqDBAtlas * m_Atlas;
662 };
663 
664 
665 class CSeqDBFileMemMap {
666 public:
667 
668     typedef CNcbiStreamoff TIndx;
669     /// Constructor
670     ///
671     /// Initializes a memory map object.
672     ///
673     /// @param filename
674     ///   file to memory map
CSeqDBFileMemMap(class CSeqDBAtlas & atlas,const string filename)675     CSeqDBFileMemMap(class CSeqDBAtlas & atlas, const string filename)
676         : m_Atlas(atlas),
677           m_DataPtr (NULL),
678           m_MappedFile( NULL),
679           m_Mapped(false)
680     {
681         Init(filename);
682     }
683 
CSeqDBFileMemMap(class CSeqDBAtlas & atlas)684     CSeqDBFileMemMap(class CSeqDBAtlas & atlas)
685         : m_Atlas(atlas),
686           m_DataPtr (NULL),
687           m_MappedFile( NULL),
688           m_Mapped(false)
689     {
690 
691     }
692 
693     /// Destructor
~CSeqDBFileMemMap()694     ~CSeqDBFileMemMap()
695     {
696         Clear();
697     }
698 
699     /// Initializes a memory map object.
700     ///
701     /// @param filename
702     ///   file to memory map
Init(const string filename)703     void Init(const string filename) {
704     	 CSeqDBLockHold locked(m_Atlas);
705     	 m_Atlas.Lock(locked);
706         if(!m_MappedFile || m_Filename != filename)
707         {
708         	Clear();
709             m_Filename = filename;
710             Init();
711         }
712         m_Atlas.Unlock(locked);
713     }
714 
715     //m_Filename is set
Init(void)716     void Init(void)
717     {
718         try {
719             m_MappedFile = m_Atlas.GetMemoryFile(m_Filename);
720             m_Mapped = true;
721         }
722         catch (const std::exception&) {
723             NCBI_THROW(CSeqDBException,
724                 eFileErr,
725                 "Cannot memory map " + m_Filename + ". Number of files opened: " + NStr::IntToString(m_Atlas.GetOpenedFilseCount()));
726         }
727 
728         m_DataPtr = (char *)(m_MappedFile->GetPtr());
729     }
730 
731 
732     /// Clears the memory mapobject.
733     ///
Clear()734     void Clear()
735     {
736 
737         if(m_MappedFile && m_Mapped ) {
738         	m_MappedFile = m_Atlas.ReturnMemoryFile(m_Filename);
739             m_Mapped = false;
740         }
741     }
742 
IsMapped()743     bool IsMapped(){return m_Mapped;}
744 
745     /// Get a pointer to the specified offset.
746     ///
747     /// Given an offset (which is assumed to be available here), this
748     /// method returns a pointer to the data at that offset.
749     ///
750     /// @param offset
751     ///   The required offset relative to the start of the file.
752     /// @return
753     ///   A pointer to the data at the requested location.
GetFileDataPtr(const string & fname,TIndx offset)754     const char *GetFileDataPtr(const string   & fname,TIndx offset)
755     {
756         if(!m_MappedFile || m_Filename != fname) {
757             Init(fname);
758         }
759 
760         return(const char *)(m_DataPtr + offset);
761     }
762 
GetFileDataPtr(TIndx offset)763     const char *GetFileDataPtr(TIndx offset)
764     {
765         return(const char *)(m_DataPtr + offset);
766     }
767 
768 private:
769     CSeqDBAtlas & m_Atlas;
770      /// Points to the beginning of the data area.
771     const char * m_DataPtr;
772 
773     string m_Filename;
774 
775     CMemoryFile *m_MappedFile;
776 
777     bool m_Mapped;
778 };
779 
780 
781 
782 END_NCBI_SCOPE
783 
784 #endif // OBJTOOLS_READERS_SEQDB__SEQDBATLAS_HPP
785 
786