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 Serialization(object): | |
20 """ | |
21 Helper class to serialize/deserialize a struct. | |
22 """ | |
23 def __init__(self, groups): | |
24 self._groups = groups | |
25 self.version = _GetVersion(groups) | |
26 main_struct = _GetStruct(groups) | |
27 self.size = HEADER_STRUCT.size + main_struct.size | |
28 self._struct_per_version = { | |
29 self.version: main_struct, | |
30 } | |
31 | |
32 def _GetMainStruct(self): | |
33 return self._GetStruct(self.version) | |
34 | |
35 def _GetStruct(self, version): | |
36 # If asking for a greater ver | |
37 version = min(version, self.version) | |
38 if version not in self._struct_per_version: | |
39 self._struct_per_version[version] = _GetStruct(_FilterGroups(self._groups, | |
40 version)) | |
41 return self._struct_per_version[version] | |
42 | |
43 def Serialize(self, obj, handle_offset): | |
44 """ | |
45 Serialize the given obj. handle_offset is the the first value to use when | |
46 encoding handles. | |
47 """ | |
48 handles = [] | |
49 data = bytearray(self.size) | |
50 HEADER_STRUCT.pack_into(data, 0, self.size, self.version) | |
51 position = HEADER_STRUCT.size | |
52 to_pack = [] | |
53 for group in self._groups: | |
54 position = position + NeededPaddingForAlignment(position, | |
55 group.GetByteSize()) | |
56 (entry, new_handles) = group.Serialize( | |
57 obj, | |
58 len(data) - position, | |
59 data, | |
60 handle_offset + len(handles)) | |
61 to_pack.append(entry) | |
62 handles.extend(new_handles) | |
63 position = position + group.GetByteSize() | |
64 self._GetMainStruct().pack_into(data, HEADER_STRUCT.size, *to_pack) | |
65 return (data, handles) | |
66 | |
67 | |
68 def NeededPaddingForAlignment(value, alignment=8): | |
69 """Returns the padding necessary to align value with the given alignment.""" | |
sdefresne
2014/09/15 08:46:54
nit: this method could return a tuple padding_leng
qsr
2014/09/15 11:01:53
Not really. It is used that way only once, but I u
sdefresne
2014/09/15 11:14:46
Make sense, thank you for the justification.
qsr
2014/09/15 11:42:00
Acknowledged.
| |
70 if value % alignment: | |
71 return alignment - (value % alignment) | |
72 return 0 | |
73 | |
74 | |
75 def _GetVersion(groups): | |
76 return reduce(lambda x, y: x+y, [len(x.descriptors) for x in groups]) | |
sdefresne
2014/09/15 08:46:54
nit: "reduce(lambda x, y: x+y, [...])" can be rewr
qsr
2014/09/15 11:01:53
Done.
| |
77 | |
78 | |
79 def _FilterGroups(groups, version): | |
80 return [group for group in groups if group.version < version] | |
81 | |
82 | |
83 def _GetStruct(groups): | |
84 index = 0 | |
85 codes = [ '=' ] | |
86 for group in groups: | |
87 code = group.GetTypeCode() | |
88 size = group.GetByteSize() | |
89 needed_padding = NeededPaddingForAlignment(index, size) | |
90 if needed_padding: | |
91 codes.append('x' * needed_padding) | |
92 index = index + needed_padding | |
93 codes.append(code) | |
94 index = index + size | |
95 end_alignment = index % 8 | |
sdefresne
2014/09/15 08:46:54
nit: you can use NeededPaddingForAlignment here.
qsr
2014/09/15 11:01:53
Done.
| |
96 if end_alignment: | |
97 codes.append('x' * (8 - end_alignment)) | |
98 return struct.Struct(''.join(codes)) | |
OLD | NEW |