This commit is contained in:
Kovid Goyal 2012-04-20 23:32:53 +05:30
parent 80c062c62b
commit 22ee415241

View File

@ -10,13 +10,20 @@ __docformat__ = 'restructuredtext en'
from collections import namedtuple from collections import namedtuple
from struct import pack from struct import pack
from io import BytesIO
from calibre.ebooks.mobi.utils import CNCX from calibre.ebooks.mobi.utils import CNCX, encint
TagMeta = namedtuple('TagMeta', TagMeta = namedtuple('TagMeta',
'name number values_per_entry bitmask end_flag') 'name number values_per_entry bitmask end_flag')
EndTagTable = TagMeta('eof', 0, 0, 0, 1) EndTagTable = TagMeta('eof', 0, 0, 0, 1)
# map of mask to number of shifts needed, works with 1 bit and two-bit wide masks
# could also be extended to 4 bit wide ones as well
mask_to_bit_shifts = { 1:0, 2:1, 3:0, 4:2, 8:3, 12:2, 16:4, 32:5, 48:4, 64:6,
128:7, 192: 6 }
class Index(object): class Index(object):
control_byte_count = 1 control_byte_count = 1
@ -33,6 +40,50 @@ class Index(object):
header += pack(b'>II', 12+len(byts), cls.control_byte_count) header += pack(b'>II', 12+len(byts), cls.control_byte_count)
return header + bytes(byts) return header + bytes(byts)
@classmethod
def calculate_control_bytes_for_each_entry(cls, entries):
control_bytes = []
for lead_text, tags in entries:
cbs = []
ans = 0
for (name, number, vpe, mask, endi) in cls.tag_types:
if endi == 1:
cbs.append(ans)
ans = 0
continue
nvals = len(tags.get(name, ()))
nentries = nvals // vpe
shifts = mask_to_bit_shifts[mask]
ans |= mask & (nentries << shifts)
if len(cbs) != cls.control_byte_count:
raise ValueError('The entry %r is invalid'%[lead_text, tags])
control_bytes.append(cbs)
return control_bytes
def build_records(self):
self.control_bytes = self.calculate_control_bytes_for_each_entry(
self.entries)
self.rendered_entries = []
offset = 0
IndexEntry = namedtuple('IndexEntry', 'offset length raw')
for i, x in enumerate(self.entries):
control_bytes = self.control_bytes[i]
leading_text, tags = x
buf = BytesIO()
raw = bytearray(leading_text)
raw.insert(0, len(leading_text))
buf.write(bytes(raw))
buf.write(control_bytes)
for tag in self.tag_types:
values = tags.get(tag.name, None)
if values:
for val in values:
buf.write(encint(val))
raw = buf.getvalue()
self.rendered_entries.append(IndexEntry(offset, len(raw), raw))
offset += len(raw)
class SkelIndex(Index): class SkelIndex(Index):
tag_types = tuple(map(TagMeta, ( tag_types = tuple(map(TagMeta, (
@ -74,5 +125,3 @@ class ChunkIndex(Index):
}) for s in chunk_table }) for s in chunk_table
] ]