Mercurial > hg
comparison mercurial/bundle2.py @ 23009:90f86ad3d4ff
bundle2: change header size and make them signed (new format)
We are changing all integers that denote the size of a chunk to read to int32.
There are two main motivations for that.
First, we change everything to the same width (32 bits) to make it possible for
a reasonably agnostic actor to forward a bundle2 without any extra processing.
With this change, this could be achieved by just reading int32s and forwarding
chunks of the size read. A bit a smartness would be logic to detect the end of
stream but nothing too complicated.
Second, we need some capacity to transmit special information during the bundle
processing. For example we would like to be able to raise an exception while a
part is being read if this exception happend while this part was generated.
Having signed integer let us use negative numbers to trigger special events
during the parsing of the bundle.
The format is renamed for B2X to B2Y because this breaks binary
compatibility. The B2X format support is dropped. It was experimental to
allow this kind of things. All elements not directly related to the binary
format remain flagged "b2x" because they are still compatible.
author | Pierre-Yves David <pierre-yves.david@fb.com> |
---|---|
date | Wed, 01 Oct 2014 23:40:23 -0500 |
parents | d3137827016a |
children | 73f394f4affc |
comparison
equal
deleted
inserted
replaced
23008:d3137827016a | 23009:90f86ad3d4ff |
---|---|
29 stream level parameters | 29 stream level parameters |
30 ------------------------ | 30 ------------------------ |
31 | 31 |
32 Binary format is as follow | 32 Binary format is as follow |
33 | 33 |
34 :params size: (16 bits integer) | 34 :params size: int32 |
35 | 35 |
36 The total number of Bytes used by the parameters | 36 The total number of Bytes used by the parameters |
37 | 37 |
38 :params value: arbitrary number of Bytes | 38 :params value: arbitrary number of Bytes |
39 | 39 |
62 Payload part | 62 Payload part |
63 ------------------------ | 63 ------------------------ |
64 | 64 |
65 Binary format is as follow | 65 Binary format is as follow |
66 | 66 |
67 :header size: (16 bits inter) | 67 :header size: int32 |
68 | 68 |
69 The total number of Bytes used by the part headers. When the header is empty | 69 The total number of Bytes used by the part headers. When the header is empty |
70 (size = 0) this is interpreted as the end of stream marker. | 70 (size = 0) this is interpreted as the end of stream marker. |
71 | 71 |
72 :header: | 72 :header: |
117 | 117 |
118 :payload: | 118 :payload: |
119 | 119 |
120 payload is a series of `<chunksize><chunkdata>`. | 120 payload is a series of `<chunksize><chunkdata>`. |
121 | 121 |
122 `chunksize` is a 32 bits integer, `chunkdata` are plain bytes (as much as | 122 `chunksize` is an int32, `chunkdata` are plain bytes (as much as |
123 `chunksize` says)` The payload part is concluded by a zero size chunk. | 123 `chunksize` says)` The payload part is concluded by a zero size chunk. |
124 | 124 |
125 The current implementation always produces either zero or one chunk. | 125 The current implementation always produces either zero or one chunk. |
126 This is an implementation limitation that will ultimately be lifted. | 126 This is an implementation limitation that will ultimately be lifted. |
127 | |
128 `chunksize` can be negative to trigger special case processing. No such | |
129 processing is in place yet. | |
127 | 130 |
128 Bundle processing | 131 Bundle processing |
129 ============================ | 132 ============================ |
130 | 133 |
131 Each part is processed in order using a "part handler". Handler are registered | 134 Each part is processed in order using a "part handler". Handler are registered |
153 from i18n import _ | 156 from i18n import _ |
154 | 157 |
155 _pack = struct.pack | 158 _pack = struct.pack |
156 _unpack = struct.unpack | 159 _unpack = struct.unpack |
157 | 160 |
158 _magicstring = 'HG2X' | 161 _magicstring = 'HG2Y' |
159 | 162 |
160 _fstreamparamsize = '>H' | 163 _fstreamparamsize = '>i' |
161 _fpartheadersize = '>H' | 164 _fpartheadersize = '>i' |
162 _fparttypesize = '>B' | 165 _fparttypesize = '>B' |
163 _fpartid = '>I' | 166 _fpartid = '>I' |
164 _fpayloadsize = '>I' | 167 _fpayloadsize = '>i' |
165 _fpartparamcount = '>BB' | 168 _fpartparamcount = '>BB' |
166 | 169 |
167 preferedchunksize = 4096 | 170 preferedchunksize = 4096 |
168 | 171 |
169 def _makefpartparamsizes(nbparams): | 172 def _makefpartparamsizes(nbparams): |
494 if header is None: | 497 if header is None: |
495 header = self._readexact(4) | 498 header = self._readexact(4) |
496 magic, version = header[0:2], header[2:4] | 499 magic, version = header[0:2], header[2:4] |
497 if magic != 'HG': | 500 if magic != 'HG': |
498 raise util.Abort(_('not a Mercurial bundle')) | 501 raise util.Abort(_('not a Mercurial bundle')) |
499 if version != '2X': | 502 if version != '2Y': |
500 raise util.Abort(_('unknown bundle version %s') % version) | 503 raise util.Abort(_('unknown bundle version %s') % version) |
501 self.ui.debug('start processing of %s stream\n' % header) | 504 self.ui.debug('start processing of %s stream\n' % header) |
502 | 505 |
503 @util.propertycache | 506 @util.propertycache |
504 def params(self): | 507 def params(self): |
779 data = self._payloadstream.read(size) | 782 data = self._payloadstream.read(size) |
780 if size is None or len(data) < size: | 783 if size is None or len(data) < size: |
781 self.consumed = True | 784 self.consumed = True |
782 return data | 785 return data |
783 | 786 |
784 capabilities = {'HG2X': (), | 787 capabilities = {'HG2Y': (), |
785 'b2x:listkeys': (), | 788 'b2x:listkeys': (), |
786 'b2x:pushkey': (), | 789 'b2x:pushkey': (), |
787 'b2x:changegroup': (), | 790 'b2x:changegroup': (), |
788 } | 791 } |
789 | 792 |