diff mercurial/bundle2.py @ 20856:8a6a86c9a5b5

bundle2: support bundling of empty part (with a type) Here start the work on bundle2 parts. Our first step is to be able to bundle a simplistic part that just have a type, no parameters, empty payload.
author Pierre-Yves David <pierre-yves.david@fb.com>
date Tue, 18 Mar 2014 14:29:33 -0700
parents 2631204d7305
children 9a75d2559cff
line wrap: on
line diff
--- a/mercurial/bundle2.py	Wed Mar 26 18:51:49 2014 -0700
+++ b/mercurial/bundle2.py	Tue Mar 18 14:29:33 2014 -0700
@@ -21,9 +21,7 @@
  - payload parts (any number)
  - end of stream marker.
 
-The current implementation accept some stream level option but no part.
-
-Details on the Binary format
+the Binary format
 ============================
 
 All numbers are unsigned and big endian.
@@ -71,7 +69,28 @@
   The total number of Bytes used by the part headers. When the header is empty
   (size = 0) this is interpreted as the end of stream marker.
 
-  Currently forced to 0 in the current state of the implementation
+:header:
+
+    The header defines how to interpret the part. It contains two piece of
+    data: the part type, and the part parameters.
+
+    The part type is used to route an application level handler, that can
+    interpret payload.
+
+    Part parameters are passed to the application level handler.  They are
+    meant to convey information that will help the application level 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.
+
+:payload:
+
+    The current payload is a 32bit integer with a value of 0. This is
+    considered an "empty" payload.
 """
 
 import util
@@ -88,15 +107,15 @@
 _magicstring = 'HG20'
 
 _fstreamparamsize = '>H'
+_fpartheadersize = '>H'
+_fparttypesize = '>B'
 
 class bundle20(object):
     """represent an outgoing bundle2 container
 
-    Use the `addparam` method to add stream level parameter. Then call
-    `getchunks` to retrieve all the binary chunks of datathat compose the
-    bundle2 container.
-
-    This object does not support payload part yet."""
+    Use the `addparam` method to add stream level parameter. and `addpart` to
+    populate it. Then call `getchunks` to retrieve all the binary chunks of
+    datathat compose the bundle2 container."""
 
     def __init__(self, ui):
         self.ui = ui
@@ -111,6 +130,12 @@
             raise ValueError('non letter first character: %r' % name)
         self._params.append((name, value))
 
+    def addpart(self, part):
+        """add a new part to the bundle2 container
+
+        Parts contains the actuall applicative payload."""
+        self._parts.append(part)
+
     def getchunks(self):
         self.ui.debug('start emission of %s stream\n' % _magicstring)
         yield _magicstring
@@ -120,9 +145,11 @@
         if param:
             yield param
 
-        # no support for parts
-        # to be obviously fixed soon.
-        assert not self._parts
+        self.ui.debug('start of parts\n')
+        for part in self._parts:
+            self.ui.debug('bundle part: "%s"\n' % part.type)
+            for chunk in part.getchunks():
+                yield chunk
         self.ui.debug('end of bundle\n')
         yield '\0\0'
 
@@ -217,5 +244,26 @@
         assert headersize == '\0\0'
         return None
 
+class part(object):
+    """A bundle2 part contains application level payload
+
+    The part `type` is used to route the part to the application level
+    handler.
+    """
+
+    def __init__(self, parttype):
+        self.type = parttype
+
+    def getchunks(self):
+        ### header
+        header = [_pack(_fparttypesize, len(self.type)),
+                  self.type,
+                  '\0\0', # No option support for now.
+                 ]
+        headerchunk = ''.join(header)
+        yield _pack(_fpartheadersize, len(headerchunk))
+        yield headerchunk
+        # force empty part for now
+        yield '\0\0\0\0'