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