##// END OF EJS Templates
fsmonitor: refresh pywatchman to upstream...
Zack Hricz -
r30656:16f4b341 default
parent child Browse files
Show More
@@ -0,0 +1,65 b''
1 # Copyright 2016-present 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 from __future__ import absolute_import
30 from __future__ import division
31 from __future__ import print_function
32 # no unicode literals
33
34 '''Compatibility module across Python 2 and 3.'''
35
36 import sys
37
38 PYTHON3 = sys.version_info >= (3, 0)
39
40 # This is adapted from https://bitbucket.org/gutworth/six, and used under the
41 # MIT license. See LICENSE for a full copyright notice.
42 if PYTHON3:
43 def reraise(tp, value, tb=None):
44 try:
45 if value is None:
46 value = tp()
47 if value.__traceback__ is not tb:
48 raise value.with_traceback(tb)
49 raise value
50 finally:
51 value = None
52 tb = None
53 else:
54 exec('''
55 def reraise(tp, value, tb=None):
56 try:
57 raise tp, value, tb
58 finally:
59 tb = None
60 '''.strip())
61
62 if PYTHON3:
63 UNICODE = str
64 else:
65 UNICODE = unicode
@@ -0,0 +1,73 b''
1 # Copyright 2016-present 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 from __future__ import absolute_import
30 from __future__ import division
31 from __future__ import print_function
32 # no unicode literals
33
34 '''Module to deal with filename encoding on the local system, as returned by
35 Watchman.'''
36
37 import sys
38
39 from . import (
40 compat,
41 )
42
43 if compat.PYTHON3:
44 default_local_errors = 'surrogateescape'
45
46 def get_local_encoding():
47 if sys.platform == 'win32':
48 # Watchman always returns UTF-8 encoded strings on Windows.
49 return 'utf-8'
50 # On the Python 3 versions we support, sys.getfilesystemencoding never
51 # returns None.
52 return sys.getfilesystemencoding()
53 else:
54 # Python 2 doesn't support surrogateescape, so use 'strict' by
55 # default. Users can register a custom surrogateescape error handler and use
56 # that if they so desire.
57 default_local_errors = 'strict'
58
59 def get_local_encoding():
60 if sys.platform == 'win32':
61 # Watchman always returns UTF-8 encoded strings on Windows.
62 return 'utf-8'
63 fsencoding = sys.getfilesystemencoding()
64 if fsencoding is None:
65 # This is very unlikely to happen, but if it does, just use UTF-8
66 fsencoding = 'utf-8'
67 return fsencoding
68
69 def encode_local(s):
70 return s.encode(get_local_encoding(), default_local_errors)
71
72 def decode_local(bs):
73 return bs.decode(get_local_encoding(), default_local_errors)
@@ -0,0 +1,107 b''
1 # Copyright 2016 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 from __future__ import absolute_import
30 from __future__ import division
31 from __future__ import print_function
32 # no unicode literals
33
34 try:
35 from . import bser
36 except ImportError:
37 from . import pybser as bser
38
39 import ctypes
40
41 EMPTY_HEADER = b"\x00\x01\x05\x00\x00\x00\x00"
42
43
44 def _read_bytes(fp, buf):
45 """Read bytes from a file-like object
46
47 @param fp: File-like object that implements read(int)
48 @type fp: file
49
50 @param buf: Buffer to read into
51 @type buf: bytes
52
53 @return: buf
54 """
55
56 # Do the first read without resizing the input buffer
57 offset = 0
58 remaining = len(buf)
59 while remaining > 0:
60 l = fp.readinto((ctypes.c_char * remaining).from_buffer(buf, offset))
61 if l is None or l == 0:
62 return offset
63 offset += l
64 remaining -= l
65 return offset
66
67
68 def load(fp, mutable=True, value_encoding=None, value_errors=None):
69 """Deserialize a BSER-encoded blob.
70
71 @param fp: The file-object to deserialize.
72 @type file:
73
74 @param mutable: Whether to return mutable results.
75 @type mutable: bool
76
77 @param value_encoding: Optional codec to use to decode values. If
78 unspecified or None, return values as bytestrings.
79 @type value_encoding: str
80
81 @param value_errors: Optional error handler for codec. 'strict' by default.
82 The other most common argument is 'surrogateescape' on
83 Python 3. If value_encoding is None, this is ignored.
84 @type value_errors: str
85 """
86 buf = ctypes.create_string_buffer(8192)
87 SNIFF_BUFFER_SIZE = len(EMPTY_HEADER)
88 header = (ctypes.c_char * SNIFF_BUFFER_SIZE).from_buffer(buf)
89 read_len = _read_bytes(fp, header)
90 if read_len < len(header):
91 return None
92
93 total_len = bser.pdu_len(buf)
94 if total_len > len(buf):
95 ctypes.resize(buf, total_len)
96
97 body = (ctypes.c_char * (total_len - len(header))).from_buffer(
98 buf, len(header))
99 read_len = _read_bytes(fp, body)
100 if read_len < len(body):
101 raise RuntimeError('bser data ended early')
102
103 return bser.loads(
104 (ctypes.c_char * total_len).from_buffer(buf, 0),
105 mutable,
106 value_encoding,
107 value_errors)
@@ -1,779 +1,1021 b''
1 # Copyright 2014-present Facebook, Inc.
1 # Copyright 2014-present Facebook, Inc.
2 # All rights reserved.
2 # All rights reserved.
3 #
3 #
4 # Redistribution and use in source and binary forms, with or without
4 # Redistribution and use in source and binary forms, with or without
5 # modification, are permitted provided that the following conditions are met:
5 # modification, are permitted provided that the following conditions are met:
6 #
6 #
7 # * Redistributions of source code must retain the above copyright notice,
7 # * Redistributions of source code must retain the above copyright notice,
8 # this list of conditions and the following disclaimer.
8 # this list of conditions and the following disclaimer.
9 #
9 #
10 # * Redistributions in binary form must reproduce the above copyright notice,
10 # * Redistributions in binary form must reproduce the above copyright notice,
11 # this list of conditions and the following disclaimer in the documentation
11 # this list of conditions and the following disclaimer in the documentation
12 # and/or other materials provided with the distribution.
12 # and/or other materials provided with the distribution.
13 #
13 #
14 # * Neither the name Facebook nor the names of its contributors may be used to
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
15 # endorse or promote products derived from this software without specific
16 # prior written permission.
16 # prior written permission.
17 #
17 #
18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
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
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
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
21 # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
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,
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
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.
27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
28
29 from __future__ import absolute_import
30 from __future__ import division
31 from __future__ import print_function
32 # no unicode literals
33
34 import inspect
35 import math
29 import os
36 import os
30 import errno
31 import math
32 import socket
37 import socket
33 import subprocess
38 import subprocess
34 import time
39 import time
35
40
36 # Sometimes it's really hard to get Python extensions to compile,
41 # Sometimes it's really hard to get Python extensions to compile,
37 # so fall back to a pure Python implementation.
42 # so fall back to a pure Python implementation.
38 try:
43 try:
39 import bser
44 from . import bser
45 # Demandimport causes modules to be loaded lazily. Force the load now
46 # so that we can fall back on pybser if bser doesn't exist
47 bser.pdu_info
40 except ImportError:
48 except ImportError:
41 import pybser as bser
49 from . import pybser as bser
42
50
43 import capabilities
51 from . import (
52 capabilities,
53 compat,
54 encoding,
55 load,
56 )
57
44
58
45 if os.name == 'nt':
59 if os.name == 'nt':
46 import ctypes
60 import ctypes
47 import ctypes.wintypes
61 import ctypes.wintypes
48
62
49 wintypes = ctypes.wintypes
63 wintypes = ctypes.wintypes
50 GENERIC_READ = 0x80000000
64 GENERIC_READ = 0x80000000
51 GENERIC_WRITE = 0x40000000
65 GENERIC_WRITE = 0x40000000
52 FILE_FLAG_OVERLAPPED = 0x40000000
66 FILE_FLAG_OVERLAPPED = 0x40000000
53 OPEN_EXISTING = 3
67 OPEN_EXISTING = 3
54 INVALID_HANDLE_VALUE = -1
68 INVALID_HANDLE_VALUE = -1
55 FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000
69 FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000
56 FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100
70 FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100
57 FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200
71 FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200
72 WAIT_FAILED = 0xFFFFFFFF
58 WAIT_TIMEOUT = 0x00000102
73 WAIT_TIMEOUT = 0x00000102
59 WAIT_OBJECT_0 = 0x00000000
74 WAIT_OBJECT_0 = 0x00000000
60 ERROR_IO_PENDING = 997
75 WAIT_IO_COMPLETION = 0x000000C0
76 INFINITE = 0xFFFFFFFF
77
78 # Overlapped I/O operation is in progress. (997)
79 ERROR_IO_PENDING = 0x000003E5
80
81 # The pointer size follows the architecture
82 # We use WPARAM since this type is already conditionally defined
83 ULONG_PTR = ctypes.wintypes.WPARAM
61
84
62 class OVERLAPPED(ctypes.Structure):
85 class OVERLAPPED(ctypes.Structure):
63 _fields_ = [
86 _fields_ = [
64 ("Internal", wintypes.ULONG), ("InternalHigh", wintypes.ULONG),
87 ("Internal", ULONG_PTR), ("InternalHigh", ULONG_PTR),
65 ("Offset", wintypes.DWORD), ("OffsetHigh", wintypes.DWORD),
88 ("Offset", wintypes.DWORD), ("OffsetHigh", wintypes.DWORD),
66 ("hEvent", wintypes.HANDLE)
89 ("hEvent", wintypes.HANDLE)
67 ]
90 ]
68
91
69 def __init__(self):
92 def __init__(self):
93 self.Internal = 0
94 self.InternalHigh = 0
70 self.Offset = 0
95 self.Offset = 0
71 self.OffsetHigh = 0
96 self.OffsetHigh = 0
72 self.hEvent = 0
97 self.hEvent = 0
73
98
74 LPDWORD = ctypes.POINTER(wintypes.DWORD)
99 LPDWORD = ctypes.POINTER(wintypes.DWORD)
75
100
76 CreateFile = ctypes.windll.kernel32.CreateFileA
101 CreateFile = ctypes.windll.kernel32.CreateFileA
77 CreateFile.argtypes = [wintypes.LPSTR, wintypes.DWORD, wintypes.DWORD,
102 CreateFile.argtypes = [wintypes.LPSTR, wintypes.DWORD, wintypes.DWORD,
78 wintypes.LPVOID, wintypes.DWORD, wintypes.DWORD,
103 wintypes.LPVOID, wintypes.DWORD, wintypes.DWORD,
79 wintypes.HANDLE]
104 wintypes.HANDLE]
80 CreateFile.restype = wintypes.HANDLE
105 CreateFile.restype = wintypes.HANDLE
81
106
82 CloseHandle = ctypes.windll.kernel32.CloseHandle
107 CloseHandle = ctypes.windll.kernel32.CloseHandle
83 CloseHandle.argtypes = [wintypes.HANDLE]
108 CloseHandle.argtypes = [wintypes.HANDLE]
84 CloseHandle.restype = wintypes.BOOL
109 CloseHandle.restype = wintypes.BOOL
85
110
86 ReadFile = ctypes.windll.kernel32.ReadFile
111 ReadFile = ctypes.windll.kernel32.ReadFile
87 ReadFile.argtypes = [wintypes.HANDLE, wintypes.LPVOID, wintypes.DWORD,
112 ReadFile.argtypes = [wintypes.HANDLE, wintypes.LPVOID, wintypes.DWORD,
88 LPDWORD, ctypes.POINTER(OVERLAPPED)]
113 LPDWORD, ctypes.POINTER(OVERLAPPED)]
89 ReadFile.restype = wintypes.BOOL
114 ReadFile.restype = wintypes.BOOL
90
115
91 WriteFile = ctypes.windll.kernel32.WriteFile
116 WriteFile = ctypes.windll.kernel32.WriteFile
92 WriteFile.argtypes = [wintypes.HANDLE, wintypes.LPVOID, wintypes.DWORD,
117 WriteFile.argtypes = [wintypes.HANDLE, wintypes.LPVOID, wintypes.DWORD,
93 LPDWORD, ctypes.POINTER(OVERLAPPED)]
118 LPDWORD, ctypes.POINTER(OVERLAPPED)]
94 WriteFile.restype = wintypes.BOOL
119 WriteFile.restype = wintypes.BOOL
95
120
96 GetLastError = ctypes.windll.kernel32.GetLastError
121 GetLastError = ctypes.windll.kernel32.GetLastError
97 GetLastError.argtypes = []
122 GetLastError.argtypes = []
98 GetLastError.restype = wintypes.DWORD
123 GetLastError.restype = wintypes.DWORD
99
124
125 SetLastError = ctypes.windll.kernel32.SetLastError
126 SetLastError.argtypes = [wintypes.DWORD]
127 SetLastError.restype = None
128
100 FormatMessage = ctypes.windll.kernel32.FormatMessageA
129 FormatMessage = ctypes.windll.kernel32.FormatMessageA
101 FormatMessage.argtypes = [wintypes.DWORD, wintypes.LPVOID, wintypes.DWORD,
130 FormatMessage.argtypes = [wintypes.DWORD, wintypes.LPVOID, wintypes.DWORD,
102 wintypes.DWORD, ctypes.POINTER(wintypes.LPSTR),
131 wintypes.DWORD, ctypes.POINTER(wintypes.LPSTR),
103 wintypes.DWORD, wintypes.LPVOID]
132 wintypes.DWORD, wintypes.LPVOID]
104 FormatMessage.restype = wintypes.DWORD
133 FormatMessage.restype = wintypes.DWORD
105
134
106 LocalFree = ctypes.windll.kernel32.LocalFree
135 LocalFree = ctypes.windll.kernel32.LocalFree
107
136
108 GetOverlappedResultEx = ctypes.windll.kernel32.GetOverlappedResultEx
137 GetOverlappedResult = ctypes.windll.kernel32.GetOverlappedResult
138 GetOverlappedResult.argtypes = [wintypes.HANDLE,
139 ctypes.POINTER(OVERLAPPED), LPDWORD,
140 wintypes.BOOL]
141 GetOverlappedResult.restype = wintypes.BOOL
142
143 GetOverlappedResultEx = getattr(ctypes.windll.kernel32,
144 'GetOverlappedResultEx', None)
145 if GetOverlappedResultEx is not None:
109 GetOverlappedResultEx.argtypes = [wintypes.HANDLE,
146 GetOverlappedResultEx.argtypes = [wintypes.HANDLE,
110 ctypes.POINTER(OVERLAPPED), LPDWORD,
147 ctypes.POINTER(OVERLAPPED), LPDWORD,
111 wintypes.DWORD, wintypes.BOOL]
148 wintypes.DWORD, wintypes.BOOL]
112 GetOverlappedResultEx.restype = wintypes.BOOL
149 GetOverlappedResultEx.restype = wintypes.BOOL
113
150
151 WaitForSingleObjectEx = ctypes.windll.kernel32.WaitForSingleObjectEx
152 WaitForSingleObjectEx.argtypes = [wintypes.HANDLE, wintypes.DWORD, wintypes.BOOL]
153 WaitForSingleObjectEx.restype = wintypes.DWORD
154
155 CreateEvent = ctypes.windll.kernel32.CreateEventA
156 CreateEvent.argtypes = [LPDWORD, wintypes.BOOL, wintypes.BOOL,
157 wintypes.LPSTR]
158 CreateEvent.restype = wintypes.HANDLE
159
160 # Windows Vista is the minimum supported client for CancelIoEx.
114 CancelIoEx = ctypes.windll.kernel32.CancelIoEx
161 CancelIoEx = ctypes.windll.kernel32.CancelIoEx
115 CancelIoEx.argtypes = [wintypes.HANDLE, ctypes.POINTER(OVERLAPPED)]
162 CancelIoEx.argtypes = [wintypes.HANDLE, ctypes.POINTER(OVERLAPPED)]
116 CancelIoEx.restype = wintypes.BOOL
163 CancelIoEx.restype = wintypes.BOOL
117
164
118 # 2 bytes marker, 1 byte int size, 8 bytes int64 value
165 # 2 bytes marker, 1 byte int size, 8 bytes int64 value
119 sniff_len = 13
166 sniff_len = 13
120
167
121 # This is a helper for debugging the client.
168 # This is a helper for debugging the client.
122 _debugging = False
169 _debugging = False
123 if _debugging:
170 if _debugging:
124
171
125 def log(fmt, *args):
172 def log(fmt, *args):
126 print('[%s] %s' %
173 print('[%s] %s' %
127 (time.strftime("%a, %d %b %Y %H:%M:%S", time.gmtime()),
174 (time.strftime("%a, %d %b %Y %H:%M:%S", time.gmtime()),
128 fmt % args[:]))
175 fmt % args[:]))
129 else:
176 else:
130
177
131 def log(fmt, *args):
178 def log(fmt, *args):
132 pass
179 pass
133
180
134
181
182 def _win32_strerror(err):
183 """ expand a win32 error code into a human readable message """
184
185 # FormatMessage will allocate memory and assign it here
186 buf = ctypes.c_char_p()
187 FormatMessage(
188 FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER
189 | FORMAT_MESSAGE_IGNORE_INSERTS, None, err, 0, buf, 0, None)
190 try:
191 return buf.value
192 finally:
193 LocalFree(buf)
194
195
135 class WatchmanError(Exception):
196 class WatchmanError(Exception):
136 pass
197 def __init__(self, msg=None, cmd=None):
198 self.msg = msg
199 self.cmd = cmd
200
201 def setCommand(self, cmd):
202 self.cmd = cmd
203
204 def __str__(self):
205 if self.cmd:
206 return '%s, while executing %s' % (self.msg, self.cmd)
207 return self.msg
208
209
210 class WatchmanEnvironmentError(WatchmanError):
211 def __init__(self, msg, errno, errmsg, cmd=None):
212 super(WatchmanEnvironmentError, self).__init__(
213 '{0}: errno={1} errmsg={2}'.format(msg, errno, errmsg),
214 cmd)
215
216
217 class SocketConnectError(WatchmanError):
218 def __init__(self, sockpath, exc):
219 super(SocketConnectError, self).__init__(
220 'unable to connect to %s: %s' % (sockpath, exc))
221 self.sockpath = sockpath
222 self.exc = exc
137
223
138
224
139 class SocketTimeout(WatchmanError):
225 class SocketTimeout(WatchmanError):
140 """A specialized exception raised for socket timeouts during communication to/from watchman.
226 """A specialized exception raised for socket timeouts during communication to/from watchman.
141 This makes it easier to implement non-blocking loops as callers can easily distinguish
227 This makes it easier to implement non-blocking loops as callers can easily distinguish
142 between a routine timeout and an actual error condition.
228 between a routine timeout and an actual error condition.
143
229
144 Note that catching WatchmanError will also catch this as it is a super-class, so backwards
230 Note that catching WatchmanError will also catch this as it is a super-class, so backwards
145 compatibility in exception handling is preserved.
231 compatibility in exception handling is preserved.
146 """
232 """
147
233
148
234
149 class CommandError(WatchmanError):
235 class CommandError(WatchmanError):
150 """error returned by watchman
236 """error returned by watchman
151
237
152 self.msg is the message returned by watchman.
238 self.msg is the message returned by watchman.
153 """
239 """
154
155 def __init__(self, msg, cmd=None):
240 def __init__(self, msg, cmd=None):
156 self.msg = msg
241 super(CommandError, self).__init__(
157 self.cmd = cmd
242 'watchman command error: %s' % (msg, ),
158 super(CommandError, self).__init__('watchman command error: %s' % msg)
243 cmd,
159
244 )
160 def setCommand(self, cmd):
161 self.cmd = cmd
162
163 def __str__(self):
164 if self.cmd:
165 return '%s, while executing %s' % (self.msg, self.cmd)
166 return self.msg
167
245
168
246
169 class Transport(object):
247 class Transport(object):
170 """ communication transport to the watchman server """
248 """ communication transport to the watchman server """
171 buf = None
249 buf = None
172
250
173 def close(self):
251 def close(self):
174 """ tear it down """
252 """ tear it down """
175 raise NotImplementedError()
253 raise NotImplementedError()
176
254
177 def readBytes(self, size):
255 def readBytes(self, size):
178 """ read size bytes """
256 """ read size bytes """
179 raise NotImplementedError()
257 raise NotImplementedError()
180
258
181 def write(self, buf):
259 def write(self, buf):
182 """ write some data """
260 """ write some data """
183 raise NotImplementedError()
261 raise NotImplementedError()
184
262
185 def setTimeout(self, value):
263 def setTimeout(self, value):
186 pass
264 pass
187
265
188 def readLine(self):
266 def readLine(self):
189 """ read a line
267 """ read a line
190 Maintains its own buffer, callers of the transport should not mix
268 Maintains its own buffer, callers of the transport should not mix
191 calls to readBytes and readLine.
269 calls to readBytes and readLine.
192 """
270 """
193 if self.buf is None:
271 if self.buf is None:
194 self.buf = []
272 self.buf = []
195
273
196 # Buffer may already have a line if we've received unilateral
274 # Buffer may already have a line if we've received unilateral
197 # response(s) from the server
275 # response(s) from the server
198 if len(self.buf) == 1 and "\n" in self.buf[0]:
276 if len(self.buf) == 1 and b"\n" in self.buf[0]:
199 (line, b) = self.buf[0].split("\n", 1)
277 (line, b) = self.buf[0].split(b"\n", 1)
200 self.buf = [b]
278 self.buf = [b]
201 return line
279 return line
202
280
203 while True:
281 while True:
204 b = self.readBytes(4096)
282 b = self.readBytes(4096)
205 if "\n" in b:
283 if b"\n" in b:
206 result = ''.join(self.buf)
284 result = b''.join(self.buf)
207 (line, b) = b.split("\n", 1)
285 (line, b) = b.split(b"\n", 1)
208 self.buf = [b]
286 self.buf = [b]
209 return result + line
287 return result + line
210 self.buf.append(b)
288 self.buf.append(b)
211
289
212
290
213 class Codec(object):
291 class Codec(object):
214 """ communication encoding for the watchman server """
292 """ communication encoding for the watchman server """
215 transport = None
293 transport = None
216
294
217 def __init__(self, transport):
295 def __init__(self, transport):
218 self.transport = transport
296 self.transport = transport
219
297
220 def receive(self):
298 def receive(self):
221 raise NotImplementedError()
299 raise NotImplementedError()
222
300
223 def send(self, *args):
301 def send(self, *args):
224 raise NotImplementedError()
302 raise NotImplementedError()
225
303
226 def setTimeout(self, value):
304 def setTimeout(self, value):
227 self.transport.setTimeout(value)
305 self.transport.setTimeout(value)
228
306
229
307
230 class UnixSocketTransport(Transport):
308 class UnixSocketTransport(Transport):
231 """ local unix domain socket transport """
309 """ local unix domain socket transport """
232 sock = None
310 sock = None
233
311
234 def __init__(self, sockpath, timeout):
312 def __init__(self, sockpath, timeout):
235 self.sockpath = sockpath
313 self.sockpath = sockpath
236 self.timeout = timeout
314 self.timeout = timeout
237
315
238 sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
316 sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
239 try:
317 try:
240 sock.settimeout(self.timeout)
318 sock.settimeout(self.timeout)
241 sock.connect(self.sockpath)
319 sock.connect(self.sockpath)
242 self.sock = sock
320 self.sock = sock
243 except socket.error as e:
321 except socket.error as e:
244 raise WatchmanError('unable to connect to %s: %s' %
322 sock.close()
245 (self.sockpath, e))
323 raise SocketConnectError(self.sockpath, e)
246
324
247 def close(self):
325 def close(self):
248 self.sock.close()
326 self.sock.close()
249 self.sock = None
327 self.sock = None
250
328
251 def setTimeout(self, value):
329 def setTimeout(self, value):
252 self.timeout = value
330 self.timeout = value
253 self.sock.settimeout(self.timeout)
331 self.sock.settimeout(self.timeout)
254
332
255 def readBytes(self, size):
333 def readBytes(self, size):
256 try:
334 try:
257 buf = [self.sock.recv(size)]
335 buf = [self.sock.recv(size)]
258 if not buf[0]:
336 if not buf[0]:
259 raise WatchmanError('empty watchman response')
337 raise WatchmanError('empty watchman response')
260 return buf[0]
338 return buf[0]
261 except socket.timeout:
339 except socket.timeout:
262 raise SocketTimeout('timed out waiting for response')
340 raise SocketTimeout('timed out waiting for response')
263
341
264 def write(self, data):
342 def write(self, data):
265 try:
343 try:
266 self.sock.sendall(data)
344 self.sock.sendall(data)
267 except socket.timeout:
345 except socket.timeout:
268 raise SocketTimeout('timed out sending query command')
346 raise SocketTimeout('timed out sending query command')
269
347
270
348
349 def _get_overlapped_result_ex_impl(pipe, olap, nbytes, millis, alertable):
350 """ Windows 7 and earlier does not support GetOverlappedResultEx. The
351 alternative is to use GetOverlappedResult and wait for read or write
352 operation to complete. This is done be using CreateEvent and
353 WaitForSingleObjectEx. CreateEvent, WaitForSingleObjectEx
354 and GetOverlappedResult are all part of Windows API since WindowsXP.
355 This is the exact same implementation that can be found in the watchman
356 source code (see get_overlapped_result_ex_impl in stream_win.c). This
357 way, maintenance should be simplified.
358 """
359 log('Preparing to wait for maximum %dms', millis )
360 if millis != 0:
361 waitReturnCode = WaitForSingleObjectEx(olap.hEvent, millis, alertable)
362 if waitReturnCode == WAIT_OBJECT_0:
363 # Event is signaled, overlapped IO operation result should be available.
364 pass
365 elif waitReturnCode == WAIT_IO_COMPLETION:
366 # WaitForSingleObjectEx returnes because the system added an I/O completion
367 # routine or an asynchronous procedure call (APC) to the thread queue.
368 SetLastError(WAIT_IO_COMPLETION)
369 pass
370 elif waitReturnCode == WAIT_TIMEOUT:
371 # We reached the maximum allowed wait time, the IO operation failed
372 # to complete in timely fashion.
373 SetLastError(WAIT_TIMEOUT)
374 return False
375 elif waitReturnCode == WAIT_FAILED:
376 # something went wrong calling WaitForSingleObjectEx
377 err = GetLastError()
378 log('WaitForSingleObjectEx failed: %s', _win32_strerror(err))
379 return False
380 else:
381 # unexpected situation deserving investigation.
382 err = GetLastError()
383 log('Unexpected error: %s', _win32_strerror(err))
384 return False
385
386 return GetOverlappedResult(pipe, olap, nbytes, False)
387
388
271 class WindowsNamedPipeTransport(Transport):
389 class WindowsNamedPipeTransport(Transport):
272 """ connect to a named pipe """
390 """ connect to a named pipe """
273
391
274 def __init__(self, sockpath, timeout):
392 def __init__(self, sockpath, timeout):
275 self.sockpath = sockpath
393 self.sockpath = sockpath
276 self.timeout = int(math.ceil(timeout * 1000))
394 self.timeout = int(math.ceil(timeout * 1000))
277 self._iobuf = None
395 self._iobuf = None
278
396
279 self.pipe = CreateFile(sockpath, GENERIC_READ | GENERIC_WRITE, 0, None,
397 self.pipe = CreateFile(sockpath, GENERIC_READ | GENERIC_WRITE, 0, None,
280 OPEN_EXISTING, FILE_FLAG_OVERLAPPED, None)
398 OPEN_EXISTING, FILE_FLAG_OVERLAPPED, None)
281
399
282 if self.pipe == INVALID_HANDLE_VALUE:
400 if self.pipe == INVALID_HANDLE_VALUE:
283 self.pipe = None
401 self.pipe = None
284 self._raise_win_err('failed to open pipe %s' % sockpath,
402 self._raise_win_err('failed to open pipe %s' % sockpath,
285 GetLastError())
403 GetLastError())
286
404
287 def _win32_strerror(self, err):
405 # event for the overlapped I/O operations
288 """ expand a win32 error code into a human readable message """
406 self._waitable = CreateEvent(None, True, False, None)
407 if self._waitable is None:
408 self._raise_win_err('CreateEvent failed', GetLastError())
289
409
290 # FormatMessage will allocate memory and assign it here
410 self._get_overlapped_result_ex = GetOverlappedResultEx
291 buf = ctypes.c_char_p()
411 if (os.getenv('WATCHMAN_WIN7_COMPAT') == '1' or
292 FormatMessage(
412 self._get_overlapped_result_ex is None):
293 FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER
413 self._get_overlapped_result_ex = _get_overlapped_result_ex_impl
294 | FORMAT_MESSAGE_IGNORE_INSERTS, None, err, 0, buf, 0, None)
295 try:
296 return buf.value
297 finally:
298 LocalFree(buf)
299
414
300 def _raise_win_err(self, msg, err):
415 def _raise_win_err(self, msg, err):
301 raise IOError('%s win32 error code: %d %s' %
416 raise IOError('%s win32 error code: %d %s' %
302 (msg, err, self._win32_strerror(err)))
417 (msg, err, _win32_strerror(err)))
303
418
304 def close(self):
419 def close(self):
305 if self.pipe:
420 if self.pipe:
421 log('Closing pipe')
306 CloseHandle(self.pipe)
422 CloseHandle(self.pipe)
307 self.pipe = None
423 self.pipe = None
308
424
425 if self._waitable is not None:
426 # We release the handle for the event
427 CloseHandle(self._waitable)
428 self._waitable = None
429
430 def setTimeout(self, value):
431 # convert to milliseconds
432 self.timeout = int(value * 1000)
433
309 def readBytes(self, size):
434 def readBytes(self, size):
310 """ A read can block for an unbounded amount of time, even if the
435 """ A read can block for an unbounded amount of time, even if the
311 kernel reports that the pipe handle is signalled, so we need to
436 kernel reports that the pipe handle is signalled, so we need to
312 always perform our reads asynchronously
437 always perform our reads asynchronously
313 """
438 """
314
439
315 # try to satisfy the read from any buffered data
440 # try to satisfy the read from any buffered data
316 if self._iobuf:
441 if self._iobuf:
317 if size >= len(self._iobuf):
442 if size >= len(self._iobuf):
318 res = self._iobuf
443 res = self._iobuf
319 self.buf = None
444 self.buf = None
320 return res
445 return res
321 res = self._iobuf[:size]
446 res = self._iobuf[:size]
322 self._iobuf = self._iobuf[size:]
447 self._iobuf = self._iobuf[size:]
323 return res
448 return res
324
449
325 # We need to initiate a read
450 # We need to initiate a read
326 buf = ctypes.create_string_buffer(size)
451 buf = ctypes.create_string_buffer(size)
327 olap = OVERLAPPED()
452 olap = OVERLAPPED()
453 olap.hEvent = self._waitable
328
454
329 log('made read buff of size %d', size)
455 log('made read buff of size %d', size)
330
456
331 # ReadFile docs warn against sending in the nread parameter for async
457 # ReadFile docs warn against sending in the nread parameter for async
332 # operations, so we always collect it via GetOverlappedResultEx
458 # operations, so we always collect it via GetOverlappedResultEx
333 immediate = ReadFile(self.pipe, buf, size, None, olap)
459 immediate = ReadFile(self.pipe, buf, size, None, olap)
334
460
335 if not immediate:
461 if not immediate:
336 err = GetLastError()
462 err = GetLastError()
337 if err != ERROR_IO_PENDING:
463 if err != ERROR_IO_PENDING:
338 self._raise_win_err('failed to read %d bytes' % size,
464 self._raise_win_err('failed to read %d bytes' % size,
339 GetLastError())
465 GetLastError())
340
466
341 nread = wintypes.DWORD()
467 nread = wintypes.DWORD()
342 if not GetOverlappedResultEx(self.pipe, olap, nread,
468 if not self._get_overlapped_result_ex(self.pipe, olap, nread,
343 0 if immediate else self.timeout, True):
469 0 if immediate else self.timeout,
470 True):
344 err = GetLastError()
471 err = GetLastError()
345 CancelIoEx(self.pipe, olap)
472 CancelIoEx(self.pipe, olap)
346
473
347 if err == WAIT_TIMEOUT:
474 if err == WAIT_TIMEOUT:
348 log('GetOverlappedResultEx timedout')
475 log('GetOverlappedResultEx timedout')
349 raise SocketTimeout('timed out after waiting %dms for read' %
476 raise SocketTimeout('timed out after waiting %dms for read' %
350 self.timeout)
477 self.timeout)
351
478
352 log('GetOverlappedResultEx reports error %d', err)
479 log('GetOverlappedResultEx reports error %d', err)
353 self._raise_win_err('error while waiting for read', err)
480 self._raise_win_err('error while waiting for read', err)
354
481
355 nread = nread.value
482 nread = nread.value
356 if nread == 0:
483 if nread == 0:
357 # Docs say that named pipes return 0 byte when the other end did
484 # Docs say that named pipes return 0 byte when the other end did
358 # a zero byte write. Since we don't ever do that, the only
485 # a zero byte write. Since we don't ever do that, the only
359 # other way this shows up is if the client has gotten in a weird
486 # other way this shows up is if the client has gotten in a weird
360 # state, so let's bail out
487 # state, so let's bail out
361 CancelIoEx(self.pipe, olap)
488 CancelIoEx(self.pipe, olap)
362 raise IOError('Async read yielded 0 bytes; unpossible!')
489 raise IOError('Async read yielded 0 bytes; unpossible!')
363
490
364 # Holds precisely the bytes that we read from the prior request
491 # Holds precisely the bytes that we read from the prior request
365 buf = buf[:nread]
492 buf = buf[:nread]
366
493
367 returned_size = min(nread, size)
494 returned_size = min(nread, size)
368 if returned_size == nread:
495 if returned_size == nread:
369 return buf
496 return buf
370
497
371 # keep any left-overs around for a later read to consume
498 # keep any left-overs around for a later read to consume
372 self._iobuf = buf[returned_size:]
499 self._iobuf = buf[returned_size:]
373 return buf[:returned_size]
500 return buf[:returned_size]
374
501
375 def write(self, data):
502 def write(self, data):
376 olap = OVERLAPPED()
503 olap = OVERLAPPED()
504 olap.hEvent = self._waitable
505
377 immediate = WriteFile(self.pipe, ctypes.c_char_p(data), len(data),
506 immediate = WriteFile(self.pipe, ctypes.c_char_p(data), len(data),
378 None, olap)
507 None, olap)
379
508
380 if not immediate:
509 if not immediate:
381 err = GetLastError()
510 err = GetLastError()
382 if err != ERROR_IO_PENDING:
511 if err != ERROR_IO_PENDING:
383 self._raise_win_err('failed to write %d bytes' % len(data),
512 self._raise_win_err('failed to write %d bytes' % len(data),
384 GetLastError())
513 GetLastError())
385
514
386 # Obtain results, waiting if needed
515 # Obtain results, waiting if needed
387 nwrote = wintypes.DWORD()
516 nwrote = wintypes.DWORD()
388 if GetOverlappedResultEx(self.pipe, olap, nwrote, 0 if immediate else
517 if self._get_overlapped_result_ex(self.pipe, olap, nwrote,
389 self.timeout, True):
518 0 if immediate else self.timeout,
519 True):
520 log('made write of %d bytes', nwrote.value)
390 return nwrote.value
521 return nwrote.value
391
522
392 err = GetLastError()
523 err = GetLastError()
393
524
394 # It's potentially unsafe to allow the write to continue after
525 # It's potentially unsafe to allow the write to continue after
395 # we unwind, so let's make a best effort to avoid that happening
526 # we unwind, so let's make a best effort to avoid that happening
396 CancelIoEx(self.pipe, olap)
527 CancelIoEx(self.pipe, olap)
397
528
398 if err == WAIT_TIMEOUT:
529 if err == WAIT_TIMEOUT:
399 raise SocketTimeout('timed out after waiting %dms for write' %
530 raise SocketTimeout('timed out after waiting %dms for write' %
400 self.timeout)
531 self.timeout)
401 self._raise_win_err('error while waiting for write of %d bytes' %
532 self._raise_win_err('error while waiting for write of %d bytes' %
402 len(data), err)
533 len(data), err)
403
534
404
535
405 class CLIProcessTransport(Transport):
536 class CLIProcessTransport(Transport):
406 """ open a pipe to the cli to talk to the service
537 """ open a pipe to the cli to talk to the service
407 This intended to be used only in the test harness!
538 This intended to be used only in the test harness!
408
539
409 The CLI is an oddball because we only support JSON input
540 The CLI is an oddball because we only support JSON input
410 and cannot send multiple commands through the same instance,
541 and cannot send multiple commands through the same instance,
411 so we spawn a new process for each command.
542 so we spawn a new process for each command.
412
543
413 We disable server spawning for this implementation, again, because
544 We disable server spawning for this implementation, again, because
414 it is intended to be used only in our test harness. You really
545 it is intended to be used only in our test harness. You really
415 should not need to use the CLI transport for anything real.
546 should not need to use the CLI transport for anything real.
416
547
417 While the CLI can output in BSER, our Transport interface doesn't
548 While the CLI can output in BSER, our Transport interface doesn't
418 support telling this instance that it should do so. That effectively
549 support telling this instance that it should do so. That effectively
419 limits this implementation to JSON input and output only at this time.
550 limits this implementation to JSON input and output only at this time.
420
551
421 It is the responsibility of the caller to set the send and
552 It is the responsibility of the caller to set the send and
422 receive codecs appropriately.
553 receive codecs appropriately.
423 """
554 """
424 proc = None
555 proc = None
425 closed = True
556 closed = True
426
557
427 def __init__(self, sockpath, timeout):
558 def __init__(self, sockpath, timeout):
428 self.sockpath = sockpath
559 self.sockpath = sockpath
429 self.timeout = timeout
560 self.timeout = timeout
430
561
431 def close(self):
562 def close(self):
432 if self.proc:
563 if self.proc:
564 if self.proc.pid is not None:
433 self.proc.kill()
565 self.proc.kill()
566 self.proc.stdin.close()
567 self.proc.stdout.close()
434 self.proc = None
568 self.proc = None
435
569
436 def _connect(self):
570 def _connect(self):
437 if self.proc:
571 if self.proc:
438 return self.proc
572 return self.proc
439 args = [
573 args = [
440 'watchman',
574 'watchman',
441 '--sockname={}'.format(self.sockpath),
575 '--sockname={0}'.format(self.sockpath),
442 '--logfile=/BOGUS',
576 '--logfile=/BOGUS',
443 '--statefile=/BOGUS',
577 '--statefile=/BOGUS',
444 '--no-spawn',
578 '--no-spawn',
445 '--no-local',
579 '--no-local',
446 '--no-pretty',
580 '--no-pretty',
447 '-j',
581 '-j',
448 ]
582 ]
449 self.proc = subprocess.Popen(args,
583 self.proc = subprocess.Popen(args,
450 stdin=subprocess.PIPE,
584 stdin=subprocess.PIPE,
451 stdout=subprocess.PIPE)
585 stdout=subprocess.PIPE)
452 return self.proc
586 return self.proc
453
587
454 def readBytes(self, size):
588 def readBytes(self, size):
455 self._connect()
589 self._connect()
456 res = self.proc.stdout.read(size)
590 res = self.proc.stdout.read(size)
457 if res == '':
591 if res == '':
458 raise WatchmanError('EOF on CLI process transport')
592 raise WatchmanError('EOF on CLI process transport')
459 return res
593 return res
460
594
461 def write(self, data):
595 def write(self, data):
462 if self.closed:
596 if self.closed:
597 self.close()
463 self.closed = False
598 self.closed = False
464 self.proc = None
465 self._connect()
599 self._connect()
466 res = self.proc.stdin.write(data)
600 res = self.proc.stdin.write(data)
467 self.proc.stdin.close()
601 self.proc.stdin.close()
468 self.closed = True
602 self.closed = True
469 return res
603 return res
470
604
471
605
472 class BserCodec(Codec):
606 class BserCodec(Codec):
473 """ use the BSER encoding. This is the default, preferred codec """
607 """ use the BSER encoding. This is the default, preferred codec """
474
608
475 def _loads(self, response):
609 def _loads(self, response):
476 return bser.loads(response)
610 return bser.loads(response) # Defaults to BSER v1
477
611
478 def receive(self):
612 def receive(self):
479 buf = [self.transport.readBytes(sniff_len)]
613 buf = [self.transport.readBytes(sniff_len)]
480 if not buf[0]:
614 if not buf[0]:
481 raise WatchmanError('empty watchman response')
615 raise WatchmanError('empty watchman response')
482
616
483 elen = bser.pdu_len(buf[0])
617 _1, _2, elen = bser.pdu_info(buf[0])
484
618
485 rlen = len(buf[0])
619 rlen = len(buf[0])
486 while elen > rlen:
620 while elen > rlen:
487 buf.append(self.transport.readBytes(elen - rlen))
621 buf.append(self.transport.readBytes(elen - rlen))
488 rlen += len(buf[-1])
622 rlen += len(buf[-1])
489
623
490 response = ''.join(buf)
624 response = b''.join(buf)
491 try:
625 try:
492 res = self._loads(response)
626 res = self._loads(response)
493 return res
627 return res
494 except ValueError as e:
628 except ValueError as e:
495 raise WatchmanError('watchman response decode error: %s' % e)
629 raise WatchmanError('watchman response decode error: %s' % e)
496
630
497 def send(self, *args):
631 def send(self, *args):
498 cmd = bser.dumps(*args)
632 cmd = bser.dumps(*args) # Defaults to BSER v1
499 self.transport.write(cmd)
633 self.transport.write(cmd)
500
634
501
635
502 class ImmutableBserCodec(BserCodec):
636 class ImmutableBserCodec(BserCodec):
503 """ use the BSER encoding, decoding values using the newer
637 """ use the BSER encoding, decoding values using the newer
504 immutable object support """
638 immutable object support """
505
639
506 def _loads(self, response):
640 def _loads(self, response):
507 return bser.loads(response, False)
641 return bser.loads(response, False) # Defaults to BSER v1
642
643
644 class Bser2WithFallbackCodec(BserCodec):
645 """ use BSER v2 encoding """
646
647 def __init__(self, transport):
648 super(Bser2WithFallbackCodec, self).__init__(transport)
649 # Once the server advertises support for bser-v2 we should switch this
650 # to 'required' on Python 3.
651 self.send(["version", {"optional": ["bser-v2"]}])
652
653 capabilities = self.receive()
654
655 if 'error' in capabilities:
656 raise Exception('Unsupported BSER version')
657
658 if capabilities['capabilities']['bser-v2']:
659 self.bser_version = 2
660 self.bser_capabilities = 0
661 else:
662 self.bser_version = 1
663 self.bser_capabilities = 0
664
665 def _loads(self, response):
666 return bser.loads(response)
667
668 def receive(self):
669 buf = [self.transport.readBytes(sniff_len)]
670 if not buf[0]:
671 raise WatchmanError('empty watchman response')
672
673 recv_bser_version, recv_bser_capabilities, elen = bser.pdu_info(buf[0])
674
675 if hasattr(self, 'bser_version'):
676 # Readjust BSER version and capabilities if necessary
677 self.bser_version = max(self.bser_version, recv_bser_version)
678 self.capabilities = self.bser_capabilities & recv_bser_capabilities
679
680 rlen = len(buf[0])
681 while elen > rlen:
682 buf.append(self.transport.readBytes(elen - rlen))
683 rlen += len(buf[-1])
684
685 response = b''.join(buf)
686 try:
687 res = self._loads(response)
688 return res
689 except ValueError as e:
690 raise WatchmanError('watchman response decode error: %s' % e)
691
692 def send(self, *args):
693 if hasattr(self, 'bser_version'):
694 cmd = bser.dumps(*args, version=self.bser_version,
695 capabilities=self.bser_capabilities)
696 else:
697 cmd = bser.dumps(*args)
698 self.transport.write(cmd)
508
699
509
700
510 class JsonCodec(Codec):
701 class JsonCodec(Codec):
511 """ Use json codec. This is here primarily for testing purposes """
702 """ Use json codec. This is here primarily for testing purposes """
512 json = None
703 json = None
513
704
514 def __init__(self, transport):
705 def __init__(self, transport):
515 super(JsonCodec, self).__init__(transport)
706 super(JsonCodec, self).__init__(transport)
516 # optional dep on json, only if JsonCodec is used
707 # optional dep on json, only if JsonCodec is used
517 import json
708 import json
518 self.json = json
709 self.json = json
519
710
520 def receive(self):
711 def receive(self):
521 line = self.transport.readLine()
712 line = self.transport.readLine()
522 try:
713 try:
714 # In Python 3, json.loads is a transformation from Unicode string to
715 # objects possibly containing Unicode strings. We typically expect
716 # the JSON blob to be ASCII-only with non-ASCII characters escaped,
717 # but it's possible we might get non-ASCII bytes that are valid
718 # UTF-8.
719 if compat.PYTHON3:
720 line = line.decode('utf-8')
523 return self.json.loads(line)
721 return self.json.loads(line)
524 except Exception as e:
722 except Exception as e:
525 print(e, line)
723 print(e, line)
526 raise
724 raise
527
725
528 def send(self, *args):
726 def send(self, *args):
529 cmd = self.json.dumps(*args)
727 cmd = self.json.dumps(*args)
530 self.transport.write(cmd + "\n")
728 # In Python 3, json.dumps is a transformation from objects possibly
729 # containing Unicode strings to Unicode string. Even with (the default)
730 # ensure_ascii=True, dumps returns a Unicode string.
731 if compat.PYTHON3:
732 cmd = cmd.encode('ascii')
733 self.transport.write(cmd + b"\n")
531
734
532
735
533 class client(object):
736 class client(object):
534 """ Handles the communication with the watchman service """
737 """ Handles the communication with the watchman service """
535 sockpath = None
738 sockpath = None
536 transport = None
739 transport = None
537 sendCodec = None
740 sendCodec = None
538 recvCodec = None
741 recvCodec = None
539 sendConn = None
742 sendConn = None
540 recvConn = None
743 recvConn = None
541 subs = {} # Keyed by subscription name
744 subs = {} # Keyed by subscription name
542 sub_by_root = {} # Keyed by root, then by subscription name
745 sub_by_root = {} # Keyed by root, then by subscription name
543 logs = [] # When log level is raised
746 logs = [] # When log level is raised
544 unilateral = ['log', 'subscription']
747 unilateral = ['log', 'subscription']
545 tport = None
748 tport = None
546 useImmutableBser = None
749 useImmutableBser = None
547
750
548 def __init__(self,
751 def __init__(self,
549 sockpath=None,
752 sockpath=None,
550 timeout=1.0,
753 timeout=1.0,
551 transport=None,
754 transport=None,
552 sendEncoding=None,
755 sendEncoding=None,
553 recvEncoding=None,
756 recvEncoding=None,
554 useImmutableBser=False):
757 useImmutableBser=False):
555 self.sockpath = sockpath
758 self.sockpath = sockpath
556 self.timeout = timeout
759 self.timeout = timeout
557 self.useImmutableBser = useImmutableBser
760 self.useImmutableBser = useImmutableBser
558
761
762 if inspect.isclass(transport) and issubclass(transport, Transport):
763 self.transport = transport
764 else:
559 transport = transport or os.getenv('WATCHMAN_TRANSPORT') or 'local'
765 transport = transport or os.getenv('WATCHMAN_TRANSPORT') or 'local'
560 if transport == 'local' and os.name == 'nt':
766 if transport == 'local' and os.name == 'nt':
561 self.transport = WindowsNamedPipeTransport
767 self.transport = WindowsNamedPipeTransport
562 elif transport == 'local':
768 elif transport == 'local':
563 self.transport = UnixSocketTransport
769 self.transport = UnixSocketTransport
564 elif transport == 'cli':
770 elif transport == 'cli':
565 self.transport = CLIProcessTransport
771 self.transport = CLIProcessTransport
566 if sendEncoding is None:
772 if sendEncoding is None:
567 sendEncoding = 'json'
773 sendEncoding = 'json'
568 if recvEncoding is None:
774 if recvEncoding is None:
569 recvEncoding = sendEncoding
775 recvEncoding = sendEncoding
570 else:
776 else:
571 raise WatchmanError('invalid transport %s' % transport)
777 raise WatchmanError('invalid transport %s' % transport)
572
778
573 sendEncoding = sendEncoding or os.getenv('WATCHMAN_ENCODING') or 'bser'
779 sendEncoding = str(sendEncoding or os.getenv('WATCHMAN_ENCODING') or
574 recvEncoding = recvEncoding or os.getenv('WATCHMAN_ENCODING') or 'bser'
780 'bser')
781 recvEncoding = str(recvEncoding or os.getenv('WATCHMAN_ENCODING') or
782 'bser')
575
783
576 self.recvCodec = self._parseEncoding(recvEncoding)
784 self.recvCodec = self._parseEncoding(recvEncoding)
577 self.sendCodec = self._parseEncoding(sendEncoding)
785 self.sendCodec = self._parseEncoding(sendEncoding)
578
786
579 def _parseEncoding(self, enc):
787 def _parseEncoding(self, enc):
580 if enc == 'bser':
788 if enc == 'bser':
581 if self.useImmutableBser:
789 if self.useImmutableBser:
582 return ImmutableBserCodec
790 return ImmutableBserCodec
583 return BserCodec
791 return BserCodec
792 elif enc == 'experimental-bser-v2':
793 return Bser2WithFallbackCodec
584 elif enc == 'json':
794 elif enc == 'json':
585 return JsonCodec
795 return JsonCodec
586 else:
796 else:
587 raise WatchmanError('invalid encoding %s' % enc)
797 raise WatchmanError('invalid encoding %s' % enc)
588
798
589 def _hasprop(self, result, name):
799 def _hasprop(self, result, name):
590 if self.useImmutableBser:
800 if self.useImmutableBser:
591 return hasattr(result, name)
801 return hasattr(result, name)
592 return name in result
802 return name in result
593
803
594 def _resolvesockname(self):
804 def _resolvesockname(self):
595 # if invoked via a trigger, watchman will set this env var; we
805 # if invoked via a trigger, watchman will set this env var; we
596 # should use it unless explicitly set otherwise
806 # should use it unless explicitly set otherwise
597 path = os.getenv('WATCHMAN_SOCK')
807 path = os.getenv('WATCHMAN_SOCK')
598 if path:
808 if path:
599 return path
809 return path
600
810
601 cmd = ['watchman', '--output-encoding=bser', 'get-sockname']
811 cmd = ['watchman', '--output-encoding=bser', 'get-sockname']
602 try:
812 try:
603 p = subprocess.Popen(cmd,
813 args = dict(stdout=subprocess.PIPE,
604 stdout=subprocess.PIPE,
605 stderr=subprocess.PIPE,
814 stderr=subprocess.PIPE,
606 close_fds=os.name != 'nt')
815 close_fds=os.name != 'nt')
816
817 if os.name == 'nt':
818 # if invoked via an application with graphical user interface,
819 # this call will cause a brief command window pop-up.
820 # Using the flag STARTF_USESHOWWINDOW to avoid this behavior.
821 startupinfo = subprocess.STARTUPINFO()
822 startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
823 args['startupinfo'] = startupinfo
824
825 p = subprocess.Popen(cmd, **args)
826
607 except OSError as e:
827 except OSError as e:
608 raise WatchmanError('"watchman" executable not in PATH (%s)', e)
828 raise WatchmanError('"watchman" executable not in PATH (%s)', e)
609
829
610 stdout, stderr = p.communicate()
830 stdout, stderr = p.communicate()
611 exitcode = p.poll()
831 exitcode = p.poll()
612
832
613 if exitcode:
833 if exitcode:
614 raise WatchmanError("watchman exited with code %d" % exitcode)
834 raise WatchmanError("watchman exited with code %d" % exitcode)
615
835
616 result = bser.loads(stdout)
836 result = bser.loads(stdout)
617 if 'error' in result:
837 if b'error' in result:
618 raise WatchmanError('get-sockname error: %s' % result['error'])
838 raise WatchmanError('get-sockname error: %s' % result['error'])
619
839
620 return result['sockname']
840 return result[b'sockname']
621
841
622 def _connect(self):
842 def _connect(self):
623 """ establish transport connection """
843 """ establish transport connection """
624
844
625 if self.recvConn:
845 if self.recvConn:
626 return
846 return
627
847
628 if self.sockpath is None:
848 if self.sockpath is None:
629 self.sockpath = self._resolvesockname()
849 self.sockpath = self._resolvesockname()
630
850
631 self.tport = self.transport(self.sockpath, self.timeout)
851 self.tport = self.transport(self.sockpath, self.timeout)
632 self.sendConn = self.sendCodec(self.tport)
852 self.sendConn = self.sendCodec(self.tport)
633 self.recvConn = self.recvCodec(self.tport)
853 self.recvConn = self.recvCodec(self.tport)
634
854
635 def __del__(self):
855 def __del__(self):
636 self.close()
856 self.close()
637
857
638 def close(self):
858 def close(self):
639 if self.tport:
859 if self.tport:
640 self.tport.close()
860 self.tport.close()
641 self.tport = None
861 self.tport = None
642 self.recvConn = None
862 self.recvConn = None
643 self.sendConn = None
863 self.sendConn = None
644
864
645 def receive(self):
865 def receive(self):
646 """ receive the next PDU from the watchman service
866 """ receive the next PDU from the watchman service
647
867
648 If the client has activated subscriptions or logs then
868 If the client has activated subscriptions or logs then
649 this PDU may be a unilateral PDU sent by the service to
869 this PDU may be a unilateral PDU sent by the service to
650 inform the client of a log event or subscription change.
870 inform the client of a log event or subscription change.
651
871
652 It may also simply be the response portion of a request
872 It may also simply be the response portion of a request
653 initiated by query.
873 initiated by query.
654
874
655 There are clients in production that subscribe and call
875 There are clients in production that subscribe and call
656 this in a loop to retrieve all subscription responses,
876 this in a loop to retrieve all subscription responses,
657 so care should be taken when making changes here.
877 so care should be taken when making changes here.
658 """
878 """
659
879
660 self._connect()
880 self._connect()
661 result = self.recvConn.receive()
881 result = self.recvConn.receive()
662 if self._hasprop(result, 'error'):
882 if self._hasprop(result, 'error'):
663 raise CommandError(result['error'])
883 error = result['error']
884 if compat.PYTHON3 and isinstance(self.recvConn, BserCodec):
885 error = result['error'].decode('utf-8', 'surrogateescape')
886 raise CommandError(error)
664
887
665 if self._hasprop(result, 'log'):
888 if self._hasprop(result, 'log'):
666 self.logs.append(result['log'])
889 log = result['log']
890 if compat.PYTHON3 and isinstance(self.recvConn, BserCodec):
891 log = log.decode('utf-8', 'surrogateescape')
892 self.logs.append(log)
667
893
668 if self._hasprop(result, 'subscription'):
894 if self._hasprop(result, 'subscription'):
669 sub = result['subscription']
895 sub = result['subscription']
670 if not (sub in self.subs):
896 if not (sub in self.subs):
671 self.subs[sub] = []
897 self.subs[sub] = []
672 self.subs[sub].append(result)
898 self.subs[sub].append(result)
673
899
674 # also accumulate in {root,sub} keyed store
900 # also accumulate in {root,sub} keyed store
675 root = os.path.normcase(result['root'])
901 root = os.path.normcase(result['root'])
676 if not root in self.sub_by_root:
902 if not root in self.sub_by_root:
677 self.sub_by_root[root] = {}
903 self.sub_by_root[root] = {}
678 if not sub in self.sub_by_root[root]:
904 if not sub in self.sub_by_root[root]:
679 self.sub_by_root[root][sub] = []
905 self.sub_by_root[root][sub] = []
680 self.sub_by_root[root][sub].append(result)
906 self.sub_by_root[root][sub].append(result)
681
907
682 return result
908 return result
683
909
684 def isUnilateralResponse(self, res):
910 def isUnilateralResponse(self, res):
911 if 'unilateral' in res and res['unilateral']:
912 return True
913 # Fall back to checking for known unilateral responses
685 for k in self.unilateral:
914 for k in self.unilateral:
686 if k in res:
915 if k in res:
687 return True
916 return True
688 return False
917 return False
689
918
690 def getLog(self, remove=True):
919 def getLog(self, remove=True):
691 """ Retrieve buffered log data
920 """ Retrieve buffered log data
692
921
693 If remove is true the data will be removed from the buffer.
922 If remove is true the data will be removed from the buffer.
694 Otherwise it will be left in the buffer
923 Otherwise it will be left in the buffer
695 """
924 """
696 res = self.logs
925 res = self.logs
697 if remove:
926 if remove:
698 self.logs = []
927 self.logs = []
699 return res
928 return res
700
929
701 def getSubscription(self, name, remove=True, root=None):
930 def getSubscription(self, name, remove=True, root=None):
702 """ Retrieve the data associated with a named subscription
931 """ Retrieve the data associated with a named subscription
703
932
704 If remove is True (the default), the subscription data is removed
933 If remove is True (the default), the subscription data is removed
705 from the buffer. Otherwise the data is returned but left in
934 from the buffer. Otherwise the data is returned but left in
706 the buffer.
935 the buffer.
707
936
708 Returns None if there is no data associated with `name`
937 Returns None if there is no data associated with `name`
709
938
710 If root is not None, then only return the subscription
939 If root is not None, then only return the subscription
711 data that matches both root and name. When used in this way,
940 data that matches both root and name. When used in this way,
712 remove processing impacts both the unscoped and scoped stores
941 remove processing impacts both the unscoped and scoped stores
713 for the subscription data.
942 for the subscription data.
714 """
943 """
944 if compat.PYTHON3 and issubclass(self.recvCodec, BserCodec):
945 # People may pass in Unicode strings here -- but currently BSER only
946 # returns bytestrings. Deal with that.
947 if isinstance(root, str):
948 root = encoding.encode_local(root)
949 if isinstance(name, str):
950 name = name.encode('utf-8')
715
951
716 if root is not None:
952 if root is not None:
717 if not root in self.sub_by_root:
953 if not root in self.sub_by_root:
718 return None
954 return None
719 if not name in self.sub_by_root[root]:
955 if not name in self.sub_by_root[root]:
720 return None
956 return None
721 sub = self.sub_by_root[root][name]
957 sub = self.sub_by_root[root][name]
722 if remove:
958 if remove:
723 del self.sub_by_root[root][name]
959 del self.sub_by_root[root][name]
724 # don't let this grow unbounded
960 # don't let this grow unbounded
725 if name in self.subs:
961 if name in self.subs:
726 del self.subs[name]
962 del self.subs[name]
727 return sub
963 return sub
728
964
729 if not (name in self.subs):
965 if not (name in self.subs):
730 return None
966 return None
731 sub = self.subs[name]
967 sub = self.subs[name]
732 if remove:
968 if remove:
733 del self.subs[name]
969 del self.subs[name]
734 return sub
970 return sub
735
971
736 def query(self, *args):
972 def query(self, *args):
737 """ Send a query to the watchman service and return the response
973 """ Send a query to the watchman service and return the response
738
974
739 This call will block until the response is returned.
975 This call will block until the response is returned.
740 If any unilateral responses are sent by the service in between
976 If any unilateral responses are sent by the service in between
741 the request-response they will be buffered up in the client object
977 the request-response they will be buffered up in the client object
742 and NOT returned via this method.
978 and NOT returned via this method.
743 """
979 """
744
980
745 log('calling client.query')
981 log('calling client.query')
746 self._connect()
982 self._connect()
747 try:
983 try:
748 self.sendConn.send(args)
984 self.sendConn.send(args)
749
985
750 res = self.receive()
986 res = self.receive()
751 while self.isUnilateralResponse(res):
987 while self.isUnilateralResponse(res):
752 res = self.receive()
988 res = self.receive()
753
989
754 return res
990 return res
755 except CommandError as ex:
991 except EnvironmentError as ee:
992 # When we can depend on Python 3, we can use PEP 3134
993 # exception chaining here.
994 raise WatchmanEnvironmentError(
995 'I/O error communicating with watchman daemon',
996 ee.errno,
997 ee.strerror,
998 args)
999 except WatchmanError as ex:
756 ex.setCommand(args)
1000 ex.setCommand(args)
757 raise ex
1001 raise
758
1002
759 def capabilityCheck(self, optional=None, required=None):
1003 def capabilityCheck(self, optional=None, required=None):
760 """ Perform a server capability check """
1004 """ Perform a server capability check """
761 res = self.query('version', {
1005 res = self.query('version', {
762 'optional': optional or [],
1006 'optional': optional or [],
763 'required': required or []
1007 'required': required or []
764 })
1008 })
765
1009
766 if not self._hasprop(res, 'capabilities'):
1010 if not self._hasprop(res, 'capabilities'):
767 # Server doesn't support capabilities, so we need to
1011 # Server doesn't support capabilities, so we need to
768 # synthesize the results based on the version
1012 # synthesize the results based on the version
769 capabilities.synthesize(res, optional)
1013 capabilities.synthesize(res, optional)
770 if 'error' in res:
1014 if 'error' in res:
771 raise CommandError(res['error'])
1015 raise CommandError(res['error'])
772
1016
773 return res
1017 return res
774
1018
775 def setTimeout(self, value):
1019 def setTimeout(self, value):
776 self.recvConn.setTimeout(value)
1020 self.recvConn.setTimeout(value)
777 self.sendConn.setTimeout(value)
1021 self.sendConn.setTimeout(value)
778
779 # no-check-code -- this is a 3rd party library
This diff has been collapsed as it changes many lines, (524 lines changed) Show them Hide them
@@ -1,950 +1,1212 b''
1 /*
1 /*
2 Copyright (c) 2013-2015, Facebook, Inc.
2 Copyright (c) 2013-2015, Facebook, Inc.
3 All rights reserved.
3 All rights reserved.
4
4
5 Redistribution and use in source and binary forms, with or without
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
6 modification, are permitted provided that the following conditions are met:
7
7
8 * Redistributions of source code must retain the above copyright notice,
8 * Redistributions of source code must retain the above copyright notice,
9 this list of conditions and the following disclaimer.
9 this list of conditions and the following disclaimer.
10
10
11 * Redistributions in binary form must reproduce the above copyright notice,
11 * Redistributions in binary form must reproduce the above copyright notice,
12 this list of conditions and the following disclaimer in the documentation
12 this list of conditions and the following disclaimer in the documentation
13 and/or other materials provided with the distribution.
13 and/or other materials provided with the distribution.
14
14
15 * Neither the name Facebook nor the names of its contributors may be used to
15 * Neither the name Facebook nor the names of its contributors may be used to
16 endorse or promote products derived from this software without specific
16 endorse or promote products derived from this software without specific
17 prior written permission.
17 prior written permission.
18
18
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
29 */
30
30
31 #include <Python.h>
31 #include <Python.h>
32 #include <bytesobject.h>
32 #ifdef _MSC_VER
33 #ifdef _MSC_VER
33 #define inline __inline
34 #define inline __inline
34 #include "msc_stdint.h"
35 #if _MSC_VER >= 1800
36 #include <stdint.h>
37 #else
38 // The compiler associated with Python 2.7 on Windows doesn't ship
39 // with stdint.h, so define the small subset that we use here.
40 typedef __int8 int8_t;
41 typedef __int16 int16_t;
42 typedef __int32 int32_t;
43 typedef __int64 int64_t;
44 typedef unsigned __int8 uint8_t;
45 typedef unsigned __int16 uint16_t;
46 typedef unsigned __int32 uint32_t;
47 typedef unsigned __int64 uint64_t;
48 #define UINT32_MAX 4294967295U
49 #endif
35 #endif
50 #endif
36
51
52 // clang-format off
37 /* Return the smallest size int that can store the value */
53 /* Return the smallest size int that can store the value */
38 #define INT_SIZE(x) (((x) == ((int8_t)x)) ? 1 : \
54 #define INT_SIZE(x) (((x) == ((int8_t)x)) ? 1 : \
39 ((x) == ((int16_t)x)) ? 2 : \
55 ((x) == ((int16_t)x)) ? 2 : \
40 ((x) == ((int32_t)x)) ? 4 : 8)
56 ((x) == ((int32_t)x)) ? 4 : 8)
41
57
42 #define BSER_ARRAY 0x00
58 #define BSER_ARRAY 0x00
43 #define BSER_OBJECT 0x01
59 #define BSER_OBJECT 0x01
44 #define BSER_STRING 0x02
60 #define BSER_BYTESTRING 0x02
45 #define BSER_INT8 0x03
61 #define BSER_INT8 0x03
46 #define BSER_INT16 0x04
62 #define BSER_INT16 0x04
47 #define BSER_INT32 0x05
63 #define BSER_INT32 0x05
48 #define BSER_INT64 0x06
64 #define BSER_INT64 0x06
49 #define BSER_REAL 0x07
65 #define BSER_REAL 0x07
50 #define BSER_TRUE 0x08
66 #define BSER_TRUE 0x08
51 #define BSER_FALSE 0x09
67 #define BSER_FALSE 0x09
52 #define BSER_NULL 0x0a
68 #define BSER_NULL 0x0a
53 #define BSER_TEMPLATE 0x0b
69 #define BSER_TEMPLATE 0x0b
54 #define BSER_SKIP 0x0c
70 #define BSER_SKIP 0x0c
71 #define BSER_UTF8STRING 0x0d
72 // clang-format on
55
73
56 // An immutable object representation of BSER_OBJECT.
74 // An immutable object representation of BSER_OBJECT.
57 // Rather than build a hash table, key -> value are obtained
75 // Rather than build a hash table, key -> value are obtained
58 // by walking the list of keys to determine the offset into
76 // by walking the list of keys to determine the offset into
59 // the values array. The assumption is that the number of
77 // the values array. The assumption is that the number of
60 // array elements will be typically small (~6 for the top
78 // array elements will be typically small (~6 for the top
61 // level query result and typically 3-5 for the file entries)
79 // level query result and typically 3-5 for the file entries)
62 // so that the time overhead for this is small compared to
80 // so that the time overhead for this is small compared to
63 // using a proper hash table. Even with this simplistic
81 // using a proper hash table. Even with this simplistic
64 // approach, this is still faster for the mercurial use case
82 // approach, this is still faster for the mercurial use case
65 // as it helps to eliminate creating N other objects to
83 // as it helps to eliminate creating N other objects to
66 // represent the stat information in the hgwatchman extension
84 // represent the stat information in the hgwatchman extension
85 // clang-format off
67 typedef struct {
86 typedef struct {
68 PyObject_HEAD
87 PyObject_HEAD
69 PyObject *keys; // tuple of field names
88 PyObject *keys; // tuple of field names
70 PyObject *values; // tuple of values
89 PyObject *values; // tuple of values
71 } bserObject;
90 } bserObject;
91 // clang-format on
72
92
73 static Py_ssize_t bserobj_tuple_length(PyObject *o) {
93 static Py_ssize_t bserobj_tuple_length(PyObject* o) {
74 bserObject *obj = (bserObject*)o;
94 bserObject* obj = (bserObject*)o;
75
95
76 return PySequence_Length(obj->keys);
96 return PySequence_Length(obj->keys);
77 }
97 }
78
98
79 static PyObject *bserobj_tuple_item(PyObject *o, Py_ssize_t i) {
99 static PyObject* bserobj_tuple_item(PyObject* o, Py_ssize_t i) {
80 bserObject *obj = (bserObject*)o;
100 bserObject* obj = (bserObject*)o;
81
101
82 return PySequence_GetItem(obj->values, i);
102 return PySequence_GetItem(obj->values, i);
83 }
103 }
84
104
105 // clang-format off
85 static PySequenceMethods bserobj_sq = {
106 static PySequenceMethods bserobj_sq = {
86 bserobj_tuple_length, /* sq_length */
107 bserobj_tuple_length, /* sq_length */
87 0, /* sq_concat */
108 0, /* sq_concat */
88 0, /* sq_repeat */
109 0, /* sq_repeat */
89 bserobj_tuple_item, /* sq_item */
110 bserobj_tuple_item, /* sq_item */
90 0, /* sq_ass_item */
111 0, /* sq_ass_item */
91 0, /* sq_contains */
112 0, /* sq_contains */
92 0, /* sq_inplace_concat */
113 0, /* sq_inplace_concat */
93 0 /* sq_inplace_repeat */
114 0 /* sq_inplace_repeat */
94 };
115 };
116 // clang-format on
95
117
96 static void bserobj_dealloc(PyObject *o) {
118 static void bserobj_dealloc(PyObject* o) {
97 bserObject *obj = (bserObject*)o;
119 bserObject* obj = (bserObject*)o;
98
120
99 Py_CLEAR(obj->keys);
121 Py_CLEAR(obj->keys);
100 Py_CLEAR(obj->values);
122 Py_CLEAR(obj->values);
101 PyObject_Del(o);
123 PyObject_Del(o);
102 }
124 }
103
125
104 static PyObject *bserobj_getattrro(PyObject *o, PyObject *name) {
126 static PyObject* bserobj_getattrro(PyObject* o, PyObject* name) {
105 bserObject *obj = (bserObject*)o;
127 bserObject* obj = (bserObject*)o;
106 Py_ssize_t i, n;
128 Py_ssize_t i, n;
129 PyObject* name_bytes = NULL;
130 PyObject* ret = NULL;
107 const char *namestr;
131 const char* namestr;
108
132
109 if (PyIndex_Check(name)) {
133 if (PyIndex_Check(name)) {
110 i = PyNumber_AsSsize_t(name, PyExc_IndexError);
134 i = PyNumber_AsSsize_t(name, PyExc_IndexError);
111 if (i == -1 && PyErr_Occurred()) {
135 if (i == -1 && PyErr_Occurred()) {
112 return NULL;
136 goto bail;
113 }
137 }
114 return PySequence_GetItem(obj->values, i);
138 ret = PySequence_GetItem(obj->values, i);
139 goto bail;
115 }
140 }
116
141
142 // We can be passed in Unicode objects here -- we don't support anything other
143 // than UTF-8 for keys.
144 if (PyUnicode_Check(name)) {
145 name_bytes = PyUnicode_AsUTF8String(name);
146 if (name_bytes == NULL) {
147 goto bail;
148 }
149 namestr = PyBytes_AsString(name_bytes);
150 } else {
151 namestr = PyBytes_AsString(name);
152 }
153
154 if (namestr == NULL) {
155 goto bail;
156 }
117 // hack^Wfeature to allow mercurial to use "st_size" to reference "size"
157 // hack^Wfeature to allow mercurial to use "st_size" to reference "size"
118 namestr = PyString_AsString(name);
119 if (!strncmp(namestr, "st_", 3)) {
158 if (!strncmp(namestr, "st_", 3)) {
120 namestr += 3;
159 namestr += 3;
121 }
160 }
122
161
123 n = PyTuple_GET_SIZE(obj->keys);
162 n = PyTuple_GET_SIZE(obj->keys);
124 for (i = 0; i < n; i++) {
163 for (i = 0; i < n; i++) {
125 const char *item_name = NULL;
164 const char* item_name = NULL;
126 PyObject *key = PyTuple_GET_ITEM(obj->keys, i);
165 PyObject* key = PyTuple_GET_ITEM(obj->keys, i);
127
166
128 item_name = PyString_AsString(key);
167 item_name = PyBytes_AsString(key);
129 if (!strcmp(item_name, namestr)) {
168 if (!strcmp(item_name, namestr)) {
130 return PySequence_GetItem(obj->values, i);
169 ret = PySequence_GetItem(obj->values, i);
170 goto bail;
131 }
171 }
132 }
172 }
133 PyErr_Format(PyExc_AttributeError,
173
134 "bserobject has no attribute '%.400s'", namestr);
174 PyErr_Format(
135 return NULL;
175 PyExc_AttributeError, "bserobject has no attribute '%.400s'", namestr);
176 bail:
177 Py_XDECREF(name_bytes);
178 return ret;
136 }
179 }
137
180
181 // clang-format off
138 static PyMappingMethods bserobj_map = {
182 static PyMappingMethods bserobj_map = {
139 bserobj_tuple_length, /* mp_length */
183 bserobj_tuple_length, /* mp_length */
140 bserobj_getattrro, /* mp_subscript */
184 bserobj_getattrro, /* mp_subscript */
141 0 /* mp_ass_subscript */
185 0 /* mp_ass_subscript */
142 };
186 };
143
187
144 PyTypeObject bserObjectType = {
188 PyTypeObject bserObjectType = {
145 PyVarObject_HEAD_INIT(NULL, 0)
189 PyVarObject_HEAD_INIT(NULL, 0)
146 "bserobj_tuple", /* tp_name */
190 "bserobj_tuple", /* tp_name */
147 sizeof(bserObject), /* tp_basicsize */
191 sizeof(bserObject), /* tp_basicsize */
148 0, /* tp_itemsize */
192 0, /* tp_itemsize */
149 bserobj_dealloc, /* tp_dealloc */
193 bserobj_dealloc, /* tp_dealloc */
150 0, /* tp_print */
194 0, /* tp_print */
151 0, /* tp_getattr */
195 0, /* tp_getattr */
152 0, /* tp_setattr */
196 0, /* tp_setattr */
153 0, /* tp_compare */
197 0, /* tp_compare */
154 0, /* tp_repr */
198 0, /* tp_repr */
155 0, /* tp_as_number */
199 0, /* tp_as_number */
156 &bserobj_sq, /* tp_as_sequence */
200 &bserobj_sq, /* tp_as_sequence */
157 &bserobj_map, /* tp_as_mapping */
201 &bserobj_map, /* tp_as_mapping */
158 0, /* tp_hash */
202 0, /* tp_hash */
159 0, /* tp_call */
203 0, /* tp_call */
160 0, /* tp_str */
204 0, /* tp_str */
161 bserobj_getattrro, /* tp_getattro */
205 bserobj_getattrro, /* tp_getattro */
162 0, /* tp_setattro */
206 0, /* tp_setattro */
163 0, /* tp_as_buffer */
207 0, /* tp_as_buffer */
164 Py_TPFLAGS_DEFAULT, /* tp_flags */
208 Py_TPFLAGS_DEFAULT, /* tp_flags */
165 "bserobj tuple", /* tp_doc */
209 "bserobj tuple", /* tp_doc */
166 0, /* tp_traverse */
210 0, /* tp_traverse */
167 0, /* tp_clear */
211 0, /* tp_clear */
168 0, /* tp_richcompare */
212 0, /* tp_richcompare */
169 0, /* tp_weaklistoffset */
213 0, /* tp_weaklistoffset */
170 0, /* tp_iter */
214 0, /* tp_iter */
171 0, /* tp_iternext */
215 0, /* tp_iternext */
172 0, /* tp_methods */
216 0, /* tp_methods */
173 0, /* tp_members */
217 0, /* tp_members */
174 0, /* tp_getset */
218 0, /* tp_getset */
175 0, /* tp_base */
219 0, /* tp_base */
176 0, /* tp_dict */
220 0, /* tp_dict */
177 0, /* tp_descr_get */
221 0, /* tp_descr_get */
178 0, /* tp_descr_set */
222 0, /* tp_descr_set */
179 0, /* tp_dictoffset */
223 0, /* tp_dictoffset */
180 0, /* tp_init */
224 0, /* tp_init */
181 0, /* tp_alloc */
225 0, /* tp_alloc */
182 0, /* tp_new */
226 0, /* tp_new */
183 };
227 };
184
228 // clang-format on
185
229
186 static PyObject *bser_loads_recursive(const char **ptr, const char *end,
230 typedef struct loads_ctx {
187 int mutable);
231 int mutable;
232 const char* value_encoding;
233 const char* value_errors;
234 uint32_t bser_version;
235 uint32_t bser_capabilities;
236 } unser_ctx_t;
237
238 static PyObject*
239 bser_loads_recursive(const char** ptr, const char* end, const unser_ctx_t* ctx);
188
240
189 static const char bser_true = BSER_TRUE;
241 static const char bser_true = BSER_TRUE;
190 static const char bser_false = BSER_FALSE;
242 static const char bser_false = BSER_FALSE;
191 static const char bser_null = BSER_NULL;
243 static const char bser_null = BSER_NULL;
192 static const char bser_string_hdr = BSER_STRING;
244 static const char bser_bytestring_hdr = BSER_BYTESTRING;
193 static const char bser_array_hdr = BSER_ARRAY;
245 static const char bser_array_hdr = BSER_ARRAY;
194 static const char bser_object_hdr = BSER_OBJECT;
246 static const char bser_object_hdr = BSER_OBJECT;
195
247
196 static inline uint32_t next_power_2(uint32_t n)
248 static inline uint32_t next_power_2(uint32_t n) {
197 {
198 n |= (n >> 16);
249 n |= (n >> 16);
199 n |= (n >> 8);
250 n |= (n >> 8);
200 n |= (n >> 4);
251 n |= (n >> 4);
201 n |= (n >> 2);
252 n |= (n >> 2);
202 n |= (n >> 1);
253 n |= (n >> 1);
203 return n + 1;
254 return n + 1;
204 }
255 }
205
256
206 // A buffer we use for building up the serialized result
257 // A buffer we use for building up the serialized result
207 struct bser_buffer {
258 struct bser_buffer {
208 char *buf;
259 char* buf;
209 int wpos, allocd;
260 int wpos, allocd;
261 uint32_t bser_version;
262 uint32_t capabilities;
210 };
263 };
211 typedef struct bser_buffer bser_t;
264 typedef struct bser_buffer bser_t;
212
265
213 static int bser_append(bser_t *bser, const char *data, uint32_t len)
266 static int bser_append(bser_t* bser, const char* data, uint32_t len) {
214 {
215 int newlen = next_power_2(bser->wpos + len);
267 int newlen = next_power_2(bser->wpos + len);
216 if (newlen > bser->allocd) {
268 if (newlen > bser->allocd) {
217 char *nbuf = realloc(bser->buf, newlen);
269 char* nbuf = realloc(bser->buf, newlen);
218 if (!nbuf) {
270 if (!nbuf) {
219 return 0;
271 return 0;
220 }
272 }
221
273
222 bser->buf = nbuf;
274 bser->buf = nbuf;
223 bser->allocd = newlen;
275 bser->allocd = newlen;
224 }
276 }
225
277
226 memcpy(bser->buf + bser->wpos, data, len);
278 memcpy(bser->buf + bser->wpos, data, len);
227 bser->wpos += len;
279 bser->wpos += len;
228 return 1;
280 return 1;
229 }
281 }
230
282
231 static int bser_init(bser_t *bser)
283 static int bser_init(bser_t* bser, uint32_t version, uint32_t capabilities) {
232 {
233 bser->allocd = 8192;
284 bser->allocd = 8192;
234 bser->wpos = 0;
285 bser->wpos = 0;
235 bser->buf = malloc(bser->allocd);
286 bser->buf = malloc(bser->allocd);
236
287 bser->bser_version = version;
288 bser->capabilities = capabilities;
237 if (!bser->buf) {
289 if (!bser->buf) {
238 return 0;
290 return 0;
239 }
291 }
240
292
241 // Leave room for the serialization header, which includes
293 // Leave room for the serialization header, which includes
242 // our overall length. To make things simpler, we'll use an
294 // our overall length. To make things simpler, we'll use an
243 // int32 for the header
295 // int32 for the header
244 #define EMPTY_HEADER "\x00\x01\x05\x00\x00\x00\x00"
296 #define EMPTY_HEADER "\x00\x01\x05\x00\x00\x00\x00"
297
298 // Version 2 also carries an integer indicating the capabilities. The
299 // capabilities integer comes before the PDU size.
300 #define EMPTY_HEADER_V2 "\x00\x02\x00\x00\x00\x00\x05\x00\x00\x00\x00"
301 if (version == 2) {
302 bser_append(bser, EMPTY_HEADER_V2, sizeof(EMPTY_HEADER_V2) - 1);
303 } else {
245 bser_append(bser, EMPTY_HEADER, sizeof(EMPTY_HEADER)-1);
304 bser_append(bser, EMPTY_HEADER, sizeof(EMPTY_HEADER) - 1);
305 }
246
306
247 return 1;
307 return 1;
248 }
308 }
249
309
250 static void bser_dtor(bser_t *bser)
310 static void bser_dtor(bser_t* bser) {
251 {
252 free(bser->buf);
311 free(bser->buf);
253 bser->buf = NULL;
312 bser->buf = NULL;
254 }
313 }
255
314
256 static int bser_long(bser_t *bser, int64_t val)
315 static int bser_long(bser_t* bser, int64_t val) {
257 {
258 int8_t i8;
316 int8_t i8;
259 int16_t i16;
317 int16_t i16;
260 int32_t i32;
318 int32_t i32;
261 int64_t i64;
319 int64_t i64;
262 char sz;
320 char sz;
263 int size = INT_SIZE(val);
321 int size = INT_SIZE(val);
264 char *iptr;
322 char* iptr;
265
323
266 switch (size) {
324 switch (size) {
267 case 1:
325 case 1:
268 sz = BSER_INT8;
326 sz = BSER_INT8;
269 i8 = (int8_t)val;
327 i8 = (int8_t)val;
270 iptr = (char*)&i8;
328 iptr = (char*)&i8;
271 break;
329 break;
272 case 2:
330 case 2:
273 sz = BSER_INT16;
331 sz = BSER_INT16;
274 i16 = (int16_t)val;
332 i16 = (int16_t)val;
275 iptr = (char*)&i16;
333 iptr = (char*)&i16;
276 break;
334 break;
277 case 4:
335 case 4:
278 sz = BSER_INT32;
336 sz = BSER_INT32;
279 i32 = (int32_t)val;
337 i32 = (int32_t)val;
280 iptr = (char*)&i32;
338 iptr = (char*)&i32;
281 break;
339 break;
282 case 8:
340 case 8:
283 sz = BSER_INT64;
341 sz = BSER_INT64;
284 i64 = (int64_t)val;
342 i64 = (int64_t)val;
285 iptr = (char*)&i64;
343 iptr = (char*)&i64;
286 break;
344 break;
287 default:
345 default:
288 PyErr_SetString(PyExc_RuntimeError,
346 PyErr_SetString(PyExc_RuntimeError, "Cannot represent this long value!?");
289 "Cannot represent this long value!?");
290 return 0;
347 return 0;
291 }
348 }
292
349
293 if (!bser_append(bser, &sz, sizeof(sz))) {
350 if (!bser_append(bser, &sz, sizeof(sz))) {
294 return 0;
351 return 0;
295 }
352 }
296
353
297 return bser_append(bser, iptr, size);
354 return bser_append(bser, iptr, size);
298 }
355 }
299
356
300 static int bser_string(bser_t *bser, PyObject *sval)
357 static int bser_bytestring(bser_t* bser, PyObject* sval) {
301 {
302 char *buf = NULL;
358 char* buf = NULL;
303 Py_ssize_t len;
359 Py_ssize_t len;
304 int res;
360 int res;
305 PyObject *utf = NULL;
361 PyObject* utf = NULL;
306
362
307 if (PyUnicode_Check(sval)) {
363 if (PyUnicode_Check(sval)) {
308 utf = PyUnicode_AsEncodedString(sval, "utf-8", "ignore");
364 utf = PyUnicode_AsEncodedString(sval, "utf-8", "ignore");
309 sval = utf;
365 sval = utf;
310 }
366 }
311
367
312 res = PyString_AsStringAndSize(sval, &buf, &len);
368 res = PyBytes_AsStringAndSize(sval, &buf, &len);
313 if (res == -1) {
369 if (res == -1) {
314 res = 0;
370 res = 0;
315 goto out;
371 goto out;
316 }
372 }
317
373
318 if (!bser_append(bser, &bser_string_hdr, sizeof(bser_string_hdr))) {
374 if (!bser_append(bser, &bser_bytestring_hdr, sizeof(bser_bytestring_hdr))) {
319 res = 0;
375 res = 0;
320 goto out;
376 goto out;
321 }
377 }
322
378
323 if (!bser_long(bser, len)) {
379 if (!bser_long(bser, len)) {
324 res = 0;
380 res = 0;
325 goto out;
381 goto out;
326 }
382 }
327
383
328 if (len > UINT32_MAX) {
384 if (len > UINT32_MAX) {
329 PyErr_Format(PyExc_ValueError, "string too big");
385 PyErr_Format(PyExc_ValueError, "string too big");
330 res = 0;
386 res = 0;
331 goto out;
387 goto out;
332 }
388 }
333
389
334 res = bser_append(bser, buf, (uint32_t)len);
390 res = bser_append(bser, buf, (uint32_t)len);
335
391
336 out:
392 out:
337 if (utf) {
393 if (utf) {
338 Py_DECREF(utf);
394 Py_DECREF(utf);
339 }
395 }
340
396
341 return res;
397 return res;
342 }
398 }
343
399
344 static int bser_recursive(bser_t *bser, PyObject *val)
400 static int bser_recursive(bser_t* bser, PyObject* val) {
345 {
346 if (PyBool_Check(val)) {
401 if (PyBool_Check(val)) {
347 if (val == Py_True) {
402 if (val == Py_True) {
348 return bser_append(bser, &bser_true, sizeof(bser_true));
403 return bser_append(bser, &bser_true, sizeof(bser_true));
349 }
404 }
350 return bser_append(bser, &bser_false, sizeof(bser_false));
405 return bser_append(bser, &bser_false, sizeof(bser_false));
351 }
406 }
352
407
353 if (val == Py_None) {
408 if (val == Py_None) {
354 return bser_append(bser, &bser_null, sizeof(bser_null));
409 return bser_append(bser, &bser_null, sizeof(bser_null));
355 }
410 }
356
411
412 // Python 3 has one integer type.
413 #if PY_MAJOR_VERSION < 3
357 if (PyInt_Check(val)) {
414 if (PyInt_Check(val)) {
358 return bser_long(bser, PyInt_AS_LONG(val));
415 return bser_long(bser, PyInt_AS_LONG(val));
359 }
416 }
417 #endif // PY_MAJOR_VERSION < 3
360
418
361 if (PyLong_Check(val)) {
419 if (PyLong_Check(val)) {
362 return bser_long(bser, PyLong_AsLongLong(val));
420 return bser_long(bser, PyLong_AsLongLong(val));
363 }
421 }
364
422
365 if (PyString_Check(val) || PyUnicode_Check(val)) {
423 if (PyBytes_Check(val) || PyUnicode_Check(val)) {
366 return bser_string(bser, val);
424 return bser_bytestring(bser, val);
367 }
425 }
368
426
369
370 if (PyFloat_Check(val)) {
427 if (PyFloat_Check(val)) {
371 double dval = PyFloat_AS_DOUBLE(val);
428 double dval = PyFloat_AS_DOUBLE(val);
372 char sz = BSER_REAL;
429 char sz = BSER_REAL;
373
430
374 if (!bser_append(bser, &sz, sizeof(sz))) {
431 if (!bser_append(bser, &sz, sizeof(sz))) {
375 return 0;
432 return 0;
376 }
433 }
377
434
378 return bser_append(bser, (char*)&dval, sizeof(dval));
435 return bser_append(bser, (char*)&dval, sizeof(dval));
379 }
436 }
380
437
381 if (PyList_Check(val)) {
438 if (PyList_Check(val)) {
382 Py_ssize_t i, len = PyList_GET_SIZE(val);
439 Py_ssize_t i, len = PyList_GET_SIZE(val);
383
440
384 if (!bser_append(bser, &bser_array_hdr, sizeof(bser_array_hdr))) {
441 if (!bser_append(bser, &bser_array_hdr, sizeof(bser_array_hdr))) {
385 return 0;
442 return 0;
386 }
443 }
387
444
388 if (!bser_long(bser, len)) {
445 if (!bser_long(bser, len)) {
389 return 0;
446 return 0;
390 }
447 }
391
448
392 for (i = 0; i < len; i++) {
449 for (i = 0; i < len; i++) {
393 PyObject *ele = PyList_GET_ITEM(val, i);
450 PyObject* ele = PyList_GET_ITEM(val, i);
394
451
395 if (!bser_recursive(bser, ele)) {
452 if (!bser_recursive(bser, ele)) {
396 return 0;
453 return 0;
397 }
454 }
398 }
455 }
399
456
400 return 1;
457 return 1;
401 }
458 }
402
459
403 if (PyTuple_Check(val)) {
460 if (PyTuple_Check(val)) {
404 Py_ssize_t i, len = PyTuple_GET_SIZE(val);
461 Py_ssize_t i, len = PyTuple_GET_SIZE(val);
405
462
406 if (!bser_append(bser, &bser_array_hdr, sizeof(bser_array_hdr))) {
463 if (!bser_append(bser, &bser_array_hdr, sizeof(bser_array_hdr))) {
407 return 0;
464 return 0;
408 }
465 }
409
466
410 if (!bser_long(bser, len)) {
467 if (!bser_long(bser, len)) {
411 return 0;
468 return 0;
412 }
469 }
413
470
414 for (i = 0; i < len; i++) {
471 for (i = 0; i < len; i++) {
415 PyObject *ele = PyTuple_GET_ITEM(val, i);
472 PyObject* ele = PyTuple_GET_ITEM(val, i);
416
473
417 if (!bser_recursive(bser, ele)) {
474 if (!bser_recursive(bser, ele)) {
418 return 0;
475 return 0;
419 }
476 }
420 }
477 }
421
478
422 return 1;
479 return 1;
423 }
480 }
424
481
425 if (PyMapping_Check(val)) {
482 if (PyMapping_Check(val)) {
426 Py_ssize_t len = PyMapping_Length(val);
483 Py_ssize_t len = PyMapping_Length(val);
427 Py_ssize_t pos = 0;
484 Py_ssize_t pos = 0;
428 PyObject *key, *ele;
485 PyObject *key, *ele;
429
486
430 if (!bser_append(bser, &bser_object_hdr, sizeof(bser_object_hdr))) {
487 if (!bser_append(bser, &bser_object_hdr, sizeof(bser_object_hdr))) {
431 return 0;
488 return 0;
432 }
489 }
433
490
434 if (!bser_long(bser, len)) {
491 if (!bser_long(bser, len)) {
435 return 0;
492 return 0;
436 }
493 }
437
494
438 while (PyDict_Next(val, &pos, &key, &ele)) {
495 while (PyDict_Next(val, &pos, &key, &ele)) {
439 if (!bser_string(bser, key)) {
496 if (!bser_bytestring(bser, key)) {
440 return 0;
497 return 0;
441 }
498 }
442 if (!bser_recursive(bser, ele)) {
499 if (!bser_recursive(bser, ele)) {
443 return 0;
500 return 0;
444 }
501 }
445 }
502 }
446
503
447 return 1;
504 return 1;
448 }
505 }
449
506
450 PyErr_SetString(PyExc_ValueError, "Unsupported value type");
507 PyErr_SetString(PyExc_ValueError, "Unsupported value type");
451 return 0;
508 return 0;
452 }
509 }
453
510
454 static PyObject *bser_dumps(PyObject *self, PyObject *args)
511 static PyObject* bser_dumps(PyObject* self, PyObject* args, PyObject* kw) {
455 {
456 PyObject *val = NULL, *res;
512 PyObject *val = NULL, *res;
457 bser_t bser;
513 bser_t bser;
458 uint32_t len;
514 uint32_t len, bser_version = 1, bser_capabilities = 0;
515
516 static char* kw_list[] = {"val", "version", "capabilities", NULL};
459
517
460 if (!PyArg_ParseTuple(args, "O", &val)) {
518 if (!PyArg_ParseTupleAndKeywords(
519 args,
520 kw,
521 "O|ii:dumps",
522 kw_list,
523 &val,
524 &bser_version,
525 &bser_capabilities)) {
461 return NULL;
526 return NULL;
462 }
527 }
463
528
464 if (!bser_init(&bser)) {
529 if (!bser_init(&bser, bser_version, bser_capabilities)) {
465 return PyErr_NoMemory();
530 return PyErr_NoMemory();
466 }
531 }
467
532
468 if (!bser_recursive(&bser, val)) {
533 if (!bser_recursive(&bser, val)) {
469 bser_dtor(&bser);
534 bser_dtor(&bser);
470 if (errno == ENOMEM) {
535 if (errno == ENOMEM) {
471 return PyErr_NoMemory();
536 return PyErr_NoMemory();
472 }
537 }
473 // otherwise, we've already set the error to something reasonable
538 // otherwise, we've already set the error to something reasonable
474 return NULL;
539 return NULL;
475 }
540 }
476
541
477 // Now fill in the overall length
542 // Now fill in the overall length
543 if (bser_version == 1) {
478 len = bser.wpos - (sizeof(EMPTY_HEADER) - 1);
544 len = bser.wpos - (sizeof(EMPTY_HEADER) - 1);
479 memcpy(bser.buf + 3, &len, sizeof(len));
545 memcpy(bser.buf + 3, &len, sizeof(len));
546 } else {
547 len = bser.wpos - (sizeof(EMPTY_HEADER_V2) - 1);
548 // The BSER capabilities block comes before the PDU length
549 memcpy(bser.buf + 2, &bser_capabilities, sizeof(bser_capabilities));
550 memcpy(bser.buf + 7, &len, sizeof(len));
551 }
480
552
481 res = PyString_FromStringAndSize(bser.buf, bser.wpos);
553 res = PyBytes_FromStringAndSize(bser.buf, bser.wpos);
482 bser_dtor(&bser);
554 bser_dtor(&bser);
483
555
484 return res;
556 return res;
485 }
557 }
486
558
487 int bunser_int(const char **ptr, const char *end, int64_t *val)
559 int bunser_int(const char** ptr, const char* end, int64_t* val) {
488 {
489 int needed;
560 int needed;
490 const char *buf = *ptr;
561 const char* buf = *ptr;
491 int8_t i8;
562 int8_t i8;
492 int16_t i16;
563 int16_t i16;
493 int32_t i32;
564 int32_t i32;
494 int64_t i64;
565 int64_t i64;
495
566
496 switch (buf[0]) {
567 switch (buf[0]) {
497 case BSER_INT8:
568 case BSER_INT8:
498 needed = 2;
569 needed = 2;
499 break;
570 break;
500 case BSER_INT16:
571 case BSER_INT16:
501 needed = 3;
572 needed = 3;
502 break;
573 break;
503 case BSER_INT32:
574 case BSER_INT32:
504 needed = 5;
575 needed = 5;
505 break;
576 break;
506 case BSER_INT64:
577 case BSER_INT64:
507 needed = 9;
578 needed = 9;
508 break;
579 break;
509 default:
580 default:
510 PyErr_Format(PyExc_ValueError,
581 PyErr_Format(
511 "invalid bser int encoding 0x%02x", buf[0]);
582 PyExc_ValueError, "invalid bser int encoding 0x%02x", buf[0]);
512 return 0;
583 return 0;
513 }
584 }
514 if (end - buf < needed) {
585 if (end - buf < needed) {
515 PyErr_SetString(PyExc_ValueError, "input buffer to small for int encoding");
586 PyErr_SetString(PyExc_ValueError, "input buffer to small for int encoding");
516 return 0;
587 return 0;
517 }
588 }
518 *ptr = buf + needed;
589 *ptr = buf + needed;
519 switch (buf[0]) {
590 switch (buf[0]) {
520 case BSER_INT8:
591 case BSER_INT8:
521 memcpy(&i8, buf + 1, sizeof(i8));
592 memcpy(&i8, buf + 1, sizeof(i8));
522 *val = i8;
593 *val = i8;
523 return 1;
594 return 1;
524 case BSER_INT16:
595 case BSER_INT16:
525 memcpy(&i16, buf + 1, sizeof(i16));
596 memcpy(&i16, buf + 1, sizeof(i16));
526 *val = i16;
597 *val = i16;
527 return 1;
598 return 1;
528 case BSER_INT32:
599 case BSER_INT32:
529 memcpy(&i32, buf + 1, sizeof(i32));
600 memcpy(&i32, buf + 1, sizeof(i32));
530 *val = i32;
601 *val = i32;
531 return 1;
602 return 1;
532 case BSER_INT64:
603 case BSER_INT64:
533 memcpy(&i64, buf + 1, sizeof(i64));
604 memcpy(&i64, buf + 1, sizeof(i64));
534 *val = i64;
605 *val = i64;
535 return 1;
606 return 1;
536 default:
607 default:
537 return 0;
608 return 0;
538 }
609 }
539 }
610 }
540
611
541 static int bunser_string(const char **ptr, const char *end,
612 static int bunser_bytestring(
542 const char **start, int64_t *len)
613 const char** ptr,
543 {
614 const char* end,
615 const char** start,
616 int64_t* len) {
544 const char *buf = *ptr;
617 const char* buf = *ptr;
545
618
546 // skip string marker
619 // skip string marker
547 buf++;
620 buf++;
548 if (!bunser_int(&buf, end, len)) {
621 if (!bunser_int(&buf, end, len)) {
549 return 0;
622 return 0;
550 }
623 }
551
624
552 if (buf + *len > end) {
625 if (buf + *len > end) {
553 PyErr_Format(PyExc_ValueError, "invalid string length in bser data");
626 PyErr_Format(PyExc_ValueError, "invalid string length in bser data");
554 return 0;
627 return 0;
555 }
628 }
556
629
557 *ptr = buf + *len;
630 *ptr = buf + *len;
558 *start = buf;
631 *start = buf;
559 return 1;
632 return 1;
560 }
633 }
561
634
562 static PyObject *bunser_array(const char **ptr, const char *end, int mutable)
635 static PyObject*
563 {
636 bunser_array(const char** ptr, const char* end, const unser_ctx_t* ctx) {
564 const char *buf = *ptr;
637 const char* buf = *ptr;
565 int64_t nitems, i;
638 int64_t nitems, i;
639 int mutable = ctx->mutable;
566 PyObject *res;
640 PyObject* res;
567
641
568 // skip array header
642 // skip array header
569 buf++;
643 buf++;
570 if (!bunser_int(&buf, end, &nitems)) {
644 if (!bunser_int(&buf, end, &nitems)) {
571 return 0;
645 return 0;
572 }
646 }
573 *ptr = buf;
647 *ptr = buf;
574
648
575 if (nitems > LONG_MAX) {
649 if (nitems > LONG_MAX) {
576 PyErr_Format(PyExc_ValueError, "too many items for python array");
650 PyErr_Format(PyExc_ValueError, "too many items for python array");
577 return NULL;
651 return NULL;
578 }
652 }
579
653
580 if (mutable) {
654 if (mutable) {
581 res = PyList_New((Py_ssize_t)nitems);
655 res = PyList_New((Py_ssize_t)nitems);
582 } else {
656 } else {
583 res = PyTuple_New((Py_ssize_t)nitems);
657 res = PyTuple_New((Py_ssize_t)nitems);
584 }
658 }
585
659
586 for (i = 0; i < nitems; i++) {
660 for (i = 0; i < nitems; i++) {
587 PyObject *ele = bser_loads_recursive(ptr, end, mutable);
661 PyObject* ele = bser_loads_recursive(ptr, end, ctx);
588
662
589 if (!ele) {
663 if (!ele) {
590 Py_DECREF(res);
664 Py_DECREF(res);
591 return NULL;
665 return NULL;
592 }
666 }
593
667
594 if (mutable) {
668 if (mutable) {
595 PyList_SET_ITEM(res, i, ele);
669 PyList_SET_ITEM(res, i, ele);
596 } else {
670 } else {
597 PyTuple_SET_ITEM(res, i, ele);
671 PyTuple_SET_ITEM(res, i, ele);
598 }
672 }
599 // DECREF(ele) not required as SET_ITEM steals the ref
673 // DECREF(ele) not required as SET_ITEM steals the ref
600 }
674 }
601
675
602 return res;
676 return res;
603 }
677 }
604
678
605 static PyObject *bunser_object(const char **ptr, const char *end,
679 static PyObject*
606 int mutable)
680 bunser_object(const char** ptr, const char* end, const unser_ctx_t* ctx) {
607 {
608 const char *buf = *ptr;
681 const char* buf = *ptr;
609 int64_t nitems, i;
682 int64_t nitems, i;
683 int mutable = ctx->mutable;
610 PyObject *res;
684 PyObject* res;
611 bserObject *obj;
685 bserObject* obj;
612
686
613 // skip array header
687 // skip array header
614 buf++;
688 buf++;
615 if (!bunser_int(&buf, end, &nitems)) {
689 if (!bunser_int(&buf, end, &nitems)) {
616 return 0;
690 return 0;
617 }
691 }
618 *ptr = buf;
692 *ptr = buf;
619
693
620 if (mutable) {
694 if (mutable) {
621 res = PyDict_New();
695 res = PyDict_New();
622 } else {
696 } else {
623 obj = PyObject_New(bserObject, &bserObjectType);
697 obj = PyObject_New(bserObject, &bserObjectType);
624 obj->keys = PyTuple_New((Py_ssize_t)nitems);
698 obj->keys = PyTuple_New((Py_ssize_t)nitems);
625 obj->values = PyTuple_New((Py_ssize_t)nitems);
699 obj->values = PyTuple_New((Py_ssize_t)nitems);
626 res = (PyObject*)obj;
700 res = (PyObject*)obj;
627 }
701 }
628
702
629 for (i = 0; i < nitems; i++) {
703 for (i = 0; i < nitems; i++) {
630 const char *keystr;
704 const char* keystr;
631 int64_t keylen;
705 int64_t keylen;
632 PyObject *key;
706 PyObject* key;
633 PyObject *ele;
707 PyObject* ele;
634
708
635 if (!bunser_string(ptr, end, &keystr, &keylen)) {
709 if (!bunser_bytestring(ptr, end, &keystr, &keylen)) {
636 Py_DECREF(res);
710 Py_DECREF(res);
637 return NULL;
711 return NULL;
638 }
712 }
639
713
640 if (keylen > LONG_MAX) {
714 if (keylen > LONG_MAX) {
641 PyErr_Format(PyExc_ValueError, "string too big for python");
715 PyErr_Format(PyExc_ValueError, "string too big for python");
642 Py_DECREF(res);
716 Py_DECREF(res);
643 return NULL;
717 return NULL;
644 }
718 }
645
719
646 key = PyString_FromStringAndSize(keystr, (Py_ssize_t)keylen);
720 if (mutable) {
721 // This will interpret the key as UTF-8.
722 key = PyUnicode_FromStringAndSize(keystr, (Py_ssize_t)keylen);
723 } else {
724 // For immutable objects we'll manage key lookups, so we can avoid going
725 // through the Unicode APIs. This avoids a potentially expensive and
726 // definitely unnecessary conversion to UTF-16 and back for Python 2.
727 // TODO: On Python 3 the Unicode APIs are smarter: we might be able to use
728 // Unicode keys there without an appreciable performance loss.
729 key = PyBytes_FromStringAndSize(keystr, (Py_ssize_t)keylen);
730 }
731
647 if (!key) {
732 if (!key) {
648 Py_DECREF(res);
733 Py_DECREF(res);
649 return NULL;
734 return NULL;
650 }
735 }
651
736
652 ele = bser_loads_recursive(ptr, end, mutable);
737 ele = bser_loads_recursive(ptr, end, ctx);
653
738
654 if (!ele) {
739 if (!ele) {
655 Py_DECREF(key);
740 Py_DECREF(key);
656 Py_DECREF(res);
741 Py_DECREF(res);
657 return NULL;
742 return NULL;
658 }
743 }
659
744
660 if (mutable) {
745 if (mutable) {
661 PyDict_SetItem(res, key, ele);
746 PyDict_SetItem(res, key, ele);
662 Py_DECREF(key);
747 Py_DECREF(key);
663 Py_DECREF(ele);
748 Py_DECREF(ele);
664 } else {
749 } else {
665 /* PyTuple_SET_ITEM steals ele, key */
750 /* PyTuple_SET_ITEM steals ele, key */
666 PyTuple_SET_ITEM(obj->values, i, ele);
751 PyTuple_SET_ITEM(obj->values, i, ele);
667 PyTuple_SET_ITEM(obj->keys, i, key);
752 PyTuple_SET_ITEM(obj->keys, i, key);
668 }
753 }
669 }
754 }
670
755
671 return res;
756 return res;
672 }
757 }
673
758
674 static PyObject *bunser_template(const char **ptr, const char *end,
759 static PyObject*
675 int mutable)
760 bunser_template(const char** ptr, const char* end, const unser_ctx_t* ctx) {
676 {
677 const char *buf = *ptr;
761 const char* buf = *ptr;
678 int64_t nitems, i;
762 int64_t nitems, i;
763 int mutable = ctx->mutable;
679 PyObject *arrval;
764 PyObject* arrval;
680 PyObject *keys;
765 PyObject* keys;
681 Py_ssize_t numkeys, keyidx;
766 Py_ssize_t numkeys, keyidx;
767 unser_ctx_t keys_ctx = {0};
768 if (mutable) {
769 keys_ctx.mutable = 1;
770 // Decode keys as UTF-8 in this case.
771 keys_ctx.value_encoding = "utf-8";
772 keys_ctx.value_errors = "strict";
773 } else {
774 // Treat keys as bytestrings in this case -- we'll do Unicode conversions at
775 // lookup time.
776 }
682
777
683 if (buf[1] != BSER_ARRAY) {
778 if (buf[1] != BSER_ARRAY) {
684 PyErr_Format(PyExc_ValueError, "Expect ARRAY to follow TEMPLATE");
779 PyErr_Format(PyExc_ValueError, "Expect ARRAY to follow TEMPLATE");
685 return NULL;
780 return NULL;
686 }
781 }
687
782
688 // skip header
783 // skip header
689 buf++;
784 buf++;
690 *ptr = buf;
785 *ptr = buf;
691
786
692 // Load template keys
787 // Load template keys.
693 keys = bunser_array(ptr, end, mutable);
788 // For keys we don't want to do any decoding right now.
789 keys = bunser_array(ptr, end, &keys_ctx);
694 if (!keys) {
790 if (!keys) {
695 return NULL;
791 return NULL;
696 }
792 }
697
793
698 numkeys = PySequence_Length(keys);
794 numkeys = PySequence_Length(keys);
699
795
700 // Load number of array elements
796 // Load number of array elements
701 if (!bunser_int(ptr, end, &nitems)) {
797 if (!bunser_int(ptr, end, &nitems)) {
702 Py_DECREF(keys);
798 Py_DECREF(keys);
703 return 0;
799 return 0;
704 }
800 }
705
801
706 if (nitems > LONG_MAX) {
802 if (nitems > LONG_MAX) {
707 PyErr_Format(PyExc_ValueError, "Too many items for python");
803 PyErr_Format(PyExc_ValueError, "Too many items for python");
708 Py_DECREF(keys);
804 Py_DECREF(keys);
709 return NULL;
805 return NULL;
710 }
806 }
711
807
712 arrval = PyList_New((Py_ssize_t)nitems);
808 arrval = PyList_New((Py_ssize_t)nitems);
713 if (!arrval) {
809 if (!arrval) {
714 Py_DECREF(keys);
810 Py_DECREF(keys);
715 return NULL;
811 return NULL;
716 }
812 }
717
813
718 for (i = 0; i < nitems; i++) {
814 for (i = 0; i < nitems; i++) {
719 PyObject *dict = NULL;
815 PyObject* dict = NULL;
720 bserObject *obj = NULL;
816 bserObject* obj = NULL;
721
817
722 if (mutable) {
818 if (mutable) {
723 dict = PyDict_New();
819 dict = PyDict_New();
724 } else {
820 } else {
725 obj = PyObject_New(bserObject, &bserObjectType);
821 obj = PyObject_New(bserObject, &bserObjectType);
726 if (obj) {
822 if (obj) {
727 obj->keys = keys;
823 obj->keys = keys;
728 Py_INCREF(obj->keys);
824 Py_INCREF(obj->keys);
729 obj->values = PyTuple_New(numkeys);
825 obj->values = PyTuple_New(numkeys);
730 }
826 }
731 dict = (PyObject*)obj;
827 dict = (PyObject*)obj;
732 }
828 }
733 if (!dict) {
829 if (!dict) {
734 fail:
830 fail:
735 Py_DECREF(keys);
831 Py_DECREF(keys);
736 Py_DECREF(arrval);
832 Py_DECREF(arrval);
737 return NULL;
833 return NULL;
738 }
834 }
739
835
740 for (keyidx = 0; keyidx < numkeys; keyidx++) {
836 for (keyidx = 0; keyidx < numkeys; keyidx++) {
741 PyObject *key;
837 PyObject* key;
742 PyObject *ele;
838 PyObject* ele;
743
839
744 if (**ptr == BSER_SKIP) {
840 if (**ptr == BSER_SKIP) {
745 *ptr = *ptr + 1;
841 *ptr = *ptr + 1;
746 ele = Py_None;
842 ele = Py_None;
747 Py_INCREF(ele);
843 Py_INCREF(ele);
748 } else {
844 } else {
749 ele = bser_loads_recursive(ptr, end, mutable);
845 ele = bser_loads_recursive(ptr, end, ctx);
750 }
846 }
751
847
752 if (!ele) {
848 if (!ele) {
753 goto fail;
849 goto fail;
754 }
850 }
755
851
756 if (mutable) {
852 if (mutable) {
757 key = PyList_GET_ITEM(keys, keyidx);
853 key = PyList_GET_ITEM(keys, keyidx);
758 PyDict_SetItem(dict, key, ele);
854 PyDict_SetItem(dict, key, ele);
759 Py_DECREF(ele);
855 Py_DECREF(ele);
760 } else {
856 } else {
761 PyTuple_SET_ITEM(obj->values, keyidx, ele);
857 PyTuple_SET_ITEM(obj->values, keyidx, ele);
762 // DECREF(ele) not required as SET_ITEM steals the ref
858 // DECREF(ele) not required as SET_ITEM steals the ref
763 }
859 }
764 }
860 }
765
861
766 PyList_SET_ITEM(arrval, i, dict);
862 PyList_SET_ITEM(arrval, i, dict);
767 // DECREF(obj) not required as SET_ITEM steals the ref
863 // DECREF(obj) not required as SET_ITEM steals the ref
768 }
864 }
769
865
770 Py_DECREF(keys);
866 Py_DECREF(keys);
771
867
772 return arrval;
868 return arrval;
773 }
869 }
774
870
775 static PyObject *bser_loads_recursive(const char **ptr, const char *end,
871 static PyObject* bser_loads_recursive(
776 int mutable)
872 const char** ptr,
777 {
873 const char* end,
874 const unser_ctx_t* ctx) {
778 const char *buf = *ptr;
875 const char* buf = *ptr;
779
876
780 switch (buf[0]) {
877 switch (buf[0]) {
781 case BSER_INT8:
878 case BSER_INT8:
782 case BSER_INT16:
879 case BSER_INT16:
783 case BSER_INT32:
880 case BSER_INT32:
784 case BSER_INT64:
881 case BSER_INT64: {
785 {
786 int64_t ival;
882 int64_t ival;
787 if (!bunser_int(ptr, end, &ival)) {
883 if (!bunser_int(ptr, end, &ival)) {
788 return NULL;
884 return NULL;
789 }
885 }
886 // Python 3 has one integer type.
887 #if PY_MAJOR_VERSION >= 3
888 return PyLong_FromLongLong(ival);
889 #else
790 if (ival < LONG_MIN || ival > LONG_MAX) {
890 if (ival < LONG_MIN || ival > LONG_MAX) {
791 return PyLong_FromLongLong(ival);
891 return PyLong_FromLongLong(ival);
792 }
892 }
793 return PyInt_FromSsize_t(Py_SAFE_DOWNCAST(ival, int64_t, Py_ssize_t));
893 return PyInt_FromSsize_t(Py_SAFE_DOWNCAST(ival, int64_t, Py_ssize_t));
894 #endif // PY_MAJOR_VERSION >= 3
794 }
895 }
795
896
796 case BSER_REAL:
897 case BSER_REAL: {
797 {
798 double dval;
898 double dval;
799 memcpy(&dval, buf + 1, sizeof(dval));
899 memcpy(&dval, buf + 1, sizeof(dval));
800 *ptr = buf + 1 + sizeof(double);
900 *ptr = buf + 1 + sizeof(double);
801 return PyFloat_FromDouble(dval);
901 return PyFloat_FromDouble(dval);
802 }
902 }
803
903
804 case BSER_TRUE:
904 case BSER_TRUE:
805 *ptr = buf + 1;
905 *ptr = buf + 1;
806 Py_INCREF(Py_True);
906 Py_INCREF(Py_True);
807 return Py_True;
907 return Py_True;
808
908
809 case BSER_FALSE:
909 case BSER_FALSE:
810 *ptr = buf + 1;
910 *ptr = buf + 1;
811 Py_INCREF(Py_False);
911 Py_INCREF(Py_False);
812 return Py_False;
912 return Py_False;
813
913
814 case BSER_NULL:
914 case BSER_NULL:
815 *ptr = buf + 1;
915 *ptr = buf + 1;
816 Py_INCREF(Py_None);
916 Py_INCREF(Py_None);
817 return Py_None;
917 return Py_None;
818
918
819 case BSER_STRING:
919 case BSER_BYTESTRING: {
820 {
821 const char *start;
920 const char* start;
822 int64_t len;
921 int64_t len;
823
922
824 if (!bunser_string(ptr, end, &start, &len)) {
923 if (!bunser_bytestring(ptr, end, &start, &len)) {
825 return NULL;
924 return NULL;
826 }
925 }
827
926
828 if (len > LONG_MAX) {
927 if (len > LONG_MAX) {
829 PyErr_Format(PyExc_ValueError, "string too long for python");
928 PyErr_Format(PyExc_ValueError, "string too long for python");
830 return NULL;
929 return NULL;
831 }
930 }
832
931
833 return PyString_FromStringAndSize(start, (long)len);
932 if (ctx->value_encoding != NULL) {
933 return PyUnicode_Decode(
934 start, (long)len, ctx->value_encoding, ctx->value_errors);
935 } else {
936 return PyBytes_FromStringAndSize(start, (long)len);
937 }
938 }
939
940 case BSER_UTF8STRING: {
941 const char* start;
942 int64_t len;
943
944 if (!bunser_bytestring(ptr, end, &start, &len)) {
945 return NULL;
946 }
947
948 if (len > LONG_MAX) {
949 PyErr_Format(PyExc_ValueError, "string too long for python");
950 return NULL;
951 }
952
953 return PyUnicode_Decode(start, (long)len, "utf-8", "strict");
834 }
954 }
835
955
836 case BSER_ARRAY:
956 case BSER_ARRAY:
837 return bunser_array(ptr, end, mutable);
957 return bunser_array(ptr, end, ctx);
838
958
839 case BSER_OBJECT:
959 case BSER_OBJECT:
840 return bunser_object(ptr, end, mutable);
960 return bunser_object(ptr, end, ctx);
841
961
842 case BSER_TEMPLATE:
962 case BSER_TEMPLATE:
843 return bunser_template(ptr, end, mutable);
963 return bunser_template(ptr, end, ctx);
844
964
845 default:
965 default:
846 PyErr_Format(PyExc_ValueError, "unhandled bser opcode 0x%02x", buf[0]);
966 PyErr_Format(PyExc_ValueError, "unhandled bser opcode 0x%02x", buf[0]);
847 }
967 }
848
968
849 return NULL;
969 return NULL;
850 }
970 }
851
971
852 // Expected use case is to read a packet from the socket and
972 static int _pdu_info_helper(
853 // then call bser.pdu_len on the packet. It returns the total
973 const char* data,
854 // length of the entire response that the peer is sending,
974 const char* end,
855 // including the bytes already received. This allows the client
975 uint32_t* bser_version_out,
856 // to compute the data size it needs to read before it can
976 uint32_t* bser_capabilities_out,
857 // decode the data
977 int64_t* expected_len_out,
858 static PyObject *bser_pdu_len(PyObject *self, PyObject *args)
978 off_t* position_out) {
859 {
979 uint32_t bser_version;
860 const char *start = NULL;
980 uint32_t bser_capabilities = 0;
861 const char *data = NULL;
981 int64_t expected_len;
862 int datalen = 0;
863 const char *end;
864 int64_t expected_len, total_len;
865
982
866 if (!PyArg_ParseTuple(args, "s#", &start, &datalen)) {
983 const char* start;
867 return NULL;
984 start = data;
868 }
869 data = start;
870 end = data + datalen;
871
872 // Validate the header and length
985 // Validate the header and length
873 if (memcmp(data, EMPTY_HEADER, 2) != 0) {
986 if (memcmp(data, EMPTY_HEADER, 2) == 0) {
987 bser_version = 1;
988 } else if (memcmp(data, EMPTY_HEADER_V2, 2) == 0) {
989 bser_version = 2;
990 } else {
874 PyErr_SetString(PyExc_ValueError, "invalid bser header");
991 PyErr_SetString(PyExc_ValueError, "invalid bser header");
875 return NULL;
992 return 0;
876 }
993 }
877
994
878 data += 2;
995 data += 2;
879
996
997 if (bser_version == 2) {
998 // Expect an integer telling us what capabilities are supported by the
999 // remote server (currently unused).
1000 if (!memcpy(&bser_capabilities, &data, sizeof(bser_capabilities))) {
1001 return 0;
1002 }
1003 data += sizeof(bser_capabilities);
1004 }
1005
880 // Expect an integer telling us how big the rest of the data
1006 // Expect an integer telling us how big the rest of the data
881 // should be
1007 // should be
882 if (!bunser_int(&data, end, &expected_len)) {
1008 if (!bunser_int(&data, end, &expected_len)) {
883 return NULL;
1009 return 0;
1010 }
1011
1012 *bser_version_out = bser_version;
1013 *bser_capabilities_out = (uint32_t)bser_capabilities;
1014 *expected_len_out = expected_len;
1015 *position_out = (off_t)(data - start);
1016 return 1;
884 }
1017 }
885
1018
886 total_len = expected_len + (data - start);
1019 // This function parses the PDU header and provides info about the packet
887 if (total_len > LONG_MAX) {
1020 // Returns false if unsuccessful
888 return PyLong_FromLongLong(total_len);
1021 static int pdu_info_helper(
889 }
1022 PyObject* self,
890 return PyInt_FromLong((long)total_len);
1023 PyObject* args,
891 }
1024 uint32_t* bser_version_out,
892
1025 uint32_t* bser_capabilities_out,
893 static PyObject *bser_loads(PyObject *self, PyObject *args)
1026 int64_t* total_len_out) {
894 {
1027 const char* start = NULL;
895 const char *data = NULL;
1028 const char* data = NULL;
896 int datalen = 0;
1029 int datalen = 0;
897 const char *end;
1030 const char* end;
898 int64_t expected_len;
1031 int64_t expected_len;
899 int mutable = 1;
1032 off_t position;
900 PyObject *mutable_obj = NULL;
1033
1034 if (!PyArg_ParseTuple(args, "s#", &start, &datalen)) {
1035 return 0;
1036 }
1037 data = start;
1038 end = data + datalen;
901
1039
902 if (!PyArg_ParseTuple(args, "s#|O:loads", &data, &datalen, &mutable_obj)) {
1040 if (!_pdu_info_helper(
1041 data,
1042 end,
1043 bser_version_out,
1044 bser_capabilities_out,
1045 &expected_len,
1046 &position)) {
1047 return 0;
1048 }
1049 *total_len_out = (int64_t)(expected_len + position);
1050 return 1;
1051 }
1052
1053 // Expected use case is to read a packet from the socket and then call
1054 // bser.pdu_info on the packet. It returns the BSER version, BSER capabilities,
1055 // and the total length of the entire response that the peer is sending,
1056 // including the bytes already received. This allows the client to compute the
1057 // data size it needs to read before it can decode the data.
1058 static PyObject* bser_pdu_info(PyObject* self, PyObject* args) {
1059 uint32_t version, capabilities;
1060 int64_t total_len;
1061 if (!pdu_info_helper(self, args, &version, &capabilities, &total_len)) {
903 return NULL;
1062 return NULL;
904 }
1063 }
905 if (mutable_obj) {
1064 return Py_BuildValue("kkL", version, capabilities, total_len);
906 mutable = PyObject_IsTrue(mutable_obj) > 0 ? 1 : 0;
1065 }
1066
1067 static PyObject* bser_pdu_len(PyObject* self, PyObject* args) {
1068 uint32_t version, capabilities;
1069 int64_t total_len;
1070 if (!pdu_info_helper(self, args, &version, &capabilities, &total_len)) {
1071 return NULL;
1072 }
1073 return Py_BuildValue("L", total_len);
907 }
1074 }
908
1075
909 end = data + datalen;
1076 static PyObject* bser_loads(PyObject* self, PyObject* args, PyObject* kw) {
1077 const char* data = NULL;
1078 int datalen = 0;
1079 const char* start;
1080 const char* end;
1081 int64_t expected_len;
1082 off_t position;
1083 PyObject* mutable_obj = NULL;
1084 const char* value_encoding = NULL;
1085 const char* value_errors = NULL;
1086 unser_ctx_t ctx = {1, 0};
910
1087
911 // Validate the header and length
1088 static char* kw_list[] = {
912 if (memcmp(data, EMPTY_HEADER, 2) != 0) {
1089 "buf", "mutable", "value_encoding", "value_errors", NULL};
913 PyErr_SetString(PyExc_ValueError, "invalid bser header");
1090
1091 if (!PyArg_ParseTupleAndKeywords(
1092 args,
1093 kw,
1094 "s#|Ozz:loads",
1095 kw_list,
1096 &start,
1097 &datalen,
1098 &mutable_obj,
1099 &value_encoding,
1100 &value_errors)) {
914 return NULL;
1101 return NULL;
915 }
1102 }
916
1103
917 data += 2;
1104 if (mutable_obj) {
1105 ctx.mutable = PyObject_IsTrue(mutable_obj) > 0 ? 1 : 0;
1106 }
1107 ctx.value_encoding = value_encoding;
1108 if (value_encoding == NULL) {
1109 ctx.value_errors = NULL;
1110 } else if (value_errors == NULL) {
1111 ctx.value_errors = "strict";
1112 } else {
1113 ctx.value_errors = value_errors;
1114 }
1115 data = start;
1116 end = data + datalen;
918
1117
919 // Expect an integer telling us how big the rest of the data
1118 if (!_pdu_info_helper(
920 // should be
1119 data,
921 if (!bunser_int(&data, end, &expected_len)) {
1120 end,
1121 &ctx.bser_version,
1122 &ctx.bser_capabilities,
1123 &expected_len,
1124 &position)) {
922 return NULL;
1125 return NULL;
923 }
1126 }
924
1127
1128 data = start + position;
925 // Verify
1129 // Verify
926 if (expected_len + data != end) {
1130 if (expected_len + data != end) {
927 PyErr_SetString(PyExc_ValueError, "bser data len != header len");
1131 PyErr_SetString(PyExc_ValueError, "bser data len != header len");
928 return NULL;
1132 return NULL;
929 }
1133 }
930
1134
931 return bser_loads_recursive(&data, end, mutable);
1135 return bser_loads_recursive(&data, end, &ctx);
932 }
1136 }
933
1137
1138 static PyObject* bser_load(PyObject* self, PyObject* args, PyObject* kw) {
1139 PyObject *load, *string;
1140 PyObject* fp = NULL;
1141 PyObject* mutable_obj = NULL;
1142 const char* value_encoding = NULL;
1143 const char* value_errors = NULL;
1144
1145 static char* kw_list[] = {
1146 "fp", "mutable", "value_encoding", "value_errors", NULL};
1147
1148 if (!PyArg_ParseTupleAndKeywords(
1149 args,
1150 kw,
1151 "OOzz:load",
1152 kw_list,
1153 &fp,
1154 &mutable_obj,
1155 &value_encoding,
1156 &value_errors)) {
1157 return NULL;
1158 }
1159
1160 load = PyImport_ImportModule("pywatchman.load");
1161 if (load == NULL) {
1162 return NULL;
1163 }
1164 string = PyObject_CallMethod(
1165 load, "load", "OOzz", fp, mutable_obj, value_encoding, value_errors);
1166 Py_DECREF(load);
1167 return string;
1168 }
1169
1170 // clang-format off
934 static PyMethodDef bser_methods[] = {
1171 static PyMethodDef bser_methods[] = {
935 {"loads", bser_loads, METH_VARARGS, "Deserialize string."},
1172 {"loads", (PyCFunction)bser_loads, METH_VARARGS | METH_KEYWORDS,
936 {"pdu_len", bser_pdu_len, METH_VARARGS, "Extract PDU length."},
1173 "Deserialize string."},
937 {"dumps", bser_dumps, METH_VARARGS, "Serialize string."},
1174 {"load", (PyCFunction)bser_load, METH_VARARGS | METH_KEYWORDS,
1175 "Deserialize a file object"},
1176 {"pdu_info", (PyCFunction)bser_pdu_info, METH_VARARGS,
1177 "Extract PDU information."},
1178 {"pdu_len", (PyCFunction)bser_pdu_len, METH_VARARGS,
1179 "Extract total PDU length."},
1180 {"dumps", (PyCFunction)bser_dumps, METH_VARARGS | METH_KEYWORDS,
1181 "Serialize string."},
938 {NULL, NULL, 0, NULL}
1182 {NULL, NULL, 0, NULL}
939 };
1183 };
940
1184
941 PyMODINIT_FUNC initbser(void)
1185 #if PY_MAJOR_VERSION >= 3
942 {
1186 static struct PyModuleDef bser_module = {
1187 PyModuleDef_HEAD_INIT,
1188 "bser",
1189 "Efficient encoding and decoding of BSER.",
1190 -1,
1191 bser_methods
1192 };
1193 // clang-format on
1194
1195 PyMODINIT_FUNC PyInit_bser(void) {
1196 PyObject* mod;
1197
1198 mod = PyModule_Create(&bser_module);
1199 PyType_Ready(&bserObjectType);
1200
1201 return mod;
1202 }
1203 #else
1204
1205 PyMODINIT_FUNC initbser(void) {
943 (void)Py_InitModule("bser", bser_methods);
1206 (void)Py_InitModule("bser", bser_methods);
944 PyType_Ready(&bserObjectType);
1207 PyType_Ready(&bserObjectType);
945 }
1208 }
1209 #endif // PY_MAJOR_VERSION >= 3
946
1210
947 /* vim:ts=2:sw=2:et:
1211 /* vim:ts=2:sw=2:et:
948 */
1212 */
949
950 // no-check-code -- this is a 3rd party library
@@ -1,69 +1,72 b''
1 # Copyright 2015 Facebook, Inc.
1 # Copyright 2015 Facebook, Inc.
2 # All rights reserved.
2 # All rights reserved.
3 #
3 #
4 # Redistribution and use in source and binary forms, with or without
4 # Redistribution and use in source and binary forms, with or without
5 # modification, are permitted provided that the following conditions are met:
5 # modification, are permitted provided that the following conditions are met:
6 #
6 #
7 # * Redistributions of source code must retain the above copyright notice,
7 # * Redistributions of source code must retain the above copyright notice,
8 # this list of conditions and the following disclaimer.
8 # this list of conditions and the following disclaimer.
9 #
9 #
10 # * Redistributions in binary form must reproduce the above copyright notice,
10 # * Redistributions in binary form must reproduce the above copyright notice,
11 # this list of conditions and the following disclaimer in the documentation
11 # this list of conditions and the following disclaimer in the documentation
12 # and/or other materials provided with the distribution.
12 # and/or other materials provided with the distribution.
13 #
13 #
14 # * Neither the name Facebook nor the names of its contributors may be used to
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
15 # endorse or promote products derived from this software without specific
16 # prior written permission.
16 # prior written permission.
17 #
17 #
18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
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
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
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
21 # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
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,
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
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.
27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
28
29 from __future__ import absolute_import
30 from __future__ import division
31 from __future__ import print_function
32 # no unicode literals
33
29 import re
34 import re
30
35
31 def parse_version(vstr):
36 def parse_version(vstr):
32 res = 0
37 res = 0
33 for n in vstr.split('.'):
38 for n in vstr.split('.'):
34 res = res * 1000
39 res = res * 1000
35 res = res + int(n)
40 res = res + int(n)
36 return res
41 return res
37
42
38 cap_versions = {
43 cap_versions = {
39 "cmd-watch-del-all": "3.1.1",
44 "cmd-watch-del-all": "3.1.1",
40 "cmd-watch-project": "3.1",
45 "cmd-watch-project": "3.1",
41 "relative_root": "3.3",
46 "relative_root": "3.3",
42 "term-dirname": "3.1",
47 "term-dirname": "3.1",
43 "term-idirname": "3.1",
48 "term-idirname": "3.1",
44 "wildmatch": "3.7",
49 "wildmatch": "3.7",
45 }
50 }
46
51
47 def check(version, name):
52 def check(version, name):
48 if name in cap_versions:
53 if name in cap_versions:
49 return version >= parse_version(cap_versions[name])
54 return version >= parse_version(cap_versions[name])
50 return False
55 return False
51
56
52 def synthesize(vers, opts):
57 def synthesize(vers, opts):
53 """ Synthesize a capability enabled version response
58 """ Synthesize a capability enabled version response
54 This is a very limited emulation for relatively recent feature sets
59 This is a very limited emulation for relatively recent feature sets
55 """
60 """
56 parsed_version = parse_version(vers['version'])
61 parsed_version = parse_version(vers['version'])
57 vers['capabilities'] = {}
62 vers['capabilities'] = {}
58 for name in opts['optional']:
63 for name in opts['optional']:
59 vers['capabilities'][name] = check(parsed_version, name)
64 vers['capabilities'][name] = check(parsed_version, name)
60 failed = False
65 failed = False
61 for name in opts['required']:
66 for name in opts['required']:
62 have = check(parsed_version, name)
67 have = check(parsed_version, name)
63 vers['capabilities'][name] = have
68 vers['capabilities'][name] = have
64 if not have:
69 if not have:
65 vers['error'] = 'client required capability `' + name + \
70 vers['error'] = 'client required capability `' + name + \
66 '` is not supported by this server'
71 '` is not supported by this server'
67 return vers
72 return vers
68
69 # no-check-code -- this is a 3rd party library
@@ -1,359 +1,484 b''
1 # Copyright 2015 Facebook, Inc.
1 # Copyright 2015 Facebook, Inc.
2 # All rights reserved.
2 # All rights reserved.
3 #
3 #
4 # Redistribution and use in source and binary forms, with or without
4 # Redistribution and use in source and binary forms, with or without
5 # modification, are permitted provided that the following conditions are met:
5 # modification, are permitted provided that the following conditions are met:
6 #
6 #
7 # * Redistributions of source code must retain the above copyright notice,
7 # * Redistributions of source code must retain the above copyright notice,
8 # this list of conditions and the following disclaimer.
8 # this list of conditions and the following disclaimer.
9 #
9 #
10 # * Redistributions in binary form must reproduce the above copyright notice,
10 # * Redistributions in binary form must reproduce the above copyright notice,
11 # this list of conditions and the following disclaimer in the documentation
11 # this list of conditions and the following disclaimer in the documentation
12 # and/or other materials provided with the distribution.
12 # and/or other materials provided with the distribution.
13 #
13 #
14 # * Neither the name Facebook nor the names of its contributors may be used to
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
15 # endorse or promote products derived from this software without specific
16 # prior written permission.
16 # prior written permission.
17 #
17 #
18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
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
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
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
21 # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
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,
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
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.
27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
28
29 from __future__ import absolute_import
30 from __future__ import division
31 from __future__ import print_function
32 # no unicode literals
33
34 import binascii
29 import collections
35 import collections
30 import ctypes
36 import ctypes
31 import struct
37 import struct
32 import sys
38 import sys
33
39
34 BSER_ARRAY = '\x00'
40 from . import (
35 BSER_OBJECT = '\x01'
41 compat,
36 BSER_STRING = '\x02'
42 )
37 BSER_INT8 = '\x03'
43
38 BSER_INT16 = '\x04'
44 BSER_ARRAY = b'\x00'
39 BSER_INT32 = '\x05'
45 BSER_OBJECT = b'\x01'
40 BSER_INT64 = '\x06'
46 BSER_BYTESTRING = b'\x02'
41 BSER_REAL = '\x07'
47 BSER_INT8 = b'\x03'
42 BSER_TRUE = '\x08'
48 BSER_INT16 = b'\x04'
43 BSER_FALSE = '\x09'
49 BSER_INT32 = b'\x05'
44 BSER_NULL = '\x0a'
50 BSER_INT64 = b'\x06'
45 BSER_TEMPLATE = '\x0b'
51 BSER_REAL = b'\x07'
46 BSER_SKIP = '\x0c'
52 BSER_TRUE = b'\x08'
53 BSER_FALSE = b'\x09'
54 BSER_NULL = b'\x0a'
55 BSER_TEMPLATE = b'\x0b'
56 BSER_SKIP = b'\x0c'
57 BSER_UTF8STRING = b'\x0d'
58
59 if compat.PYTHON3:
60 STRING_TYPES = (str, bytes)
61 unicode = str
62 def tobytes(i):
63 return str(i).encode('ascii')
64 long = int
65 else:
66 STRING_TYPES = (unicode, str)
67 tobytes = bytes
47
68
48 # Leave room for the serialization header, which includes
69 # Leave room for the serialization header, which includes
49 # our overall length. To make things simpler, we'll use an
70 # our overall length. To make things simpler, we'll use an
50 # int32 for the header
71 # int32 for the header
51 EMPTY_HEADER = "\x00\x01\x05\x00\x00\x00\x00"
72 EMPTY_HEADER = b"\x00\x01\x05\x00\x00\x00\x00"
52
73 EMPTY_HEADER_V2 = b"\x00\x02\x00\x00\x00\x00\x05\x00\x00\x00\x00"
53 # Python 3 conditional for supporting Python 2's int/long types
54 if sys.version_info > (3,):
55 long = int
56
74
57 def _int_size(x):
75 def _int_size(x):
58 """Return the smallest size int that can store the value"""
76 """Return the smallest size int that can store the value"""
59 if -0x80 <= x <= 0x7F:
77 if -0x80 <= x <= 0x7F:
60 return 1
78 return 1
61 elif -0x8000 <= x <= 0x7FFF:
79 elif -0x8000 <= x <= 0x7FFF:
62 return 2
80 return 2
63 elif -0x80000000 <= x <= 0x7FFFFFFF:
81 elif -0x80000000 <= x <= 0x7FFFFFFF:
64 return 4
82 return 4
65 elif long(-0x8000000000000000) <= x <= long(0x7FFFFFFFFFFFFFFF):
83 elif long(-0x8000000000000000) <= x <= long(0x7FFFFFFFFFFFFFFF):
66 return 8
84 return 8
67 else:
85 else:
68 raise RuntimeError('Cannot represent value: ' + str(x))
86 raise RuntimeError('Cannot represent value: ' + str(x))
69
87
88 def _buf_pos(buf, pos):
89 ret = buf[pos]
90 # In Python 2, buf is a str array so buf[pos] is a string. In Python 3, buf
91 # is a bytes array and buf[pos] is an integer.
92 if compat.PYTHON3:
93 ret = bytes((ret,))
94 return ret
70
95
71 class _bser_buffer(object):
96 class _bser_buffer(object):
72
97
73 def __init__(self):
98 def __init__(self, version):
99 self.bser_version = version
74 self.buf = ctypes.create_string_buffer(8192)
100 self.buf = ctypes.create_string_buffer(8192)
75 struct.pack_into(str(len(EMPTY_HEADER)) + 's', self.buf, 0, EMPTY_HEADER)
101 if self.bser_version == 1:
102 struct.pack_into(tobytes(len(EMPTY_HEADER)) + b's', self.buf, 0,
103 EMPTY_HEADER)
76 self.wpos = len(EMPTY_HEADER)
104 self.wpos = len(EMPTY_HEADER)
105 else:
106 assert self.bser_version == 2
107 struct.pack_into(tobytes(len(EMPTY_HEADER_V2)) + b's', self.buf, 0,
108 EMPTY_HEADER_V2)
109 self.wpos = len(EMPTY_HEADER_V2)
77
110
78 def ensure_size(self, size):
111 def ensure_size(self, size):
79 while ctypes.sizeof(self.buf) - self.wpos < size:
112 while ctypes.sizeof(self.buf) - self.wpos < size:
80 ctypes.resize(self.buf, ctypes.sizeof(self.buf) * 2)
113 ctypes.resize(self.buf, ctypes.sizeof(self.buf) * 2)
81
114
82 def append_long(self, val):
115 def append_long(self, val):
83 size = _int_size(val)
116 size = _int_size(val)
84 to_write = size + 1
117 to_write = size + 1
85 self.ensure_size(to_write)
118 self.ensure_size(to_write)
86 if size == 1:
119 if size == 1:
87 struct.pack_into('=cb', self.buf, self.wpos, BSER_INT8, val)
120 struct.pack_into(b'=cb', self.buf, self.wpos, BSER_INT8, val)
88 elif size == 2:
121 elif size == 2:
89 struct.pack_into('=ch', self.buf, self.wpos, BSER_INT16, val)
122 struct.pack_into(b'=ch', self.buf, self.wpos, BSER_INT16, val)
90 elif size == 4:
123 elif size == 4:
91 struct.pack_into('=ci', self.buf, self.wpos, BSER_INT32, val)
124 struct.pack_into(b'=ci', self.buf, self.wpos, BSER_INT32, val)
92 elif size == 8:
125 elif size == 8:
93 struct.pack_into('=cq', self.buf, self.wpos, BSER_INT64, val)
126 struct.pack_into(b'=cq', self.buf, self.wpos, BSER_INT64, val)
94 else:
127 else:
95 raise RuntimeError('Cannot represent this long value')
128 raise RuntimeError('Cannot represent this long value')
96 self.wpos += to_write
129 self.wpos += to_write
97
130
98
131
99 def append_string(self, s):
132 def append_string(self, s):
100 if isinstance(s, unicode):
133 if isinstance(s, unicode):
101 s = s.encode('utf-8')
134 s = s.encode('utf-8')
102 s_len = len(s)
135 s_len = len(s)
103 size = _int_size(s_len)
136 size = _int_size(s_len)
104 to_write = 2 + size + s_len
137 to_write = 2 + size + s_len
105 self.ensure_size(to_write)
138 self.ensure_size(to_write)
106 if size == 1:
139 if size == 1:
107 struct.pack_into('=ccb' + str(s_len) + 's', self.buf, self.wpos, BSER_STRING, BSER_INT8, s_len, s)
140 struct.pack_into(b'=ccb' + tobytes(s_len) + b's', self.buf,
141 self.wpos, BSER_BYTESTRING, BSER_INT8, s_len, s)
108 elif size == 2:
142 elif size == 2:
109 struct.pack_into('=cch' + str(s_len) + 's', self.buf, self.wpos, BSER_STRING, BSER_INT16, s_len, s)
143 struct.pack_into(b'=cch' + tobytes(s_len) + b's', self.buf,
144 self.wpos, BSER_BYTESTRING, BSER_INT16, s_len, s)
110 elif size == 4:
145 elif size == 4:
111 struct.pack_into('=cci' + str(s_len) + 's', self.buf, self.wpos, BSER_STRING, BSER_INT32, s_len, s)
146 struct.pack_into(b'=cci' + tobytes(s_len) + b's', self.buf,
147 self.wpos, BSER_BYTESTRING, BSER_INT32, s_len, s)
112 elif size == 8:
148 elif size == 8:
113 struct.pack_into('=ccq' + str(s_len) + 's', self.buf, self.wpos, BSER_STRING, BSER_INT64, s_len, s)
149 struct.pack_into(b'=ccq' + tobytes(s_len) + b's', self.buf,
150 self.wpos, BSER_BYTESTRING, BSER_INT64, s_len, s)
114 else:
151 else:
115 raise RuntimeError('Cannot represent this string value')
152 raise RuntimeError('Cannot represent this string value')
116 self.wpos += to_write
153 self.wpos += to_write
117
154
118
155
119 def append_recursive(self, val):
156 def append_recursive(self, val):
120 if isinstance(val, bool):
157 if isinstance(val, bool):
121 needed = 1
158 needed = 1
122 self.ensure_size(needed)
159 self.ensure_size(needed)
123 if val:
160 if val:
124 to_encode = BSER_TRUE
161 to_encode = BSER_TRUE
125 else:
162 else:
126 to_encode = BSER_FALSE
163 to_encode = BSER_FALSE
127 struct.pack_into('=c', self.buf, self.wpos, to_encode)
164 struct.pack_into(b'=c', self.buf, self.wpos, to_encode)
128 self.wpos += needed
165 self.wpos += needed
129 elif val is None:
166 elif val is None:
130 needed = 1
167 needed = 1
131 self.ensure_size(needed)
168 self.ensure_size(needed)
132 struct.pack_into('=c', self.buf, self.wpos, BSER_NULL)
169 struct.pack_into(b'=c', self.buf, self.wpos, BSER_NULL)
133 self.wpos += needed
170 self.wpos += needed
134 elif isinstance(val, (int, long)):
171 elif isinstance(val, (int, long)):
135 self.append_long(val)
172 self.append_long(val)
136 elif isinstance(val, (str, unicode)):
173 elif isinstance(val, STRING_TYPES):
137 self.append_string(val)
174 self.append_string(val)
138 elif isinstance(val, float):
175 elif isinstance(val, float):
139 needed = 9
176 needed = 9
140 self.ensure_size(needed)
177 self.ensure_size(needed)
141 struct.pack_into('=cd', self.buf, self.wpos, BSER_REAL, val)
178 struct.pack_into(b'=cd', self.buf, self.wpos, BSER_REAL, val)
142 self.wpos += needed
179 self.wpos += needed
143 elif isinstance(val, collections.Mapping) and isinstance(val, collections.Sized):
180 elif isinstance(val, collections.Mapping) and \
181 isinstance(val, collections.Sized):
144 val_len = len(val)
182 val_len = len(val)
145 size = _int_size(val_len)
183 size = _int_size(val_len)
146 needed = 2 + size
184 needed = 2 + size
147 self.ensure_size(needed)
185 self.ensure_size(needed)
148 if size == 1:
186 if size == 1:
149 struct.pack_into('=ccb', self.buf, self.wpos, BSER_OBJECT, BSER_INT8, val_len)
187 struct.pack_into(b'=ccb', self.buf, self.wpos, BSER_OBJECT,
188 BSER_INT8, val_len)
150 elif size == 2:
189 elif size == 2:
151 struct.pack_into('=cch', self.buf, self.wpos, BSER_OBJECT, BSER_INT16, val_len)
190 struct.pack_into(b'=cch', self.buf, self.wpos, BSER_OBJECT,
191 BSER_INT16, val_len)
152 elif size == 4:
192 elif size == 4:
153 struct.pack_into('=cci', self.buf, self.wpos, BSER_OBJECT, BSER_INT32, val_len)
193 struct.pack_into(b'=cci', self.buf, self.wpos, BSER_OBJECT,
194 BSER_INT32, val_len)
154 elif size == 8:
195 elif size == 8:
155 struct.pack_into('=ccq', self.buf, self.wpos, BSER_OBJECT, BSER_INT64, val_len)
196 struct.pack_into(b'=ccq', self.buf, self.wpos, BSER_OBJECT,
197 BSER_INT64, val_len)
156 else:
198 else:
157 raise RuntimeError('Cannot represent this mapping value')
199 raise RuntimeError('Cannot represent this mapping value')
158 self.wpos += needed
200 self.wpos += needed
159 for k, v in val.iteritems():
201 if compat.PYTHON3:
202 iteritems = val.items()
203 else:
204 iteritems = val.iteritems()
205 for k, v in iteritems:
160 self.append_string(k)
206 self.append_string(k)
161 self.append_recursive(v)
207 self.append_recursive(v)
162 elif isinstance(val, collections.Iterable) and isinstance(val, collections.Sized):
208 elif isinstance(val, collections.Iterable) and \
209 isinstance(val, collections.Sized):
163 val_len = len(val)
210 val_len = len(val)
164 size = _int_size(val_len)
211 size = _int_size(val_len)
165 needed = 2 + size
212 needed = 2 + size
166 self.ensure_size(needed)
213 self.ensure_size(needed)
167 if size == 1:
214 if size == 1:
168 struct.pack_into('=ccb', self.buf, self.wpos, BSER_ARRAY, BSER_INT8, val_len)
215 struct.pack_into(b'=ccb', self.buf, self.wpos, BSER_ARRAY,
216 BSER_INT8, val_len)
169 elif size == 2:
217 elif size == 2:
170 struct.pack_into('=cch', self.buf, self.wpos, BSER_ARRAY, BSER_INT16, val_len)
218 struct.pack_into(b'=cch', self.buf, self.wpos, BSER_ARRAY,
219 BSER_INT16, val_len)
171 elif size == 4:
220 elif size == 4:
172 struct.pack_into('=cci', self.buf, self.wpos, BSER_ARRAY, BSER_INT32, val_len)
221 struct.pack_into(b'=cci', self.buf, self.wpos, BSER_ARRAY,
222 BSER_INT32, val_len)
173 elif size == 8:
223 elif size == 8:
174 struct.pack_into('=ccq', self.buf, self.wpos, BSER_ARRAY, BSER_INT64, val_len)
224 struct.pack_into(b'=ccq', self.buf, self.wpos, BSER_ARRAY,
225 BSER_INT64, val_len)
175 else:
226 else:
176 raise RuntimeError('Cannot represent this sequence value')
227 raise RuntimeError('Cannot represent this sequence value')
177 self.wpos += needed
228 self.wpos += needed
178 for v in val:
229 for v in val:
179 self.append_recursive(v)
230 self.append_recursive(v)
180 else:
231 else:
181 raise RuntimeError('Cannot represent unknown value type')
232 raise RuntimeError('Cannot represent unknown value type')
182
233
183
234
184 def dumps(obj):
235 def dumps(obj, version=1, capabilities=0):
185 bser_buf = _bser_buffer()
236 bser_buf = _bser_buffer(version=version)
186 bser_buf.append_recursive(obj)
237 bser_buf.append_recursive(obj)
187 # Now fill in the overall length
238 # Now fill in the overall length
239 if version == 1:
188 obj_len = bser_buf.wpos - len(EMPTY_HEADER)
240 obj_len = bser_buf.wpos - len(EMPTY_HEADER)
189 struct.pack_into('=i', bser_buf.buf, 3, obj_len)
241 struct.pack_into(b'=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:
242 else:
211 raise ValueError('Invalid bser int encoding 0x%02x' % int(int_type))
243 obj_len = bser_buf.wpos - len(EMPTY_HEADER_V2)
212 int_val = struct.unpack_from(fmt, buf, pos + 1)[0]
244 struct.pack_into(b'=i', bser_buf.buf, 2, capabilities)
213 return (int_val, pos + needed)
245 struct.pack_into(b'=i', bser_buf.buf, 7, obj_len)
214
246 return bser_buf.buf.raw[:bser_buf.wpos]
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
247
235 # This is a quack-alike with the bserObjectType in bser.c
248 # This is a quack-alike with the bserObjectType in bser.c
236 # It provides by getattr accessors and getitem for both index
249 # It provides by getattr accessors and getitem for both index
237 # and name.
250 # and name.
238 class _BunserDict(object):
251 class _BunserDict(object):
239 __slots__ = ('_keys', '_values')
252 __slots__ = ('_keys', '_values')
240
253
241 def __init__(self, keys, values):
254 def __init__(self, keys, values):
242 self._keys = keys
255 self._keys = keys
243 self._values = values
256 self._values = values
244
257
245 def __getattr__(self, name):
258 def __getattr__(self, name):
246 return self.__getitem__(name)
259 return self.__getitem__(name)
247
260
248 def __getitem__(self, key):
261 def __getitem__(self, key):
249 if isinstance(key, (int, long)):
262 if isinstance(key, (int, long)):
250 return self._values[key]
263 return self._values[key]
251 elif key.startswith('st_'):
264 elif key.startswith('st_'):
252 # hack^Wfeature to allow mercurial to use "st_size" to
265 # hack^Wfeature to allow mercurial to use "st_size" to
253 # reference "size"
266 # reference "size"
254 key = key[3:]
267 key = key[3:]
255 try:
268 try:
256 return self._values[self._keys.index(key)]
269 return self._values[self._keys.index(key)]
257 except ValueError as ex:
270 except ValueError as ex:
258 raise KeyError('_BunserDict has no key %s' % key)
271 raise KeyError('_BunserDict has no key %s' % key)
259
272
260 def __len__(self):
273 def __len__(self):
261 return len(self._keys)
274 return len(self._keys)
262
275
263 def _bunser_object(buf, pos, mutable=True):
276 class Bunser(object):
264 obj_len, pos = _bunser_int(buf, pos + 1)
277 def __init__(self, mutable=True, value_encoding=None, value_errors=None):
265 if mutable:
278 self.mutable = mutable
279 self.value_encoding = value_encoding
280
281 if value_encoding is None:
282 self.value_errors = None
283 elif value_errors is None:
284 self.value_errors = 'strict'
285 else:
286 self.value_errors = value_errors
287
288 @staticmethod
289 def unser_int(buf, pos):
290 try:
291 int_type = _buf_pos(buf, pos)
292 except IndexError:
293 raise ValueError('Invalid bser int encoding, pos out of range')
294 if int_type == BSER_INT8:
295 needed = 2
296 fmt = b'=b'
297 elif int_type == BSER_INT16:
298 needed = 3
299 fmt = b'=h'
300 elif int_type == BSER_INT32:
301 needed = 5
302 fmt = b'=i'
303 elif int_type == BSER_INT64:
304 needed = 9
305 fmt = b'=q'
306 else:
307 raise ValueError('Invalid bser int encoding 0x%s' %
308 binascii.hexlify(int_type).decode('ascii'))
309 int_val = struct.unpack_from(fmt, buf, pos + 1)[0]
310 return (int_val, pos + needed)
311
312 def unser_utf8_string(self, buf, pos):
313 str_len, pos = self.unser_int(buf, pos + 1)
314 str_val = struct.unpack_from(tobytes(str_len) + b's', buf, pos)[0]
315 return (str_val.decode('utf-8'), pos + str_len)
316
317 def unser_bytestring(self, buf, pos):
318 str_len, pos = self.unser_int(buf, pos + 1)
319 str_val = struct.unpack_from(tobytes(str_len) + b's', buf, pos)[0]
320 if self.value_encoding is not None:
321 str_val = str_val.decode(self.value_encoding, self.value_errors)
322 # str_len stays the same because that's the length in bytes
323 return (str_val, pos + str_len)
324
325 def unser_array(self, buf, pos):
326 arr_len, pos = self.unser_int(buf, pos + 1)
327 arr = []
328 for i in range(arr_len):
329 arr_item, pos = self.loads_recursive(buf, pos)
330 arr.append(arr_item)
331
332 if not self.mutable:
333 arr = tuple(arr)
334
335 return arr, pos
336
337 def unser_object(self, buf, pos):
338 obj_len, pos = self.unser_int(buf, pos + 1)
339 if self.mutable:
266 obj = {}
340 obj = {}
267 else:
341 else:
268 keys = []
342 keys = []
269 vals = []
343 vals = []
270
344
271 for i in range(obj_len):
345 for i in range(obj_len):
272 key, pos = _bunser_string(buf, pos)
346 key, pos = self.unser_utf8_string(buf, pos)
273 val, pos = _bser_loads_recursive(buf, pos, mutable)
347 val, pos = self.loads_recursive(buf, pos)
274 if mutable:
348 if self.mutable:
275 obj[key] = val
349 obj[key] = val
276 else:
350 else:
277 keys.append(key)
351 keys.append(key)
278 vals.append(val)
352 vals.append(val)
279
353
280 if not mutable:
354 if not self.mutable:
281 obj = _BunserDict(keys, vals)
355 obj = _BunserDict(keys, vals)
282
356
283 return obj, pos
357 return obj, pos
284
358
285
359 def unser_template(self, buf, pos):
286 def _bunser_template(buf, pos, mutable=True):
360 val_type = _buf_pos(buf, pos + 1)
287 if buf[pos + 1] != BSER_ARRAY:
361 if val_type != BSER_ARRAY:
288 raise RuntimeError('Expect ARRAY to follow TEMPLATE')
362 raise RuntimeError('Expect ARRAY to follow TEMPLATE')
289 keys, pos = _bunser_array(buf, pos + 1)
363 # force UTF-8 on keys
290 nitems, pos = _bunser_int(buf, pos)
364 keys_bunser = Bunser(mutable=self.mutable, value_encoding='utf-8')
365 keys, pos = keys_bunser.unser_array(buf, pos + 1)
366 nitems, pos = self.unser_int(buf, pos)
291 arr = []
367 arr = []
292 for i in range(nitems):
368 for i in range(nitems):
293 if mutable:
369 if self.mutable:
294 obj = {}
370 obj = {}
295 else:
371 else:
296 vals = []
372 vals = []
297
373
298 for keyidx in range(len(keys)):
374 for keyidx in range(len(keys)):
299 if buf[pos] == BSER_SKIP:
375 if _buf_pos(buf, pos) == BSER_SKIP:
300 pos += 1
376 pos += 1
301 ele = None
377 ele = None
302 else:
378 else:
303 ele, pos = _bser_loads_recursive(buf, pos, mutable)
379 ele, pos = self.loads_recursive(buf, pos)
304
380
305 if mutable:
381 if self.mutable:
306 key = keys[keyidx]
382 key = keys[keyidx]
307 obj[key] = ele
383 obj[key] = ele
308 else:
384 else:
309 vals.append(ele)
385 vals.append(ele)
310
386
311 if not mutable:
387 if not self.mutable:
312 obj = _BunserDict(keys, vals)
388 obj = _BunserDict(keys, vals)
313
389
314 arr.append(obj)
390 arr.append(obj)
315 return arr, pos
391 return arr, pos
316
392
317
393 def loads_recursive(self, buf, pos):
318 def _bser_loads_recursive(buf, pos, mutable=True):
394 val_type = _buf_pos(buf, pos)
319 val_type = buf[pos]
320 if (val_type == BSER_INT8 or val_type == BSER_INT16 or
395 if (val_type == BSER_INT8 or val_type == BSER_INT16 or
321 val_type == BSER_INT32 or val_type == BSER_INT64):
396 val_type == BSER_INT32 or val_type == BSER_INT64):
322 return _bunser_int(buf, pos)
397 return self.unser_int(buf, pos)
323 elif val_type == BSER_REAL:
398 elif val_type == BSER_REAL:
324 val = struct.unpack_from('=d', buf, pos + 1)[0]
399 val = struct.unpack_from(b'=d', buf, pos + 1)[0]
325 return (val, pos + 9)
400 return (val, pos + 9)
326 elif val_type == BSER_TRUE:
401 elif val_type == BSER_TRUE:
327 return (True, pos + 1)
402 return (True, pos + 1)
328 elif val_type == BSER_FALSE:
403 elif val_type == BSER_FALSE:
329 return (False, pos + 1)
404 return (False, pos + 1)
330 elif val_type == BSER_NULL:
405 elif val_type == BSER_NULL:
331 return (None, pos + 1)
406 return (None, pos + 1)
332 elif val_type == BSER_STRING:
407 elif val_type == BSER_BYTESTRING:
333 return _bunser_string(buf, pos)
408 return self.unser_bytestring(buf, pos)
409 elif val_type == BSER_UTF8STRING:
410 return self.unser_utf8_string(buf, pos)
334 elif val_type == BSER_ARRAY:
411 elif val_type == BSER_ARRAY:
335 return _bunser_array(buf, pos, mutable)
412 return self.unser_array(buf, pos)
336 elif val_type == BSER_OBJECT:
413 elif val_type == BSER_OBJECT:
337 return _bunser_object(buf, pos, mutable)
414 return self.unser_object(buf, pos)
338 elif val_type == BSER_TEMPLATE:
415 elif val_type == BSER_TEMPLATE:
339 return _bunser_template(buf, pos, mutable)
416 return self.unser_template(buf, pos)
340 else:
417 else:
341 raise RuntimeError('unhandled bser opcode 0x%02x' % (val_type,))
418 raise ValueError('unhandled bser opcode 0x%s' %
419 binascii.hexlify(val_type).decode('ascii'))
420
421
422 def _pdu_info_helper(buf):
423 bser_version = -1
424 if buf[0:2] == EMPTY_HEADER[0:2]:
425 bser_version = 1
426 bser_capabilities = 0
427 expected_len, pos2 = Bunser.unser_int(buf, 2)
428 elif buf[0:2] == EMPTY_HEADER_V2[0:2]:
429 if len(buf) < 8:
430 raise ValueError('Invalid BSER header')
431 bser_version = 2
432 bser_capabilities = struct.unpack_from("I", buf, 2)[0]
433 expected_len, pos2 = Bunser.unser_int(buf, 6)
434 else:
435 raise ValueError('Invalid BSER header')
436
437 return bser_version, bser_capabilities, expected_len, pos2
438
439
440 def pdu_info(buf):
441 info = _pdu_info_helper(buf)
442 return info[0], info[1], info[2] + info[3]
342
443
343
444
344 def pdu_len(buf):
445 def pdu_len(buf):
345 if buf[0:2] != EMPTY_HEADER[0:2]:
446 info = _pdu_info_helper(buf)
346 raise RuntimeError('Invalid BSER header')
447 return info[2] + info[3]
347 expected_len, pos = _bunser_int(buf, 2)
348 return expected_len + pos
349
448
350
449
351 def loads(buf, mutable=True):
450 def loads(buf, mutable=True, value_encoding=None, value_errors=None):
352 if buf[0:2] != EMPTY_HEADER[0:2]:
451 """Deserialize a BSER-encoded blob.
353 raise RuntimeError('Invalid BSER header')
452
354 expected_len, pos = _bunser_int(buf, 2)
453 @param buf: The buffer to deserialize.
454 @type buf: bytes
455
456 @param mutable: Whether to return mutable results.
457 @type mutable: bool
458
459 @param value_encoding: Optional codec to use to decode values. If
460 unspecified or None, return values as bytestrings.
461 @type value_encoding: str
462
463 @param value_errors: Optional error handler for codec. 'strict' by default.
464 The other most common argument is 'surrogateescape' on
465 Python 3. If value_encoding is None, this is ignored.
466 @type value_errors: str
467 """
468
469 info = _pdu_info_helper(buf)
470 expected_len = info[2]
471 pos = info[3]
472
355 if len(buf) != expected_len + pos:
473 if len(buf) != expected_len + pos:
356 raise RuntimeError('bser data len != header len')
474 raise ValueError('bser data len != header len')
357 return _bser_loads_recursive(buf, pos, mutable)[0]
475
476 bunser = Bunser(mutable=mutable, value_encoding=value_encoding,
477 value_errors=value_errors)
358
478
359 # no-check-code -- this is a 3rd party library
479 return bunser.loads_recursive(buf, pos)[0]
480
481
482 def load(fp, mutable=True, value_encoding=None, value_errors=None):
483 from . import load
484 return load.load(fp, mutable, value_encoding, value_errors)
@@ -1,47 +1,43 b''
1 #require test-repo
1 #require test-repo
2
2
3 $ . "$TESTDIR/helpers-testrepo.sh"
3 $ . "$TESTDIR/helpers-testrepo.sh"
4 $ cd "$TESTDIR"/..
4 $ cd "$TESTDIR"/..
5
5
6 $ hg files 'set:(**.py)' | sed 's|\\|/|g' | xargs python contrib/check-py3-compat.py
6 $ hg files 'set:(**.py)' | sed 's|\\|/|g' | xargs python contrib/check-py3-compat.py
7 contrib/python-zstandard/setup.py not using absolute_import
7 contrib/python-zstandard/setup.py not using absolute_import
8 contrib/python-zstandard/setup_zstd.py not using absolute_import
8 contrib/python-zstandard/setup_zstd.py not using absolute_import
9 contrib/python-zstandard/tests/common.py not using absolute_import
9 contrib/python-zstandard/tests/common.py not using absolute_import
10 contrib/python-zstandard/tests/test_cffi.py not using absolute_import
10 contrib/python-zstandard/tests/test_cffi.py not using absolute_import
11 contrib/python-zstandard/tests/test_compressor.py not using absolute_import
11 contrib/python-zstandard/tests/test_compressor.py not using absolute_import
12 contrib/python-zstandard/tests/test_data_structures.py not using absolute_import
12 contrib/python-zstandard/tests/test_data_structures.py not using absolute_import
13 contrib/python-zstandard/tests/test_decompressor.py not using absolute_import
13 contrib/python-zstandard/tests/test_decompressor.py not using absolute_import
14 contrib/python-zstandard/tests/test_estimate_sizes.py not using absolute_import
14 contrib/python-zstandard/tests/test_estimate_sizes.py not using absolute_import
15 contrib/python-zstandard/tests/test_module_attributes.py not using absolute_import
15 contrib/python-zstandard/tests/test_module_attributes.py not using absolute_import
16 contrib/python-zstandard/tests/test_roundtrip.py not using absolute_import
16 contrib/python-zstandard/tests/test_roundtrip.py not using absolute_import
17 contrib/python-zstandard/tests/test_train_dictionary.py not using absolute_import
17 contrib/python-zstandard/tests/test_train_dictionary.py not using absolute_import
18 hgext/fsmonitor/pywatchman/__init__.py not using absolute_import
19 hgext/fsmonitor/pywatchman/__init__.py requires print_function
20 hgext/fsmonitor/pywatchman/capabilities.py not using absolute_import
21 hgext/fsmonitor/pywatchman/pybser.py not using absolute_import
22 i18n/check-translation.py not using absolute_import
18 i18n/check-translation.py not using absolute_import
23 setup.py not using absolute_import
19 setup.py not using absolute_import
24 tests/test-demandimport.py not using absolute_import
20 tests/test-demandimport.py not using absolute_import
25
21
26 #if py3exe
22 #if py3exe
27 $ hg files 'set:(**.py) - grep(pygments)' | sed 's|\\|/|g' \
23 $ hg files 'set:(**.py) - grep(pygments)' | sed 's|\\|/|g' \
28 > | xargs $PYTHON3 contrib/check-py3-compat.py \
24 > | xargs $PYTHON3 contrib/check-py3-compat.py \
29 > | sed 's/[0-9][0-9]*)$/*)/'
25 > | sed 's/[0-9][0-9]*)$/*)/'
30 hgext/convert/transport.py: error importing: <ImportError> No module named 'svn.client' (error at transport.py:*)
26 hgext/convert/transport.py: error importing: <ImportError> No module named 'svn.client' (error at transport.py:*)
31 hgext/fsmonitor/pywatchman/capabilities.py: error importing: <ImportError> No module named 'pybser' (error at __init__.py:*)
27 hgext/fsmonitor/pywatchman/capabilities.py: error importing: <ImportError> No module named 'pybser' (error at __init__.py:*)
32 hgext/fsmonitor/pywatchman/pybser.py: error importing: <ImportError> No module named 'pybser' (error at __init__.py:*)
28 hgext/fsmonitor/pywatchman/pybser.py: error importing: <ImportError> No module named 'pybser' (error at __init__.py:*)
33 hgext/fsmonitor/watchmanclient.py: error importing: <ImportError> No module named 'pybser' (error at __init__.py:*)
29 hgext/fsmonitor/watchmanclient.py: error importing: <ImportError> No module named 'pybser' (error at __init__.py:*)
34 mercurial/cffi/bdiff.py: error importing: <ImportError> No module named 'mercurial.cffi' (error at check-py3-compat.py:*)
30 mercurial/cffi/bdiff.py: error importing: <ImportError> No module named 'mercurial.cffi' (error at check-py3-compat.py:*)
35 mercurial/cffi/mpatch.py: error importing: <ImportError> No module named 'mercurial.cffi' (error at check-py3-compat.py:*)
31 mercurial/cffi/mpatch.py: error importing: <ImportError> No module named 'mercurial.cffi' (error at check-py3-compat.py:*)
36 mercurial/cffi/osutil.py: error importing: <ImportError> No module named 'mercurial.cffi' (error at check-py3-compat.py:*)
32 mercurial/cffi/osutil.py: error importing: <ImportError> No module named 'mercurial.cffi' (error at check-py3-compat.py:*)
37 mercurial/scmwindows.py: error importing: <ImportError> No module named 'msvcrt' (error at win32.py:*)
33 mercurial/scmwindows.py: error importing: <ImportError> No module named 'msvcrt' (error at win32.py:*)
38 mercurial/win32.py: error importing: <ImportError> No module named 'msvcrt' (error at win32.py:*)
34 mercurial/win32.py: error importing: <ImportError> No module named 'msvcrt' (error at win32.py:*)
39 mercurial/windows.py: error importing: <ImportError> No module named 'msvcrt' (error at windows.py:*)
35 mercurial/windows.py: error importing: <ImportError> No module named 'msvcrt' (error at windows.py:*)
40
36
41 #endif
37 #endif
42
38
43 #if py3exe py3pygments
39 #if py3exe py3pygments
44 $ hg files 'set:(**.py) and grep(pygments)' | sed 's|\\|/|g' \
40 $ hg files 'set:(**.py) and grep(pygments)' | sed 's|\\|/|g' \
45 > | xargs $PYTHON3 contrib/check-py3-compat.py \
41 > | xargs $PYTHON3 contrib/check-py3-compat.py \
46 > | sed 's/[0-9][0-9]*)$/*)/'
42 > | sed 's/[0-9][0-9]*)$/*)/'
47 #endif
43 #endif
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now