Commit 42b73042 authored by Antoine Pitrou's avatar Antoine Pitrou

Issue #13589: Fix some serialization primitives in the aifc module.

Patch by Oleg Plakhotnyuk.
parent 6c576594
...@@ -162,6 +162,12 @@ def _read_short(file): ...@@ -162,6 +162,12 @@ def _read_short(file):
except struct.error: except struct.error:
raise EOFError raise EOFError
def _read_ushort(file):
try:
return struct.unpack('>H', file.read(2))[0]
except struct.error:
raise EOFError
def _read_string(file): def _read_string(file):
length = ord(file.read(1)) length = ord(file.read(1))
if length == 0: if length == 0:
...@@ -194,13 +200,19 @@ def _read_float(f): # 10 bytes ...@@ -194,13 +200,19 @@ def _read_float(f): # 10 bytes
def _write_short(f, x): def _write_short(f, x):
f.write(struct.pack('>h', x)) f.write(struct.pack('>h', x))
def _write_ushort(f, x):
f.write(struct.pack('>H', x))
def _write_long(f, x): def _write_long(f, x):
f.write(struct.pack('>l', x))
def _write_ulong(f, x):
f.write(struct.pack('>L', x)) f.write(struct.pack('>L', x))
def _write_string(f, s): def _write_string(f, s):
if len(s) > 255: if len(s) > 255:
raise ValueError("string exceeds maximum pstring length") raise ValueError("string exceeds maximum pstring length")
f.write(chr(len(s))) f.write(struct.pack('B', len(s)))
f.write(s) f.write(s)
if len(s) & 1 == 0: if len(s) & 1 == 0:
f.write(chr(0)) f.write(chr(0))
...@@ -218,7 +230,7 @@ def _write_float(f, x): ...@@ -218,7 +230,7 @@ def _write_float(f, x):
lomant = 0 lomant = 0
else: else:
fmant, expon = math.frexp(x) fmant, expon = math.frexp(x)
if expon > 16384 or fmant >= 1: # Infinity or NaN if expon > 16384 or fmant >= 1 or fmant != fmant: # Infinity or NaN
expon = sign|0x7FFF expon = sign|0x7FFF
himant = 0 himant = 0
lomant = 0 lomant = 0
...@@ -234,9 +246,9 @@ def _write_float(f, x): ...@@ -234,9 +246,9 @@ def _write_float(f, x):
fmant = math.ldexp(fmant - fsmant, 32) fmant = math.ldexp(fmant - fsmant, 32)
fsmant = math.floor(fmant) fsmant = math.floor(fmant)
lomant = long(fsmant) lomant = long(fsmant)
_write_short(f, expon) _write_ushort(f, expon)
_write_long(f, himant) _write_ulong(f, himant)
_write_long(f, lomant) _write_ulong(f, lomant)
from chunk import Chunk from chunk import Chunk
...@@ -840,15 +852,15 @@ class Aifc_write: ...@@ -840,15 +852,15 @@ class Aifc_write:
if self._aifc: if self._aifc:
self._file.write('AIFC') self._file.write('AIFC')
self._file.write('FVER') self._file.write('FVER')
_write_long(self._file, 4) _write_ulong(self._file, 4)
_write_long(self._file, self._version) _write_ulong(self._file, self._version)
else: else:
self._file.write('AIFF') self._file.write('AIFF')
self._file.write('COMM') self._file.write('COMM')
_write_long(self._file, commlength) _write_ulong(self._file, commlength)
_write_short(self._file, self._nchannels) _write_short(self._file, self._nchannels)
self._nframes_pos = self._file.tell() self._nframes_pos = self._file.tell()
_write_long(self._file, self._nframes) _write_ulong(self._file, self._nframes)
_write_short(self._file, self._sampwidth * 8) _write_short(self._file, self._sampwidth * 8)
_write_float(self._file, self._framerate) _write_float(self._file, self._framerate)
if self._aifc: if self._aifc:
...@@ -856,9 +868,9 @@ class Aifc_write: ...@@ -856,9 +868,9 @@ class Aifc_write:
_write_string(self._file, self._compname) _write_string(self._file, self._compname)
self._file.write('SSND') self._file.write('SSND')
self._ssnd_length_pos = self._file.tell() self._ssnd_length_pos = self._file.tell()
_write_long(self._file, self._datalength + 8) _write_ulong(self._file, self._datalength + 8)
_write_long(self._file, 0) _write_ulong(self._file, 0)
_write_long(self._file, 0) _write_ulong(self._file, 0)
def _write_form_length(self, datalength): def _write_form_length(self, datalength):
if self._aifc: if self._aifc:
...@@ -869,8 +881,8 @@ class Aifc_write: ...@@ -869,8 +881,8 @@ class Aifc_write:
else: else:
commlength = 18 commlength = 18
verslength = 0 verslength = 0
_write_long(self._file, 4 + verslength + self._marklength + \ _write_ulong(self._file, 4 + verslength + self._marklength + \
8 + commlength + 16 + datalength) 8 + commlength + 16 + datalength)
return commlength return commlength
def _patchheader(self): def _patchheader(self):
...@@ -888,9 +900,9 @@ class Aifc_write: ...@@ -888,9 +900,9 @@ class Aifc_write:
self._file.seek(self._form_length_pos, 0) self._file.seek(self._form_length_pos, 0)
dummy = self._write_form_length(datalength) dummy = self._write_form_length(datalength)
self._file.seek(self._nframes_pos, 0) self._file.seek(self._nframes_pos, 0)
_write_long(self._file, self._nframeswritten) _write_ulong(self._file, self._nframeswritten)
self._file.seek(self._ssnd_length_pos, 0) self._file.seek(self._ssnd_length_pos, 0)
_write_long(self._file, datalength + 8) _write_ulong(self._file, datalength + 8)
self._file.seek(curpos, 0) self._file.seek(curpos, 0)
self._nframes = self._nframeswritten self._nframes = self._nframeswritten
self._datalength = datalength self._datalength = datalength
...@@ -905,13 +917,13 @@ class Aifc_write: ...@@ -905,13 +917,13 @@ class Aifc_write:
length = length + len(name) + 1 + 6 length = length + len(name) + 1 + 6
if len(name) & 1 == 0: if len(name) & 1 == 0:
length = length + 1 length = length + 1
_write_long(self._file, length) _write_ulong(self._file, length)
self._marklength = length + 8 self._marklength = length + 8
_write_short(self._file, len(self._markers)) _write_short(self._file, len(self._markers))
for marker in self._markers: for marker in self._markers:
id, pos, name = marker id, pos, name = marker
_write_short(self._file, id) _write_short(self._file, id)
_write_long(self._file, pos) _write_ulong(self._file, pos)
_write_string(self._file, name) _write_string(self._file, name)
def open(f, mode=None): def open(f, mode=None):
......
from test.test_support import findfile, run_unittest, TESTFN from test.test_support import findfile, run_unittest, TESTFN
import unittest import unittest
import os import os
import io
import aifc import aifc
...@@ -107,8 +108,45 @@ class AIFCTest(unittest.TestCase): ...@@ -107,8 +108,45 @@ class AIFCTest(unittest.TestCase):
self.assertEqual(testfile.closed, True) self.assertEqual(testfile.closed, True)
class AIFCLowLevelTest(unittest.TestCase):
def test_read_written(self):
def read_written(self, what):
f = io.BytesIO()
getattr(aifc, '_write_' + what)(f, x)
f.seek(0)
return getattr(aifc, '_read_' + what)(f)
for x in (-1, 0, 0.1, 1):
self.assertEqual(read_written(x, 'float'), x)
for x in (float('NaN'), float('Inf')):
self.assertEqual(read_written(x, 'float'), aifc._HUGE_VAL)
for x in (b'', b'foo', b'a' * 255):
self.assertEqual(read_written(x, 'string'), x)
for x in (-0x7FFFFFFF, -1, 0, 1, 0x7FFFFFFF):
self.assertEqual(read_written(x, 'long'), x)
for x in (0, 1, 0xFFFFFFFF):
self.assertEqual(read_written(x, 'ulong'), x)
for x in (-0x7FFF, -1, 0, 1, 0x7FFF):
self.assertEqual(read_written(x, 'short'), x)
for x in (0, 1, 0xFFFF):
self.assertEqual(read_written(x, 'ushort'), x)
def test_read_raises(self):
f = io.BytesIO(b'\x00')
self.assertRaises(EOFError, aifc._read_ulong, f)
self.assertRaises(EOFError, aifc._read_long, f)
self.assertRaises(EOFError, aifc._read_ushort, f)
self.assertRaises(EOFError, aifc._read_short, f)
def test_write_long_string_raises(self):
f = io.BytesIO()
with self.assertRaises(ValueError):
aifc._write_string(f, b'too long' * 255)
def test_main(): def test_main():
run_unittest(AIFCTest) run_unittest(AIFCTest)
run_unittest(AIFCLowLevelTest)
if __name__ == "__main__": if __name__ == "__main__":
......
...@@ -662,6 +662,7 @@ Zach Pincus ...@@ -662,6 +662,7 @@ Zach Pincus
Michael Piotrowski Michael Piotrowski
Antoine Pitrou Antoine Pitrou
Jean-François Piéronne Jean-François Piéronne
Oleg Plakhotnyuk
Guilherme Polo Guilherme Polo
Michael Pomraning Michael Pomraning
Iustin Pop Iustin Pop
......
...@@ -89,6 +89,9 @@ Core and Builtins ...@@ -89,6 +89,9 @@ Core and Builtins
Library Library
------- -------
- Issue #13589: Fix some serialization primitives in the aifc module.
Patch by Oleg Plakhotnyuk.
- Issue #13642: Unquote before b64encoding user:password during Basic - Issue #13642: Unquote before b64encoding user:password during Basic
Authentication. Patch contributed by Joonas Kuorilehto and Michele Orrù. Authentication. Patch contributed by Joonas Kuorilehto and Michele Orrù.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment