sidedata.py
93 lines
| 2.8 KiB
| text/x-python
|
PythonLexer
r43301 | # sidedata.py - Logic around store extra data alongside revlog revisions | |||
# | ||||
# Copyright 2019 Pierre-Yves David <pierre-yves.david@octobus.net) | ||||
# | ||||
# This software may be used and distributed according to the terms of the | ||||
# GNU General Public License version 2 or any later version. | ||||
"""core code for "sidedata" support | ||||
The "sidedata" are stored alongside the revision without actually being part of | ||||
its content and not affecting its hash. It's main use cases is to cache | ||||
important information related to a changesets. | ||||
The current implementation is experimental and subject to changes. Do not rely | ||||
on it in production. | ||||
Raphaël Gomès
|
r47443 | Sidedata are stored in the revlog itself, thanks to a new version of the | ||
revlog. The following format is currently used:: | ||||
r43301 | ||||
initial header: | ||||
<number of sidedata; 2 bytes> | ||||
sidedata (repeated N times): | ||||
<sidedata-key; 2 bytes> | ||||
<sidedata-entry-length: 4 bytes> | ||||
<sidedata-content-sha1-digest: 20 bytes> | ||||
<sidedata-content; X bytes> | ||||
normal raw text: | ||||
<all bytes remaining in the rawtext> | ||||
Joerg Sonnenberger
|
r46811 | This is a simple and effective format. It should be enough to experiment with | ||
r43301 | the concept. | |||
""" | ||||
from __future__ import absolute_import | ||||
r43302 | ||||
import struct | ||||
from .. import error | ||||
Augie Fackler
|
r44517 | from ..utils import hashutil | ||
r43302 | ||||
r43308 | ## sidedata type constant | |||
# reserve a block for testing purposes. | ||||
SD_TEST1 = 1 | ||||
SD_TEST2 = 2 | ||||
SD_TEST3 = 3 | ||||
SD_TEST4 = 4 | ||||
SD_TEST5 = 5 | ||||
SD_TEST6 = 6 | ||||
SD_TEST7 = 7 | ||||
r43412 | # key to store copies related information | |||
SD_P1COPIES = 8 | ||||
SD_P2COPIES = 9 | ||||
SD_FILESADDED = 10 | ||||
SD_FILESREMOVED = 11 | ||||
r46211 | SD_FILES = 12 | |||
r43412 | ||||
r43308 | # internal format constant | |||
Augie Fackler
|
r43906 | SIDEDATA_HEADER = struct.Struct('>H') | ||
SIDEDATA_ENTRY = struct.Struct('>HL20s') | ||||
r43302 | ||||
Augie Fackler
|
r43346 | |||
Raphaël Gomès
|
r47443 | def serialize_sidedata(sidedata): | ||
r43303 | sidedata = list(sidedata.items()) | |||
sidedata.sort() | ||||
Raphaël Gomès
|
r47443 | buf = [SIDEDATA_HEADER.pack(len(sidedata))] | ||
r43303 | for key, value in sidedata: | |||
Augie Fackler
|
r44517 | digest = hashutil.sha1(value).digest() | ||
Raphaël Gomès
|
r47443 | buf.append(SIDEDATA_ENTRY.pack(key, len(value), digest)) | ||
r43303 | for key, value in sidedata: | |||
Raphaël Gomès
|
r47443 | buf.append(value) | ||
buf = b''.join(buf) | ||||
return buf | ||||
r43303 | ||||
Augie Fackler
|
r43346 | |||
Raphaël Gomès
|
r47443 | def deserialize_sidedata(blob): | ||
r43302 | sidedata = {} | |||
offset = 0 | ||||
Raphaël Gomès
|
r47443 | (nbentry,) = SIDEDATA_HEADER.unpack(blob[: SIDEDATA_HEADER.size]) | ||
r43302 | offset += SIDEDATA_HEADER.size | |||
dataoffset = SIDEDATA_HEADER.size + (SIDEDATA_ENTRY.size * nbentry) | ||||
for i in range(nbentry): | ||||
nextoffset = offset + SIDEDATA_ENTRY.size | ||||
Raphaël Gomès
|
r47443 | key, size, storeddigest = SIDEDATA_ENTRY.unpack(blob[offset:nextoffset]) | ||
r43302 | offset = nextoffset | |||
# read the data associated with that entry | ||||
nextdataoffset = dataoffset + size | ||||
Raphaël Gomès
|
r47443 | entrytext = bytes(blob[dataoffset:nextdataoffset]) | ||
Augie Fackler
|
r44517 | readdigest = hashutil.sha1(entrytext).digest() | ||
r43302 | if storeddigest != readdigest: | |||
raise error.SidedataHashError(key, storeddigest, readdigest) | ||||
sidedata[key] = entrytext | ||||
dataoffset = nextdataoffset | ||||
Raphaël Gomès
|
r47443 | return sidedata | ||