| 72 | |
| 73 | |
| 74 | def read_range( |
| 75 | source_path: str, offset: int, length: int, *, chunk_size: int = 1024 * 1024 |
| 76 | ) -> bytes: |
| 77 | _validate_range(offset, length, chunk_size) |
| 78 | source_fd = _open_source(source_path) |
| 79 | position = offset |
| 80 | remaining = length |
| 81 | chunks: list[bytes] = [] |
| 82 | |
| 83 | try: |
| 84 | while remaining > 0: |
| 85 | to_read = min(chunk_size, remaining) |
| 86 | chunk = _safe_pread(source_fd, to_read, position, source_path) |
| 87 | if not chunk: |
| 88 | break |
| 89 | chunks.append(chunk) |
| 90 | read_len = len(chunk) |
| 91 | remaining -= read_len |
| 92 | position += read_len |
| 93 | finally: |
| 94 | os.close(source_fd) |
| 95 | |
| 96 | if remaining > 0: |
| 97 | raise BlockExtractionError( |
| 98 | "Requested range exceeds available data.", |
| 99 | f"Cannot read requested range from {source_path}: reached end of file/device.", |
| 100 | ) |
| 101 | |
| 102 | return b"".join(chunks) |
| 103 | |
| 104 | |
| 105 | def _open_source(source_path: str) -> int: |