##// END OF EJS Templates
bundle2: a very first version of bundle2 unbundler...
Pierre-Yves David -
r20802:520df53a default
parent child Browse files
Show More
@@ -1,84 +1,133 b''
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 current implementation is limited to empty bundle.
24 The current implementation is limited to empty bundle.
25
25
26 Details on the Binary format
26 Details on the Binary format
27 ============================
27 ============================
28
28
29 All numbers are unsigned and big endian.
29 All numbers are unsigned and big endian.
30
30
31 stream level parameters
31 stream level parameters
32 ------------------------
32 ------------------------
33
33
34 Binary format is as follow
34 Binary format is as follow
35
35
36 :params size: (16 bits integer)
36 :params size: (16 bits integer)
37
37
38 The total number of Bytes used by the parameters
38 The total number of Bytes used by the parameters
39
39
40 Currently force to 0.
40 Currently force to 0.
41
41
42 :params value: arbitrary number of Bytes
42 :params value: arbitrary number of Bytes
43
43
44 A blob of `params size` containing the serialized version of all stream level
44 A blob of `params size` containing the serialized version of all stream level
45 parameters.
45 parameters.
46
46
47 Currently always empty.
47 Currently always empty.
48
48
49
49
50 Payload part
50 Payload part
51 ------------------------
51 ------------------------
52
52
53 Binary format is as follow
53 Binary format is as follow
54
54
55 :header size: (16 bits inter)
55 :header size: (16 bits inter)
56
56
57 The total number of Bytes used by the part headers. When the header is empty
57 The total number of Bytes used by the part headers. When the header is empty
58 (size = 0) this is interpreted as the end of stream marker.
58 (size = 0) this is interpreted as the end of stream marker.
59
59
60 Currently forced to 0 in the current state of the implementation
60 Currently forced to 0 in the current state of the implementation
61 """
61 """
62
62
63 import util
64 import changegroup
65
66
63 _magicstring = 'HG20'
67 _magicstring = 'HG20'
64
68
65 class bundle20(object):
69 class bundle20(object):
66 """represent an outgoing bundle2 container
70 """represent an outgoing bundle2 container
67
71
68 People will eventually be able to add param and parts to this object and
72 People will eventually be able to add param and parts to this object and
69 generated a stream from it."""
73 generated a stream from it."""
70
74
71 def __init__(self):
75 def __init__(self):
72 self._params = []
76 self._params = []
73 self._parts = []
77 self._parts = []
74
78
75 def getchunks(self):
79 def getchunks(self):
76 yield _magicstring
80 yield _magicstring
77 # no support for any param yet
81 # no support for any param yet
78 # to be obviously fixed soon.
82 # to be obviously fixed soon.
79 assert not self._params
83 assert not self._params
80 yield '\0\0'
84 yield '\0\0'
81 # no support for parts
85 # no support for parts
82 # to be obviously fixed soon.
86 # to be obviously fixed soon.
83 assert not self._parts
87 assert not self._parts
84 yield '\0\0'
88 yield '\0\0'
89
90 class unbundle20(object):
91 """interpret a bundle2 stream
92
93 (this will eventually yield parts)"""
94
95 def __init__(self, fp):
96 # assume the magic string is ok and drop it
97 # to be obviously fixed soon.
98 self._fp = fp
99 self._readexact(4)
100
101 def _unpack(self, format):
102 """unpack this struct format from the stream"""
103 data = self._readexact(struct.calcsize(format))
104 return _unpack(format, data)
105
106 def _readexact(self, size):
107 """read exactly <size> bytes from the stream"""
108 return changegroup.readexactly(self._fp, size)
109
110 @util.propertycache
111 def params(self):
112 """dictionnary of stream level parameters"""
113 paramsize = self._readexact(2)
114 assert paramsize == '\0\0'
115 return {}
116
117 def __iter__(self):
118 """yield all parts contained in the stream"""
119 # make sure param have been loaded
120 self.params
121 part = self._readpart()
122 while part is not None:
123 yield part
124 part = self._readpart()
125
126 def _readpart(self):
127 """return None when an end of stream markers is reach"""
128 headersize = self._readexact(2)
129 assert headersize == '\0\0'
130 return None
131
132
133
@@ -1,36 +1,51 b''
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 > from mercurial import cmdutil
12 > from mercurial import cmdutil
12 > from mercurial import bundle2
13 > from mercurial import bundle2
13 > cmdtable = {}
14 > cmdtable = {}
14 > command = cmdutil.command(cmdtable)
15 > command = cmdutil.command(cmdtable)
15 >
16 >
16 > @command('bundle2', [], '')
17 > @command('bundle2', [], '')
17 > def cmdbundle2(ui, repo):
18 > def cmdbundle2(ui, repo):
18 > """write a bundle2 container on standard ouput"""
19 > """write a bundle2 container on standard ouput"""
19 > bundle = bundle2.bundle20()
20 > bundle = bundle2.bundle20()
20 > for chunk in bundle.getchunks():
21 > for chunk in bundle.getchunks():
21 > ui.write(chunk)
22 > ui.write(chunk)
23 >
24 > @command('unbundle2', [], '')
25 > def cmdunbundle2(ui, repo):
26 > """read a bundle2 container from standard input"""
27 > unbundler = bundle2.unbundle20(sys.stdin)
28 > ui.write('options count: %i\n' % len(unbundler.params))
29 > parts = list(unbundler)
30 > ui.write('parts count: %i\n' % len(parts))
22 > EOF
31 > EOF
23 $ cat >> $HGRCPATH << EOF
32 $ cat >> $HGRCPATH << EOF
24 > [extensions]
33 > [extensions]
25 > bundle2=$TESTTMP/bundle2.py
34 > bundle2=$TESTTMP/bundle2.py
26 > EOF
35 > EOF
27
36
28 The extension requires a repo (currently unused)
37 The extension requires a repo (currently unused)
29
38
30 $ hg init main
39 $ hg init main
31 $ cd main
40 $ cd main
32
41
33 Test simple generation of empty bundle
42 Test simple generation of empty bundle
34
43
35 $ hg bundle2
44 $ hg bundle2
36 HG20\x00\x00\x00\x00 (no-eol) (esc)
45 HG20\x00\x00\x00\x00 (no-eol) (esc)
46
47 Test parsing of an empty bundle
48
49 $ hg bundle2 | hg unbundle2
50 options count: 0
51 parts count: 0
General Comments 0
You need to be logged in to leave comments. Login now