Show More
@@ -84,8 +84,31 b' Binary format is as follow' | |||||
84 | The binary format of the header is has follow |
|
84 | The binary format of the header is has follow | |
85 |
|
85 | |||
86 | :typesize: (one byte) |
|
86 | :typesize: (one byte) | |
|
87 | ||||
87 | :typename: alphanumerical part name |
|
88 | :typename: alphanumerical part name | |
88 | :option: we do not support option yet this denoted by two 16 bites zero. |
|
89 | ||
|
90 | :parameters: | |||
|
91 | ||||
|
92 | Part's parameter may have arbitraty content, the binary structure is:: | |||
|
93 | ||||
|
94 | <mandatory-count><advisory-count><param-sizes><param-data> | |||
|
95 | ||||
|
96 | :mandatory-count: 1 byte, number of mandatory parameters | |||
|
97 | ||||
|
98 | :advisory-count: 1 byte, number of advisory parameters | |||
|
99 | ||||
|
100 | :param-sizes: | |||
|
101 | ||||
|
102 | N couple of bytes, where N is the total number of parameters. Each | |||
|
103 | couple contains (<size-of-key>, <size-of-value) for one parameter. | |||
|
104 | ||||
|
105 | :param-data: | |||
|
106 | ||||
|
107 | A blob of bytes from which each parameter key and value can be | |||
|
108 | retrieved using the list of size couples stored in the previous | |||
|
109 | field. | |||
|
110 | ||||
|
111 | Mandatory parameters comes first, then the advisory ones. | |||
89 |
|
112 | |||
90 | :payload: |
|
113 | :payload: | |
91 |
|
114 | |||
@@ -115,6 +138,15 b' from i18n import _' | |||||
115 | _fpartheadersize = '>H' |
|
138 | _fpartheadersize = '>H' | |
116 | _fparttypesize = '>B' |
|
139 | _fparttypesize = '>B' | |
117 | _fpayloadsize = '>I' |
|
140 | _fpayloadsize = '>I' | |
|
141 | _fpartparamcount = '>BB' | |||
|
142 | ||||
|
143 | def _makefpartparamsizes(nbparams): | |||
|
144 | """return a struct format to read part parameter sizes | |||
|
145 | ||||
|
146 | The number parameters is variable so we need to build that format | |||
|
147 | dynamically. | |||
|
148 | """ | |||
|
149 | return '>'+('BB'*nbparams) | |||
118 |
|
150 | |||
119 | class bundle20(object): |
|
151 | class bundle20(object): | |
120 | """represent an outgoing bundle2 container |
|
152 | """represent an outgoing bundle2 container | |
@@ -263,9 +295,27 b' class unbundle20(object):' | |||||
263 | typesize = _unpack(_fparttypesize, fromheader(1))[0] |
|
295 | typesize = _unpack(_fparttypesize, fromheader(1))[0] | |
264 | parttype = fromheader(typesize) |
|
296 | parttype = fromheader(typesize) | |
265 | self.ui.debug('part type: "%s"\n' % parttype) |
|
297 | self.ui.debug('part type: "%s"\n' % parttype) | |
266 | assert fromheader(2) == '\0\0' # no option for now |
|
298 | ## reading parameters | |
|
299 | # param count | |||
|
300 | mancount, advcount = _unpack(_fpartparamcount, fromheader(2)) | |||
|
301 | self.ui.debug('part parameters: %i\n' % (mancount + advcount)) | |||
|
302 | # param size | |||
|
303 | paramsizes = _unpack(_makefpartparamsizes(mancount + advcount), | |||
|
304 | fromheader(2*(mancount + advcount))) | |||
|
305 | # make it a list of couple again | |||
|
306 | paramsizes = zip(paramsizes[::2], paramsizes[1::2]) | |||
|
307 | # split mandatory from advisory | |||
|
308 | mansizes = paramsizes[:mancount] | |||
|
309 | advsizes = paramsizes[mancount:] | |||
|
310 | # retrive param value | |||
|
311 | manparams = [] | |||
|
312 | for key, value in mansizes: | |||
|
313 | manparams.append((fromheader(key), fromheader(value))) | |||
|
314 | advparams = [] | |||
|
315 | for key, value in advsizes: | |||
|
316 | advparams.append((fromheader(key), fromheader(value))) | |||
267 | del self._offset # clean up layer, nobody saw anything. |
|
317 | del self._offset # clean up layer, nobody saw anything. | |
268 | self.ui.debug('part parameters: 0\n') |
|
318 | ## part payload | |
269 | payload = [] |
|
319 | payload = [] | |
270 | payloadsize = self._unpack(_fpayloadsize)[0] |
|
320 | payloadsize = self._unpack(_fpayloadsize)[0] | |
271 | self.ui.debug('payload chunk size: %i\n' % payloadsize) |
|
321 | self.ui.debug('payload chunk size: %i\n' % payloadsize) | |
@@ -274,7 +324,7 b' class unbundle20(object):' | |||||
274 | payloadsize = self._unpack(_fpayloadsize)[0] |
|
324 | payloadsize = self._unpack(_fpayloadsize)[0] | |
275 | self.ui.debug('payload chunk size: %i\n' % payloadsize) |
|
325 | self.ui.debug('payload chunk size: %i\n' % payloadsize) | |
276 | payload = ''.join(payload) |
|
326 | payload = ''.join(payload) | |
277 | current = part(parttype, data=payload) |
|
327 | current = part(parttype, manparams, advparams, data=payload) | |
278 | return current |
|
328 | return current | |
279 |
|
329 | |||
280 |
|
330 | |||
@@ -285,19 +335,46 b' class part(object):' | |||||
285 | handler. |
|
335 | handler. | |
286 | """ |
|
336 | """ | |
287 |
|
337 | |||
288 |
def __init__(self, parttype, data= |
|
338 | def __init__(self, parttype, mandatoryparams=(), advisoryparams=(), | |
|
339 | data=''): | |||
289 | self.type = parttype |
|
340 | self.type = parttype | |
290 | self.data = data |
|
341 | self.data = data | |
|
342 | self.mandatoryparams = mandatoryparams | |||
|
343 | self.advisoryparams = advisoryparams | |||
291 |
|
344 | |||
292 | def getchunks(self): |
|
345 | def getchunks(self): | |
293 | ### header |
|
346 | #### header | |
|
347 | ## parttype | |||
294 | header = [_pack(_fparttypesize, len(self.type)), |
|
348 | header = [_pack(_fparttypesize, len(self.type)), | |
295 | self.type, |
|
349 | self.type, | |
296 | '\0\0', # No option support for now. |
|
|||
297 | ] |
|
350 | ] | |
|
351 | ## parameters | |||
|
352 | # count | |||
|
353 | manpar = self.mandatoryparams | |||
|
354 | advpar = self.advisoryparams | |||
|
355 | header.append(_pack(_fpartparamcount, len(manpar), len(advpar))) | |||
|
356 | # size | |||
|
357 | parsizes = [] | |||
|
358 | for key, value in manpar: | |||
|
359 | parsizes.append(len(key)) | |||
|
360 | parsizes.append(len(value)) | |||
|
361 | for key, value in advpar: | |||
|
362 | parsizes.append(len(key)) | |||
|
363 | parsizes.append(len(value)) | |||
|
364 | paramsizes = _pack(_makefpartparamsizes(len(parsizes) / 2), *parsizes) | |||
|
365 | header.append(paramsizes) | |||
|
366 | # key, value | |||
|
367 | for key, value in manpar: | |||
|
368 | header.append(key) | |||
|
369 | header.append(value) | |||
|
370 | for key, value in advpar: | |||
|
371 | header.append(key) | |||
|
372 | header.append(value) | |||
|
373 | ## finalize header | |||
298 | headerchunk = ''.join(header) |
|
374 | headerchunk = ''.join(header) | |
299 | yield _pack(_fpartheadersize, len(headerchunk)) |
|
375 | yield _pack(_fpartheadersize, len(headerchunk)) | |
300 | yield headerchunk |
|
376 | yield headerchunk | |
|
377 | ## payload | |||
301 | # we only support fixed size data now. |
|
378 | # we only support fixed size data now. | |
302 | # This will be improved in the future. |
|
379 | # This will be improved in the future. | |
303 | if len(self.data): |
|
380 | if len(self.data): | |
@@ -306,4 +383,3 b' class part(object):' | |||||
306 | # end of payload |
|
383 | # end of payload | |
307 | yield _pack(_fpayloadsize, 0) |
|
384 | yield _pack(_fpayloadsize, 0) | |
308 |
|
385 | |||
309 |
|
@@ -42,6 +42,11 b' Create an extension to test bundle2 API' | |||||
42 | > bundler.addpart(part) |
|
42 | > bundler.addpart(part) | |
43 | > part = bundle2.part('test:song', data=ELEPHANTSSONG) |
|
43 | > part = bundle2.part('test:song', data=ELEPHANTSSONG) | |
44 | > bundler.addpart(part) |
|
44 | > bundler.addpart(part) | |
|
45 | > part = bundle2.part('test:math', | |||
|
46 | > [('pi', '3.14'), ('e', '2.72')], | |||
|
47 | > [('cooking', 'raw')], | |||
|
48 | > '42') | |||
|
49 | > bundler.addpart(part) | |||
45 | > |
|
50 | > | |
46 | > if path is None: |
|
51 | > if path is None: | |
47 | > file = sys.stdout |
|
52 | > file = sys.stdout | |
@@ -69,6 +74,8 b' Create an extension to test bundle2 API' | |||||
69 | > ui.write('parts count: %i\n' % len(parts)) |
|
74 | > ui.write('parts count: %i\n' % len(parts)) | |
70 | > for p in parts: |
|
75 | > for p in parts: | |
71 | > ui.write(' :%s:\n' % p.type) |
|
76 | > ui.write(' :%s:\n' % p.type) | |
|
77 | > ui.write(' mandatory: %i\n' % len(p.mandatoryparams)) | |||
|
78 | > ui.write(' advisory: %i\n' % len(p.advisoryparams)) | |||
72 | > ui.write(' payload: %i bytes\n' % len(p.data)) |
|
79 | > ui.write(' payload: %i bytes\n' % len(p.data)) | |
73 | > EOF |
|
80 | > EOF | |
74 | $ cat >> $HGRCPATH << EOF |
|
81 | $ cat >> $HGRCPATH << EOF | |
@@ -247,6 +254,7 b' Test part' | |||||
247 | bundle part: "test:empty" |
|
254 | bundle part: "test:empty" | |
248 | bundle part: "test:empty" |
|
255 | bundle part: "test:empty" | |
249 | bundle part: "test:song" |
|
256 | bundle part: "test:song" | |
|
257 | bundle part: "test:math" | |||
250 | end of bundle |
|
258 | end of bundle | |
251 |
|
259 | |||
252 | $ cat ../parts.hg2 |
|
260 | $ cat ../parts.hg2 | |
@@ -254,18 +262,28 b' Test part' | |||||
254 | test:empty\x00\x00\x00\x00\x00\x00\x00\r (esc) |
|
262 | test:empty\x00\x00\x00\x00\x00\x00\x00\r (esc) | |
255 | 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) |
|
263 | 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) | |
256 | Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko |
|
264 | Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko | |
257 | Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.\x00\x00\x00\x00\x00\x00 (no-eol) (esc) |
|
265 | 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) | |
258 |
|
266 | |||
259 |
|
267 | |||
260 | $ hg unbundle2 < ../parts.hg2 |
|
268 | $ hg unbundle2 < ../parts.hg2 | |
261 |
|
|
269 | options count: 0 | |
262 |
|
|
270 | parts count: 4 | |
263 |
|
|
271 | :test:empty: | |
|
272 | mandatory: 0 | |||
|
273 | advisory: 0 | |||
264 |
|
|
274 | payload: 0 bytes | |
265 |
|
|
275 | :test:empty: | |
|
276 | mandatory: 0 | |||
|
277 | advisory: 0 | |||
266 |
|
|
278 | payload: 0 bytes | |
267 |
|
|
279 | :test:song: | |
|
280 | mandatory: 0 | |||
|
281 | advisory: 0 | |||
268 |
|
|
282 | payload: 178 bytes | |
|
283 | :test:math: | |||
|
284 | mandatory: 2 | |||
|
285 | advisory: 1 | |||
|
286 | payload: 2 bytes | |||
269 |
|
287 | |||
270 |
|
|
288 | $ hg unbundle2 --debug < ../parts.hg2 | |
271 |
|
|
289 | start processing of HG20 stream | |
@@ -285,12 +303,27 b' Test part' | |||||
285 |
|
|
303 | part parameters: 0 | |
286 |
|
|
304 | payload chunk size: 178 | |
287 |
|
|
305 | payload chunk size: 0 | |
|
306 | part header size: 39 | |||
|
307 | part type: "test:math" | |||
|
308 | part parameters: 3 | |||
|
309 | payload chunk size: 2 | |||
|
310 | payload chunk size: 0 | |||
288 |
|
|
311 | part header size: 0 | |
289 |
|
|
312 | end of bundle2 stream | |
290 |
|
|
313 | parts count: 4 | |
291 |
|
|
314 | :test:empty: | |
|
315 | mandatory: 0 | |||
|
316 | advisory: 0 | |||
292 |
|
|
317 | payload: 0 bytes | |
293 |
|
|
318 | :test:empty: | |
|
319 | mandatory: 0 | |||
|
320 | advisory: 0 | |||
294 |
|
|
321 | payload: 0 bytes | |
295 |
|
|
322 | :test:song: | |
|
323 | mandatory: 0 | |||
|
324 | advisory: 0 | |||
296 |
|
|
325 | payload: 178 bytes | |
|
326 | :test:math: | |||
|
327 | mandatory: 2 | |||
|
328 | advisory: 1 | |||
|
329 | payload: 2 bytes |
General Comments 0
You need to be logged in to leave comments.
Login now