1891601f5Smrg''' 2891601f5SmrgThis module contains helper classes for alignment arithmetic and checks 3891601f5Smrg''' 4891601f5Smrg 5d5c9b07bSmrgfrom sys import version_info 6d5c9b07bSmrg 7d5c9b07bSmrgif version_info[:2] >= (3, 5): 8d5c9b07bSmrg from math import gcd 9d5c9b07bSmrgelse: 10d5c9b07bSmrg from fractions import gcd 11891601f5Smrg 12891601f5Smrgclass Alignment(object): 13891601f5Smrg 14891601f5Smrg def __init__(self, align=4, offset=0): 15891601f5Smrg self.align = align 16891601f5Smrg # normalize the offset (just in case) 17891601f5Smrg self.offset = offset % align 18891601f5Smrg 19891601f5Smrg 20891601f5Smrg def __eq__(self, other): 21891601f5Smrg return self.align == other.align and self.offset == other.offset 22891601f5Smrg 23891601f5Smrg def __str__(self): 24a27842ffSmrg return "(align=%d, offset=%d)" % (self.align, self.offset) 25891601f5Smrg 26891601f5Smrg @staticmethod 27891601f5Smrg def for_primitive_type(size): 28a27842ffSmrg # compute the required start_alignment based on the size of the type 29a27842ffSmrg if size % 8 == 0: 30891601f5Smrg # do 8-byte primitives require 8-byte alignment in X11? 31891601f5Smrg return Alignment(8,0) 32891601f5Smrg elif size % 4 == 0: 33891601f5Smrg return Alignment(4,0) 34891601f5Smrg elif size % 2 == 0: 35891601f5Smrg return Alignment(2,0) 36891601f5Smrg else: 37891601f5Smrg return Alignment(1,0) 38891601f5Smrg 39891601f5Smrg 40891601f5Smrg def align_after_fixed_size(self, size): 41a27842ffSmrg new_offset = (self.offset + size) % self.align 42891601f5Smrg return Alignment(self.align, new_offset) 43891601f5Smrg 44891601f5Smrg 45891601f5Smrg def is_guaranteed_at(self, external_align): 46891601f5Smrg ''' 47891601f5Smrg Assuming the given external_align, checks whether 48891601f5Smrg self is fulfilled for all cases. 49a27842ffSmrg Returns True if yes, False otherwise. 50891601f5Smrg ''' 51891601f5Smrg if self.align == 1 and self.offset == 0: 52891601f5Smrg # alignment 1 with offset 0 is always fulfilled 53891601f5Smrg return True 54891601f5Smrg 55891601f5Smrg if external_align is None: 56891601f5Smrg # there is no external align -> fail 57891601f5Smrg return False 58891601f5Smrg 59891601f5Smrg if external_align.align < self.align: 60891601f5Smrg # the external align guarantees less alignment -> not guaranteed 61891601f5Smrg return False 62891601f5Smrg 63a27842ffSmrg if external_align.align % self.align != 0: 64891601f5Smrg # the external align cannot be divided by our align 65a27842ffSmrg # -> not guaranteed 66891601f5Smrg # (this can only happen if there are alignments that are not 67891601f5Smrg # a power of 2, which is highly discouraged. But better be 68891601f5Smrg # safe and check for it) 69891601f5Smrg return False 70891601f5Smrg 71891601f5Smrg if external_align.offset % self.align != self.offset: 72891601f5Smrg # offsets do not match 73891601f5Smrg return False 74891601f5Smrg 75891601f5Smrg return True 76891601f5Smrg 77891601f5Smrg 78891601f5Smrg def combine_with(self, other): 79891601f5Smrg # returns the alignment that is guaranteed when 80a27842ffSmrg # both, self or other, can happen 81891601f5Smrg new_align = gcd(self.align, other.align) 82891601f5Smrg new_offset_candidate1 = self.offset % new_align 83891601f5Smrg new_offset_candidate2 = other.offset % new_align 84891601f5Smrg if new_offset_candidate1 == new_offset_candidate2: 85891601f5Smrg new_offset = new_offset_candidate1 86891601f5Smrg else: 87891601f5Smrg offset_diff = abs(new_offset_candidate2 - new_offset_candidate1) 88891601f5Smrg new_align = gcd(new_align, offset_diff) 89891601f5Smrg new_offset_candidate1 = self.offset % new_align 90891601f5Smrg new_offset_candidate2 = other.offset % new_align 91a27842ffSmrg assert new_offset_candidate1 == new_offset_candidate2 92a27842ffSmrg new_offset = new_offset_candidate1 93891601f5Smrg # return the result 94891601f5Smrg return Alignment(new_align, new_offset) 95891601f5Smrg 96891601f5Smrg 97891601f5Smrgclass AlignmentLog(object): 98891601f5Smrg 99891601f5Smrg def __init__(self): 100a27842ffSmrg self.ok_list = [] 101a27842ffSmrg self.fail_list = [] 102a27842ffSmrg self.verbosity = 1 103891601f5Smrg 104891601f5Smrg def __str__(self): 105a27842ffSmrg result = "" 106891601f5Smrg 107a27842ffSmrg # output the OK-list 108a27842ffSmrg for (align_before, field_name, type_obj, callstack, align_after) in self.ok_list: 109a27842ffSmrg stacksize = len(callstack) 110891601f5Smrg indent = ' ' * stacksize 111a27842ffSmrg if self.ok_callstack_is_relevant(callstack): 112891601f5Smrg if field_name is None or field_name == "": 113a27842ffSmrg result += (" %sok: %s:\n\t%sbefore: %s, after: %s\n" 114a27842ffSmrg % (indent, str(type_obj), indent, str(align_before), str(align_after))) 115a27842ffSmrg else: 116a27842ffSmrg result += (" %sok: field \"%s\" in %s:\n\t%sbefore: %s, after: %s\n" 117a27842ffSmrg % (indent, str(field_name), str(type_obj), 118a27842ffSmrg indent, str(align_before), str(align_after))) 119891601f5Smrg if self.verbosity >= 1: 120a27842ffSmrg result += self.callstack_to_str(indent, callstack) 121891601f5Smrg 122a27842ffSmrg # output the fail-list 123a27842ffSmrg for (align_before, field_name, type_obj, callstack, reason) in self.fail_list: 124a27842ffSmrg stacksize = len(callstack) 125891601f5Smrg indent = ' ' * stacksize 126a27842ffSmrg if field_name is None or field_name == "": 127a27842ffSmrg result += (" %sfail: align %s is incompatible with\n\t%s%s\n\t%sReason: %s\n" 128a27842ffSmrg % (indent, str(align_before), indent, str(type_obj), indent, reason)) 129a27842ffSmrg else: 130a27842ffSmrg result += (" %sfail: align %s is incompatible with\n\t%sfield \"%s\" in %s\n\t%sReason: %s\n" 131a27842ffSmrg % (indent, str(align_before), indent, str(field_name), str(type_obj), indent, reason)) 132891601f5Smrg 133891601f5Smrg if self.verbosity >= 1: 134a27842ffSmrg result += self.callstack_to_str(indent, callstack) 135891601f5Smrg 136891601f5Smrg 137a27842ffSmrg return result 138891601f5Smrg 139891601f5Smrg 140891601f5Smrg def callstack_to_str(self, indent, callstack): 141891601f5Smrg result = "\t%scallstack: [\n" % indent 142891601f5Smrg for stack_elem in callstack: 143891601f5Smrg result += "\t %s%s\n" % (indent, str(stack_elem)) 144891601f5Smrg result += "\t%s]\n" % indent 145a27842ffSmrg return result 146891601f5Smrg 147891601f5Smrg 148891601f5Smrg def ok_callstack_is_relevant(self, ok_callstack): 149891601f5Smrg # determine whether an ok callstack is relevant for logging 150a27842ffSmrg if self.verbosity >= 2: 151a27842ffSmrg return True 152891601f5Smrg 153891601f5Smrg # empty callstacks are always relevant 154a27842ffSmrg if len(ok_callstack) == 0: 155891601f5Smrg return True 156891601f5Smrg 157a27842ffSmrg # check whether the ok_callstack is a subset or equal to a fail_callstack 158891601f5Smrg for (align_before, field_name, type_obj, fail_callstack, reason) in self.fail_list: 159891601f5Smrg if len(ok_callstack) <= len(fail_callstack): 160891601f5Smrg zipped = zip(ok_callstack, fail_callstack[:len(ok_callstack)]) 161a27842ffSmrg is_subset = all([i == j for i, j in zipped]) 162a27842ffSmrg if is_subset: 163891601f5Smrg return True 164891601f5Smrg 165891601f5Smrg return False 166891601f5Smrg 167891601f5Smrg 168891601f5Smrg def ok(self, align_before, field_name, type_obj, callstack, align_after): 169a27842ffSmrg self.ok_list.append((align_before, field_name, type_obj, callstack, align_after)) 170891601f5Smrg 171891601f5Smrg def fail(self, align_before, field_name, type_obj, callstack, reason): 172a27842ffSmrg self.fail_list.append((align_before, field_name, type_obj, callstack, reason)) 173891601f5Smrg 174891601f5Smrg def append(self, other): 175a27842ffSmrg self.ok_list.extend(other.ok_list) 176a27842ffSmrg self.fail_list.extend(other.fail_list) 177891601f5Smrg 178891601f5Smrg def ok_count(self): 179a27842ffSmrg return len(self.ok_list) 180891601f5Smrg 181891601f5Smrg 182891601f5Smrg 183