@@ -119,6 +119,104 @@ def test_same_as_binascii_crc32(self):
119119 self .assertEqual (binascii .crc32 (b'spam' ), zlib .crc32 (b'spam' ))
120120
121121
122+ class ChecksumCombineMixin :
123+ """Mixin class for testing checksum combination."""
124+
125+ N = 1000
126+ default_iv : int
127+
128+ def parse_iv (self , iv ):
129+ """Parse an IV value.
130+
131+ - The default IV is returned if *iv* is None.
132+ - A random IV is returned if *iv* is -1.
133+ - Otherwise, *iv* is returned as is.
134+ """
135+ if iv is None :
136+ return self .default_iv
137+ if iv == - 1 :
138+ return random .randint (1 , 0x80000000 )
139+ return iv
140+
141+ def checksum (self , data , init = None ):
142+ """Compute the checksum of data with a given initial value.
143+
144+ The *init* value is parsed by ``parse_iv``.
145+ """
146+ iv = self .parse_iv (init )
147+ return self ._checksum (data , iv )
148+
149+ def _checksum (self , data , init ):
150+ raise NotImplementedError
151+
152+ def combine (self , a , b , blen ):
153+ """Combine two checksums together."""
154+ raise NotImplementedError
155+
156+ def get_random_data (self , data_len , * , iv = None ):
157+ """Get a triplet (data, iv, checksum)."""
158+ data = random .randbytes (data_len )
159+ init = self .parse_iv (iv )
160+ checksum = self .checksum (data , init )
161+ return data , init , checksum
162+
163+ def test_combine_empty (self ):
164+ for _ in range (self .N ):
165+ a , iv , checksum = self .get_random_data (32 , iv = - 1 )
166+ res = self .combine (iv , self .checksum (a ), len (a ))
167+ self .assertEqual (res , checksum )
168+
169+ def test_combine_no_iv (self ):
170+ for _ in range (self .N ):
171+ a , _ , chk_a = self .get_random_data (32 )
172+ b , _ , chk_b = self .get_random_data (64 )
173+ res = self .combine (chk_a , chk_b , len (b ))
174+ self .assertEqual (res , self .checksum (a + b ))
175+
176+ def test_combine_with_iv (self ):
177+ for _ in range (self .N ):
178+ a , iv_a , chk_a_with_iv = self .get_random_data (32 , iv = - 1 )
179+ chk_a_no_iv = self .checksum (a )
180+ b , iv_b , chk_b_with_iv = self .get_random_data (64 , iv = - 1 )
181+ chk_b_no_iv = self .checksum (b )
182+
183+ # We can represent c = COMBINE(CHK(a, iv_a), CHK(b, iv_b)) as:
184+ #
185+ # c = CHK(CHK(b'', iv_a) + CHK(a) + CHK(b'', iv_b) + CHK(b))
186+ # = COMBINE(
187+ # COMBINE(CHK(b'', iv_a), CHK(a)),
188+ # COMBINE(CHK(b'', iv_b), CHK(b)),
189+ # )
190+ # = COMBINE(COMBINE(iv_a, CHK(a)), COMBINE(iv_b, CHK(b)))
191+ tmp0 = self .combine (iv_a , chk_a_no_iv , len (a ))
192+ tmp1 = self .combine (iv_b , chk_b_no_iv , len (b ))
193+ expected = self .combine (tmp0 , tmp1 , len (b ))
194+ checksum = self .combine (chk_a_with_iv , chk_b_with_iv , len (b ))
195+ self .assertEqual (checksum , expected )
196+
197+
198+ class CRC32CombineTestCase (ChecksumCombineMixin , unittest .TestCase ):
199+
200+ default_iv = 0
201+
202+ def _checksum (self , data , init ):
203+ return zlib .crc32 (data , init )
204+
205+ def combine (self , a , b , blen ):
206+ return zlib .crc32_combine (a , b , blen )
207+
208+
209+ class Adler32CombineTestCase (ChecksumCombineMixin , unittest .TestCase ):
210+
211+ default_iv = 1
212+
213+ def _checksum (self , data , init ):
214+ return zlib .adler32 (data , init )
215+
216+ def combine (self , a , b , blen ):
217+ return zlib .adler32_combine (a , b , blen )
218+
219+
122220# Issue #10276 - check that inputs >=4 GiB are handled correctly.
123221class ChecksumBigBufferTestCase (unittest .TestCase ):
124222
0 commit comments