Skip to content

Commit 19a8e75

Browse files
committed
Reading empty files (odd use case) and reading files past the end of a file now raises an EOFError. Test suite adjusted to account for the the change
1 parent b659ea3 commit 19a8e75

File tree

3 files changed

+97
-93
lines changed

3 files changed

+97
-93
lines changed

sc2reader/readers.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,6 @@ def read(self, filecontents, replay):
180180
type, code = buffer.shift(3), buffer.read_byte()
181181

182182
parser = PARSERS[type](code)
183-
print parser
184183
if parser:
185184
event = parser(buffer, frames, type, code, pid)
186185
buffer.align()

sc2reader/utils.py

Lines changed: 96 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -244,101 +244,106 @@ def shift(self, bits):
244244
If there is no loaded byte, or the loaded byte has been exhausted,
245245
then Buffer.shift(8) could technically be used to read a single
246246
byte-aligned byte.
247-
"""
248-
if bits == 0:
249-
return 0
250-
251-
elif bits <= (8-self.bit_shift):
252-
#Grab a new byte if the currently loaded one is exhausted
253-
if self.bit_shift == 0:
254-
self.last_byte = ord(self.io.read(1))
247+
"""
248+
try:
249+
if bits == 0:
250+
return 0
255251

256-
#Get the requested bits from the byte, and adjust state
257-
ret = (self.last_byte >> self.bit_shift) & (2**bits-1)
258-
self.bit_shift = (self.bit_shift + bits) % 8
259-
return ret
260-
261-
else:
262-
msg = "Cannot shift off %s bits. Only %s bits remaining."
263-
raise ValueError(msg % (bits, 8-self.bit_shift))
252+
elif bits <= (8-self.bit_shift):
253+
#Grab a new byte if the currently loaded one is exhausted
254+
if self.bit_shift == 0:
255+
self.last_byte = ord(self.io.read(1))
256+
257+
#Get the requested bits from the byte, and adjust state
258+
ret = (self.last_byte >> self.bit_shift) & (2**bits-1)
259+
self.bit_shift = (self.bit_shift + bits) % 8
260+
return ret
261+
262+
else:
263+
msg = "Cannot shift off %s bits. Only %s bits remaining."
264+
raise ValueError(msg % (bits, 8-self.bit_shift))
265+
except TypeError:
266+
raise EOFError("Cannot shift new byte. End of buffer reached")
264267

