| 693 | |
| 694 | @async_wrapper |
| 695 | def read(self, fh, offset, size): |
| 696 | parts = [] |
| 697 | item = self.get_item(fh) |
| 698 | |
| 699 | # optimize for linear reads: |
| 700 | # we cache the chunk number and the in-file offset of the chunk in _last_pos[fh] |
| 701 | chunk_no, chunk_offset = self._last_pos.get(fh, (0, 0)) |
| 702 | if chunk_offset > offset: |
| 703 | # this is not a linear read, so we lost track and need to start from beginning again... |
| 704 | chunk_no, chunk_offset = (0, 0) |
| 705 | |
| 706 | offset -= chunk_offset |
| 707 | chunks = item.chunks |
| 708 | # note: using index iteration to avoid frequently copying big (sub)lists by slicing |
| 709 | for idx in range(chunk_no, len(chunks)): |
| 710 | id, s = chunks[idx] |
| 711 | if s < offset: |
| 712 | offset -= s |
| 713 | chunk_offset += s |
| 714 | chunk_no += 1 |
| 715 | continue |
| 716 | n = min(size, s - offset) |
| 717 | if id in self.data_cache: |
| 718 | data = self.data_cache[id] |
| 719 | if offset + n == len(data): |
| 720 | # evict fully read chunk from cache |
| 721 | del self.data_cache[id] |
| 722 | else: |
| 723 | try: |
| 724 | cdata = self.repository_uncached.get(id) |
| 725 | except Repository.ObjectNotFound: |
| 726 | if self.allow_damaged_files: |
| 727 | data = zeros[:s] |
| 728 | assert len(data) == s |
| 729 | else: |
| 730 | raise llfuse.FUSEError(errno.EIO) from None |
| 731 | else: |
| 732 | _, data = self.repo_objs.parse(id, cdata, ro_type=ROBJ_FILE_STREAM) |
| 733 | if offset + n < len(data): |
| 734 | # chunk was only partially read, cache it |
| 735 | self.data_cache[id] = data |
| 736 | parts.append(data[offset : offset + n]) |
| 737 | offset = 0 |
| 738 | size -= n |
| 739 | if not size: |
| 740 | if fh in self._last_pos: |
| 741 | self._last_pos.replace(fh, (chunk_no, chunk_offset)) |
| 742 | else: |
| 743 | self._last_pos[fh] = (chunk_no, chunk_offset) |
| 744 | break |
| 745 | return b"".join(parts) |
| 746 | |
| 747 | # note: we can't have a generator (with yield) and not a generator (async) in the same method |
| 748 | if has_pyfuse3: |