11"""Module containnig a memory memory manager which provides a sliding window on a number of memory mapped files"""
2-
3- __all__ = ["MappedMemoryManager" , "MemoryCursor" ]
4-
52from util import (
63 MemoryWindow ,
74 MappedRegion ,
85 MappedRegionList ,
96 )
107
8+ from weakref import proxy
9+
10+ __all__ = ["MappedMemoryManager" ]
11+ #{ Utilities
12+
13+ #}END utilities
1114
1215class MemoryCursor (object ):
1316 """Pointer into the mapped region of the memory manager, keeping the current window
14- alive until it is destroyed"""
17+ alive until it is destroyed.
18+
19+ Cursors should not be created manually, but are instead returned by the MappedMemoryManager"""
1520 __slots__ = (
1621 '_manager' , # the manger keeping all file regions
17- '_regions ' , # a regions list with regions for our file
18- '_region' , # WEAK REF to our current region
22+ '_rlist ' , # a regions list with regions for our file
23+ '_region' , # our current region or None
1924 '_ofs' , # relative offset from the actually mapped area to our start area
2025 '_size' # maximum size we should provide
2126 )
2227
2328 def __init__ (self , manager = None , regions = None ):
2429 self ._manager = manager
25- self ._regions = regions
26- self ._region = region
30+ self ._rlist = regions
31+ self ._region = None
2732 self ._ofs = 0
2833 self ._size = 0
2934
@@ -32,12 +37,97 @@ def __del__(self):
3237
3338 def _destroy (self ):
3439 """Destruction code to decrement counters"""
40+ self .unuse_region ()
41+
42+ if self ._rlist is not None :
43+ # Actual client count, which doesn't include the reference kept by the manager, nor ours
44+ # as we are about to be deleted
45+ num_clients = self ._rlist .client_count () - 2
46+ if num_clients == 0 and len (self ._rlist ) == 0 :
47+ # Free all resources associated with the mapped file
48+ self ._manager ._files .pop (self ._rlist .path ())
49+ #END remove regions list from manager
50+ #END handle regions
3551
3652 def _copy_from (self , rhs ):
3753 """Copy all data from rhs into this instance, handles usage count"""
54+ self ._manager = rhs ._manager
55+ self ._rlist = rhs ._rlist
56+ self ._region = rhs ._region
57+ self ._ofs = rhs ._ofs
58+ self ._size = rhs ._size
59+
60+ if self ._region is not None :
61+ self ._region .increment_usage_count (1 )
62+ # END handle regions
63+
64+ def __copy__ (self ):
65+ """copy module interface"""
66+ cpy = type (self )()
67+ cpy ._copy_from (self )
68+ return cpy
3869
3970 #{ Interface
71+ def assign (self , rhs ):
72+ """Assign rhs to this instance. This is required in order to get a real copy.
73+ Alternativly, you can copy an existing instance using the copy module"""
74+ self ._destroy ()
75+ self ._copy_from (rhs )
76+
77+ def use_region (self , offset , size ):
78+ """Assure we point to a window which allows access to the given offset into the file
79+ :param offset: absolute offset in bytes into the file
80+ :param size: amount of bytes to map
81+ :return: this instance - it should be queried for whether it points to a valid memory region.
82+ This is not the case if the mapping failed becaues we reached the end of the file
83+ :note: The size actually mapped may be smaller than the given size. If that is the case,
84+ either the file has reached its end, or the map was created between two existing regions"""
85+
86+ def unuse_region (self ):
87+ """Unuse the ucrrent region. Does nothing if we have no current region
88+ :note: the cursor unuses the region automatically upon destruction. It is recommended
89+ to unuse the region once you are done reading from it in persistent cursors as it
90+ helps to free up resource more quickly"""
91+ self ._region = None
4092
93+ def is_valid (self ):
94+ """:return: True if we have a valid and usable region"""
95+ return self ._region is not None
96+
97+ def is_associated (self ):
98+ """:return: True if we are associated with a specific file already"""
99+ return self ._rlist is not None
100+
101+ def ofs_begin (self ):
102+ """:return: offset to the first byte pointed to by our cursor"""
103+ return self ._region .ofs_begin () + self ._ofs
104+
105+ def size (self ):
106+ """:return: amount of bytes we point to"""
107+ return self ._size
108+
109+ def region_ref (self ):
110+ """:return: weak proxy to our mapped region.
111+ :raise AssertionError: if we have no current region. This is only useful for debugging"""
112+ if self ._region is None :
113+ raise AssertionError ("region not set" )
114+ return proxy (self ._region )
115+
116+ def includes_ofs (self , ofs ):
117+ """:return: True if the given absolute offset is contained in the cursors
118+ current region
119+ :note: always False if the cursor does not point to a valid region"""
120+ if self ._region is None :
121+ return False
122+ return (self .ofs_begin () <= ofs ) and (ofs < self .ofs_end ())
123+
124+ def file_size (self ):
125+ """:return: size of the underlying file"""
126+ return self ._rlist .file_size ()
127+
128+ def path (self ):
129+ """:return: path of the underlying mapped file"""
130+ return self ._rlist .path ()
41131
42132 #} END interface
43133
0 commit comments