|
6 | 6 | __all__ = ["SlidingWindowMapBuffer"] |
7 | 7 |
|
8 | 8 | class SlidingWindowMapBuffer(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 the provided cursor. |
11 | | - |
12 | | - The buffer is relative, that is if you map an offset, index 0 will map to the |
13 | | - first byte at the offset you used during initialization or begin_access |
14 | | - |
15 | | - **Note:** Although this type effectively hides the fact that there are mapped windows |
16 | | - underneath, it can unfortunately not be used in any non-pure python method which |
17 | | - needs a buffer or string""" |
18 | | - __slots__ = ( |
19 | | - '_c', # our cursor |
20 | | - '_size', # our supposed size |
21 | | - ) |
22 | | - |
23 | | - |
24 | | - def __init__(self, cursor = None, offset = 0, size = sys.maxint, flags = 0): |
25 | | - """Initalize the instance to operate on the given cursor. |
26 | | - :param cursor: if not None, the associated cursor to the file you want to access |
27 | | - If None, you have call begin_access before using the buffer and provide a cursor |
28 | | - :param offset: absolute offset in bytes |
29 | | - :param size: the total size of the mapping. Defaults to the maximum possible size |
30 | | - From that point on, the __len__ of the buffer will be the given size or the file size. |
31 | | - If the size is larger than the mappable area, you can only access the actually available |
32 | | - area, although the length of the buffer is reported to be your given size. |
33 | | - Hence it is in your own interest to provide a proper 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 = cursor |
37 | | - if cursor and not self.begin_access(cursor, offset, size, flags): |
38 | | - raise ValueError("Failed to allocate the buffer - probably the given offset is out of bounds") |
39 | | - # END handle offset |
| 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 the provided cursor. |
| 11 | + |
| 12 | + The buffer is relative, that is if you map an offset, index 0 will map to the |
| 13 | + first byte at the offset you used during initialization or begin_access |
| 14 | + |
| 15 | + **Note:** Although this type effectively hides the fact that there are mapped windows |
| 16 | + underneath, it can unfortunately not be used in any non-pure python method which |
| 17 | + needs a buffer or string""" |
| 18 | + __slots__ = ( |
| 19 | + '_c', # our cursor |
| 20 | + '_size', # our supposed size |
| 21 | + ) |
| 22 | + |
| 23 | + |
| 24 | + def __init__(self, cursor = None, offset = 0, size = sys.maxint, flags = 0): |
| 25 | + """Initalize the instance to operate on the given cursor. |
| 26 | + :param cursor: if not None, the associated cursor to the file you want to access |
| 27 | + If None, you have call begin_access before using the buffer and provide a cursor |
| 28 | + :param offset: absolute offset in bytes |
| 29 | + :param size: the total size of the mapping. Defaults to the maximum possible size |
| 30 | + From that point on, the __len__ of the buffer will be the given size or the file size. |
| 31 | + If the size is larger than the mappable area, you can only access the actually available |
| 32 | + area, although the length of the buffer is reported to be your given size. |
| 33 | + Hence it is in your own interest to provide a proper 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 = cursor |
| 37 | + if cursor and not self.begin_access(cursor, offset, size, flags): |
| 38 | + raise ValueError("Failed to allocate the buffer - probably the given offset is out of bounds") |
| 39 | + # END handle offset |
40 | 40 |
|
41 | | - def __del__(self): |
42 | | - self.end_access() |
43 | | - |
44 | | - def __len__(self): |
45 | | - return self._size |
46 | | - |
47 | | - def __getitem__(self, i): |
48 | | - c = self._c |
49 | | - assert c.is_valid() |
50 | | - if i < 0: |
51 | | - i = self._size + i |
52 | | - if not c.includes_ofs(i): |
53 | | - c.use_region(i, 1) |
54 | | - # END handle region usage |
55 | | - return c.buffer()[i-c.ofs_begin()] |
56 | | - |
57 | | - def __getslice__(self, i, j): |
58 | | - c = self._c |
59 | | - # fast path, slice fully included - safes a concatenate operation and |
60 | | - # should be the default |
61 | | - assert c.is_valid() |
62 | | - if i < 0: |
63 | | - i = self._size + i |
64 | | - if j == sys.maxint: |
65 | | - j = self._size |
66 | | - if j < 0: |
67 | | - j = self._size + j |
68 | | - if (c.ofs_begin() <= i) and (j < c.ofs_end()): |
69 | | - b = c.ofs_begin() |
70 | | - return c.buffer()[i-b:j-b] |
71 | | - else: |
72 | | - l = j-i # total length |
73 | | - ofs = i |
74 | | - # Keeping tokens in a list could possible be faster, but the list |
75 | | - # overhead outweighs the benefits (tested) ! |
76 | | - md = str() |
77 | | - while l: |
78 | | - c.use_region(ofs, l) |
79 | | - assert c.is_valid() |
80 | | - d = c.buffer()[:l] |
81 | | - ofs += len(d) |
82 | | - l -= len(d) |
83 | | - md += d |
84 | | - #END while there are bytes to read |
85 | | - return md |
86 | | - # END fast or slow path |
87 | | - #{ Interface |
88 | | - |
89 | | - def begin_access(self, cursor = None, offset = 0, size = sys.maxint, flags = 0): |
90 | | - """Call this before the first use of this instance. The method was already |
91 | | - called by the constructor in case sufficient information was provided. |
92 | | - |
93 | | - For more information no the parameters, see the __init__ method |
94 | | - :param path: if cursor is None the existing one will be used. |
95 | | - :return: True if the buffer can be used""" |
96 | | - if cursor: |
97 | | - self._c = cursor |
98 | | - #END update our cursor |
99 | | - |
100 | | - # reuse existing cursors if possible |
101 | | - if self._c is not None and self._c.is_associated(): |
102 | | - res = self._c.use_region(offset, size, flags).is_valid() |
103 | | - if res: |
104 | | - # if given size is too large or default, we computer a proper size |
105 | | - # If its smaller, we assume the combination between offset and size |
106 | | - # as chosen by the user is correct and use it ! |
107 | | - # If not, the user is in trouble. |
108 | | - if size > self._c.file_size(): |
109 | | - size = self._c.file_size() - offset |
110 | | - #END handle size |
111 | | - self._size = size |
112 | | - #END set size |
113 | | - return res |
114 | | - # END use our cursor |
115 | | - return False |
116 | | - |
117 | | - def end_access(self): |
118 | | - """Call this method once you are done using the instance. It is automatically |
119 | | - called on destruction, and should be called just in time to allow system |
120 | | - resources to be freed. |
121 | | - |
122 | | - Once you called end_access, you must call begin access before reusing this instance!""" |
123 | | - self._size = 0 |
124 | | - if self._c is not None: |
125 | | - self._c.unuse_region() |
126 | | - #END unuse region |
127 | | - |
128 | | - def cursor(self): |
129 | | - """:return: the currently set cursor which provides access to the data""" |
130 | | - return self._c |
131 | | - |
132 | | - #}END interface |
| 41 | + def __del__(self): |
| 42 | + self.end_access() |
| 43 | + |
| 44 | + def __len__(self): |
| 45 | + return self._size |
| 46 | + |
| 47 | + def __getitem__(self, i): |
| 48 | + c = self._c |
| 49 | + assert c.is_valid() |
| 50 | + if i < 0: |
| 51 | + i = self._size + i |
| 52 | + if not c.includes_ofs(i): |
| 53 | + c.use_region(i, 1) |
| 54 | + # END handle region usage |
| 55 | + return c.buffer()[i-c.ofs_begin()] |
| 56 | + |
| 57 | + def __getslice__(self, i, j): |
| 58 | + c = self._c |
| 59 | + # fast path, slice fully included - safes a concatenate operation and |
| 60 | + # should be the default |
| 61 | + assert c.is_valid() |
| 62 | + if i < 0: |
| 63 | + i = self._size + i |
| 64 | + if j == sys.maxint: |
| 65 | + j = self._size |
| 66 | + if j < 0: |
| 67 | + j = self._size + j |
| 68 | + if (c.ofs_begin() <= i) and (j < c.ofs_end()): |
| 69 | + b = c.ofs_begin() |
| 70 | + return c.buffer()[i-b:j-b] |
| 71 | + else: |
| 72 | + l = j-i # total length |
| 73 | + ofs = i |
| 74 | + # Keeping tokens in a list could possible be faster, but the list |
| 75 | + # overhead outweighs the benefits (tested) ! |
| 76 | + md = str() |
| 77 | + while l: |
| 78 | + c.use_region(ofs, l) |
| 79 | + assert c.is_valid() |
| 80 | + d = c.buffer()[:l] |
| 81 | + ofs += len(d) |
| 82 | + l -= len(d) |
| 83 | + md += d |
| 84 | + #END while there are bytes to read |
| 85 | + return md |
| 86 | + # END fast or slow path |
| 87 | + #{ Interface |
| 88 | + |
| 89 | + def begin_access(self, cursor = None, offset = 0, size = sys.maxint, flags = 0): |
| 90 | + """Call this before the first use of this instance. The method was already |
| 91 | + called by the constructor in case sufficient information was provided. |
| 92 | + |
| 93 | + For more information no the parameters, see the __init__ method |
| 94 | + :param path: if cursor is None the existing one will be used. |
| 95 | + :return: True if the buffer can be used""" |
| 96 | + if cursor: |
| 97 | + self._c = cursor |
| 98 | + #END update our cursor |
| 99 | + |
| 100 | + # reuse existing cursors if possible |
| 101 | + if self._c is not None and self._c.is_associated(): |
| 102 | + res = self._c.use_region(offset, size, flags).is_valid() |
| 103 | + if res: |
| 104 | + # if given size is too large or default, we computer a proper size |
| 105 | + # If its smaller, we assume the combination between offset and size |
| 106 | + # as chosen by the user is correct and use it ! |
| 107 | + # If not, the user is in trouble. |
| 108 | + if size > self._c.file_size(): |
| 109 | + size = self._c.file_size() - offset |
| 110 | + #END handle size |
| 111 | + self._size = size |
| 112 | + #END set size |
| 113 | + return res |
| 114 | + # END use our cursor |
| 115 | + return False |
| 116 | + |
| 117 | + def end_access(self): |
| 118 | + """Call this method once you are done using the instance. It is automatically |
| 119 | + called on destruction, and should be called just in time to allow system |
| 120 | + resources to be freed. |
| 121 | + |
| 122 | + Once you called end_access, you must call begin access before reusing this instance!""" |
| 123 | + self._size = 0 |
| 124 | + if self._c is not None: |
| 125 | + self._c.unuse_region() |
| 126 | + #END unuse region |
| 127 | + |
| 128 | + def cursor(self): |
| 129 | + """:return: the currently set cursor which provides access to the data""" |
| 130 | + return self._c |
| 131 | + |
| 132 | + #}END interface |
133 | 133 |
|
134 | 134 |
|
0 commit comments