Mercurial > hg
comparison hgext/fsmonitor/pywatchman/pybser.py @ 28432:2377c4ac4eec
fsmonitor: dependencies for new experimental extension
In preparation for the filesystem monitor extension, include the pywatchman
library. The fbmonitor extension relies on this library to communicate with
the Watchman service. The library is BSD licensed and is taken from
https://github.com/facebook/watchman/tree/master/python.
This package has not been updated to mercurial code standards.
author | Martijn Pieters <mjpieters@fb.com> |
---|---|
date | Wed, 02 Mar 2016 16:25:12 +0000 |
parents | |
children | 16f4b341288d |
comparison
equal
deleted
inserted
replaced
28431:a7e3b72cf756 | 28432:2377c4ac4eec |
---|---|
1 # Copyright 2015 Facebook, Inc. | |
2 # All rights reserved. | |
3 # | |
4 # Redistribution and use in source and binary forms, with or without | |
5 # modification, are permitted provided that the following conditions are met: | |
6 # | |
7 # * Redistributions of source code must retain the above copyright notice, | |
8 # this list of conditions and the following disclaimer. | |
9 # | |
10 # * Redistributions in binary form must reproduce the above copyright notice, | |
11 # this list of conditions and the following disclaimer in the documentation | |
12 # and/or other materials provided with the distribution. | |
13 # | |
14 # * Neither the name Facebook nor the names of its contributors may be used to | |
15 # endorse or promote products derived from this software without specific | |
16 # prior written permission. | |
17 # | |
18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
19 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
20 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
21 # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | |
22 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
23 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |
24 # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | |
25 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |
26 # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
28 | |
29 import collections | |
30 import ctypes | |
31 import struct | |
32 import sys | |
33 | |
34 BSER_ARRAY = '\x00' | |
35 BSER_OBJECT = '\x01' | |
36 BSER_STRING = '\x02' | |
37 BSER_INT8 = '\x03' | |
38 BSER_INT16 = '\x04' | |
39 BSER_INT32 = '\x05' | |
40 BSER_INT64 = '\x06' | |
41 BSER_REAL = '\x07' | |
42 BSER_TRUE = '\x08' | |
43 BSER_FALSE = '\x09' | |
44 BSER_NULL = '\x0a' | |
45 BSER_TEMPLATE = '\x0b' | |
46 BSER_SKIP = '\x0c' | |
47 | |
48 # Leave room for the serialization header, which includes | |
49 # our overall length. To make things simpler, we'll use an | |
50 # int32 for the header | |
51 EMPTY_HEADER = "\x00\x01\x05\x00\x00\x00\x00" | |
52 | |
53 # Python 3 conditional for supporting Python 2's int/long types | |
54 if sys.version_info > (3,): | |
55 long = int | |
56 | |
57 def _int_size(x): | |
58 """Return the smallest size int that can store the value""" | |
59 if -0x80 <= x <= 0x7F: | |
60 return 1 | |
61 elif -0x8000 <= x <= 0x7FFF: | |
62 return 2 | |
63 elif -0x80000000 <= x <= 0x7FFFFFFF: | |
64 return 4 | |
65 elif long(-0x8000000000000000) <= x <= long(0x7FFFFFFFFFFFFFFF): | |
66 return 8 | |
67 else: | |
68 raise RuntimeError('Cannot represent value: ' + str(x)) | |
69 | |
70 | |
71 class _bser_buffer(object): | |
72 | |
73 def __init__(self): | |
74 self.buf = ctypes.create_string_buffer(8192) | |
75 struct.pack_into(str(len(EMPTY_HEADER)) + 's', self.buf, 0, EMPTY_HEADER) | |
76 self.wpos = len(EMPTY_HEADER) | |
77 | |
78 def ensure_size(self, size): | |
79 while ctypes.sizeof(self.buf) - self.wpos < size: | |
80 ctypes.resize(self.buf, ctypes.sizeof(self.buf) * 2) | |
81 | |
82 def append_long(self, val): | |
83 size = _int_size(val) | |
84 to_write = size + 1 | |
85 self.ensure_size(to_write) | |
86 if size == 1: | |
87 struct.pack_into('=cb', self.buf, self.wpos, BSER_INT8, val) | |
88 elif size == 2: | |
89 struct.pack_into('=ch', self.buf, self.wpos, BSER_INT16, val) | |
90 elif size == 4: | |
91 struct.pack_into('=ci', self.buf, self.wpos, BSER_INT32, val) | |
92 elif size == 8: | |
93 struct.pack_into('=cq', self.buf, self.wpos, BSER_INT64, val) | |
94 else: | |
95 raise RuntimeError('Cannot represent this long value') | |
96 self.wpos += to_write | |
97 | |
98 | |
99 def append_string(self, s): | |
100 if isinstance(s, unicode): | |
101 s = s.encode('utf-8') | |
102 s_len = len(s) | |
103 size = _int_size(s_len) | |
104 to_write = 2 + size + s_len | |
105 self.ensure_size(to_write) | |
106 if size == 1: | |
107 struct.pack_into('=ccb' + str(s_len) + 's', self.buf, self.wpos, BSER_STRING, BSER_INT8, s_len, s) | |
108 elif size == 2: | |
109 struct.pack_into('=cch' + str(s_len) + 's', self.buf, self.wpos, BSER_STRING, BSER_INT16, s_len, s) | |
110 elif size == 4: | |
111 struct.pack_into('=cci' + str(s_len) + 's', self.buf, self.wpos, BSER_STRING, BSER_INT32, s_len, s) | |
112 elif size == 8: | |
113 struct.pack_into('=ccq' + str(s_len) + 's', self.buf, self.wpos, BSER_STRING, BSER_INT64, s_len, s) | |
114 else: | |
115 raise RuntimeError('Cannot represent this string value') | |
116 self.wpos += to_write | |
117 | |
118 | |
119 def append_recursive(self, val): | |
120 if isinstance(val, bool): | |
121 needed = 1 | |
122 self.ensure_size(needed) | |
123 if val: | |
124 to_encode = BSER_TRUE | |
125 else: | |
126 to_encode = BSER_FALSE | |
127 struct.pack_into('=c', self.buf, self.wpos, to_encode) | |
128 self.wpos += needed | |
129 elif val is None: | |
130 needed = 1 | |
131 self.ensure_size(needed) | |
132 struct.pack_into('=c', self.buf, self.wpos, BSER_NULL) | |
133 self.wpos += needed | |
134 elif isinstance(val, (int, long)): | |
135 self.append_long(val) | |
136 elif isinstance(val, (str, unicode)): | |
137 self.append_string(val) | |
138 elif isinstance(val, float): | |
139 needed = 9 | |
140 self.ensure_size(needed) | |
141 struct.pack_into('=cd', self.buf, self.wpos, BSER_REAL, val) | |
142 self.wpos += needed | |
143 elif isinstance(val, collections.Mapping) and isinstance(val, collections.Sized): | |
144 val_len = len(val) | |
145 size = _int_size(val_len) | |
146 needed = 2 + size | |
147 self.ensure_size(needed) | |
148 if size == 1: | |
149 struct.pack_into('=ccb', self.buf, self.wpos, BSER_OBJECT, BSER_INT8, val_len) | |
150 elif size == 2: | |
151 struct.pack_into('=cch', self.buf, self.wpos, BSER_OBJECT, BSER_INT16, val_len) | |
152 elif size == 4: | |
153 struct.pack_into('=cci', self.buf, self.wpos, BSER_OBJECT, BSER_INT32, val_len) | |
154 elif size == 8: | |
155 struct.pack_into('=ccq', self.buf, self.wpos, BSER_OBJECT, BSER_INT64, val_len) | |
156 else: | |
157 raise RuntimeError('Cannot represent this mapping value') | |
158 self.wpos += needed | |
159 for k, v in val.iteritems(): | |
160 self.append_string(k) | |
161 self.append_recursive(v) | |
162 elif isinstance(val, collections.Iterable) and isinstance(val, collections.Sized): | |
163 val_len = len(val) | |
164 size = _int_size(val_len) | |
165 needed = 2 + size | |
166 self.ensure_size(needed) | |
167 if size == 1: | |
168 struct.pack_into('=ccb', self.buf, self.wpos, BSER_ARRAY, BSER_INT8, val_len) | |
169 elif size == 2: | |
170 struct.pack_into('=cch', self.buf, self.wpos, BSER_ARRAY, BSER_INT16, val_len) | |
171 elif size == 4: | |
172 struct.pack_into('=cci', self.buf, self.wpos, BSER_ARRAY, BSER_INT32, val_len) | |
173 elif size == 8: | |
174 struct.pack_into('=ccq', self.buf, self.wpos, BSER_ARRAY, BSER_INT64, val_len) | |
175 else: | |
176 raise RuntimeError('Cannot represent this sequence value') | |
177 self.wpos += needed | |
178 for v in val: | |
179 self.append_recursive(v) | |
180 else: | |
181 raise RuntimeError('Cannot represent unknown value type') | |
182 | |
183 | |
184 def dumps(obj): | |
185 bser_buf = _bser_buffer() | |
186 bser_buf.append_recursive(obj) | |
187 # Now fill in the overall length | |
188 obj_len = bser_buf.wpos - len(EMPTY_HEADER) | |
189 struct.pack_into('=i', bser_buf.buf, 3, obj_len) | |
190 return bser_buf.buf.raw[:bser_buf.wpos] | |
191 | |
192 | |
193 def _bunser_int(buf, pos): | |
194 try: | |
195 int_type = buf[pos] | |
196 except IndexError: | |
197 raise ValueError('Invalid bser int encoding, pos out of range') | |
198 if int_type == BSER_INT8: | |
199 needed = 2 | |
200 fmt = '=b' | |
201 elif int_type == BSER_INT16: | |
202 needed = 3 | |
203 fmt = '=h' | |
204 elif int_type == BSER_INT32: | |
205 needed = 5 | |
206 fmt = '=i' | |
207 elif int_type == BSER_INT64: | |
208 needed = 9 | |
209 fmt = '=q' | |
210 else: | |
211 raise ValueError('Invalid bser int encoding 0x%02x' % int(int_type)) | |
212 int_val = struct.unpack_from(fmt, buf, pos + 1)[0] | |
213 return (int_val, pos + needed) | |
214 | |
215 | |
216 def _bunser_string(buf, pos): | |
217 str_len, pos = _bunser_int(buf, pos + 1) | |
218 str_val = struct.unpack_from(str(str_len) + 's', buf, pos)[0] | |
219 return (str_val, pos + str_len) | |
220 | |
221 | |
222 def _bunser_array(buf, pos, mutable=True): | |
223 arr_len, pos = _bunser_int(buf, pos + 1) | |
224 arr = [] | |
225 for i in range(arr_len): | |
226 arr_item, pos = _bser_loads_recursive(buf, pos, mutable) | |
227 arr.append(arr_item) | |
228 | |
229 if not mutable: | |
230 arr = tuple(arr) | |
231 | |
232 return arr, pos | |
233 | |
234 | |
235 # This is a quack-alike with the bserObjectType in bser.c | |
236 # It provides by getattr accessors and getitem for both index | |
237 # and name. | |
238 class _BunserDict(object): | |
239 __slots__ = ('_keys', '_values') | |
240 | |
241 def __init__(self, keys, values): | |
242 self._keys = keys | |
243 self._values = values | |
244 | |
245 def __getattr__(self, name): | |
246 return self.__getitem__(name) | |
247 | |
248 def __getitem__(self, key): | |
249 if isinstance(key, (int, long)): | |
250 return self._values[key] | |
251 elif key.startswith('st_'): | |
252 # hack^Wfeature to allow mercurial to use "st_size" to | |
253 # reference "size" | |
254 key = key[3:] | |
255 try: | |
256 return self._values[self._keys.index(key)] | |
257 except ValueError as ex: | |
258 raise KeyError('_BunserDict has no key %s' % key) | |
259 | |
260 def __len__(self): | |
261 return len(self._keys) | |
262 | |
263 def _bunser_object(buf, pos, mutable=True): | |
264 obj_len, pos = _bunser_int(buf, pos + 1) | |
265 if mutable: | |
266 obj = {} | |
267 else: | |
268 keys = [] | |
269 vals = [] | |
270 | |
271 for i in range(obj_len): | |
272 key, pos = _bunser_string(buf, pos) | |
273 val, pos = _bser_loads_recursive(buf, pos, mutable) | |
274 if mutable: | |
275 obj[key] = val | |
276 else: | |
277 keys.append(key) | |
278 vals.append(val) | |
279 | |
280 if not mutable: | |
281 obj = _BunserDict(keys, vals) | |
282 | |
283 return obj, pos | |
284 | |
285 | |
286 def _bunser_template(buf, pos, mutable=True): | |
287 if buf[pos + 1] != BSER_ARRAY: | |
288 raise RuntimeError('Expect ARRAY to follow TEMPLATE') | |
289 keys, pos = _bunser_array(buf, pos + 1) | |
290 nitems, pos = _bunser_int(buf, pos) | |
291 arr = [] | |
292 for i in range(nitems): | |
293 if mutable: | |
294 obj = {} | |
295 else: | |
296 vals = [] | |
297 | |
298 for keyidx in range(len(keys)): | |
299 if buf[pos] == BSER_SKIP: | |
300 pos += 1 | |
301 ele = None | |
302 else: | |
303 ele, pos = _bser_loads_recursive(buf, pos, mutable) | |
304 | |
305 if mutable: | |
306 key = keys[keyidx] | |
307 obj[key] = ele | |
308 else: | |
309 vals.append(ele) | |
310 | |
311 if not mutable: | |
312 obj = _BunserDict(keys, vals) | |
313 | |
314 arr.append(obj) | |
315 return arr, pos | |
316 | |
317 | |
318 def _bser_loads_recursive(buf, pos, mutable=True): | |
319 val_type = buf[pos] | |
320 if (val_type == BSER_INT8 or val_type == BSER_INT16 or | |
321 val_type == BSER_INT32 or val_type == BSER_INT64): | |
322 return _bunser_int(buf, pos) | |
323 elif val_type == BSER_REAL: | |
324 val = struct.unpack_from('=d', buf, pos + 1)[0] | |
325 return (val, pos + 9) | |
326 elif val_type == BSER_TRUE: | |
327 return (True, pos + 1) | |
328 elif val_type == BSER_FALSE: | |
329 return (False, pos + 1) | |
330 elif val_type == BSER_NULL: | |
331 return (None, pos + 1) | |
332 elif val_type == BSER_STRING: | |
333 return _bunser_string(buf, pos) | |
334 elif val_type == BSER_ARRAY: | |
335 return _bunser_array(buf, pos, mutable) | |
336 elif val_type == BSER_OBJECT: | |
337 return _bunser_object(buf, pos, mutable) | |
338 elif val_type == BSER_TEMPLATE: | |
339 return _bunser_template(buf, pos, mutable) | |
340 else: | |
341 raise RuntimeError('unhandled bser opcode 0x%02x' % (val_type,)) | |
342 | |
343 | |
344 def pdu_len(buf): | |
345 if buf[0:2] != EMPTY_HEADER[0:2]: | |
346 raise RuntimeError('Invalid BSER header') | |
347 expected_len, pos = _bunser_int(buf, 2) | |
348 return expected_len + pos | |
349 | |
350 | |
351 def loads(buf, mutable=True): | |
352 if buf[0:2] != EMPTY_HEADER[0:2]: | |
353 raise RuntimeError('Invalid BSER header') | |
354 expected_len, pos = _bunser_int(buf, 2) | |
355 if len(buf) != expected_len + pos: | |
356 raise RuntimeError('bser data len != header len') | |
357 return _bser_loads_recursive(buf, pos, mutable)[0] | |
358 | |
359 # no-check-code -- this is a 3rd party library |