xref: /freebsd/contrib/lib9p/pytest/numalloc.py (revision 134e17798c9af53632b372348ab828e75e65bf46)
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