OLD | NEW |
| (Empty) |
1 # Copyright 2014 The Chromium Authors. All rights reserved. | |
2 # Use of this source code is governed by a BSD-style license that can be | |
3 # found in the LICENSE file. | |
4 | |
5 """Utility classes for serialization""" | |
6 | |
7 import struct | |
8 | |
9 | |
10 # Format of a header for a struct or an array. | |
11 HEADER_STRUCT = struct.Struct("<II") | |
12 | |
13 | |
14 class SerializationException(Exception): | |
15 """Error when strying to serialize a struct.""" | |
16 pass | |
17 | |
18 | |
19 class DeserializationException(Exception): | |
20 """Error when strying to deserialize a struct.""" | |
21 pass | |
22 | |
23 | |
24 class DeserializationContext(object): | |
25 | |
26 def ClaimHandle(self, handle): | |
27 raise NotImplementedError() | |
28 | |
29 def ClaimMemory(self, start, size): | |
30 raise NotImplementedError() | |
31 | |
32 def GetSubContext(self, offset): | |
33 raise NotImplementedError() | |
34 | |
35 def IsInitialContext(self): | |
36 raise NotImplementedError() | |
37 | |
38 | |
39 class RootDeserializationContext(DeserializationContext): | |
40 def __init__(self, data, handles): | |
41 if isinstance(data, buffer): | |
42 self.data = data | |
43 else: | |
44 self.data = buffer(data) | |
45 self._handles = handles | |
46 self._next_handle = 0; | |
47 self._next_memory = 0; | |
48 | |
49 def ClaimHandle(self, handle): | |
50 if handle < self._next_handle: | |
51 raise DeserializationException('Accessing handles out of order.') | |
52 self._next_handle = handle + 1 | |
53 return self._handles[handle] | |
54 | |
55 def ClaimMemory(self, start, size): | |
56 if start < self._next_memory: | |
57 raise DeserializationException('Accessing buffer out of order.') | |
58 self._next_memory = start + size | |
59 | |
60 def GetSubContext(self, offset): | |
61 return _ChildDeserializationContext(self, offset) | |
62 | |
63 def IsInitialContext(self): | |
64 return True | |
65 | |
66 | |
67 class _ChildDeserializationContext(DeserializationContext): | |
68 def __init__(self, parent, offset): | |
69 self._parent = parent | |
70 self._offset = offset | |
71 self.data = buffer(parent.data, offset) | |
72 | |
73 def ClaimHandle(self, handle): | |
74 return self._parent.ClaimHandle(handle) | |
75 | |
76 def ClaimMemory(self, start, size): | |
77 return self._parent.ClaimMemory(self._offset + start, size) | |
78 | |
79 def GetSubContext(self, offset): | |
80 return self._parent.GetSubContext(self._offset + offset) | |
81 | |
82 def IsInitialContext(self): | |
83 return False | |
84 | |
85 | |
86 class Serialization(object): | |
87 """ | |
88 Helper class to serialize/deserialize a struct. | |
89 """ | |
90 def __init__(self, groups): | |
91 self.version = _GetVersion(groups) | |
92 self._groups = groups | |
93 main_struct = _GetStruct(groups) | |
94 self.size = HEADER_STRUCT.size + main_struct.size | |
95 self._struct_per_version = { | |
96 self.version: main_struct, | |
97 } | |
98 self._groups_per_version = { | |
99 self.version: groups, | |
100 } | |
101 | |
102 def _GetMainStruct(self): | |
103 return self._GetStruct(self.version) | |
104 | |
105 def _GetGroups(self, version): | |
106 # If asking for a version greater than the last known. | |
107 version = min(version, self.version) | |
108 if version not in self._groups_per_version: | |
109 self._groups_per_version[version] = _FilterGroups(self._groups, version) | |
110 return self._groups_per_version[version] | |
111 | |
112 def _GetStruct(self, version): | |
113 # If asking for a version greater than the last known. | |
114 version = min(version, self.version) | |
115 if version not in self._struct_per_version: | |
116 self._struct_per_version[version] = _GetStruct(self._GetGroups(version)) | |
117 return self._struct_per_version[version] | |
118 | |
119 def Serialize(self, obj, handle_offset): | |
120 """ | |
121 Serialize the given obj. handle_offset is the the first value to use when | |
122 encoding handles. | |
123 """ | |
124 handles = [] | |
125 data = bytearray(self.size) | |
126 HEADER_STRUCT.pack_into(data, 0, self.size, self.version) | |
127 position = HEADER_STRUCT.size | |
128 to_pack = [] | |
129 for group in self._groups: | |
130 position = position + NeededPaddingForAlignment(position, | |
131 group.GetByteSize()) | |
132 (entry, new_handles) = group.Serialize( | |
133 obj, | |
134 len(data) - position, | |
135 data, | |
136 handle_offset + len(handles)) | |
137 to_pack.append(entry) | |
138 handles.extend(new_handles) | |
139 position = position + group.GetByteSize() | |
140 self._GetMainStruct().pack_into(data, HEADER_STRUCT.size, *to_pack) | |
141 return (data, handles) | |
142 | |
143 def Deserialize(self, fields, context): | |
144 if len(context.data) < HEADER_STRUCT.size: | |
145 raise DeserializationException( | |
146 'Available data too short to contain header.') | |
147 (size, version) = HEADER_STRUCT.unpack_from(context.data) | |
148 if len(context.data) < size or size < HEADER_STRUCT.size: | |
149 raise DeserializationException('Header size is incorrect.') | |
150 if context.IsInitialContext(): | |
151 context.ClaimMemory(0, size) | |
152 version_struct = self._GetStruct(version) | |
153 entitities = version_struct.unpack_from(context.data, HEADER_STRUCT.size) | |
154 filtered_groups = self._GetGroups(version) | |
155 position = HEADER_STRUCT.size | |
156 for (group, value) in zip(filtered_groups, entitities): | |
157 position = position + NeededPaddingForAlignment(position, | |
158 group.GetByteSize()) | |
159 fields.update(group.Deserialize(value, context.GetSubContext(position))) | |
160 position += group.GetByteSize() | |
161 | |
162 | |
163 def NeededPaddingForAlignment(value, alignment=8): | |
164 """Returns the padding necessary to align value with the given alignment.""" | |
165 if value % alignment: | |
166 return alignment - (value % alignment) | |
167 return 0 | |
168 | |
169 | |
170 def _GetVersion(groups): | |
171 return sum([len(x.descriptors) for x in groups]) | |
172 | |
173 | |
174 def _FilterGroups(groups, version): | |
175 return [group for group in groups if group.GetVersion() < version] | |
176 | |
177 | |
178 def _GetStruct(groups): | |
179 index = 0 | |
180 codes = [ '<' ] | |
181 for group in groups: | |
182 code = group.GetTypeCode() | |
183 size = group.GetByteSize() | |
184 needed_padding = NeededPaddingForAlignment(index, size) | |
185 if needed_padding: | |
186 codes.append('x' * needed_padding) | |
187 index = index + needed_padding | |
188 codes.append(code) | |
189 index = index + size | |
190 alignment_needed = NeededPaddingForAlignment(index) | |
191 if alignment_needed: | |
192 codes.append('x' * alignment_needed) | |
193 return struct.Struct(''.join(codes)) | |
OLD | NEW |