align.py revision 891601f5
1891601f5Smrg'''
2891601f5SmrgThis module contains helper classes for alignment arithmetic and checks
3891601f5Smrg'''
4891601f5Smrg
5891601f5Smrgfrom fractions import gcd
6891601f5Smrg
7891601f5Smrgclass Alignment(object):
8891601f5Smrg
9891601f5Smrg    def __init__(self, align=4, offset=0):
10891601f5Smrg        self.align = align
11891601f5Smrg        # normalize the offset (just in case)
12891601f5Smrg        self.offset = offset % align
13891601f5Smrg
14891601f5Smrg
15891601f5Smrg    def __eq__(self, other):
16891601f5Smrg        return self.align == other.align and self.offset == other.offset
17891601f5Smrg
18891601f5Smrg    def __str__(self):
19891601f5Smrg	return "(align=%d, offset=%d)" % (self.align, self.offset)
20891601f5Smrg
21891601f5Smrg    @staticmethod
22891601f5Smrg    def for_primitive_type(size):
23891601f5Smrg	# compute the required start_alignment based on the size of the type
24891601f5Smrg	if size % 8 == 0:
25891601f5Smrg            # do 8-byte primitives require 8-byte alignment in X11?
26891601f5Smrg            return Alignment(8,0)
27891601f5Smrg        elif size % 4 == 0:
28891601f5Smrg            return Alignment(4,0)
29891601f5Smrg        elif size % 2 == 0:
30891601f5Smrg            return Alignment(2,0)
31891601f5Smrg        else:
32891601f5Smrg            return Alignment(1,0)
33891601f5Smrg
34891601f5Smrg
35891601f5Smrg    def align_after_fixed_size(self, size):
36891601f5Smrg	new_offset = (self.offset + size) % self.align
37891601f5Smrg        return Alignment(self.align, new_offset)
38891601f5Smrg
39891601f5Smrg
40891601f5Smrg    def is_guaranteed_at(self, external_align):
41891601f5Smrg        '''
42891601f5Smrg        Assuming the given external_align, checks whether
43891601f5Smrg        self is fulfilled for all cases.
44891601f5Smrg	Returns True if yes, False otherwise.
45891601f5Smrg        '''
46891601f5Smrg        if self.align == 1 and self.offset == 0:
47891601f5Smrg            # alignment 1 with offset 0 is always fulfilled
48891601f5Smrg            return True
49891601f5Smrg
50891601f5Smrg        if external_align is None:
51891601f5Smrg            # there is no external align -> fail
52891601f5Smrg            return False
53891601f5Smrg
54891601f5Smrg        if external_align.align < self.align:
55891601f5Smrg            # the external align guarantees less alignment -> not guaranteed
56891601f5Smrg            return False
57891601f5Smrg
58891601f5Smrg	if external_align.align % self.align != 0:
59891601f5Smrg            # the external align cannot be divided by our align
60891601f5Smrg	    # -> not guaranteed
61891601f5Smrg            # (this can only happen if there are alignments that are not
62891601f5Smrg            # a power of 2, which is highly discouraged. But better be
63891601f5Smrg            # safe and check for it)
64891601f5Smrg            return False
65891601f5Smrg
66891601f5Smrg        if external_align.offset % self.align != self.offset:
67891601f5Smrg            # offsets do not match
68891601f5Smrg            return False
69891601f5Smrg
70891601f5Smrg        return True
71891601f5Smrg
72891601f5Smrg
73891601f5Smrg    def combine_with(self, other):
74891601f5Smrg        # returns the alignment that is guaranteed when
75891601f5Smrg	# both, self or other, can happen
76891601f5Smrg        new_align = gcd(self.align, other.align)
77891601f5Smrg        new_offset_candidate1 = self.offset % new_align
78891601f5Smrg        new_offset_candidate2 = other.offset % new_align
79891601f5Smrg        if new_offset_candidate1 == new_offset_candidate2:
80891601f5Smrg            new_offset = new_offset_candidate1
81891601f5Smrg        else:
82891601f5Smrg            offset_diff = abs(new_offset_candidate2 - new_offset_candidate1)
83891601f5Smrg            new_align = gcd(new_align, offset_diff)
84891601f5Smrg            new_offset_candidate1 = self.offset % new_align
85891601f5Smrg            new_offset_candidate2 = other.offset % new_align
86891601f5Smrg	    assert new_offset_candidate1 == new_offset_candidate2
87891601f5Smrg	    new_offset = new_offset_candidate1
88891601f5Smrg        # return the result
89891601f5Smrg        return Alignment(new_align, new_offset)
90891601f5Smrg
91891601f5Smrg
92891601f5Smrgclass AlignmentLog(object):
93891601f5Smrg
94891601f5Smrg    def __init__(self):
95891601f5Smrg	self.ok_list = []
96891601f5Smrg	self.fail_list = []
97891601f5Smrg	self.verbosity = 1
98891601f5Smrg
99891601f5Smrg    def __str__(self):
100891601f5Smrg	result = ""
101891601f5Smrg
102891601f5Smrg	# output the OK-list
103891601f5Smrg	for (align_before, field_name, type_obj, callstack, align_after) in self.ok_list:
104891601f5Smrg	    stacksize = len(callstack)
105891601f5Smrg            indent = '  ' * stacksize
106891601f5Smrg	    if self.ok_callstack_is_relevant(callstack):
107891601f5Smrg                if field_name is None or field_name == "":
108891601f5Smrg	            result += ("    %sok: %s:\n\t%sbefore: %s, after: %s\n"
109891601f5Smrg		        % (indent, str(type_obj), indent, str(align_before), str(align_after)))
110891601f5Smrg	        else:
111891601f5Smrg		    result += ("    %sok: field \"%s\" in %s:\n\t%sbefore: %s, after: %s\n"
112891601f5Smrg		        % (indent, str(field_name), str(type_obj),
113891601f5Smrg		           indent, str(align_before), str(align_after)))
114891601f5Smrg                if self.verbosity >= 1:
115891601f5Smrg		    result += self.callstack_to_str(indent, callstack)
116891601f5Smrg
117891601f5Smrg	# output the fail-list
118891601f5Smrg	for (align_before, field_name, type_obj, callstack, reason) in self.fail_list:
119891601f5Smrg	    stacksize = len(callstack)
120891601f5Smrg            indent = '  ' * stacksize
121891601f5Smrg	    if field_name is None or field_name == "":
122891601f5Smrg	        result += ("    %sfail: align %s is incompatible with\n\t%s%s\n\t%sReason: %s\n"
123891601f5Smrg		    % (indent, str(align_before), indent, str(type_obj), indent, reason))
124891601f5Smrg	    else:
125891601f5Smrg		result += ("    %sfail: align %s is incompatible with\n\t%sfield \"%s\" in %s\n\t%sReason: %s\n"
126891601f5Smrg		    % (indent, str(align_before), indent, str(field_name), str(type_obj), indent, reason))
127891601f5Smrg
128891601f5Smrg            if self.verbosity >= 1:
129891601f5Smrg	        result += self.callstack_to_str(indent, callstack)
130891601f5Smrg
131891601f5Smrg
132891601f5Smrg	return result
133891601f5Smrg
134891601f5Smrg
135891601f5Smrg    def callstack_to_str(self, indent, callstack):
136891601f5Smrg        result = "\t%scallstack: [\n" % indent
137891601f5Smrg        for stack_elem in callstack:
138891601f5Smrg            result += "\t  %s%s\n" % (indent, str(stack_elem))
139891601f5Smrg        result += "\t%s]\n" % indent
140891601f5Smrg	return result
141891601f5Smrg
142891601f5Smrg
143891601f5Smrg    def ok_callstack_is_relevant(self, ok_callstack):
144891601f5Smrg        # determine whether an ok callstack is relevant for logging
145891601f5Smrg	if self.verbosity >= 2:
146891601f5Smrg	    return True
147891601f5Smrg
148891601f5Smrg        # empty callstacks are always relevant
149891601f5Smrg	if len(ok_callstack) == 0:
150891601f5Smrg            return True
151891601f5Smrg
152891601f5Smrg	# check whether the ok_callstack is a subset or equal to a fail_callstack
153891601f5Smrg        for (align_before, field_name, type_obj, fail_callstack, reason) in self.fail_list:
154891601f5Smrg            if len(ok_callstack) <= len(fail_callstack):
155891601f5Smrg                zipped = zip(ok_callstack, fail_callstack[:len(ok_callstack)])
156891601f5Smrg		is_subset = all([i == j for i, j in zipped])
157891601f5Smrg		if is_subset:
158891601f5Smrg                    return True
159891601f5Smrg
160891601f5Smrg        return False
161891601f5Smrg
162891601f5Smrg
163891601f5Smrg    def ok(self, align_before, field_name, type_obj, callstack, align_after):
164891601f5Smrg	self.ok_list.append((align_before, field_name, type_obj, callstack, align_after))
165891601f5Smrg
166891601f5Smrg    def fail(self, align_before, field_name, type_obj, callstack, reason):
167891601f5Smrg	self.fail_list.append((align_before, field_name, type_obj, callstack, reason))
168891601f5Smrg
169891601f5Smrg    def append(self, other):
170891601f5Smrg	self.ok_list.extend(other.ok_list)
171891601f5Smrg	self.fail_list.extend(other.fail_list)
172891601f5Smrg
173891601f5Smrg    def ok_count(self):
174891601f5Smrg	return len(self.ok_list)
175891601f5Smrg
176891601f5Smrg
177891601f5Smrg
178