Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 03757ec

Browse files
committed
Issue #13589: Fix some serialization primitives in the aifc module.
Patch by Oleg Plakhotnyuk.
1 parent c77bb65 commit 03757ec

4 files changed

Lines changed: 71 additions & 18 deletions

File tree

Lib/aifc.py

Lines changed: 30 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,12 @@ def _read_short(file):
162162
except struct.error:
163163
raise EOFError
164164

165+
def _read_ushort(file):
166+
try:
167+
return struct.unpack('>H', file.read(2))[0]
168+
except struct.error:
169+
raise EOFError
170+
165171
def _read_string(file):
166172
length = ord(file.read(1))
167173
if length == 0:
@@ -194,13 +200,19 @@ def _read_float(f): # 10 bytes
194200
def _write_short(f, x):
195201
f.write(struct.pack('>h', x))
196202

203+
def _write_ushort(f, x):
204+
f.write(struct.pack('>H', x))
205+
197206
def _write_long(f, x):
207+
f.write(struct.pack('>l', x))
208+
209+
def _write_ulong(f, x):
198210
f.write(struct.pack('>L', x))
199211

200212
def _write_string(f, s):
201213
if len(s) > 255:
202214
raise ValueError("string exceeds maximum pstring length")
203-
f.write(struct.pack('b', len(s)))
215+
f.write(struct.pack('B', len(s)))
204216
f.write(s)
205217
if len(s) & 1 == 0:
206218
f.write(b'\x00')
@@ -218,7 +230,7 @@ def _write_float(f, x):
218230
lomant = 0
219231
else:
220232
fmant, expon = math.frexp(x)
221-
if expon > 16384 or fmant >= 1: # Infinity or NaN
233+
if expon > 16384 or fmant >= 1 or fmant != fmant: # Infinity or NaN
222234
expon = sign|0x7FFF
223235
himant = 0
224236
lomant = 0
@@ -234,9 +246,9 @@ def _write_float(f, x):
234246
fmant = math.ldexp(fmant - fsmant, 32)
235247
fsmant = math.floor(fmant)
236248
lomant = int(fsmant)
237-
_write_short(f, expon)
238-
_write_long(f, himant)
239-
_write_long(f, lomant)
249+
_write_ushort(f, expon)
250+
_write_ulong(f, himant)
251+
_write_ulong(f, lomant)
240252

241253
from chunk import Chunk
242254

@@ -762,25 +774,25 @@ def _write_header(self, initlength):
762774
if self._aifc:
763775
self._file.write(b'AIFC')
764776
self._file.write(b'FVER')
765-
_write_long(self._file, 4)
766-
_write_long(self._file, self._version)
777+
_write_ulong(self._file, 4)
778+
_write_ulong(self._file, self._version)
767779
else:
768780
self._file.write(b'AIFF')
769781
self._file.write(b'COMM')
770-
_write_long(self._file, commlength)
782+
_write_ulong(self._file, commlength)
771783
_write_short(self._file, self._nchannels)
772784
self._nframes_pos = self._file.tell()
773-
_write_long(self._file, self._nframes)
785+
_write_ulong(self._file, self._nframes)
774786
_write_short(self._file, self._sampwidth * 8)
775787
_write_float(self._file, self._framerate)
776788
if self._aifc:
777789
self._file.write(self._comptype)
778790
_write_string(self._file, self._compname)
779791
self._file.write(b'SSND')
780792
self._ssnd_length_pos = self._file.tell()
781-
_write_long(self._file, self._datalength + 8)
782-
_write_long(self._file, 0)
783-
_write_long(self._file, 0)
793+
_write_ulong(self._file, self._datalength + 8)
794+
_write_ulong(self._file, 0)
795+
_write_ulong(self._file, 0)
784796

785797
def _write_form_length(self, datalength):
786798
if self._aifc:
@@ -791,8 +803,8 @@ def _write_form_length(self, datalength):
791803
else:
792804
commlength = 18
793805
verslength = 0
794-
_write_long(self._file, 4 + verslength + self._marklength + \
795-
8 + commlength + 16 + datalength)
806+
_write_ulong(self._file, 4 + verslength + self._marklength + \
807+
8 + commlength + 16 + datalength)
796808
return commlength
797809

