99
1010from exc import RegionCollectionError
1111from weakref import proxy
12+ import sys
1213
1314__all__ = ["MappedMemoryManager" ]
1415#{ Utilities
@@ -77,23 +78,138 @@ def assign(self, rhs):
7778 self ._destroy ()
7879 self ._copy_from (rhs )
7980
80- def use_region (self , offset , size ):
81+ def use_region (self , offset , size , _is_recursive = False ):
8182 """Assure we point to a window which allows access to the given offset into the file
8283 :param offset: absolute offset in bytes into the file
8384 :param size: amount of bytes to map
8485 :return: this instance - it should be queried for whether it points to a valid memory region.
8586 This is not the case if the mapping failed becaues we reached the end of the file
8687 :note: The size actually mapped may be smaller than the given size. If that is the case,
8788 either the file has reached its end, or the map was created between two existing regions"""
89+ need_region = True
90+ man = self ._manager
91+ size = min (size , man .window_size ()) # clamp size to window size
8892
93+ if self ._region is not None :
94+ if self ._region .includes_ofs (offset ):
95+ need_region = False
96+ else :
97+ self .unuse_region ()
98+ # END handle existing region
99+ # END check existing region
100+
101+ if need_region :
102+ # abort on offsets beyond our mapped file's size - currently we are invalid
103+ if offset > self .file_size ():
104+ return self
105+ # END handle offset too large
106+
107+ existing_region = None
108+ for region in self ._rlist :
109+ if region .includes_ofs (offset ):
110+ existing_region = region
111+ break
112+ #END handle existing region
113+ #END for each existing region
89114
115+ if existing_region is None :
116+ left = MemoryWindow (0 , 0 )
117+ mid = MemoryWindow (offset , size )
118+ right = MemoryWindow (self .file_size (), 0 )
119+
120+ # we want to honor the max memory size, and assure we have anough
121+ # memory available
122+ man ._collect_lru_region (man .window_size ())
123+
124+ # we assume the list remains sorted by offset
125+ insert_pos = 0
126+ len_regions = len (self ._rlist )
127+ if len_regions == 1 :
128+ if self ._rlist [0 ].ofs_begin () <= offset :
129+ insert_pos = 1
130+ #END maintain sort
131+ else :
132+ # find insert position
133+ insert_pos = len_regions
134+ for i , region in enumerate (self ._rlist ):
135+ if region .ofs_begin () > offset :
136+ insert_pos = i
137+ break
138+ #END if insert position is correct
139+ #END for each region
140+ # END obtain insert pos
141+
142+ # adjust the actual offset and size values to create the largest
143+ # possible mapping
144+ if insert_pos == 0 :
145+ if len_regions :
146+ right = MemoryWindow .from_region (self ._rlist [insert_pos ])
147+ #END adjust right side
148+ else :
149+ if insert_pos != len_regions :
150+ right = MemoryWindow .from_region (self ._rlist [insert_pos ])
151+ # END adjust right window
152+ left = MemoryWindow .from_region (self ._rlist [insert_pos - 1 ])
153+ #END adjust surrounding windows
154+
155+ mid .extend_left_to (left , man ._window_size )
156+ mid .extend_right_to (right , man ._window_size )
157+ mid .align ()
158+
159+ # it can happen that we align beyond the end of the file
160+ if mid .ofs_end () > right .ofs :
161+ mid .size = right .ofs - mid .ofs
162+ #END readjust size
163+
164+ # insert new region at the right offset to keep the order
165+ try :
166+ if man ._handle_count >= man ._max_handle_count :
167+ raise Exception
168+ #END assert own imposed max file handles
169+ self ._region = MappedRegion (self ._rlist .path (), mid .ofs , mid .size )
170+ except Exception :
171+ # apparently we are out of system resources or hit a limit
172+ # As many more operations are likely to fail in that condition (
173+ # like reading a file from disk, etc) we free up as much as possible
174+ # As this invalidates our insert position, we have to recurse here
175+ # NOTE: The c++ version uses a linked list to curcumvent this, but
176+ # using that in python is probably too slow anyway
177+ if _is_recursive :
178+ # we already tried this, and still have no success in obtaining
179+ # a mapping. This is an exception, so we propagate it
180+ raise
181+ #END handle existing recursion
182+ man ._collect_lru_region (0 )
183+ return self .use_region (offset , size , True )
184+ #END handle exceptions
185+
186+ man ._handle_count += 1
187+ man ._memory_size += self ._region .size ()
188+ self ._rlist .insert (insert_pos , self ._region )
189+ else :
190+ self ._region = existing_region
191+ #END need region handling
192+ #END handle acquire region
193+
194+ self ._region .increment_usage_count ()
195+ self ._ofs = offset - self ._region .ofs_begin ()
196+ self ._size = min (size , self ._region .ofs_end () - offset )
197+
198+ return self
199+
90200 def unuse_region (self ):
91201 """Unuse the ucrrent region. Does nothing if we have no current region
92202 :note: the cursor unuses the region automatically upon destruction. It is recommended
93203 to unuse the region once you are done reading from it in persistent cursors as it
94204 helps to free up resource more quickly"""
95205 self ._region = None
96-
206+
207+ def buffer (self ):
208+ """Return a buffer object which allows access to our memory region from our offset
209+ to the window size. Please note that it might be smaller than you requested
210+ :note: You can only obtain a buffer if this instance is_valid() !"""
211+ return buffer (self ._region .buffer (), self ._ofs , self ._size )
212+
97213 def is_valid (self ):
98214 """:return: True if we have a valid and usable region"""
99215 return self ._region is not None
@@ -136,7 +252,6 @@ def path(self):
136252 #} END interface
137253
138254
139-
140255class MappedMemoryManager (object ):
141256 """Maintains a list of ranges of mapped memory regions in one or more files and allows to easily
142257 obtain additional regions assuring there is no overlap.
@@ -161,13 +276,13 @@ class MappedMemoryManager(object):
161276
162277 _MB_in_bytes = 1024 * 1024
163278
164- def __init__ (self , window_size = 0 , max_memory_size = 0 , max_open_handles = ~ 0 ):
279+ def __init__ (self , window_size = 0 , max_memory_size = 0 , max_open_handles = sys . maxint ):
165280 """initialize the manager with the given parameters.
166281 :param window_size: if 0, a default window size will be chosen depending on
167282 the operating system's architechture. It will internally be quantified to a multiple of the page size
168283 :param max_memory_size: maximum amount of memory we may map at once before releasing mapped regions.
169284 If 0, a viable default iwll be set dependning on the system's architecture.
170- :param max_open_handles: if not ~0, lmit the amount of open file handles to the given number.
285+ :param max_open_handles: if not maxin, limit the amount of open file handles to the given number.
171286 Otherwise the amount is only limited by the system iteself. If a system or soft limit is hit,
172287 the manager will free as many handles as posisble"""
173288 self ._fdict = dict ()
@@ -193,7 +308,7 @@ def __init__(self, window_size = 0, max_memory_size = 0, max_open_handles = ~0):
193308 self ._max_memory_size = coeff * self ._MB_in_bytes
194309 #END handle max memory size
195310
196- def _collect_one_lru_region (self , size ):
311+ def _collect_lru_region (self , size ):
197312 """Unmap the region which was least-recently used and has no client
198313 :param size: size of the region we want to map next (assuming its not already mapped partially or full
199314 if 0, we try to free any available region
@@ -253,6 +368,10 @@ def mapped_memory_size(self):
253368 """:return: amount of bytes currently mapped in total"""
254369 return self ._memory_size
255370
371+ def max_file_handles (self ):
372+ """:return: maximium amount of handles we may have opened"""
373+ return self ._max_handle_count
374+
256375 def max_mapped_memory_size (self ):
257376 """:return: maximum amount of memory we may allocate"""
258377 return self ._max_memory_size
0 commit comments