265268
def read(self, bytes=0, bits=0):
266-
bytes, bits = bytes+bits/8, bits%8,
267-
bit_count = bytes*8+bits
268-
269-
#check special case of not having to do any work
270-
if bit_count == 0: return []
271-
272-
#check sepcial case of intra-byte read
273-
if bit_count <= (8-self.bit_shift):
274-
return [self.shift(bit_count)]
275-
276-
#check special case of byte-aligned reads, performance booster
277-
if self.bit_shift == 0:
278-
base = [ord(self.io.read(1)) for byte in range(bytes)]
279-
if bits != 0:
280-
return base+[self.shift(bits)]
281-
return base
282-
283-
# Calculated shifts
284-
old_bit_shift = self.bit_shift
285-
new_bit_shift = (self.bit_shift+bits) % 8
286-
287-
# Masks
288-
lo_mask = 2**old_bit_shift-1
289-
lo_mask_inv = 0xFF - 2**(8-old_bit_shift)+1
290-
hi_mask = 0xFF ^ lo_mask
291-
hi_mask_inv = 0xFF ^ lo_mask_inv
292-
293-
#last byte parameters
294-
if new_bit_shift == 0: #this means we filled the last byte (8)
295-
last_mask = 0xFF
296-
adjustment = 8-old_bit_shift
297-
else:
298-
last_mask = 2**new_bit_shift-1
299-
adjustment = new_bit_shift-old_bit_shift
300-
301-
#Set up for the looping with a list, the bytes, and an initial part
302-
raw_bytes = list()
303-
prev, next = self.last_byte, ord(self.io.read(1))
304-
first = prev & hi_mask
305-
bit_count -= 8-old_bit_shift
306-
307-
while bit_count > 0:
308-
309-
if bit_count <= 8: #this is the last byte
310-
#The bits in the last byte are included in order starting at
311-
#the new_bit_shift boundary with extra bits bumped back a byte
312-
#because we can have odd bit requests, the bit shift can change
313-
last = (next & last_mask)
314-
315-
# we need to bring the first byte closer
316-
# if the adjustment is lower than 0
317-
if adjustment < 0:
318-
first = first >> abs(adjustment)
319-
320-
raw_bytes.append(first | (last >> max(adjustment,0)))
321-
if adjustment > 0:
322-
raw_bytes.append(last & (2**adjustment-1))
269+
try:
270+
bytes, bits = bytes+bits/8, bits%8
271+
bit_count = bytes*8+bits
272+
273+
#check special case of not having to do any work
274+
if bit_count == 0: return []
275+
276+
#check sepcial case of intra-byte read
277+
if bit_count <= (8-self.bit_shift):
278+
return [self.shift(bit_count)]
279+
280+
#check special case of byte-aligned reads, performance booster
281+
if self.bit_shift == 0:
282+
base = [ord(self.io.read(1)) for byte in range(bytes)]
283+
if bits != 0:
284+
return base+[self.shift(bits)]
285+
return base
286+
287+
# Calculated shifts
288+
old_bit_shift = self.bit_shift
289+
new_bit_shift = (self.bit_shift+bits) % 8
290+
291+
# Masks
292+
lo_mask = 2**old_bit_shift-1
293+
lo_mask_inv = 0xFF - 2**(8-old_bit_shift)+1
294+
hi_mask = 0xFF ^ lo_mask
295+
hi_mask_inv = 0xFF ^ lo_mask_inv
296+
297+
#last byte parameters
298+
if new_bit_shift == 0: #this means we filled the last byte (8)
299+
last_mask = 0xFF
300+
adjustment = 8-old_bit_shift
301+
else:
302+
last_mask = 2**new_bit_shift-1
303+
adjustment = new_bit_shift-old_bit_shift
304+
305+
#Set up for the looping with a list, the bytes, and an initial part
306+
raw_bytes = list()
307+
prev, next = self.last_byte, ord(self.io.read(1))
308+
first = prev & hi_mask
309+
bit_count -= 8-old_bit_shift
310+
311+
while bit_count > 0:
312+
313+
if bit_count <= 8: #this is the last byte
314+
#The bits in the last byte are included in order starting at
315+
#the new_bit_shift boundary with extra bits bumped back a byte
316+
#because we can have odd bit requests, the bit shift can change
317+
last = (next & last_mask)
318+
319+
# we need to bring the first byte closer
320+
# if the adjustment is lower than 0
321+
if adjustment < 0:
322+
first = first >> abs(adjustment)
323323

324-
bit_count = 0
325-
326-
if bit_count > 8: #We can do simple wrapping for middle bytes
327-
second = (next & lo_mask_inv) >> (8-old_bit_shift)
328-
raw_bytes.append(first | second)
329-
330-
#To remain consistent, always shfit these bits into the hi_mask
331-
first = (next & hi_mask_inv) << old_bit_shift
332-
bit_count -= 8
333-
334-
#Cycle down to the next byte
335-
prev,next = next,ord(self.io.read(1))
336-
337-
#Make sure to set our internals back together
338-
self.last_byte = next
339-
self.bit_shift = new_bit_shift
340-
341-
return raw_bytes
324+
raw_bytes.append(first | (last >> max(adjustment,0)))
325+
if adjustment > 0:
326+
raw_bytes.append(last & (2**adjustment-1))
327+
328+
bit_count = 0
329+
330+
if bit_count > 8: #We can do simple wrapping for middle bytes
331+
second = (next & lo_mask_inv) >> (8-old_bit_shift)
332+
raw_bytes.append(first | second)
333+
334+
#To remain consistent, always shfit these bits into the hi_mask
335+
first = (next & hi_mask_inv) << old_bit_shift
336+
bit_count -= 8
337+
338+
#Cycle down to the next byte
339+
prev,next = next,ord(self.io.read(1))
340+
341+
self.last_byte = next
342+
self.bit_shift = new_bit_shift
343+
return raw_bytes
344+
345+
except TypeError:
346+
raise EOFError("Cannot read requested bits/bytes. End of buffer reached")
342347

343348

344349
class PersonDict(dict):

test_replays/test_all.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ def test_empty():
1515
# Todo: Are we happy with it raising a ValueError? Should it be rather ParseError or something else?
1616
# Maybe a "production" mode would be nice to have, so that it simply returns a status of the parse without
1717
# raising an exception.
18-
with pytest.raises(ValueError):
18+
with pytest.raises(EOFError):
1919
sc2reader.read("test_replays/corrupted/empty.SC2Replay")
2020

2121
# Tests for build 17811 replays

0 commit comments

Comments
 (0)