Skip to content

Commit c1b1a23

Browse files
authored
Fix Unpacker.tell() (#427)
Fixes #426. Co-authored-by: folz <[email protected]>
1 parent b046900 commit c1b1a23

File tree

3 files changed

+45
-9
lines changed

3 files changed

+45
-9
lines changed

msgpack/_unpacker.pyx

+8-2
Original file line numberDiff line numberDiff line change
@@ -484,8 +484,10 @@ cdef class Unpacker(object):
484484
nread = min(self.buf_tail - self.buf_head, nbytes)
485485
ret = PyBytes_FromStringAndSize(self.buf + self.buf_head, nread)
486486
self.buf_head += nread
487-
if len(ret) < nbytes and self.file_like is not None:
488-
ret += self.file_like.read(nbytes - len(ret))
487+
if nread < nbytes and self.file_like is not None:
488+
ret += self.file_like.read(nbytes - nread)
489+
nread = len(ret)
490+
self.stream_offset += nread
489491
return ret
490492

491493
def unpack(self):
@@ -519,6 +521,10 @@ cdef class Unpacker(object):
519521
return self._unpack(read_map_header)
520522

521523
def tell(self):
524+
"""Returns the current position of the Unpacker in bytes, i.e., the
525+
number of bytes that were read from the input, also the starting
526+
position of the next object.
527+
"""
522528
return self.stream_offset
523529

524530
def __iter__(self):

msgpack/fallback.py

+8-7
Original file line numberDiff line numberDiff line change
@@ -365,18 +365,19 @@ def _get_extradata(self):
365365
return self._buffer[self._buff_i :]
366366

367367
def read_bytes(self, n):
368-
ret = self._read(n)
368+
ret = self._read(n, raise_outofdata=False)
369369
self._consume()
370370
return ret
371371

372-
def _read(self, n):
372+
def _read(self, n, raise_outofdata=True):
373373
# (int) -> bytearray
374-
self._reserve(n)
374+
self._reserve(n, raise_outofdata=raise_outofdata)
375375
i = self._buff_i
376-
self._buff_i = i + n
377-
return self._buffer[i : i + n]
376+
ret = self._buffer[i : i + n]
377+
self._buff_i = i + len(ret)
378+
return ret
378379

379-
def _reserve(self, n):
380+
def _reserve(self, n, raise_outofdata=True):
380381
remain_bytes = len(self._buffer) - self._buff_i - n
381382

382383
# Fast path: buffer has n bytes already
@@ -404,7 +405,7 @@ def _reserve(self, n):
404405
self._buffer += read_data
405406
remain_bytes -= len(read_data)
406407

407-
if len(self._buffer) < n + self._buff_i:
408+
if len(self._buffer) < n + self._buff_i and raise_outofdata:
408409
self._buff_i = 0 # rollback
409410
raise OutOfData
410411

test/test_unpack.py

+29
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@
33
from msgpack import Unpacker, packb, OutOfData, ExtType
44
from pytest import raises, mark
55

6+
try:
7+
from itertools import izip as zip
8+
except ImportError:
9+
pass
10+
611

712
def test_unpack_array_header_from_file():
813
f = BytesIO(packb([1, 2, 3, 4]))
@@ -64,7 +69,31 @@ def _hook(self, code, data):
6469
assert unpacker.unpack() == {"a": ExtType(2, b"321")}
6570

6671

72+
def test_unpacker_tell():
73+
objects = 1, 2, u"abc", u"def", u"ghi"
74+
packed = b"\x01\x02\xa3abc\xa3def\xa3ghi"
75+
positions = 1, 2, 6, 10, 14
76+
unpacker = Unpacker(BytesIO(packed))
77+
for obj, unp, pos in zip(objects, unpacker, positions):
78+
assert obj == unp
79+
assert pos == unpacker.tell()
80+
81+
82+
def test_unpacker_tell_read_bytes():
83+
objects = 1, u"abc", u"ghi"
84+
packed = b"\x01\x02\xa3abc\xa3def\xa3ghi"
85+
raw_data = b"\x02", b"\xa3def", b""
86+
lenghts = 1, 4, 999
87+
positions = 1, 6, 14
88+
unpacker = Unpacker(BytesIO(packed))
89+
for obj, unp, pos, n, raw in zip(objects, unpacker, positions, lenghts, raw_data):
90+
assert obj == unp
91+
assert pos == unpacker.tell()
92+
assert unpacker.read_bytes(n) == raw
93+
94+
6795
if __name__ == "__main__":
6896
test_unpack_array_header_from_file()
6997
test_unpacker_hook_refcnt()
7098
test_unpacker_ext_hook()
99+
test_unpacker_tell()

0 commit comments

Comments
 (0)