mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
...
This commit is contained in:
parent
80c062c62b
commit
22ee415241
@ -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
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user