Coverage for oarepo_c4gh / crypt4gh / rawio.py: 100%
59 statements
« prev ^ index » next coverage.py v7.12.0, created at 2025-12-06 16:58 +0000
« prev ^ index » next coverage.py v7.12.0, created at 2025-12-06 16:58 +0000
1"""This module provides a wrapper on top of any Proto4GH-compatible
2object with RawIO protocol for the linear stream of cleartext data
3from the Crypt4GH container.
5"""
7from io import RawIOBase
8from .common.proto4gh import Proto4GH
11class Crypt4GHRawIO(RawIOBase):
12 """RawIO-compatible read-only wrapper around Proto4GH. Implements
13 only the `readinto` method - the rest of functionality must be
14 provided by BufferedIOBase and TextIOBase wrappers.
16 """
18 def __init__(self, container: Proto4GH) -> None:
19 """Initializes the container wrapper and sets internal block
20 caching up.
22 Parameters:
23 container: opened Crypt4GH container providing the underlying
24 data blocks
26 """
27 self._container = container
28 self._data_blocks = None
29 self._current_block = None
30 self._current_pos = 0
31 self._finished = False
33 def readinto(self, b: bytearray) -> int:
34 """As required by RawIO, read bytes into a pre-allocated,
35 writable bytes-like object b, and return the number of bytes
36 read.
38 Parameters:
39 b: buffer to read the data into
41 Returns:
42 The number of bytes read.
43 """
44 if self._finished:
45 return 0
46 if self._data_blocks is None:
47 self._data_blocks = self._container.data_blocks
48 self._edit_list = self._container.header.edit_list.copy()
49 self._edit_skipping = True
50 blen = len(b)
51 bpos = 0
52 while bpos < blen:
53 # Load next block if the current one is completely used.
54 if self._current_block is None or self._current_pos >= len(
55 self._current_block
56 ):
57 try:
58 nxt = next(self._data_blocks)
59 except StopIteration:
60 # Cannot read more blocks, end immediately.
61 self._finished = True
62 return bpos
63 self._current_pos = 0
64 if not nxt.is_deciphered:
65 # The error raised is similar to failed read from
66 # a disk which resembles this situation the most.
67 raise OSError
68 self._current_block = nxt.cleartext
69 # Check for skipping first
70 if self._edit_skipping and (len(self._edit_list) > 0):
71 # How much is left for skipping and how much data in
72 # the current block remains so that it can be skipped
73 # immediately
74 skip_req = self._edit_list[0]
75 avail = len(self._current_block) - self._current_pos
76 to_skip = min(skip_req, avail)
77 self._edit_list[0] = self._edit_list[0] - to_skip
78 self._current_pos = self._current_pos + to_skip
79 if self._edit_list[0] == 0:
80 # Skipped enough, flip the flag, advance to next
81 # count and continue
82 self._edit_list = self._edit_list[1:]
83 self._edit_skipping = False
84 else:
85 # Get how much can be copied first.
86 avail = len(self._current_block) - self._current_pos
87 to_copy = min(blen - bpos, avail)
88 # Check whether there are still some edit list counts
89 # left and apply it if necessary.
90 if len(self._edit_list) > 0:
91 # Adjust to_copy to maximum
92 max_copy = self._edit_list[0]
93 to_copy = min(to_copy, max_copy)
94 b[bpos : bpos + to_copy] = self._current_block[
95 self._current_pos : self._current_pos + to_copy
96 ]
97 self._current_pos = self._current_pos + to_copy
98 bpos = bpos + to_copy
99 if len(self._edit_list) > 0:
100 # Reduce current not-skipping counter
101 self._edit_list[0] = self._edit_list[0] - to_copy
102 if self._edit_list[0] == 0:
103 # Finished the not-skipping part
104 self._edit_list = self._edit_list[1:]
105 if len(self._edit_list) > 0:
106 # Something left, therefore set the
107 # skipping flag again.
108 self._edit_skipping = True
109 else:
110 # Nothing left
111 self._finished = True
112 return bpos
113 return bpos
115 def writable(self) -> bool:
116 """According to RawIO specification this method returning
117 always False ensures no write-like methods can be used as this
118 implementation provides read-only access.
120 Returns:
121 Always False.
122 """
123 return False
125 def readable(self) -> bool:
126 """According to RawIO specification this method returning
127 always True ensures read-like methods can be used.
129 Returns:
130 Always True.
131 """
132 return True