[PATCH 4 of 4] bundle2: part params
pierre-yves.david at ens-lyon.org
pierre-yves.david at ens-lyon.org
Thu Mar 27 02:19:45 UTC 2014
# HG changeset patch
# User Pierre-Yves David <pierre-yves.david at fb.com>
# Date 1395303885 25200
# Thu Mar 20 01:24:45 2014 -0700
# Node ID 57ab0045d562efb1e6beb676bbbb3ba4e7b3ae8d
# Parent 00b0aeb4f82b8056395ed53ce3fadcf8d16bd02c
bundle2: part params
diff --git a/mercurial/bundle2.py b/mercurial/bundle2.py
--- a/mercurial/bundle2.py
+++ b/mercurial/bundle2.py
@@ -82,12 +82,35 @@ Binary format is as follow
object to interpret the part payload.
The binary format of the header is has follow
:typesize: (one byte)
+
:typename: alphanumerical part name
- :option: we do not support option yet this denoted by two 16 bites zero.
+
+ :parameters:
+
+ Part's parameter may have arbitraty content, the binary structure is::
+
+ <mandatory-count><advisory-count><param-sizes><param-data>
+
+ :mandatory-count: 1 byte, number of mandatory parameters
+
+ :advisory-count: 1 byte, number of advisory parameters
+
+ :param-sizes:
+
+ N couple of Bytes, where N is the total number of parameters. each
+ couple if the size of the key and value for a parameters
+
+ :param-data:
+
+ A blob of bytes from which each parameter key and value can be
+ retrieved using the list of size couples stored in the previous
+ field.
+
+ Mandatory parameters comes first, then the advisory ones.
:payload:
payload is a series of `<chunksize><chunkdata>`.
@@ -113,10 +136,19 @@ from i18n import _
_fstreamparamsize = '>H'
_fpartheadersize = '>H'
_fparttypesize = '>B'
_fpayloadsize = '>I'
+_fpartparamcount = '>BB'
+
+def _makefpartparamsizes(nbparams):
+ """return a struct format to read part parameter sizes
+
+ The number parameters is variable so we need to build that format
+ dynamically.
+ """
+ return '>'+('BB'*nbparams)
class bundle20(object):
"""represent an outgoing bundle2 container
Use the `addparam` method to add stream level parameter. and `addpart` to
@@ -260,48 +292,92 @@ class unbundle20(object):
_offset[0] = offset + size
return data
typesize = _unpack(_fparttypesize, fromheader(1))[0]
parttype = fromheader(typesize)
self.ui.debug('part type: "%s"\n' % parttype)
- assert fromheader(2) == '\0\0' # no option for now
- self.ui.debug('part parameters: 0\n')
+ ## reading parameters
+ # param count
+ mancount, advcount = _unpack(_fpartparamcount, fromheader(2))
+ self.ui.debug('part parameters: %i\n' % (mancount + advcount))
+ # param size
+ paramsizes = _unpack(_makefpartparamsizes(mancount + advcount),
+ fromheader(2*(mancount + advcount)))
+ # make it a list of couple again
+ paramsizes = zip(paramsizes[::2], paramsizes[1::2])
+ # split mandatory from advisory
+ mansizes = paramsizes[:mancount]
+ advsizes = paramsizes[mancount:]
+ # retrive param value
+ manparams = []
+ for key, value in mansizes:
+ manparams.append((fromheader(key), fromheader(value)))
+ advparams = []
+ for key, value in advsizes:
+ advparams.append((fromheader(key), fromheader(value)))
+ ## part payload
payload = []
payloadsize = self._unpack(_fpayloadsize)[0]
self.ui.debug('payload chunk size: %i\n' % payloadsize)
while payloadsize:
payload.append(self._readexact(payloadsize))
payloadsize = self._unpack(_fpayloadsize)[0]
self.ui.debug('payload chunk size: %i\n' % payloadsize)
payload = ''.join(payload)
- current = part(parttype, data=payload)
+ current = part(parttype, manparams, advparams, data=payload)
return current
class part(object):
"""A bundle2 part contains actual application level payload
The part have `type` used to route it to proper application level object
that interpret its content.
"""
- def __init__(self, parttype, data=''):
+ def __init__(self, parttype, mandatoryparams=(), advisoryparams=(),
+ data=''):
self.type = parttype
self.data = data
+ self.mandatoryparams = mandatoryparams
+ self.advisoryparams = advisoryparams
def getchunks(self):
- ### header
+ #### header
+ ## parttype
header = [_pack(_fparttypesize, len(self.type)),
self.type,
- '\0\0', # No option support for now.
]
+ ## parameters
+ # count
+ manpar = self.mandatoryparams
+ advpar = self.advisoryparams
+ header.append(_pack(_fpartparamcount, len(manpar), len(advpar)))
+ # size
+ parsizes = []
+ for key, value in manpar:
+ parsizes.append(len(key))
+ parsizes.append(len(value))
+ for key, value in advpar:
+ parsizes.append(len(key))
+ parsizes.append(len(value))
+ paramsizes = _pack(_makefpartparamsizes(len(parsizes) / 2), *parsizes)
+ header.append(paramsizes)
+ # key, value
+ for key, value in manpar:
+ header.append(key)
+ header.append(value)
+ for key, value in advpar:
+ header.append(key)
+ header.append(value)
+ ## finalize header
headerchunk = ''.join(header)
yield _pack(_fpartheadersize, len(headerchunk))
yield headerchunk
+ ## payload
# we only support fixed size data now.
# This will be improved in the future.
yield _pack(_fpayloadsize, len(self.data))
yield self.data
if len(self.data):
# end of payload
yield _pack(_fpayloadsize, 0)
-
diff --git a/tests/test-bundle2.t b/tests/test-bundle2.t
--- a/tests/test-bundle2.t
+++ b/tests/test-bundle2.t
@@ -40,10 +40,15 @@ Create an extension to test bundle2 API
> # add a second one to make sure we handle multiple parts
> part = bundle2.part('test:empty')
> bundler.addpart(part)
> part = bundle2.part('test:song', data=ELEPHANTSSONG)
> bundler.addpart(part)
+ > part = bundle2.part('test:math',
+ > [('pi', '3.14'), ('e', '2.72')],
+ > [('cooking', 'raw')],
+ > '42')
+ > bundler.addpart(part)
>
> if path is None:
> file = sys.stdout
> else:
> file = open(path, 'w')
@@ -67,10 +72,12 @@ Create an extension to test bundle2 API
> ui.write(' %s\n' % value)
> parts = list(unbundler)
> ui.write('parts count: %i\n' % len(parts))
> for p in parts:
> ui.write(' :%s:\n' % p.type)
+ > ui.write(' mandatory: %i\n' % len(p.mandatoryparams))
+ > ui.write(' advisory: %i\n' % len(p.advisoryparams))
> ui.write(' payload: %i bytes\n' % len(p.data))
> EOF
$ cat >> $HGRCPATH << EOF
> [extensions]
> bundle2=$TESTTMP/bundle2.py
@@ -245,29 +252,40 @@ Test part
bundle parameter:
start of parts
bundling part: "test:empty"
bundling part: "test:empty"
bundling part: "test:song"
+ bundling part: "test:math"
end of bundle
$ cat ../parts.hg2
HG20\x00\x00\x00\r (esc)
test:empty\x00\x00\x00\x00\x00\x00\x00\r (esc)
test:empty\x00\x00\x00\x00\x00\x00\x00\x0c test:song\x00\x00\x00\x00\x00\xb2Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko (esc)
Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
- Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.\x00\x00\x00\x00\x00\x00 (no-eol) (esc)
+ Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.\x00\x00\x00\x00\x00' test:math\x02\x01\x02\x04\x01\x04\x07\x03pi3.14e2.72cookingraw\x00\x00\x00\x0242\x00\x00\x00\x00\x00\x00 (no-eol) (esc)
$ hg unbundle2 < ../parts.hg2
options count: 0
- parts count: 3
+ parts count: 4
:test:empty:
+ mandatory: 0
+ advisory: 0
payload: 0 bytes
:test:empty:
+ mandatory: 0
+ advisory: 0
payload: 0 bytes
:test:song:
+ mandatory: 0
+ advisory: 0
payload: 178 bytes
+ :test:math:
+ mandatory: 2
+ advisory: 1
+ payload: 2 bytes
$ hg unbundle2 --debug < ../parts.hg2
start processing of HG20 stream
reading bundle2 stream parameters
options count: 0
@@ -283,14 +301,29 @@ Test part
part header size: 12
part type: "test:song"
part parameters: 0
payload chunk size: 178
payload chunk size: 0
+ part header size: 39
+ part type: "test:math"
+ part parameters: 3
+ payload chunk size: 2
+ payload chunk size: 0
part header size: 0
end of bundle2 stream
- parts count: 3
+ parts count: 4
:test:empty:
+ mandatory: 0
+ advisory: 0
payload: 0 bytes
:test:empty:
+ mandatory: 0
+ advisory: 0
payload: 0 bytes
:test:song:
+ mandatory: 0
+ advisory: 0
payload: 178 bytes
+ :test:math:
+ mandatory: 2
+ advisory: 1
+ payload: 2 bytes
More information about the Mercurial-devel
mailing list