798810
def _patchheader(self):
@@ -810,9 +822,9 @@ def _patchheader(self):
810822
self._file.seek(self._form_length_pos, 0)
811823
dummy = self._write_form_length(datalength)
812824
self._file.seek(self._nframes_pos, 0)
813-
_write_long(self._file, self._nframeswritten)
825+
_write_ulong(self._file, self._nframeswritten)
814826
self._file.seek(self._ssnd_length_pos, 0)
815-
_write_long(self._file, datalength + 8)
827+
_write_ulong(self._file, datalength + 8)
816828
self._file.seek(curpos, 0)
817829
self._nframes = self._nframeswritten
818830
self._datalength = datalength
@@ -827,13 +839,13 @@ def _writemarkers(self):
827839
length = length + len(name) + 1 + 6
828840
if len(name) & 1 == 0:
829841
length = length + 1
830-
_write_long(self._file, length)
842+
_write_ulong(self._file, length)
831843
self._marklength = length + 8
832844
_write_short(self._file, len(self._markers))
833845
for marker in self._markers:
834846
id, pos, name = marker
835847
_write_short(self._file, id)
836-
_write_long(self._file, pos)
848+
_write_ulong(self._file, pos)
837849
_write_string(self._file, name)
838850

839851
def open(f, mode=None):

Lib/test/test_aifc.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,45 @@ def test_read_markers(self):
144144
self.assertRaises(aifc.Error, f.getmark, 3)
145145

146146

147+
class AIFCLowLevelTest(unittest.TestCase):
148+
149+
def test_read_written(self):
150+
def read_written(self, what):
151+
f = io.BytesIO()
152+
getattr(aifc, '_write_' + what)(f, x)
153+
f.seek(0)
154+
return getattr(aifc, '_read_' + what)(f)
155+
for x in (-1, 0, 0.1, 1):
156+
self.assertEqual(read_written(x, 'float'), x)
157+
for x in (float('NaN'), float('Inf')):
158+
self.assertEqual(read_written(x, 'float'), aifc._HUGE_VAL)
159+
for x in (b'', b'foo', b'a' * 255):
160+
self.assertEqual(read_written(x, 'string'), x)
161+
for x in (-0x7FFFFFFF, -1, 0, 1, 0x7FFFFFFF):
162+
self.assertEqual(read_written(x, 'long'), x)
163+
for x in (0, 1, 0xFFFFFFFF):
164+
self.assertEqual(read_written(x, 'ulong'), x)
165+
for x in (-0x7FFF, -1, 0, 1, 0x7FFF):
166+
self.assertEqual(read_written(x, 'short'), x)
167+
for x in (0, 1, 0xFFFF):
168+
self.assertEqual(read_written(x, 'ushort'), x)
169+
170+
def test_read_raises(self):
171+
f = io.BytesIO(b'\x00')
172+
self.assertRaises(EOFError, aifc._read_ulong, f)
173+
self.assertRaises(EOFError, aifc._read_long, f)
174+
self.assertRaises(EOFError, aifc._read_ushort, f)
175+
self.assertRaises(EOFError, aifc._read_short, f)
176+
177+
def test_write_long_string_raises(self):
178+
f = io.BytesIO()
179+
with self.assertRaises(ValueError):
180+
aifc._write_string(f, b'too long' * 255)
181+
182+
147183
def test_main():
148184
run_unittest(AIFCTest)
185+
run_unittest(AIFCLowLevelTest)
149186

150187

151188
if __name__ == "__main__":

Misc/ACKS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -719,6 +719,7 @@ Zach Pincus
719719
Michael Piotrowski
720720
Antoine Pitrou
721721
Jean-François Piéronne
722+
Oleg Plakhotnyuk
722723
Guilherme Polo
723724
Michael Pomraning
724725
Iustin Pop

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,9 @@ Core and Builtins
100100
Library
101101
-------
102102

103+
- Issue #13589: Fix some serialization primitives in the aifc module.
104+
Patch by Oleg Plakhotnyuk.
105+
103106
- Issue #13642: Unquote before b64encoding user:password during Basic
104107
Authentication. Patch contributed by Joonas Kuorilehto.
105108

0 commit comments

Comments
 (0)