| 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 |