1*134e1779SJakub Wojciech Klama#! /usr/bin/env python 2*134e1779SJakub Wojciech Klama 3*134e1779SJakub Wojciech Klama""" 4*134e1779SJakub Wojciech KlamaInteger number allocator. 5*134e1779SJakub Wojciech Klama 6*134e1779SJakub Wojciech KlamaBasically, these keep track of a set of allocatable values in 7*134e1779SJakub Wojciech Klamasome range (you provide min and max) and let you allocate out of 8*134e1779SJakub Wojciech Klamathe range and return values into the range. 9*134e1779SJakub Wojciech Klama 10*134e1779SJakub Wojciech KlamaYou may pick a value using "next since last time", or "next 11*134e1779SJakub Wojciech Klamaavailable after provided value". Note that next-after will 12*134e1779SJakub Wojciech Klamawrap around as needed (modular arithmetic style). 13*134e1779SJakub Wojciech Klama 14*134e1779SJakub Wojciech KlamaThe free lists are thread-locked so that this code can be used 15*134e1779SJakub Wojciech Klamawith threads. 16*134e1779SJakub Wojciech Klama 17*134e1779SJakub Wojciech Klama >>> a = NumAlloc(5, 10) # note closed interval: 5..10 inclusive 18*134e1779SJakub Wojciech Klama >>> a 19*134e1779SJakub Wojciech Klama NumAlloc(5, 10) 20*134e1779SJakub Wojciech Klama >>> a.avail 21*134e1779SJakub Wojciech Klama [[5, 10]] 22*134e1779SJakub Wojciech Klama >>> a.alloc() 23*134e1779SJakub Wojciech Klama 5 24*134e1779SJakub Wojciech Klama >>> a.avail 25*134e1779SJakub Wojciech Klama [[6, 10]] 26*134e1779SJakub Wojciech Klama >>> a.alloc(8) 27*134e1779SJakub Wojciech Klama 8 28*134e1779SJakub Wojciech Klama >>> a.avail 29*134e1779SJakub Wojciech Klama [[6, 7], [9, 10]] 30*134e1779SJakub Wojciech Klama >>> a.free(5) 31*134e1779SJakub Wojciech Klama >>> a.avail 32*134e1779SJakub Wojciech Klama [[5, 7], [9, 10]] 33*134e1779SJakub Wojciech Klama >>> a.free(8) 34*134e1779SJakub Wojciech Klama >>> a.avail 35*134e1779SJakub Wojciech Klama [[5, 10]] 36*134e1779SJakub Wojciech Klama 37*134e1779SJakub Wojciech KlamaAttempting to free a value that is already free is an error: 38*134e1779SJakub Wojciech Klama 39*134e1779SJakub Wojciech Klama >>> a.free(5) 40*134e1779SJakub Wojciech Klama Traceback (most recent call last): 41*134e1779SJakub Wojciech Klama ... 42*134e1779SJakub Wojciech Klama ValueError: free: 5 already available 43*134e1779SJakub Wojciech Klama 44*134e1779SJakub Wojciech KlamaYou can, however, free a value that is outside the min/max 45*134e1779SJakub Wojciech Klamarange. You can also free multiple values at once: 46*134e1779SJakub Wojciech Klama 47*134e1779SJakub Wojciech Klama >>> a.free_multi([0, 1, 2, 4]) 48*134e1779SJakub Wojciech Klama >>> a.avail 49*134e1779SJakub Wojciech Klama [[0, 2], [4, 10]] 50*134e1779SJakub Wojciech Klama >>> a.free_multi([3, 12]) 51*134e1779SJakub Wojciech Klama >>> a.avail 52*134e1779SJakub Wojciech Klama [[0, 10], [12, 12]] 53*134e1779SJakub Wojciech Klama 54*134e1779SJakub Wojciech KlamaNote that this changes the min/max values: 55*134e1779SJakub Wojciech Klama 56*134e1779SJakub Wojciech Klama >>> a 57*134e1779SJakub Wojciech Klama NumAlloc(0, 12) 58*134e1779SJakub Wojciech Klama 59*134e1779SJakub Wojciech KlamaTo prevent adding values outside the min/max range, create the 60*134e1779SJakub Wojciech KlamaNumArray with autoextend=False, or set .autoextend=False at any 61*134e1779SJakub Wojciech Klamatime: 62*134e1779SJakub Wojciech Klama 63*134e1779SJakub Wojciech Klama >>> a.autoextend = False 64*134e1779SJakub Wojciech Klama >>> a 65*134e1779SJakub Wojciech Klama NumAlloc(0, 12, autoextend=False) 66*134e1779SJakub Wojciech Klama >>> a.free(13) 67*134e1779SJakub Wojciech Klama Traceback (most recent call last): 68*134e1779SJakub Wojciech Klama ... 69*134e1779SJakub Wojciech Klama ValueError: free: 13 is outside range limit 70*134e1779SJakub Wojciech Klama 71*134e1779SJakub Wojciech KlamaYou can create an empty range, which is really only useful once 72*134e1779SJakub Wojciech Klamayou free values into it: 73*134e1779SJakub Wojciech Klama 74*134e1779SJakub Wojciech Klama >>> r = NumAlloc(0, -1) 75*134e1779SJakub Wojciech Klama >>> r 76*134e1779SJakub Wojciech Klama NumAlloc(0, -1) 77*134e1779SJakub Wojciech Klama >>> r.alloc() is None 78*134e1779SJakub Wojciech Klama True 79*134e1779SJakub Wojciech Klama >>> r.free_multi(range(50)) 80*134e1779SJakub Wojciech Klama >>> r 81*134e1779SJakub Wojciech Klama NumAlloc(0, 49) 82*134e1779SJakub Wojciech Klama 83*134e1779SJakub Wojciech KlamaNote that r.alloc() starts from where you last left off, even if 84*134e1779SJakub Wojciech Klamayou've freed a value: 85*134e1779SJakub Wojciech Klama 86*134e1779SJakub Wojciech Klama >>> r.alloc() 87*134e1779SJakub Wojciech Klama 0 88*134e1779SJakub Wojciech Klama >>> r.free(0) 89*134e1779SJakub Wojciech Klama >>> r.alloc() 90*134e1779SJakub Wojciech Klama 1 91*134e1779SJakub Wojciech Klama 92*134e1779SJakub Wojciech KlamaOf course, in multithreaded code you can't really depend on this 93*134e1779SJakub Wojciech Klamasince it will race other threads. Still, it generally makes for 94*134e1779SJakub Wojciech Klamaefficient allocation. To force allocation to start from the 95*134e1779SJakub Wojciech Klamarange's minimum, provide the minimum (e.g., r.min_val) as an 96*134e1779SJakub Wojciech Klamaargument to r.alloc(): 97*134e1779SJakub Wojciech Klama 98*134e1779SJakub Wojciech Klama >>> r.alloc() 99*134e1779SJakub Wojciech Klama 2 100*134e1779SJakub Wojciech Klama >>> r.alloc(r.min_val) 101*134e1779SJakub Wojciech Klama 0 102*134e1779SJakub Wojciech Klama 103*134e1779SJakub Wojciech KlamaProviding a number to alloc() tries to allocate that number, 104*134e1779SJakub Wojciech Klamabut wraps around to the next one if needed: 105*134e1779SJakub Wojciech Klama 106*134e1779SJakub Wojciech Klama >>> r.alloc(49) 107*134e1779SJakub Wojciech Klama 49 108*134e1779SJakub Wojciech Klama >>> r.alloc(49) 109*134e1779SJakub Wojciech Klama 3 110*134e1779SJakub Wojciech Klama >>> r.alloc(99999) 111*134e1779SJakub Wojciech Klama 4 112*134e1779SJakub Wojciech Klama >>> r.avail 113*134e1779SJakub Wojciech Klama [[5, 48]] 114*134e1779SJakub Wojciech Klama 115*134e1779SJakub Wojciech KlamaThere is currently no way to find all allocated values, although 116*134e1779SJakub Wojciech Klamathe obvious method (going through r.avail) will work. Any iterator 117*134e1779SJakub Wojciech Klamawould not be thread-safe. 118*134e1779SJakub Wojciech Klama""" 119*134e1779SJakub Wojciech Klama 120*134e1779SJakub Wojciech Klamaimport threading 121*134e1779SJakub Wojciech Klama 122*134e1779SJakub Wojciech Klamaclass NumAlloc(object): 123*134e1779SJakub Wojciech Klama """ 124*134e1779SJakub Wojciech Klama Number allocator object. 125*134e1779SJakub Wojciech Klama """ 126*134e1779SJakub Wojciech Klama def __init__(self, min_val, max_val, autoextend=True): 127*134e1779SJakub Wojciech Klama self.min_val = min_val 128*134e1779SJakub Wojciech Klama self.max_val = max_val 129*134e1779SJakub Wojciech Klama if min_val <= max_val: 130*134e1779SJakub Wojciech Klama self.avail = [[min_val, max_val]] 131*134e1779SJakub Wojciech Klama else: 132*134e1779SJakub Wojciech Klama self.avail = [] 133*134e1779SJakub Wojciech Klama self.autoextend = autoextend 134*134e1779SJakub Wojciech Klama self.last = None 135*134e1779SJakub Wojciech Klama self.lock = threading.Lock() 136*134e1779SJakub Wojciech Klama 137*134e1779SJakub Wojciech Klama def __repr__(self): 138*134e1779SJakub Wojciech Klama myname = self.__class__.__name__ 139*134e1779SJakub Wojciech Klama if self.autoextend: 140*134e1779SJakub Wojciech Klama ae = '' 141*134e1779SJakub Wojciech Klama else: 142*134e1779SJakub Wojciech Klama ae = ', autoextend=False' 143*134e1779SJakub Wojciech Klama return '{0}({1}, {2}{3})'.format(myname, self.min_val, self.max_val, ae) 144*134e1779SJakub Wojciech Klama 145*134e1779SJakub Wojciech Klama def _find_block(self, val): 146*134e1779SJakub Wojciech Klama """ 147*134e1779SJakub Wojciech Klama Find the block that contains val, or that should contain val. 148*134e1779SJakub Wojciech Klama Remember that self.avail is a list of avaliable ranges of 149*134e1779SJakub Wojciech Klama the form [[min1, max1], [min2, max2], ..., [minN, maxN]] 150*134e1779SJakub Wojciech Klama where max1 < min2, max2 < min3, ..., < minN. 151*134e1779SJakub Wojciech Klama 152*134e1779SJakub Wojciech Klama The input value either falls into one of the available 153*134e1779SJakub Wojciech Klama blocks, or falls into a gap between two available blocks. 154*134e1779SJakub Wojciech Klama We want to know which block it goes in, or if it goes 155*134e1779SJakub Wojciech Klama between two, which block it comes before. 156*134e1779SJakub Wojciech Klama 157*134e1779SJakub Wojciech Klama We can do a binary search to find this block. When we 158*134e1779SJakub Wojciech Klama find it, return its index and its values. 159*134e1779SJakub Wojciech Klama 160*134e1779SJakub Wojciech Klama If we find that val is not in a block, return the position 161*134e1779SJakub Wojciech Klama where the value should go, were it to be put into a new 162*134e1779SJakub Wojciech Klama block by itself. E.g., suppose val is 17, and there is a 163*134e1779SJakub Wojciech Klama block [14,16] and a block [18,20]. We would make this 164*134e1779SJakub Wojciech Klama [14,16],[17,17],[18,20] by inserting [17,17] between them. 165*134e1779SJakub Wojciech Klama (Afterward, we will want to fuse all three blocks to make 166*134e1779SJakub Wojciech Klama [14,18]. However, if we insert as block 0, e.g., if the 167*134e1779SJakub Wojciech Klama list starts with [18,20] and we insert to get 168*134e1779SJakub Wojciech Klama [17,17][18,20], we really end up just modifying block 0 to 169*134e1779SJakub Wojciech Klama [17,20]. Or, if we insert as the new final block, we 170*134e1779SJakub Wojciech Klama might end up modifying the last block.) 171*134e1779SJakub Wojciech Klama """ 172*134e1779SJakub Wojciech Klama low = 0 173*134e1779SJakub Wojciech Klama high = len(self.avail) - 1 174*134e1779SJakub Wojciech Klama while low <= high: 175*134e1779SJakub Wojciech Klama mid = low + ((high - low) // 2) 176*134e1779SJakub Wojciech Klama pair = self.avail[mid] 177*134e1779SJakub Wojciech Klama if val < pair[0]: 178*134e1779SJakub Wojciech Klama # must go before block mid 179*134e1779SJakub Wojciech Klama high = mid - 1 180*134e1779SJakub Wojciech Klama elif val > pair[1]: 181*134e1779SJakub Wojciech Klama # must go after block mid 182*134e1779SJakub Wojciech Klama low = mid + 1 183*134e1779SJakub Wojciech Klama else: 184*134e1779SJakub Wojciech Klama # val >= first and val <= last, so we found it 185*134e1779SJakub Wojciech Klama return mid, pair 186*134e1779SJakub Wojciech Klama # Low > high: no block actually contains val, or 187*134e1779SJakub Wojciech Klama # there are no blocks at all. If there are no blocks, 188*134e1779SJakub Wojciech Klama # return block #0 and None. Otherwise return the 189*134e1779SJakub Wojciech Klama return low, None 190*134e1779SJakub Wojciech Klama 191*134e1779SJakub Wojciech Klama def alloc(self, val=None): 192*134e1779SJakub Wojciech Klama """ 193*134e1779SJakub Wojciech Klama Get new available value. 194*134e1779SJakub Wojciech Klama 195*134e1779SJakub Wojciech Klama If val is None, we start from the most recently 196*134e1779SJakub Wojciech Klama allocated value, plus 1. 197*134e1779SJakub Wojciech Klama 198*134e1779SJakub Wojciech Klama If val is a numeric value, we start from that value. 199*134e1779SJakub Wojciech Klama Hence, since the range is min_val..max_val, you can 200*134e1779SJakub Wojciech Klama provide min_val to take the first available value. 201*134e1779SJakub Wojciech Klama 202*134e1779SJakub Wojciech Klama This may return None, if no values are still available. 203*134e1779SJakub Wojciech Klama """ 204*134e1779SJakub Wojciech Klama with self.lock: 205*134e1779SJakub Wojciech Klama if val is None: 206*134e1779SJakub Wojciech Klama val = self.last + 1 if self.last is not None else self.min_val 207*134e1779SJakub Wojciech Klama if val is None or val > self.max_val or val < self.min_val: 208*134e1779SJakub Wojciech Klama val = self.min_val 209*134e1779SJakub Wojciech Klama i, pair = self._find_block(val) 210*134e1779SJakub Wojciech Klama if pair is None: 211*134e1779SJakub Wojciech Klama # Value is is not available. The next 212*134e1779SJakub Wojciech Klama # available value that is greater than val 213*134e1779SJakub Wojciech Klama # is in the block right after block i. 214*134e1779SJakub Wojciech Klama # If there is no block after i, the next 215*134e1779SJakub Wojciech Klama # available value is in block 0. If there 216*134e1779SJakub Wojciech Klama # is no block 0, there are no available 217*134e1779SJakub Wojciech Klama # values. 218*134e1779SJakub Wojciech Klama nblocks = len(self.avail) 219*134e1779SJakub Wojciech Klama i += 1 220*134e1779SJakub Wojciech Klama if i >= nblocks: 221*134e1779SJakub Wojciech Klama if nblocks == 0: 222*134e1779SJakub Wojciech Klama return None 223*134e1779SJakub Wojciech Klama i = 0 224*134e1779SJakub Wojciech Klama pair = self.avail[i] 225*134e1779SJakub Wojciech Klama val = pair[0] 226*134e1779SJakub Wojciech Klama # Value val is available - take it. 227*134e1779SJakub Wojciech Klama # 228*134e1779SJakub Wojciech Klama # There are four special cases to handle. 229*134e1779SJakub Wojciech Klama # 230*134e1779SJakub Wojciech Klama # 1. pair[0] < val < pair[1]: split the pair. 231*134e1779SJakub Wojciech Klama # 2. pair[0] == val < pair[1]: increase pair[0]. 232*134e1779SJakub Wojciech Klama # 3. pair[0] == val == pair[1]: delete the pair 233*134e1779SJakub Wojciech Klama # 4. pair[0] < val == pair[1]: decrease pair[1]. 234*134e1779SJakub Wojciech Klama assert pair[0] <= val <= pair[1] 235*134e1779SJakub Wojciech Klama if pair[0] == val: 236*134e1779SJakub Wojciech Klama # case 2 or 3: Take the left edge or delete the pair. 237*134e1779SJakub Wojciech Klama if val == pair[1]: 238*134e1779SJakub Wojciech Klama del self.avail[i] 239*134e1779SJakub Wojciech Klama else: 240*134e1779SJakub Wojciech Klama pair[0] = val + 1 241*134e1779SJakub Wojciech Klama else: 242*134e1779SJakub Wojciech Klama # case 1 or 4: split the pair or take the right edge. 243*134e1779SJakub Wojciech Klama if val == pair[1]: 244*134e1779SJakub Wojciech Klama pair[1] = val - 1 245*134e1779SJakub Wojciech Klama else: 246*134e1779SJakub Wojciech Klama newpair = [val + 1, pair[1]] 247*134e1779SJakub Wojciech Klama pair[1] = val - 1 248*134e1779SJakub Wojciech Klama self.avail.insert(i + 1, newpair) 249*134e1779SJakub Wojciech Klama self.last = val 250*134e1779SJakub Wojciech Klama return val 251*134e1779SJakub Wojciech Klama 252*134e1779SJakub Wojciech Klama def free(self, val): 253*134e1779SJakub Wojciech Klama "Free one value" 254*134e1779SJakub Wojciech Klama self._free_multi('free', [val]) 255*134e1779SJakub Wojciech Klama 256*134e1779SJakub Wojciech Klama def free_multi(self, values): 257*134e1779SJakub Wojciech Klama "Free many values (provide any iterable)" 258*134e1779SJakub Wojciech Klama values = list(values) 259*134e1779SJakub Wojciech Klama values.sort() 260*134e1779SJakub Wojciech Klama self._free_multi('free_multi', values) 261*134e1779SJakub Wojciech Klama 262*134e1779SJakub Wojciech Klama def _free_multi(self, how, values): 263*134e1779SJakub Wojciech Klama """ 264*134e1779SJakub Wojciech Klama Free a (sorted) list of values. 265*134e1779SJakub Wojciech Klama """ 266*134e1779SJakub Wojciech Klama if len(values) == 0: 267*134e1779SJakub Wojciech Klama return 268*134e1779SJakub Wojciech Klama with self.lock: 269*134e1779SJakub Wojciech Klama while values: 270*134e1779SJakub Wojciech Klama # Take highest value, and any contiguous lower values. 271*134e1779SJakub Wojciech Klama # Note that it can be significantly faster this way 272*134e1779SJakub Wojciech Klama # since coalesced ranges make for shorter copies. 273*134e1779SJakub Wojciech Klama highval = values.pop() 274*134e1779SJakub Wojciech Klama val = highval 275*134e1779SJakub Wojciech Klama while len(values) and values[-1] == val - 1: 276*134e1779SJakub Wojciech Klama val = values.pop() 277*134e1779SJakub Wojciech Klama self._free_range(how, val, highval) 278*134e1779SJakub Wojciech Klama 279*134e1779SJakub Wojciech Klama def _maybe_increase_max(self, how, val): 280*134e1779SJakub Wojciech Klama """ 281*134e1779SJakub Wojciech Klama If needed, widen our range to include new high val -- i.e., 282*134e1779SJakub Wojciech Klama possibly increase self.max_val. Do nothing if this is not a 283*134e1779SJakub Wojciech Klama new all time high; fail if we have autoextend disabled. 284*134e1779SJakub Wojciech Klama """ 285*134e1779SJakub Wojciech Klama if val <= self.max_val: 286*134e1779SJakub Wojciech Klama return 287*134e1779SJakub Wojciech Klama if self.autoextend: 288*134e1779SJakub Wojciech Klama self.max_val = val 289*134e1779SJakub Wojciech Klama return 290*134e1779SJakub Wojciech Klama raise ValueError('{0}: {1} is outside range limit'.format(how, val)) 291*134e1779SJakub Wojciech Klama 292*134e1779SJakub Wojciech Klama def _maybe_decrease_min(self, how, val): 293*134e1779SJakub Wojciech Klama """ 294*134e1779SJakub Wojciech Klama If needed, widen our range to include new low val -- i.e., 295*134e1779SJakub Wojciech Klama possibly decrease self.min_val. Do nothing if this is not a 296*134e1779SJakub Wojciech Klama new all time low; fail if we have autoextend disabled. 297*134e1779SJakub Wojciech Klama """ 298*134e1779SJakub Wojciech Klama if val >= self.min_val: 299*134e1779SJakub Wojciech Klama return 300*134e1779SJakub Wojciech Klama if self.autoextend: 301*134e1779SJakub Wojciech Klama self.min_val = val 302*134e1779SJakub Wojciech Klama return 303*134e1779SJakub Wojciech Klama raise ValueError('{0}: {1} is outside range limit'.format(how, val)) 304*134e1779SJakub Wojciech Klama 305*134e1779SJakub Wojciech Klama def _free_range(self, how, val, highval): 306*134e1779SJakub Wojciech Klama """ 307*134e1779SJakub Wojciech Klama Free the range [val..highval]. Note, val==highval it's just 308*134e1779SJakub Wojciech Klama a one-element range. 309*134e1779SJakub Wojciech Klama 310*134e1779SJakub Wojciech Klama The lock is already held. 311*134e1779SJakub Wojciech Klama """ 312*134e1779SJakub Wojciech Klama # Find the place to store the lower value. 313*134e1779SJakub Wojciech Klama # We should never find an actual pair here. 314*134e1779SJakub Wojciech Klama i, pair = self._find_block(val) 315*134e1779SJakub Wojciech Klama if pair: 316*134e1779SJakub Wojciech Klama raise ValueError('{0}: {1} already available'.format(how, val)) 317*134e1779SJakub Wojciech Klama # If we're freeing a range, check that the high val 318*134e1779SJakub Wojciech Klama # does not span into the *next* range, either. 319*134e1779SJakub Wojciech Klama if highval > val and i < len(self.avail): 320*134e1779SJakub Wojciech Klama if self.avail[i][0] <= highval: 321*134e1779SJakub Wojciech Klama raise ValueError('{0}: {2} (from {{1}..{2}) already ' 322*134e1779SJakub Wojciech Klama 'available'.format(how, val, highval)) 323*134e1779SJakub Wojciech Klama 324*134e1779SJakub Wojciech Klama # We'll need to insert a block and perhaps fuse it 325*134e1779SJakub Wojciech Klama # with blocks before and/or after. First, check 326*134e1779SJakub Wojciech Klama # whether there *is* a before and/or after, and find 327*134e1779SJakub Wojciech Klama # their corresponding edges and whether we abut them. 328*134e1779SJakub Wojciech Klama if i > 0: 329*134e1779SJakub Wojciech Klama abuts_below = self.avail[i - 1][1] + 1 == val 330*134e1779SJakub Wojciech Klama else: 331*134e1779SJakub Wojciech Klama abuts_below = False 332*134e1779SJakub Wojciech Klama if i < len(self.avail): 333*134e1779SJakub Wojciech Klama abuts_above = self.avail[i][0] - 1 == highval 334*134e1779SJakub Wojciech Klama else: 335*134e1779SJakub Wojciech Klama abuts_above = False 336*134e1779SJakub Wojciech Klama # Now there are these four cases: 337*134e1779SJakub Wojciech Klama # 1. abuts below and above: fuse the two blocks. 338*134e1779SJakub Wojciech Klama # 2. abuts below only: adjust previous (i-1'th) block 339*134e1779SJakub Wojciech Klama # 3. abuts above only: adjust next (i'th) block 340*134e1779SJakub Wojciech Klama # 4. doesn't abut: insert new block 341*134e1779SJakub Wojciech Klama if abuts_below: 342*134e1779SJakub Wojciech Klama if abuts_above: 343*134e1779SJakub Wojciech Klama # case 1 344*134e1779SJakub Wojciech Klama self.avail[i - 1][1] = self.avail[i][1] 345*134e1779SJakub Wojciech Klama del self.avail[i] 346*134e1779SJakub Wojciech Klama else: 347*134e1779SJakub Wojciech Klama # case 2 348*134e1779SJakub Wojciech Klama self._maybe_increase_max(how, highval) 349*134e1779SJakub Wojciech Klama self.avail[i - 1][1] = highval 350*134e1779SJakub Wojciech Klama else: 351*134e1779SJakub Wojciech Klama if abuts_above: 352*134e1779SJakub Wojciech Klama # case 3 353*134e1779SJakub Wojciech Klama self._maybe_decrease_min(how, val) 354*134e1779SJakub Wojciech Klama self.avail[i][0] = val 355*134e1779SJakub Wojciech Klama else: 356*134e1779SJakub Wojciech Klama # case 4 357*134e1779SJakub Wojciech Klama self._maybe_decrease_min(how, val) 358*134e1779SJakub Wojciech Klama self._maybe_increase_max(how, highval) 359*134e1779SJakub Wojciech Klama newblock = [val, highval] 360*134e1779SJakub Wojciech Klama self.avail.insert(i, newblock) 361*134e1779SJakub Wojciech Klama 362*134e1779SJakub Wojciech Klamaif __name__ == '__main__': 363*134e1779SJakub Wojciech Klama import doctest 364*134e1779SJakub Wojciech Klama import sys 365*134e1779SJakub Wojciech Klama 366*134e1779SJakub Wojciech Klama doctest.testmod() 367*134e1779SJakub Wojciech Klama if sys.version_info[0] >= 3: 368*134e1779SJakub Wojciech Klama xrange = range 369*134e1779SJakub Wojciech Klama # run some worst case tests 370*134e1779SJakub Wojciech Klama # NB: coalesce is terribly slow when done bottom up 371*134e1779SJakub Wojciech Klama r = NumAlloc(0, 2**16 - 1) 372*134e1779SJakub Wojciech Klama for i in xrange(r.min_val, r.max_val, 2): 373*134e1779SJakub Wojciech Klama r.alloc(i) 374*134e1779SJakub Wojciech Klama print('worst case alloc: len(r.avail) = {0}'.format(len(r.avail))) 375*134e1779SJakub Wojciech Klama for i in xrange(r.max_val - 1, r.min_val, -2): 376*134e1779SJakub Wojciech Klama r.free(i) 377*134e1779SJakub Wojciech Klama print('free again; len(r.avail) should be 1; is {0}'.format(len(r.avail))) 378*134e1779SJakub Wojciech Klama if len(r.avail) != 1: 379*134e1779SJakub Wojciech Klama sys.exit('failure') 380