|
1 | | -"""Module with a simple stream implementation using the memory manager""" |
| 1 | +"""Module with a simple buffer implementation using the memory manager""" |
| 2 | +from mman import MemoryCursor |
2 | 3 |
|
3 | | -from mman import * |
| 4 | +import sys |
4 | 5 |
|
5 | | -__all__ = [] |
| 6 | +__all__ = ["MappedMemoryBuffer"] |
| 7 | + |
| 8 | +class MappedMemoryBuffer(object): |
| 9 | + """A buffer like object which allows direct byte-wise object and slicing into |
| 10 | + memory of a mapped file. The mapping is controlled by an underlying memory manager. |
| 11 | + |
| 12 | + A buffer, once initialized, stays put on providing access to eactly one path. |
| 13 | + A custom interface allows you to change paths mid way, and to optimize |
| 14 | + the resource usage. |
| 15 | + |
| 16 | + Please note that this type is only fully usable if you configure it with the |
| 17 | + MappedMemoryManager to use. |
| 18 | + |
| 19 | + The buffer is relative, that is if you map an offset, index 0 will map to the |
| 20 | + first byte at your given offset.""" |
| 21 | + __slots__ = '_c' # our cursor |
| 22 | + |
| 23 | + #{ Configuration |
| 24 | + # A subclass must provide an instance of a (usually global) MappedMemoryManager |
| 25 | + manager = None |
| 26 | + #}END configuration |
| 27 | + |
| 28 | + def __init__(self, path = None, offset = 0, size = sys.maxint, flags = 0): |
| 29 | + """Initalize the instance to operate on the given path if given. |
| 30 | + :param path: if not None, the path to the file you want to access |
| 31 | + If None, you have call begin_access before using the buffer |
| 32 | + :param offset: absolute offset in bytes |
| 33 | + :param size: the total size of the mapping. Defaults to the maximum possible size |
| 34 | + :param flags: Additional flags to be passed to os.open |
| 35 | + :raise ValueError: if the buffer could not achieve a valid state""" |
| 36 | + self._c = MemoryCursor(self.manager) |
| 37 | + assert self.manager is not None, "Require the cls.manager variable to be set in subclass" |
| 38 | + if path and not self.begin_access(path, offset, size, flags): |
| 39 | + raise ValueError("Failed to allocate the buffer - probably the given offset is out of bounds") |
| 40 | + # END handle offset |
| 41 | + |
| 42 | + def __del__(self): |
| 43 | + self.end_access() |
| 44 | + |
| 45 | + def __getitem__(self, i): |
| 46 | + c = self._c |
| 47 | + if not c.includes_ofs(i): |
| 48 | + c.use_region(i, 1) |
| 49 | + # END handle region usage |
| 50 | + assert c.is_valid() # TODO: remove for performance |
| 51 | + return c.buffer()[i] |
| 52 | + |
| 53 | + def __getslice__(self, i, j): |
| 54 | + c = self._c |
| 55 | + # fast path, slice fully included - safes a concatenate operation and |
| 56 | + # should be the default |
| 57 | + if c.ofs_begin() >= i and j < c.ofs_end(): |
| 58 | + return c.buffer()[i:j] |
| 59 | + raise NotImplementedError() |
| 60 | + #{ Interface |
| 61 | + |
| 62 | + def begin_access(self, path = None, offset = 0, size = sys.maxint, flags = 0): |
| 63 | + """Call this before the first use of this instance. The method was already |
| 64 | + called by the constructor in case sufficient information was provided. |
| 65 | + |
| 66 | + For more information no the parameters, see the __init__ method |
| 67 | + :param path: if path is empty or None the existing path will be used if possible. |
| 68 | + :return: True if the buffer can be used""" |
| 69 | + if path and (not self._c.is_associated() or self._c.path() != path): |
| 70 | + self._c = self.manager.make_cursor(path) |
| 71 | + #END get associated cursor |
| 72 | + |
| 73 | + # reuse existing cursors if possible |
| 74 | + if self._c.is_associated(): |
| 75 | + return self._c.use_region(offset, size, flags).is_valid() |
| 76 | + return False |
| 77 | + |
| 78 | + def end_access(self): |
| 79 | + """Call this method once you are done using the instance. It is automatically |
| 80 | + called on destruction, and should be called just in time to allow system |
| 81 | + resources to be freed. |
| 82 | + |
| 83 | + Once you called end_access, you must call begin access before reusing this instance!""" |
| 84 | + self._c.unuse_region() |
| 85 | + |
| 86 | + def cursor(self): |
| 87 | + """:return: the currently set cursor which provides access to the data""" |
| 88 | + return self._c |
| 89 | + |
| 90 | + #}END interface |
6 | 91 |
|
7 | 92 |
|
0 commit comments