sidedata.py
106 lines
| 3.2 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. | ||||
Sidedata are stored in the revlog itself, withing the revision rawtext. They | ||||
are inserted, removed from it using the flagprocessors mechanism. The following | ||||
format is currently used:: | ||||
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> | ||||
This is a simple and effective format. It should be enought to experiment with | ||||
the concept. | ||||
""" | ||||
from __future__ import absolute_import | ||||
r43302 | ||||
import hashlib | ||||
import struct | ||||
from .. import error | ||||
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 | ||||
r43308 | # internal format constant | |||
r43318 | SIDEDATA_HEADER = struct.Struct(r'>H') | |||
SIDEDATA_ENTRY = struct.Struct(r'>HL20s') | ||||
r43302 | ||||
Augie Fackler
|
r43346 | |||
r43303 | def sidedatawriteprocessor(rl, text, sidedata): | |||
sidedata = list(sidedata.items()) | ||||
sidedata.sort() | ||||
rawtext = [SIDEDATA_HEADER.pack(len(sidedata))] | ||||
for key, value in sidedata: | ||||
digest = hashlib.sha1(value).digest() | ||||
rawtext.append(SIDEDATA_ENTRY.pack(key, len(value), digest)) | ||||
for key, value in sidedata: | ||||
rawtext.append(value) | ||||
rawtext.append(bytes(text)) | ||||
Augie Fackler
|
r43347 | return b''.join(rawtext), False | ||
r43303 | ||||
Augie Fackler
|
r43346 | |||
r43302 | def sidedatareadprocessor(rl, text): | |||
sidedata = {} | ||||
offset = 0 | ||||
Augie Fackler
|
r43346 | (nbentry,) = SIDEDATA_HEADER.unpack(text[: 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 | ||||
key, size, storeddigest = SIDEDATA_ENTRY.unpack(text[offset:nextoffset]) | ||||
offset = nextoffset | ||||
# read the data associated with that entry | ||||
nextdataoffset = dataoffset + size | ||||
entrytext = text[dataoffset:nextdataoffset] | ||||
readdigest = hashlib.sha1(entrytext).digest() | ||||
if storeddigest != readdigest: | ||||
raise error.SidedataHashError(key, storeddigest, readdigest) | ||||
sidedata[key] = entrytext | ||||
dataoffset = nextdataoffset | ||||
text = text[dataoffset:] | ||||
return text, True, sidedata | ||||
r43304 | ||||
Augie Fackler
|
r43346 | |||
r43304 | def sidedatarawprocessor(rl, text): | |||
# side data modifies rawtext and prevent rawtext hash validation | ||||
return False | ||||
r43305 | ||||
Augie Fackler
|
r43346 | |||
r43305 | processors = ( | |||
sidedatareadprocessor, | ||||
sidedatawriteprocessor, | ||||
sidedatarawprocessor, | ||||
) | ||||