-
Notifications
You must be signed in to change notification settings - Fork 81
/
BlockDecompressorReader.py
65 lines (54 loc) · 2.19 KB
/
BlockDecompressorReader.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
from zstandard import ZstdDecompressor
class BlockDecompressorReader:
#Position in decompressed data
Position = 0
BlockHeader = None
CurrentBlock = b""
CurrentBlockId = -1
def __init__(self, nspf, BlockHeader):
self.BlockHeader = BlockHeader
initialOffset = nspf.tell()
self.nspf = nspf
if BlockHeader.blockSizeExponent < 14 or BlockHeader.blockSizeExponent > 32:
raise ValueError("Corrupted NCZBLOCK header: Block size must be between 14 and 32")
self.BlockSize = 2**BlockHeader.blockSizeExponent
self.CompressedBlockOffsetList = [initialOffset]
for compressedBlockSize in BlockHeader.compressedBlockSizeList[:-1]:
self.CompressedBlockOffsetList.append(self.CompressedBlockOffsetList[-1] + compressedBlockSize)
self.CompressedBlockSizeList = BlockHeader.compressedBlockSizeList
def __decompressBlock(self, blockID):
if self.CurrentBlockId == blockID:
return self.CurrentBlock
decompressedBlockSize = self.BlockSize
if blockID >= len(self.CompressedBlockOffsetList) - 1:
if blockID >= len(self.CompressedBlockOffsetList):
raise EOFError("BlockID exceeds the amounts of compressed blocks in that file!")
decompressedBlockSize = self.BlockHeader.decompressedSize % self.BlockSize
self.nspf.seek(self.CompressedBlockOffsetList[blockID])
if self.CompressedBlockSizeList[blockID] < decompressedBlockSize:
self.CurrentBlock = ZstdDecompressor().decompress(self.nspf.read(decompressedBlockSize))
else:
self.CurrentBlock = self.nspf.read(decompressedBlockSize)
self.CurrentBlockId = blockID
return self.CurrentBlock
def seek(self, offset, whence = 0):
if whence == 0:
self.Position = offset
elif whence == 1:
self.Position += offset
elif whence == 2:
self.Position = self.BlockHeader.decompressedSize + offset
else:
raise ValueError("whence argument must be 0, 1 or 2")
def read(self, length):
buffer = b""
blockOffset = self.Position%self.BlockSize
blockID = self.Position//self.BlockSize
while(len(buffer) - blockOffset < length):
if blockID >= len(self.CompressedBlockOffsetList):
break
buffer += self.__decompressBlock(blockID)
blockID += 1
buffer = buffer[blockOffset:blockOffset+length]
self.Position += length
return buffer