[PATCH 1 of 8 V11] bookmarks: introduce binary encoding
Stanislau Hlebik
stash at fb.com
Tue Nov 22 10:17:43 UTC 2016
# HG changeset patch
# User Stanislau Hlebik <stash at fb.com>
# Date 1479807211 28800
# Tue Nov 22 01:33:31 2016 -0800
# Node ID 8efced91de4d48478555d91c53e89cd1b4e3b78d
# Parent 259296eb59e77951572872fb2f542c6dbff8bc74
bookmarks: introduce binary encoding
Bookmarks binary encoding will be used for `bookmarks` bundle2 part.
The format is: <4 bytes - bookmark size, big-endian><bookmark>
<1 byte - 0 if node is empty 1 otherwise><20 bytes node>
BookmarksEncodeError and BookmarksDecodeError maybe thrown if input is
incorrect.
diff --git a/mercurial/bookmarks.py b/mercurial/bookmarks.py
--- a/mercurial/bookmarks.py
+++ b/mercurial/bookmarks.py
@@ -8,7 +8,9 @@
from __future__ import absolute_import
import errno
+import io
import os
+import struct
from .i18n import _
from .node import (
@@ -23,6 +25,72 @@
util,
)
+_NONEMPTYNODE = struct.pack('?', False)
+_EMPTYNODE = struct.pack('?', True)
+
+def _unpackbookmarksize(stream):
+ """Returns 0 if stream is empty.
+ """
+
+ intstruct = struct.Struct('>i')
+ expectedsize = intstruct.size
+ encodedbookmarksize = stream.read(expectedsize)
+ if not encodedbookmarksize:
+ return 0
+ if len(encodedbookmarksize) != expectedsize:
+ raise ValueError(
+ _('cannot decode bookmark size: '
+ 'expected size: %d, actual size: %d') %
+ (expectedsize, len(encodedbookmarksize)))
+ return intstruct.unpack(encodedbookmarksize)[0]
+
+def encodebookmarks(bookmarks):
+ """Encodes bookmark to node mapping to the byte string.
+
+ Format: <4 bytes - bookmark size><bookmark>
+ <1 byte - 0 if node is empty 1 otherwise><20 bytes node>
+ Node may be 20 byte binary string or empty
+ """
+
+ intstruct = struct.Struct('>i')
+ for bookmark, node in sorted(bookmarks.iteritems()):
+ encodedbookmark = encoding.fromlocal(bookmark)
+ yield intstruct.pack(len(encodedbookmark))
+ yield encodedbookmark
+ if node:
+ if len(node) != 20:
+ raise ValueError(_('node must be 20 or bytes long'))
+ yield _NONEMPTYNODE
+ yield node
+ else:
+ yield _EMPTYNODE
+
+def decodebookmarks(buf):
+ """Decodes buffer into bookmark to node mapping.
+
+ Node is either 20 bytes or empty.
+ """
+
+ stream = io.BytesIO(buf)
+ bookmarks = {}
+ bookmarksize = _unpackbookmarksize(stream)
+ boolstruct = struct.Struct('?')
+ while bookmarksize:
+ bookmark = stream.read(bookmarksize)
+ if len(bookmark) != bookmarksize:
+ raise ValueError(
+ _('cannot decode bookmark: expected size: %d, '
+ 'actual size: %d') % (bookmarksize, len(bookmark)))
+ bookmark = encoding.tolocal(bookmark)
+ packedemptynodeflag = stream.read(boolstruct.size)
+ emptynode = boolstruct.unpack(packedemptynodeflag)[0]
+ node = ''
+ if not emptynode:
+ node = stream.read(20)
+ bookmarks[bookmark] = node
+ bookmarksize = _unpackbookmarksize(stream)
+ return bookmarks
+
def _getbkfile(repo):
"""Hook so that extensions that mess with the store can hook bm storage.
More information about the Mercurial-devel
mailing list