##// END OF EJS Templates
bundle2: introduce a `parthandler` decorator...
Pierre-Yves David -
r20890:ec7fc110 default
parent child Browse files
Show More
@@ -1,433 +1,448
1 # bundle2.py - generic container format to transmit arbitrary data.
1 # bundle2.py - generic container format to transmit arbitrary data.
2 #
2 #
3 # Copyright 2013 Facebook, Inc.
3 # Copyright 2013 Facebook, Inc.
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7 """Handling of the new bundle2 format
7 """Handling of the new bundle2 format
8
8
9 The goal of bundle2 is to act as an atomically packet to transmit a set of
9 The goal of bundle2 is to act as an atomically packet to transmit a set of
10 payloads in an application agnostic way. It consist in a sequence of "parts"
10 payloads in an application agnostic way. It consist in a sequence of "parts"
11 that will be handed to and processed by the application layer.
11 that will be handed to and processed by the application layer.
12
12
13
13
14 General format architecture
14 General format architecture
15 ===========================
15 ===========================
16
16
17 The format is architectured as follow
17 The format is architectured as follow
18
18
19 - magic string
19 - magic string
20 - stream level parameters
20 - stream level parameters
21 - payload parts (any number)
21 - payload parts (any number)
22 - end of stream marker.
22 - end of stream marker.
23
23
24 the Binary format
24 the Binary format
25 ============================
25 ============================
26
26
27 All numbers are unsigned and big endian.
27 All numbers are unsigned and big endian.
28
28
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: (16 bits integer)
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
40 A blob of `params size` containing the serialized version of all stream level
40 A blob of `params size` containing the serialized version of all stream level
41 parameters.
41 parameters.
42
42
43 The blob contains a space separated list of parameters. parameter with value
43 The blob contains a space separated list of parameters. parameter with value
44 are stored in the form `<name>=<value>`. Both name and value are urlquoted.
44 are stored in the form `<name>=<value>`. Both name and value are urlquoted.
45
45
46 Empty name are obviously forbidden.
46 Empty name are obviously forbidden.
47
47
48 Name MUST start with a letter. If this first letter is lower case, the
48 Name MUST start with a letter. If this first letter is lower case, the
49 parameter is advisory and can be safefly ignored. However when the first
49 parameter is advisory and can be safefly ignored. However when the first
50 letter is capital, the parameter is mandatory and the bundling process MUST
50 letter is capital, the parameter is mandatory and the bundling process MUST
51 stop if he is not able to proceed it.
51 stop if he is not able to proceed it.
52
52
53 Stream parameters use a simple textual format for two main reasons:
53 Stream parameters use a simple textual format for two main reasons:
54
54
55 - Stream level parameters should remains simple and we want to discourage any
55 - Stream level parameters should remains simple and we want to discourage any
56 crazy usage.
56 crazy usage.
57 - Textual data allow easy human inspection of a the bundle2 header in case of
57 - Textual data allow easy human inspection of a the bundle2 header in case of
58 troubles.
58 troubles.
59
59
60 Any Applicative level options MUST go into a bundle2 part instead.
60 Any Applicative level options MUST go into a bundle2 part instead.
61
61
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: (16 bits inter)
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:
73
73
74 The header defines how to interpret the part. It contains two piece of
74 The header defines how to interpret the part. It contains two piece of
75 data: the part type, and the part parameters.
75 data: the part type, and the part parameters.
76
76
77 The part type is used to route an application level handler, that can
77 The part type is used to route an application level handler, that can
78 interpret payload.
78 interpret payload.
79
79
80 Part parameters are passed to the application level handler. They are
80 Part parameters are passed to the application level handler. They are
81 meant to convey information that will help the application level object to
81 meant to convey information that will help the application level object to
82 interpret the part payload.
82 interpret the part payload.
83
83
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
88 :typename: alphanumerical part name
88 :typename: alphanumerical part name
89
89
90 :parameters:
90 :parameters:
91
91
92 Part's parameter may have arbitraty content, the binary structure is::
92 Part's parameter may have arbitraty content, the binary structure is::
93
93
94 <mandatory-count><advisory-count><param-sizes><param-data>
94 <mandatory-count><advisory-count><param-sizes><param-data>
95
95
96 :mandatory-count: 1 byte, number of mandatory parameters
96 :mandatory-count: 1 byte, number of mandatory parameters
97
97
98 :advisory-count: 1 byte, number of advisory parameters
98 :advisory-count: 1 byte, number of advisory parameters
99
99
100 :param-sizes:
100 :param-sizes:
101
101
102 N couple of bytes, where N is the total number of parameters. Each
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.
103 couple contains (<size-of-key>, <size-of-value) for one parameter.
104
104
105 :param-data:
105 :param-data:
106
106
107 A blob of bytes from which each parameter key and value can be
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
108 retrieved using the list of size couples stored in the previous
109 field.
109 field.
110
110
111 Mandatory parameters comes first, then the advisory ones.
111 Mandatory parameters comes first, then the advisory ones.
112
112
113 :payload:
113 :payload:
114
114
115 payload is a series of `<chunksize><chunkdata>`.
115 payload is a series of `<chunksize><chunkdata>`.
116
116
117 `chunksize` is a 32 bits integer, `chunkdata` are plain bytes (as much as
117 `chunksize` is a 32 bits integer, `chunkdata` are plain bytes (as much as
118 `chunksize` says)` The payload part is concluded by a zero size chunk.
118 `chunksize` says)` The payload part is concluded by a zero size chunk.
119
119
120 The current implementation always produces either zero or one chunk.
120 The current implementation always produces either zero or one chunk.
121 This is an implementation limitation that will ultimatly be lifted.
121 This is an implementation limitation that will ultimatly be lifted.
122 """
122 """
123
123
124 import util
124 import util
125 import struct
125 import struct
126 import urllib
126 import urllib
127 import string
127 import string
128
128
129 import changegroup
129 import changegroup
130 from i18n import _
130 from i18n import _
131
131
132 _pack = struct.pack
132 _pack = struct.pack
133 _unpack = struct.unpack
133 _unpack = struct.unpack
134
134
135 _magicstring = 'HG20'
135 _magicstring = 'HG20'
136
136
137 _fstreamparamsize = '>H'
137 _fstreamparamsize = '>H'
138 _fpartheadersize = '>H'
138 _fpartheadersize = '>H'
139 _fparttypesize = '>B'
139 _fparttypesize = '>B'
140 _fpayloadsize = '>I'
140 _fpayloadsize = '>I'
141 _fpartparamcount = '>BB'
141 _fpartparamcount = '>BB'
142
142
143 def _makefpartparamsizes(nbparams):
143 def _makefpartparamsizes(nbparams):
144 """return a struct format to read part parameter sizes
144 """return a struct format to read part parameter sizes
145
145
146 The number parameters is variable so we need to build that format
146 The number parameters is variable so we need to build that format
147 dynamically.
147 dynamically.
148 """
148 """
149 return '>'+('BB'*nbparams)
149 return '>'+('BB'*nbparams)
150
150
151 parthandlermapping = {}
151
152
152 parthandlermapping = {}
153 def parthandler(parttype):
154 """decorator that register a function as a bundle2 part handler
155
156 eg::
157
158 @parthandler('myparttype')
159 def myparttypehandler(...):
160 '''process a part of type "my part".'''
161 ...
162 """
163 def _decorator(func):
164 assert parttype not in parthandlermapping
165 parthandlermapping[parttype] = func
166 return func
167 return _decorator
153
168
154 def processbundle(repo, stream):
169 def processbundle(repo, stream):
155 """This function process a bundle, apply effect to/from a repo
170 """This function process a bundle, apply effect to/from a repo
156
171
157 Currently it:
172 Currently it:
158 - parse a stream into an unbundle20 object
173 - parse a stream into an unbundle20 object
159 - iterate over each parts then search and use the proper handling code to
174 - iterate over each parts then search and use the proper handling code to
160 process the part.
175 process the part.
161
176
162 Parts are processes in order.
177 Parts are processes in order.
163
178
164 This is very early version of this function that will be strongly reworked
179 This is very early version of this function that will be strongly reworked
165 before final usage.
180 before final usage.
166
181
167 All unknown parts are currently ignored (Mandatory parts logic will comes
182 All unknown parts are currently ignored (Mandatory parts logic will comes
168 later).
183 later).
169 """
184 """
170 ui = repo.ui
185 ui = repo.ui
171 # Extraction of the unbundler object will most likely change. It may be
186 # Extraction of the unbundler object will most likely change. It may be
172 # done outside of this function, the unbundler would be passed as argument.
187 # done outside of this function, the unbundler would be passed as argument.
173 # in all case the unbundler will eventually be created by a
188 # in all case the unbundler will eventually be created by a
174 # `changegroup.readbundle` style function.
189 # `changegroup.readbundle` style function.
175 unbundler = unbundle20(ui, stream)
190 unbundler = unbundle20(ui, stream)
176 # todo:
191 # todo:
177 # - replace this is a init function soon.
192 # - replace this is a init function soon.
178 # - exception catching
193 # - exception catching
179 unbundler.params
194 unbundler.params
180 for part in unbundler:
195 for part in unbundler:
181 parttype = part.type
196 parttype = part.type
182 # part key are matched lower case
197 # part key are matched lower case
183 key = parttype.lower()
198 key = parttype.lower()
184 try:
199 try:
185 handler = parthandlermapping[key]
200 handler = parthandlermapping[key]
186 ui.debug('found an handler for part %r\n' % parttype)
201 ui.debug('found an handler for part %r\n' % parttype)
187 except KeyError:
202 except KeyError:
188 ui.debug('ignoring unknown advisory part %r\n' % key)
203 ui.debug('ignoring unknown advisory part %r\n' % key)
189 # todo: consume the part (once we use streamed parts)
204 # todo: consume the part (once we use streamed parts)
190 continue
205 continue
191 handler(repo, part)
206 handler(repo, part)
192
207
193 class bundle20(object):
208 class bundle20(object):
194 """represent an outgoing bundle2 container
209 """represent an outgoing bundle2 container
195
210
196 Use the `addparam` method to add stream level parameter. and `addpart` to
211 Use the `addparam` method to add stream level parameter. and `addpart` to
197 populate it. Then call `getchunks` to retrieve all the binary chunks of
212 populate it. Then call `getchunks` to retrieve all the binary chunks of
198 datathat compose the bundle2 container."""
213 datathat compose the bundle2 container."""
199
214
200 def __init__(self, ui):
215 def __init__(self, ui):
201 self.ui = ui
216 self.ui = ui
202 self._params = []
217 self._params = []
203 self._parts = []
218 self._parts = []
204
219
205 def addparam(self, name, value=None):
220 def addparam(self, name, value=None):
206 """add a stream level parameter"""
221 """add a stream level parameter"""
207 if not name:
222 if not name:
208 raise ValueError('empty parameter name')
223 raise ValueError('empty parameter name')
209 if name[0] not in string.letters:
224 if name[0] not in string.letters:
210 raise ValueError('non letter first character: %r' % name)
225 raise ValueError('non letter first character: %r' % name)
211 self._params.append((name, value))
226 self._params.append((name, value))
212
227
213 def addpart(self, part):
228 def addpart(self, part):
214 """add a new part to the bundle2 container
229 """add a new part to the bundle2 container
215
230
216 Parts contains the actuall applicative payload."""
231 Parts contains the actuall applicative payload."""
217 self._parts.append(part)
232 self._parts.append(part)
218
233
219 def getchunks(self):
234 def getchunks(self):
220 self.ui.debug('start emission of %s stream\n' % _magicstring)
235 self.ui.debug('start emission of %s stream\n' % _magicstring)
221 yield _magicstring
236 yield _magicstring
222 param = self._paramchunk()
237 param = self._paramchunk()
223 self.ui.debug('bundle parameter: %s\n' % param)
238 self.ui.debug('bundle parameter: %s\n' % param)
224 yield _pack(_fstreamparamsize, len(param))
239 yield _pack(_fstreamparamsize, len(param))
225 if param:
240 if param:
226 yield param
241 yield param
227
242
228 self.ui.debug('start of parts\n')
243 self.ui.debug('start of parts\n')
229 for part in self._parts:
244 for part in self._parts:
230 self.ui.debug('bundle part: "%s"\n' % part.type)
245 self.ui.debug('bundle part: "%s"\n' % part.type)
231 for chunk in part.getchunks():
246 for chunk in part.getchunks():
232 yield chunk
247 yield chunk
233 self.ui.debug('end of bundle\n')
248 self.ui.debug('end of bundle\n')
234 yield '\0\0'
249 yield '\0\0'
235
250
236 def _paramchunk(self):
251 def _paramchunk(self):
237 """return a encoded version of all stream parameters"""
252 """return a encoded version of all stream parameters"""
238 blocks = []
253 blocks = []
239 for par, value in self._params:
254 for par, value in self._params:
240 par = urllib.quote(par)
255 par = urllib.quote(par)
241 if value is not None:
256 if value is not None:
242 value = urllib.quote(value)
257 value = urllib.quote(value)
243 par = '%s=%s' % (par, value)
258 par = '%s=%s' % (par, value)
244 blocks.append(par)
259 blocks.append(par)
245 return ' '.join(blocks)
260 return ' '.join(blocks)
246
261
247 class unbundle20(object):
262 class unbundle20(object):
248 """interpret a bundle2 stream
263 """interpret a bundle2 stream
249
264
250 (this will eventually yield parts)"""
265 (this will eventually yield parts)"""
251
266
252 def __init__(self, ui, fp):
267 def __init__(self, ui, fp):
253 self.ui = ui
268 self.ui = ui
254 self._fp = fp
269 self._fp = fp
255 header = self._readexact(4)
270 header = self._readexact(4)
256 magic, version = header[0:2], header[2:4]
271 magic, version = header[0:2], header[2:4]
257 if magic != 'HG':
272 if magic != 'HG':
258 raise util.Abort(_('not a Mercurial bundle'))
273 raise util.Abort(_('not a Mercurial bundle'))
259 if version != '20':
274 if version != '20':
260 raise util.Abort(_('unknown bundle version %s') % version)
275 raise util.Abort(_('unknown bundle version %s') % version)
261 self.ui.debug('start processing of %s stream\n' % header)
276 self.ui.debug('start processing of %s stream\n' % header)
262
277
263 def _unpack(self, format):
278 def _unpack(self, format):
264 """unpack this struct format from the stream"""
279 """unpack this struct format from the stream"""
265 data = self._readexact(struct.calcsize(format))
280 data = self._readexact(struct.calcsize(format))
266 return _unpack(format, data)
281 return _unpack(format, data)
267
282
268 def _readexact(self, size):
283 def _readexact(self, size):
269 """read exactly <size> bytes from the stream"""
284 """read exactly <size> bytes from the stream"""
270 return changegroup.readexactly(self._fp, size)
285 return changegroup.readexactly(self._fp, size)
271
286
272 @util.propertycache
287 @util.propertycache
273 def params(self):
288 def params(self):
274 """dictionnary of stream level parameters"""
289 """dictionnary of stream level parameters"""
275 self.ui.debug('reading bundle2 stream parameters\n')
290 self.ui.debug('reading bundle2 stream parameters\n')
276 params = {}
291 params = {}
277 paramssize = self._unpack(_fstreamparamsize)[0]
292 paramssize = self._unpack(_fstreamparamsize)[0]
278 if paramssize:
293 if paramssize:
279 for p in self._readexact(paramssize).split(' '):
294 for p in self._readexact(paramssize).split(' '):
280 p = p.split('=', 1)
295 p = p.split('=', 1)
281 p = [urllib.unquote(i) for i in p]
296 p = [urllib.unquote(i) for i in p]
282 if len(p) < 2:
297 if len(p) < 2:
283 p.append(None)
298 p.append(None)
284 self._processparam(*p)
299 self._processparam(*p)
285 params[p[0]] = p[1]
300 params[p[0]] = p[1]
286 return params
301 return params
287
302
288 def _processparam(self, name, value):
303 def _processparam(self, name, value):
289 """process a parameter, applying its effect if needed
304 """process a parameter, applying its effect if needed
290
305
291 Parameter starting with a lower case letter are advisory and will be
306 Parameter starting with a lower case letter are advisory and will be
292 ignored when unknown. Those starting with an upper case letter are
307 ignored when unknown. Those starting with an upper case letter are
293 mandatory and will this function will raise a KeyError when unknown.
308 mandatory and will this function will raise a KeyError when unknown.
294
309
295 Note: no option are currently supported. Any input will be either
310 Note: no option are currently supported. Any input will be either
296 ignored or failing.
311 ignored or failing.
297 """
312 """
298 if not name:
313 if not name:
299 raise ValueError('empty parameter name')
314 raise ValueError('empty parameter name')
300 if name[0] not in string.letters:
315 if name[0] not in string.letters:
301 raise ValueError('non letter first character: %r' % name)
316 raise ValueError('non letter first character: %r' % name)
302 # Some logic will be later added here to try to process the option for
317 # Some logic will be later added here to try to process the option for
303 # a dict of known parameter.
318 # a dict of known parameter.
304 if name[0].islower():
319 if name[0].islower():
305 self.ui.debug("ignoring unknown parameter %r\n" % name)
320 self.ui.debug("ignoring unknown parameter %r\n" % name)
306 else:
321 else:
307 raise KeyError(name)
322 raise KeyError(name)
308
323
309
324
310 def __iter__(self):
325 def __iter__(self):
311 """yield all parts contained in the stream"""
326 """yield all parts contained in the stream"""
312 # make sure param have been loaded
327 # make sure param have been loaded
313 self.params
328 self.params
314 self.ui.debug('start extraction of bundle2 parts\n')
329 self.ui.debug('start extraction of bundle2 parts\n')
315 part = self._readpart()
330 part = self._readpart()
316 while part is not None:
331 while part is not None:
317 yield part
332 yield part
318 part = self._readpart()
333 part = self._readpart()
319 self.ui.debug('end of bundle2 stream\n')
334 self.ui.debug('end of bundle2 stream\n')
320
335
321 def _readpart(self):
336 def _readpart(self):
322 """return None when an end of stream markers is reach"""
337 """return None when an end of stream markers is reach"""
323
338
324 headersize = self._unpack(_fpartheadersize)[0]
339 headersize = self._unpack(_fpartheadersize)[0]
325 self.ui.debug('part header size: %i\n' % headersize)
340 self.ui.debug('part header size: %i\n' % headersize)
326 if not headersize:
341 if not headersize:
327 return None
342 return None
328 headerblock = self._readexact(headersize)
343 headerblock = self._readexact(headersize)
329 # some utility to help reading from the header block
344 # some utility to help reading from the header block
330 self._offset = 0 # layer violation to have something easy to understand
345 self._offset = 0 # layer violation to have something easy to understand
331 def fromheader(size):
346 def fromheader(size):
332 """return the next <size> byte from the header"""
347 """return the next <size> byte from the header"""
333 offset = self._offset
348 offset = self._offset
334 data = headerblock[offset:(offset + size)]
349 data = headerblock[offset:(offset + size)]
335 self._offset = offset + size
350 self._offset = offset + size
336 return data
351 return data
337 def unpackheader(format):
352 def unpackheader(format):
338 """read given format from header
353 """read given format from header
339
354
340 This automatically compute the size of the format to read."""
355 This automatically compute the size of the format to read."""
341 data = fromheader(struct.calcsize(format))
356 data = fromheader(struct.calcsize(format))
342 return _unpack(format, data)
357 return _unpack(format, data)
343
358
344 typesize = unpackheader(_fparttypesize)[0]
359 typesize = unpackheader(_fparttypesize)[0]
345 parttype = fromheader(typesize)
360 parttype = fromheader(typesize)
346 self.ui.debug('part type: "%s"\n' % parttype)
361 self.ui.debug('part type: "%s"\n' % parttype)
347 ## reading parameters
362 ## reading parameters
348 # param count
363 # param count
349 mancount, advcount = unpackheader(_fpartparamcount)
364 mancount, advcount = unpackheader(_fpartparamcount)
350 self.ui.debug('part parameters: %i\n' % (mancount + advcount))
365 self.ui.debug('part parameters: %i\n' % (mancount + advcount))
351 # param size
366 # param size
352 paramsizes = unpackheader(_makefpartparamsizes(mancount + advcount))
367 paramsizes = unpackheader(_makefpartparamsizes(mancount + advcount))
353 # make it a list of couple again
368 # make it a list of couple again
354 paramsizes = zip(paramsizes[::2], paramsizes[1::2])
369 paramsizes = zip(paramsizes[::2], paramsizes[1::2])
355 # split mandatory from advisory
370 # split mandatory from advisory
356 mansizes = paramsizes[:mancount]
371 mansizes = paramsizes[:mancount]
357 advsizes = paramsizes[mancount:]
372 advsizes = paramsizes[mancount:]
358 # retrive param value
373 # retrive param value
359 manparams = []
374 manparams = []
360 for key, value in mansizes:
375 for key, value in mansizes:
361 manparams.append((fromheader(key), fromheader(value)))
376 manparams.append((fromheader(key), fromheader(value)))
362 advparams = []
377 advparams = []
363 for key, value in advsizes:
378 for key, value in advsizes:
364 advparams.append((fromheader(key), fromheader(value)))
379 advparams.append((fromheader(key), fromheader(value)))
365 del self._offset # clean up layer, nobody saw anything.
380 del self._offset # clean up layer, nobody saw anything.
366 ## part payload
381 ## part payload
367 payload = []
382 payload = []
368 payloadsize = self._unpack(_fpayloadsize)[0]
383 payloadsize = self._unpack(_fpayloadsize)[0]
369 self.ui.debug('payload chunk size: %i\n' % payloadsize)
384 self.ui.debug('payload chunk size: %i\n' % payloadsize)
370 while payloadsize:
385 while payloadsize:
371 payload.append(self._readexact(payloadsize))
386 payload.append(self._readexact(payloadsize))
372 payloadsize = self._unpack(_fpayloadsize)[0]
387 payloadsize = self._unpack(_fpayloadsize)[0]
373 self.ui.debug('payload chunk size: %i\n' % payloadsize)
388 self.ui.debug('payload chunk size: %i\n' % payloadsize)
374 payload = ''.join(payload)
389 payload = ''.join(payload)
375 current = part(parttype, manparams, advparams, data=payload)
390 current = part(parttype, manparams, advparams, data=payload)
376 return current
391 return current
377
392
378
393
379 class part(object):
394 class part(object):
380 """A bundle2 part contains application level payload
395 """A bundle2 part contains application level payload
381
396
382 The part `type` is used to route the part to the application level
397 The part `type` is used to route the part to the application level
383 handler.
398 handler.
384 """
399 """
385
400
386 def __init__(self, parttype, mandatoryparams=(), advisoryparams=(),
401 def __init__(self, parttype, mandatoryparams=(), advisoryparams=(),
387 data=''):
402 data=''):
388 self.type = parttype
403 self.type = parttype
389 self.data = data
404 self.data = data
390 self.mandatoryparams = mandatoryparams
405 self.mandatoryparams = mandatoryparams
391 self.advisoryparams = advisoryparams
406 self.advisoryparams = advisoryparams
392
407
393 def getchunks(self):
408 def getchunks(self):
394 #### header
409 #### header
395 ## parttype
410 ## parttype
396 header = [_pack(_fparttypesize, len(self.type)),
411 header = [_pack(_fparttypesize, len(self.type)),
397 self.type,
412 self.type,
398 ]
413 ]
399 ## parameters
414 ## parameters
400 # count
415 # count
401 manpar = self.mandatoryparams
416 manpar = self.mandatoryparams
402 advpar = self.advisoryparams
417 advpar = self.advisoryparams
403 header.append(_pack(_fpartparamcount, len(manpar), len(advpar)))
418 header.append(_pack(_fpartparamcount, len(manpar), len(advpar)))
404 # size
419 # size
405 parsizes = []
420 parsizes = []
406 for key, value in manpar:
421 for key, value in manpar:
407 parsizes.append(len(key))
422 parsizes.append(len(key))
408 parsizes.append(len(value))
423 parsizes.append(len(value))
409 for key, value in advpar:
424 for key, value in advpar:
410 parsizes.append(len(key))
425 parsizes.append(len(key))
411 parsizes.append(len(value))
426 parsizes.append(len(value))
412 paramsizes = _pack(_makefpartparamsizes(len(parsizes) / 2), *parsizes)
427 paramsizes = _pack(_makefpartparamsizes(len(parsizes) / 2), *parsizes)
413 header.append(paramsizes)
428 header.append(paramsizes)
414 # key, value
429 # key, value
415 for key, value in manpar:
430 for key, value in manpar:
416 header.append(key)
431 header.append(key)
417 header.append(value)
432 header.append(value)
418 for key, value in advpar:
433 for key, value in advpar:
419 header.append(key)
434 header.append(key)
420 header.append(value)
435 header.append(value)
421 ## finalize header
436 ## finalize header
422 headerchunk = ''.join(header)
437 headerchunk = ''.join(header)
423 yield _pack(_fpartheadersize, len(headerchunk))
438 yield _pack(_fpartheadersize, len(headerchunk))
424 yield headerchunk
439 yield headerchunk
425 ## payload
440 ## payload
426 # we only support fixed size data now.
441 # we only support fixed size data now.
427 # This will be improved in the future.
442 # This will be improved in the future.
428 if len(self.data):
443 if len(self.data):
429 yield _pack(_fpayloadsize, len(self.data))
444 yield _pack(_fpayloadsize, len(self.data))
430 yield self.data
445 yield self.data
431 # end of payload
446 # end of payload
432 yield _pack(_fpayloadsize, 0)
447 yield _pack(_fpayloadsize, 0)
433
448
@@ -1,380 +1,379
1
1
2 Create an extension to test bundle2 API
2 Create an extension to test bundle2 API
3
3
4 $ cat > bundle2.py << EOF
4 $ cat > bundle2.py << EOF
5 > """A small extension to test bundle2 implementation
5 > """A small extension to test bundle2 implementation
6 >
6 >
7 > Current bundle2 implementation is far too limited to be used in any core
7 > Current bundle2 implementation is far too limited to be used in any core
8 > code. We still need to be able to test it while it grow up.
8 > code. We still need to be able to test it while it grow up.
9 > """
9 > """
10 >
10 >
11 > import sys
11 > import sys
12 > from mercurial import cmdutil
12 > from mercurial import cmdutil
13 > from mercurial import util
13 > from mercurial import util
14 > from mercurial import bundle2
14 > from mercurial import bundle2
15 > cmdtable = {}
15 > cmdtable = {}
16 > command = cmdutil.command(cmdtable)
16 > command = cmdutil.command(cmdtable)
17 >
17 >
18 > ELEPHANTSSONG = """Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
18 > ELEPHANTSSONG = """Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
19 > Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
19 > Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
20 > Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko."""
20 > Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko."""
21 > assert len(ELEPHANTSSONG) == 178 # future test say 178 bytes, trust it.
21 > assert len(ELEPHANTSSONG) == 178 # future test say 178 bytes, trust it.
22 >
22 >
23 > @bundle2.parthandler('test:song')
23 > def songhandler(repo, part):
24 > def songhandler(repo, part):
24 > """handle a "test:song" bundle2 part, printing the lyrics on stdin"""
25 > """handle a "test:song" bundle2 part, printing the lyrics on stdin"""
25 > repo.ui.write('The choir start singing:\n')
26 > repo.ui.write('The choir start singing:\n')
26 > for line in part.data.split('\n'):
27 > for line in part.data.split('\n'):
27 > repo.ui.write(' %s\n' % line)
28 > repo.ui.write(' %s\n' % line)
28 >
29 >
29 > bundle2.parthandlermapping['test:song'] = songhandler
30 >
31 > @command('bundle2',
30 > @command('bundle2',
32 > [('', 'param', [], 'stream level parameter'),
31 > [('', 'param', [], 'stream level parameter'),
33 > ('', 'parts', False, 'include some arbitrary parts to the bundle'),],
32 > ('', 'parts', False, 'include some arbitrary parts to the bundle'),],
34 > '[OUTPUTFILE]')
33 > '[OUTPUTFILE]')
35 > def cmdbundle2(ui, repo, path=None, **opts):
34 > def cmdbundle2(ui, repo, path=None, **opts):
36 > """write a bundle2 container on standard ouput"""
35 > """write a bundle2 container on standard ouput"""
37 > bundler = bundle2.bundle20(ui)
36 > bundler = bundle2.bundle20(ui)
38 > for p in opts['param']:
37 > for p in opts['param']:
39 > p = p.split('=', 1)
38 > p = p.split('=', 1)
40 > try:
39 > try:
41 > bundler.addparam(*p)
40 > bundler.addparam(*p)
42 > except ValueError, exc:
41 > except ValueError, exc:
43 > raise util.Abort('%s' % exc)
42 > raise util.Abort('%s' % exc)
44 >
43 >
45 > if opts['parts']:
44 > if opts['parts']:
46 > part = bundle2.part('test:empty')
45 > part = bundle2.part('test:empty')
47 > bundler.addpart(part)
46 > bundler.addpart(part)
48 > # add a second one to make sure we handle multiple parts
47 > # add a second one to make sure we handle multiple parts
49 > part = bundle2.part('test:empty')
48 > part = bundle2.part('test:empty')
50 > bundler.addpart(part)
49 > bundler.addpart(part)
51 > part = bundle2.part('test:song', data=ELEPHANTSSONG)
50 > part = bundle2.part('test:song', data=ELEPHANTSSONG)
52 > bundler.addpart(part)
51 > bundler.addpart(part)
53 > part = bundle2.part('test:math',
52 > part = bundle2.part('test:math',
54 > [('pi', '3.14'), ('e', '2.72')],
53 > [('pi', '3.14'), ('e', '2.72')],
55 > [('cooking', 'raw')],
54 > [('cooking', 'raw')],
56 > '42')
55 > '42')
57 > bundler.addpart(part)
56 > bundler.addpart(part)
58 >
57 >
59 > if path is None:
58 > if path is None:
60 > file = sys.stdout
59 > file = sys.stdout
61 > else:
60 > else:
62 > file = open(path, 'w')
61 > file = open(path, 'w')
63 >
62 >
64 > for chunk in bundler.getchunks():
63 > for chunk in bundler.getchunks():
65 > file.write(chunk)
64 > file.write(chunk)
66 >
65 >
67 > @command('unbundle2', [], '')
66 > @command('unbundle2', [], '')
68 > def cmdunbundle2(ui, repo):
67 > def cmdunbundle2(ui, repo):
69 > """process a bundle2 stream from stdin on the current repo"""
68 > """process a bundle2 stream from stdin on the current repo"""
70 > bundle2.processbundle(repo, sys.stdin)
69 > bundle2.processbundle(repo, sys.stdin)
71 >
70 >
72 > @command('statbundle2', [], '')
71 > @command('statbundle2', [], '')
73 > def cmdstatbundle2(ui, repo):
72 > def cmdstatbundle2(ui, repo):
74 > """print statistic on the bundle2 container read from stdin"""
73 > """print statistic on the bundle2 container read from stdin"""
75 > unbundler = bundle2.unbundle20(ui, sys.stdin)
74 > unbundler = bundle2.unbundle20(ui, sys.stdin)
76 > try:
75 > try:
77 > params = unbundler.params
76 > params = unbundler.params
78 > except KeyError, exc:
77 > except KeyError, exc:
79 > raise util.Abort('unknown parameters: %s' % exc)
78 > raise util.Abort('unknown parameters: %s' % exc)
80 > ui.write('options count: %i\n' % len(params))
79 > ui.write('options count: %i\n' % len(params))
81 > for key in sorted(params):
80 > for key in sorted(params):
82 > ui.write('- %s\n' % key)
81 > ui.write('- %s\n' % key)
83 > value = params[key]
82 > value = params[key]
84 > if value is not None:
83 > if value is not None:
85 > ui.write(' %s\n' % value)
84 > ui.write(' %s\n' % value)
86 > parts = list(unbundler)
85 > parts = list(unbundler)
87 > ui.write('parts count: %i\n' % len(parts))
86 > ui.write('parts count: %i\n' % len(parts))
88 > for p in parts:
87 > for p in parts:
89 > ui.write(' :%s:\n' % p.type)
88 > ui.write(' :%s:\n' % p.type)
90 > ui.write(' mandatory: %i\n' % len(p.mandatoryparams))
89 > ui.write(' mandatory: %i\n' % len(p.mandatoryparams))
91 > ui.write(' advisory: %i\n' % len(p.advisoryparams))
90 > ui.write(' advisory: %i\n' % len(p.advisoryparams))
92 > ui.write(' payload: %i bytes\n' % len(p.data))
91 > ui.write(' payload: %i bytes\n' % len(p.data))
93 > EOF
92 > EOF
94 $ cat >> $HGRCPATH << EOF
93 $ cat >> $HGRCPATH << EOF
95 > [extensions]
94 > [extensions]
96 > bundle2=$TESTTMP/bundle2.py
95 > bundle2=$TESTTMP/bundle2.py
97 > EOF
96 > EOF
98
97
99 The extension requires a repo (currently unused)
98 The extension requires a repo (currently unused)
100
99
101 $ hg init main
100 $ hg init main
102 $ cd main
101 $ cd main
103 $ touch a
102 $ touch a
104 $ hg add a
103 $ hg add a
105 $ hg commit -m 'a'
104 $ hg commit -m 'a'
106
105
107
106
108 Empty bundle
107 Empty bundle
109 =================
108 =================
110
109
111 - no option
110 - no option
112 - no parts
111 - no parts
113
112
114 Test bundling
113 Test bundling
115
114
116 $ hg bundle2
115 $ hg bundle2
117 HG20\x00\x00\x00\x00 (no-eol) (esc)
116 HG20\x00\x00\x00\x00 (no-eol) (esc)
118
117
119 Test unbundling
118 Test unbundling
120
119
121 $ hg bundle2 | hg statbundle2
120 $ hg bundle2 | hg statbundle2
122 options count: 0
121 options count: 0
123 parts count: 0
122 parts count: 0
124
123
125 Test old style bundle are detected and refused
124 Test old style bundle are detected and refused
126
125
127 $ hg bundle --all ../bundle.hg
126 $ hg bundle --all ../bundle.hg
128 1 changesets found
127 1 changesets found
129 $ hg statbundle2 < ../bundle.hg
128 $ hg statbundle2 < ../bundle.hg
130 abort: unknown bundle version 10
129 abort: unknown bundle version 10
131 [255]
130 [255]
132
131
133 Test parameters
132 Test parameters
134 =================
133 =================
135
134
136 - some options
135 - some options
137 - no parts
136 - no parts
138
137
139 advisory parameters, no value
138 advisory parameters, no value
140 -------------------------------
139 -------------------------------
141
140
142 Simplest possible parameters form
141 Simplest possible parameters form
143
142
144 Test generation simple option
143 Test generation simple option
145
144
146 $ hg bundle2 --param 'caution'
145 $ hg bundle2 --param 'caution'
147 HG20\x00\x07caution\x00\x00 (no-eol) (esc)
146 HG20\x00\x07caution\x00\x00 (no-eol) (esc)
148
147
149 Test unbundling
148 Test unbundling
150
149
151 $ hg bundle2 --param 'caution' | hg statbundle2
150 $ hg bundle2 --param 'caution' | hg statbundle2
152 options count: 1
151 options count: 1
153 - caution
152 - caution
154 parts count: 0
153 parts count: 0
155
154
156 Test generation multiple option
155 Test generation multiple option
157
156
158 $ hg bundle2 --param 'caution' --param 'meal'
157 $ hg bundle2 --param 'caution' --param 'meal'
159 HG20\x00\x0ccaution meal\x00\x00 (no-eol) (esc)
158 HG20\x00\x0ccaution meal\x00\x00 (no-eol) (esc)
160
159
161 Test unbundling
160 Test unbundling
162
161
163 $ hg bundle2 --param 'caution' --param 'meal' | hg statbundle2
162 $ hg bundle2 --param 'caution' --param 'meal' | hg statbundle2
164 options count: 2
163 options count: 2
165 - caution
164 - caution
166 - meal
165 - meal
167 parts count: 0
166 parts count: 0
168
167
169 advisory parameters, with value
168 advisory parameters, with value
170 -------------------------------
169 -------------------------------
171
170
172 Test generation
171 Test generation
173
172
174 $ hg bundle2 --param 'caution' --param 'meal=vegan' --param 'elephants'
173 $ hg bundle2 --param 'caution' --param 'meal=vegan' --param 'elephants'
175 HG20\x00\x1ccaution meal=vegan elephants\x00\x00 (no-eol) (esc)
174 HG20\x00\x1ccaution meal=vegan elephants\x00\x00 (no-eol) (esc)
176
175
177 Test unbundling
176 Test unbundling
178
177
179 $ hg bundle2 --param 'caution' --param 'meal=vegan' --param 'elephants' | hg statbundle2
178 $ hg bundle2 --param 'caution' --param 'meal=vegan' --param 'elephants' | hg statbundle2
180 options count: 3
179 options count: 3
181 - caution
180 - caution
182 - elephants
181 - elephants
183 - meal
182 - meal
184 vegan
183 vegan
185 parts count: 0
184 parts count: 0
186
185
187 parameter with special char in value
186 parameter with special char in value
188 ---------------------------------------------------
187 ---------------------------------------------------
189
188
190 Test generation
189 Test generation
191
190
192 $ hg bundle2 --param 'e|! 7/=babar%#==tutu' --param simple
191 $ hg bundle2 --param 'e|! 7/=babar%#==tutu' --param simple
193 HG20\x00)e%7C%21%207/=babar%25%23%3D%3Dtutu simple\x00\x00 (no-eol) (esc)
192 HG20\x00)e%7C%21%207/=babar%25%23%3D%3Dtutu simple\x00\x00 (no-eol) (esc)
194
193
195 Test unbundling
194 Test unbundling
196
195
197 $ hg bundle2 --param 'e|! 7/=babar%#==tutu' --param simple | hg statbundle2
196 $ hg bundle2 --param 'e|! 7/=babar%#==tutu' --param simple | hg statbundle2
198 options count: 2
197 options count: 2
199 - e|! 7/
198 - e|! 7/
200 babar%#==tutu
199 babar%#==tutu
201 - simple
200 - simple
202 parts count: 0
201 parts count: 0
203
202
204 Test unknown mandatory option
203 Test unknown mandatory option
205 ---------------------------------------------------
204 ---------------------------------------------------
206
205
207 $ hg bundle2 --param 'Gravity' | hg statbundle2
206 $ hg bundle2 --param 'Gravity' | hg statbundle2
208 abort: unknown parameters: 'Gravity'
207 abort: unknown parameters: 'Gravity'
209 [255]
208 [255]
210
209
211 Test debug output
210 Test debug output
212 ---------------------------------------------------
211 ---------------------------------------------------
213
212
214 bundling debug
213 bundling debug
215
214
216 $ hg bundle2 --debug --param 'e|! 7/=babar%#==tutu' --param simple ../out.hg2
215 $ hg bundle2 --debug --param 'e|! 7/=babar%#==tutu' --param simple ../out.hg2
217 start emission of HG20 stream
216 start emission of HG20 stream
218 bundle parameter: e%7C%21%207/=babar%25%23%3D%3Dtutu simple
217 bundle parameter: e%7C%21%207/=babar%25%23%3D%3Dtutu simple
219 start of parts
218 start of parts
220 end of bundle
219 end of bundle
221
220
222 file content is ok
221 file content is ok
223
222
224 $ cat ../out.hg2
223 $ cat ../out.hg2
225 HG20\x00)e%7C%21%207/=babar%25%23%3D%3Dtutu simple\x00\x00 (no-eol) (esc)
224 HG20\x00)e%7C%21%207/=babar%25%23%3D%3Dtutu simple\x00\x00 (no-eol) (esc)
226
225
227 unbundling debug
226 unbundling debug
228
227
229 $ hg statbundle2 --debug < ../out.hg2
228 $ hg statbundle2 --debug < ../out.hg2
230 start processing of HG20 stream
229 start processing of HG20 stream
231 reading bundle2 stream parameters
230 reading bundle2 stream parameters
232 ignoring unknown parameter 'e|! 7/'
231 ignoring unknown parameter 'e|! 7/'
233 ignoring unknown parameter 'simple'
232 ignoring unknown parameter 'simple'
234 options count: 2
233 options count: 2
235 - e|! 7/
234 - e|! 7/
236 babar%#==tutu
235 babar%#==tutu
237 - simple
236 - simple
238 start extraction of bundle2 parts
237 start extraction of bundle2 parts
239 part header size: 0
238 part header size: 0
240 end of bundle2 stream
239 end of bundle2 stream
241 parts count: 0
240 parts count: 0
242
241
243
242
244 Test buggy input
243 Test buggy input
245 ---------------------------------------------------
244 ---------------------------------------------------
246
245
247 empty parameter name
246 empty parameter name
248
247
249 $ hg bundle2 --param '' --quiet
248 $ hg bundle2 --param '' --quiet
250 abort: empty parameter name
249 abort: empty parameter name
251 [255]
250 [255]
252
251
253 bad parameter name
252 bad parameter name
254
253
255 $ hg bundle2 --param 42babar
254 $ hg bundle2 --param 42babar
256 abort: non letter first character: '42babar'
255 abort: non letter first character: '42babar'
257 [255]
256 [255]
258
257
259
258
260 Test part
259 Test part
261 =================
260 =================
262
261
263 $ hg bundle2 --parts ../parts.hg2 --debug
262 $ hg bundle2 --parts ../parts.hg2 --debug
264 start emission of HG20 stream
263 start emission of HG20 stream
265 bundle parameter:
264 bundle parameter:
266 start of parts
265 start of parts
267 bundle part: "test:empty"
266 bundle part: "test:empty"
268 bundle part: "test:empty"
267 bundle part: "test:empty"
269 bundle part: "test:song"
268 bundle part: "test:song"
270 bundle part: "test:math"
269 bundle part: "test:math"
271 end of bundle
270 end of bundle
272
271
273 $ cat ../parts.hg2
272 $ cat ../parts.hg2
274 HG20\x00\x00\x00\r (esc)
273 HG20\x00\x00\x00\r (esc)
275 test:empty\x00\x00\x00\x00\x00\x00\x00\r (esc)
274 test:empty\x00\x00\x00\x00\x00\x00\x00\r (esc)
276 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)
275 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)
277 Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
276 Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
278 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)
277 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)
279
278
280
279
281 $ hg statbundle2 < ../parts.hg2
280 $ hg statbundle2 < ../parts.hg2
282 options count: 0
281 options count: 0
283 parts count: 4
282 parts count: 4
284 :test:empty:
283 :test:empty:
285 mandatory: 0
284 mandatory: 0
286 advisory: 0
285 advisory: 0
287 payload: 0 bytes
286 payload: 0 bytes
288 :test:empty:
287 :test:empty:
289 mandatory: 0
288 mandatory: 0
290 advisory: 0
289 advisory: 0
291 payload: 0 bytes
290 payload: 0 bytes
292 :test:song:
291 :test:song:
293 mandatory: 0
292 mandatory: 0
294 advisory: 0
293 advisory: 0
295 payload: 178 bytes
294 payload: 178 bytes
296 :test:math:
295 :test:math:
297 mandatory: 2
296 mandatory: 2
298 advisory: 1
297 advisory: 1
299 payload: 2 bytes
298 payload: 2 bytes
300
299
301 $ hg statbundle2 --debug < ../parts.hg2
300 $ hg statbundle2 --debug < ../parts.hg2
302 start processing of HG20 stream
301 start processing of HG20 stream
303 reading bundle2 stream parameters
302 reading bundle2 stream parameters
304 options count: 0
303 options count: 0
305 start extraction of bundle2 parts
304 start extraction of bundle2 parts
306 part header size: 13
305 part header size: 13
307 part type: "test:empty"
306 part type: "test:empty"
308 part parameters: 0
307 part parameters: 0
309 payload chunk size: 0
308 payload chunk size: 0
310 part header size: 13
309 part header size: 13
311 part type: "test:empty"
310 part type: "test:empty"
312 part parameters: 0
311 part parameters: 0
313 payload chunk size: 0
312 payload chunk size: 0
314 part header size: 12
313 part header size: 12
315 part type: "test:song"
314 part type: "test:song"
316 part parameters: 0
315 part parameters: 0
317 payload chunk size: 178
316 payload chunk size: 178
318 payload chunk size: 0
317 payload chunk size: 0
319 part header size: 39
318 part header size: 39
320 part type: "test:math"
319 part type: "test:math"
321 part parameters: 3
320 part parameters: 3
322 payload chunk size: 2
321 payload chunk size: 2
323 payload chunk size: 0
322 payload chunk size: 0
324 part header size: 0
323 part header size: 0
325 end of bundle2 stream
324 end of bundle2 stream
326 parts count: 4
325 parts count: 4
327 :test:empty:
326 :test:empty:
328 mandatory: 0
327 mandatory: 0
329 advisory: 0
328 advisory: 0
330 payload: 0 bytes
329 payload: 0 bytes
331 :test:empty:
330 :test:empty:
332 mandatory: 0
331 mandatory: 0
333 advisory: 0
332 advisory: 0
334 payload: 0 bytes
333 payload: 0 bytes
335 :test:song:
334 :test:song:
336 mandatory: 0
335 mandatory: 0
337 advisory: 0
336 advisory: 0
338 payload: 178 bytes
337 payload: 178 bytes
339 :test:math:
338 :test:math:
340 mandatory: 2
339 mandatory: 2
341 advisory: 1
340 advisory: 1
342 payload: 2 bytes
341 payload: 2 bytes
343
342
344 Test actual unbundling
343 Test actual unbundling
345 ========================
344 ========================
346
345
347 Process the bundle
346 Process the bundle
348
347
349 $ hg unbundle2 --debug < ../parts.hg2
348 $ hg unbundle2 --debug < ../parts.hg2
350 start processing of HG20 stream
349 start processing of HG20 stream
351 reading bundle2 stream parameters
350 reading bundle2 stream parameters
352 start extraction of bundle2 parts
351 start extraction of bundle2 parts
353 part header size: 13
352 part header size: 13
354 part type: "test:empty"
353 part type: "test:empty"
355 part parameters: 0
354 part parameters: 0
356 payload chunk size: 0
355 payload chunk size: 0
357 ignoring unknown advisory part 'test:empty'
356 ignoring unknown advisory part 'test:empty'
358 part header size: 13
357 part header size: 13
359 part type: "test:empty"
358 part type: "test:empty"
360 part parameters: 0
359 part parameters: 0
361 payload chunk size: 0
360 payload chunk size: 0
362 ignoring unknown advisory part 'test:empty'
361 ignoring unknown advisory part 'test:empty'
363 part header size: 12
362 part header size: 12
364 part type: "test:song"
363 part type: "test:song"
365 part parameters: 0
364 part parameters: 0
366 payload chunk size: 178
365 payload chunk size: 178
367 payload chunk size: 0
366 payload chunk size: 0
368 found an handler for part 'test:song'
367 found an handler for part 'test:song'
369 The choir start singing:
368 The choir start singing:
370 Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
369 Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
371 Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
370 Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
372 Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.
371 Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.
373 part header size: 39
372 part header size: 39
374 part type: "test:math"
373 part type: "test:math"
375 part parameters: 3
374 part parameters: 3
376 payload chunk size: 2
375 payload chunk size: 2
377 payload chunk size: 0
376 payload chunk size: 0
378 ignoring unknown advisory part 'test:math'
377 ignoring unknown advisory part 'test:math'
379 part header size: 0
378 part header size: 0
380 end of bundle2 stream
379 end of bundle2 stream
General Comments 0
You need to be logged in to leave comments. Login now