|
10 | 10 | ) |
11 | 11 | from util import ( |
12 | 12 | zlib, |
| 13 | + mman, |
13 | 14 | LazyMixin, |
14 | 15 | unpack_from, |
15 | 16 | bin_to_hex, |
16 | | - file_contents_ro_filepath, |
17 | 17 | ) |
18 | 18 |
|
19 | 19 | from fun import ( |
@@ -247,7 +247,7 @@ class PackIndexFile(LazyMixin): |
247 | 247 |
|
248 | 248 | # Dont use slots as we dynamically bind functions for each version, need a dict for this |
249 | 249 | # The slots you see here are just to keep track of our instance variables |
250 | | - # __slots__ = ('_indexpath', '_fanout_table', '_data', '_version', |
| 250 | + # __slots__ = ('_indexpath', '_fanout_table', '_cursor', '_version', |
251 | 251 | # '_sha_list_offset', '_crc_list_offset', '_pack_offset', '_pack_64_offset') |
252 | 252 |
|
253 | 253 | # used in v2 indices |
@@ -261,22 +261,23 @@ def __init__(self, indexpath): |
261 | 261 |
|
262 | 262 | def _set_cache_(self, attr): |
263 | 263 | if attr == "_packfile_checksum": |
264 | | - self._packfile_checksum = self._data[-40:-20] |
| 264 | + self._packfile_checksum = self._cursor.map()[-40:-20] |
265 | 265 | elif attr == "_packfile_checksum": |
266 | | - self._packfile_checksum = self._data[-20:] |
267 | | - elif attr == "_data": |
| 266 | + self._packfile_checksum = self._cursor.map()[-20:] |
| 267 | + elif attr == "_cursor": |
268 | 268 | # Note: We don't lock the file when reading as we cannot be sure |
269 | 269 | # that we can actually write to the location - it could be a read-only |
270 | 270 | # alternate for instance |
271 | | - self._data = file_contents_ro_filepath(self._indexpath) |
| 271 | + self._cursor = mman.make_cursor(self._indexpath).use_region() |
272 | 272 | else: |
273 | 273 | # now its time to initialize everything - if we are here, someone wants |
274 | 274 | # to access the fanout table or related properties |
275 | 275 |
|
276 | 276 | # CHECK VERSION |
277 | | - self._version = (self._data[:4] == self.index_v2_signature and 2) or 1 |
| 277 | + mmap = self._cursor.map() |
| 278 | + self._version = (mmap[:4] == self.index_v2_signature and 2) or 1 |
278 | 279 | if self._version == 2: |
279 | | - version_id = unpack_from(">L", self._data, 4)[0] |
| 280 | + version_id = unpack_from(">L", mmap, 4)[0] |
280 | 281 | assert version_id == self._version, "Unsupported index version: %i" % version_id |
281 | 282 | # END assert version |
282 | 283 |
|
@@ -297,16 +298,16 @@ def _set_cache_(self, attr): |
297 | 298 |
|
298 | 299 | def _entry_v1(self, i): |
299 | 300 | """:return: tuple(offset, binsha, 0)""" |
300 | | - return unpack_from(">L20s", self._data, 1024 + i*24) + (0, ) |
| 301 | + return unpack_from(">L20s", self._cursor.map(), 1024 + i*24) + (0, ) |
301 | 302 |
|
302 | 303 | def _offset_v1(self, i): |
303 | 304 | """see ``_offset_v2``""" |
304 | | - return unpack_from(">L", self._data, 1024 + i*24)[0] |
| 305 | + return unpack_from(">L", self._cursor.map(), 1024 + i*24)[0] |
305 | 306 |
|
306 | 307 | def _sha_v1(self, i): |
307 | 308 | """see ``_sha_v2``""" |
308 | 309 | base = 1024 + (i*24)+4 |
309 | | - return self._data[base:base+20] |
| 310 | + return self._cursor.map()[base:base+20] |
310 | 311 |
|
311 | 312 | def _crc_v1(self, i): |
312 | 313 | """unsupported""" |
@@ -322,25 +323,25 @@ def _entry_v2(self, i): |
322 | 323 | def _offset_v2(self, i): |
323 | 324 | """:return: 32 or 64 byte offset into pack files. 64 byte offsets will only |
324 | 325 | be returned if the pack is larger than 4 GiB, or 2^32""" |
325 | | - offset = unpack_from(">L", self._data, self._pack_offset + i * 4)[0] |
| 326 | + offset = unpack_from(">L", self._cursor.map(), self._pack_offset + i * 4)[0] |
326 | 327 |
|
327 | 328 | # if the high-bit is set, this indicates that we have to lookup the offset |
328 | 329 | # in the 64 bit region of the file. The current offset ( lower 31 bits ) |
329 | 330 | # are the index into it |
330 | 331 | if offset & 0x80000000: |
331 | | - offset = unpack_from(">Q", self._data, self._pack_64_offset + (offset & ~0x80000000) * 8)[0] |
| 332 | + offset = unpack_from(">Q", self._cursor.map(), self._pack_64_offset + (offset & ~0x80000000) * 8)[0] |
332 | 333 | # END handle 64 bit offset |
333 | 334 |
|
334 | 335 | return offset |
335 | 336 |
|
336 | 337 | def _sha_v2(self, i): |
337 | 338 | """:return: sha at the given index of this file index instance""" |
338 | 339 | base = self._sha_list_offset + i * 20 |
339 | | - return self._data[base:base+20] |
| 340 | + return self._cursor.map()[base:base+20] |
340 | 341 |
|
341 | 342 | def _crc_v2(self, i): |
342 | 343 | """:return: 4 bytes crc for the object at index i""" |
343 | | - return unpack_from(">L", self._data, self._crc_list_offset + i * 4)[0] |
| 344 | + return unpack_from(">L", self._cursor.map(), self._crc_list_offset + i * 4)[0] |
344 | 345 |
|
345 | 346 | #} END access V2 |
346 | 347 |
|
@@ -358,7 +359,7 @@ def _initialize(self): |
358 | 359 |
|
359 | 360 | def _read_fanout(self, byte_offset): |
360 | 361 | """Generate a fanout table from our data""" |
361 | | - d = self._data |
| 362 | + d = self._cursor.map() |
362 | 363 | out = list() |
363 | 364 | append = out.append |
364 | 365 | for i in range(256): |
@@ -382,19 +383,19 @@ def path(self): |
382 | 383 |
|
383 | 384 | def packfile_checksum(self): |
384 | 385 | """:return: 20 byte sha representing the sha1 hash of the pack file""" |
385 | | - return self._data[-40:-20] |
| 386 | + return self._cursor.map()[-40:-20] |
386 | 387 |
|
387 | 388 | def indexfile_checksum(self): |
388 | 389 | """:return: 20 byte sha representing the sha1 hash of this index file""" |
389 | | - return self._data[-20:] |
| 390 | + return self._cursor.map()[-20:] |
390 | 391 |
|
391 | 392 | def offsets(self): |
392 | 393 | """:return: sequence of all offsets in the order in which they were written |
393 | 394 | :note: return value can be random accessed, but may be immmutable""" |
394 | 395 | if self._version == 2: |
395 | 396 | # read stream to array, convert to tuple |
396 | 397 | a = array.array('I') # 4 byte unsigned int, long are 8 byte on 64 bit it appears |
397 | | - a.fromstring(buffer(self._data, self._pack_offset, self._pack_64_offset - self._pack_offset)) |
| 398 | + a.fromstring(buffer(self._cursor.map(), self._pack_offset, self._pack_64_offset - self._pack_offset)) |
398 | 399 |
|
399 | 400 | # networkbyteorder to something array likes more |
400 | 401 | if sys.byteorder == 'little': |
@@ -501,7 +502,7 @@ class PackFile(LazyMixin): |
501 | 502 | for some reason - one clearly doesn't want to read 10GB at once in that |
502 | 503 | case""" |
503 | 504 |
|
504 | | - __slots__ = ('_packpath', '_data', '_size', '_version') |
| 505 | + __slots__ = ('_packpath', '_cursor', '_size', '_version') |
505 | 506 | pack_signature = 0x5041434b # 'PACK' |
506 | 507 | pack_version_default = 2 |
507 | 508 |
|
@@ -513,26 +514,20 @@ def __init__(self, packpath): |
513 | 514 | self._packpath = packpath |
514 | 515 |
|
515 | 516 | def _set_cache_(self, attr): |
516 | | - if attr == '_data': |
517 | | - self._data = file_contents_ro_filepath(self._packpath) |
518 | | - |
519 | | - # read the header information |
520 | | - type_id, self._version, self._size = unpack_from(">LLL", self._data, 0) |
521 | | - |
522 | | - # TODO: figure out whether we should better keep the lock, or maybe |
523 | | - # add a .keep file instead ? |
524 | | - else: # must be '_size' or '_version' |
525 | | - # read header info - we do that just with a file stream |
526 | | - type_id, self._version, self._size = unpack(">LLL", open(self._packpath).read(12)) |
527 | | - # END handle header |
| 517 | + # we fill the whole cache, whichever attribute gets queried first |
| 518 | + self._cursor = mman.make_cursor(self._packpath).use_region() |
528 | 519 |
|
| 520 | + # read the header information |
| 521 | + type_id, self._version, self._size = unpack_from(">LLL", self._cursor.map(), 0) |
| 522 | + |
| 523 | + # TODO: figure out whether we should better keep the lock, or maybe |
| 524 | + # add a .keep file instead ? |
529 | 525 | if type_id != self.pack_signature: |
530 | 526 | raise ParseError("Invalid pack signature: %i" % type_id) |
531 | | - #END assert type id |
532 | 527 |
|
533 | 528 | def _iter_objects(self, start_offset, as_stream=True): |
534 | 529 | """Handle the actual iteration of objects within this pack""" |
535 | | - data = self._data |
| 530 | + data = self._cursor.map() |
536 | 531 | content_size = len(data) - self.footer_size |
537 | 532 | cur_offset = start_offset or self.first_object_offset |
538 | 533 |
|
@@ -568,11 +563,11 @@ def data(self): |
568 | 563 | """ |
569 | 564 | :return: read-only data of this pack. It provides random access and usually |
570 | 565 | is a memory map""" |
571 | | - return self._data |
| 566 | + return self._cursor.map() |
572 | 567 |
|
573 | 568 | def checksum(self): |
574 | 569 | """:return: 20 byte sha1 hash on all object sha's contained in this file""" |
575 | | - return self._data[-20:] |
| 570 | + return self._cursor.map()[-20:] |
576 | 571 |
|
577 | 572 | def path(self): |
578 | 573 | """:return: path to the packfile""" |
@@ -591,8 +586,9 @@ def collect_streams(self, offset): |
591 | 586 | If the object at offset is no delta, the size of the list is 1. |
592 | 587 | :param offset: specifies the first byte of the object within this pack""" |
593 | 588 | out = list() |
| 589 | + data = self._cursor.map() |
594 | 590 | while True: |
595 | | - ostream = pack_object_at(self._data, offset, True)[1] |
| 591 | + ostream = pack_object_at(data, offset, True)[1] |
596 | 592 | out.append(ostream) |
597 | 593 | if ostream.type_id == OFS_DELTA: |
598 | 594 | offset = ostream.pack_offset - ostream.delta_info |
@@ -614,14 +610,14 @@ def info(self, offset): |
614 | 610 | |
615 | 611 | :param offset: byte offset |
616 | 612 | :return: OPackInfo instance, the actual type differs depending on the type_id attribute""" |
617 | | - return pack_object_at(self._data, offset or self.first_object_offset, False)[1] |
| 613 | + return pack_object_at(self._cursor.map(), offset or self.first_object_offset, False)[1] |
618 | 614 |
|
619 | 615 | def stream(self, offset): |
620 | 616 | """Retrieve an object at the given file-relative offset as stream along with its information |
621 | 617 | |
622 | 618 | :param offset: byte offset |
623 | 619 | :return: OPackStream instance, the actual type differs depending on the type_id attribute""" |
624 | | - return pack_object_at(self._data, offset or self.first_object_offset, True)[1] |
| 620 | + return pack_object_at(self._cursor.map(), offset or self.first_object_offset, True)[1] |
625 | 621 |
|
626 | 622 | def stream_iter(self, start_offset=0): |
627 | 623 | """ |
@@ -704,7 +700,7 @@ def _object(self, sha, as_stream, index=-1): |
704 | 700 | sha = self._index.sha(index) |
705 | 701 | # END assure sha is present ( in output ) |
706 | 702 | offset = self._index.offset(index) |
707 | | - type_id, uncomp_size, data_rela_offset = pack_object_header_info(buffer(self._pack._data, offset)) |
| 703 | + type_id, uncomp_size, data_rela_offset = pack_object_header_info(buffer(self._pack._cursor.map(), offset)) |
708 | 704 | if as_stream: |
709 | 705 | if type_id not in delta_types: |
710 | 706 | packstream = self._pack.stream(offset) |
|