1 /*
2 Copyright (c) 2005, 2021, Oracle and/or its affiliates.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License, version 2.0,
6 as published by the Free Software Foundation.
7
8 This program is also distributed with certain software (including
9 but not limited to OpenSSL) that is licensed under separate terms,
10 as designated in a particular file or component or in included license
11 documentation. The authors of MySQL hereby grant you an additional
12 permission to link the program and your derivative works with the
13 separately licensed software that they have included with MySQL.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License, version 2.0, for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 */
24
25 #ifndef LINEAR_POOL_HPP
26 #define LINEAR_POOL_HPP
27
28 #include <Bitmask.hpp>
29 #include "SuperPool.hpp"
30
31 /*
32 * LinearPool - indexed record pool
33 *
34 * LinearPool implements a pool where each record has a 0-based index.
35 * Any index value (up to 2^32-1) is allowed. Normal efficient usage is
36 * to assign index values in sequence and to re-use any values which
37 * have become free. This is default seize/release behaviour.
38 *
39 * LinearPool has 2 internal RecordPool instances:
40 *
41 * (a) record pool of T (the template argument class)
42 * (b) record pool of "maps" (array of Uint32)
43 *
44 * The maps translate an index into an i-value in (a). Each map has
45 * a level. Level 0 maps point to i-values. Level N+1 maps point to
46 * level N maps. There is a unique "root map" at top.
47 *
48 * This works exactly like numbers in a given base. Each map has base
49 * size entries. For implementation convenience the base must be power
50 * of 2 between 2^1 and 2^15. It is given by its log2 value (1-15).
51 *
52 * A position in a map is also called a "digit".
53 *
54 * There is a doubly linked list of available maps (some free entries)
55 * on each level. There is a doubly linked freelist within each map.
56 * There is also a bitmask of used entries in each map.
57 *
58 * Level 0 free entry has space for one record. Level N free entry
59 * implies space for base^N records. The implied levels are created and
60 * removed on demand. Empty maps are usually removed.
61 *
62 * Default base is 256 (log2 = 8) which requires maximum 4 levels or
63 * digits (similar to ip address).
64 *
65 * TODO
66 *
67 * - move most of the inline code to LinearPool.cpp
68 * - optimize for common case
69 * - add optimized 2-level implementation (?)
70 */
71
72 #include "SuperPool.hpp"
73
74 #define JAM_FILE_ID 264
75
76
77 template <class T, Uint32 LogBase = 8>
78 class LinearPool {
79 typedef SuperPool::PtrI PtrI;
80
81 // Base.
82 STATIC_CONST( Base = 1 << LogBase );
83
84 // Digit mask.
85 STATIC_CONST( DigitMask = Base - 1 );
86
87 // Max possible levels (0 to max root level).
88 STATIC_CONST( MaxLevels = (32 + LogBase - 1) / LogBase );
89
90 // Number of words in map used bit mask.
91 STATIC_CONST( BitmaskSize = (Base + 31) / 32 );
92
93 // Map.
94 struct Map {
95 Uint32 m_level;
96 Uint32 m_occup; // number of used entries
97 Uint32 m_firstfree; // position of first free entry
98 PtrI m_parent; // parent map
99 Uint32 m_index; // from root to here
100 PtrI m_nextavail;
101 PtrI m_prevavail;
102 Uint32 m_bitmask[BitmaskSize];
103 PtrI m_entry[Base];
104 };
105
106 public:
107
108 // Constructor.
109 LinearPool(GroupPool& gp);
110
111 // Destructor.
112 ~LinearPool();
113
114 // Update pointer ptr.p according to index value ptr.i.
115 void getPtr(Ptr<T>& ptr);
116
117 // Allocate record from the pool. Reuses free index if possible.
118 bool seize(Ptr<T>& ptr);
119
120 // Allocate given index. Like seize but returns -1 if in use.
121 int seize_index(Ptr<T>& ptr, Uint32 index);
122
123 // Return record to the pool.
124 void release(Ptr<T>& ptr);
125
126 // Return number of used records (may require 1 page scan).
127 Uint32 count();
128
129 // Verify (debugging).
130 void verify();
131
132 private:
133
134 // Given index find the bottom map.
135 void get_map(Ptr<Map>& map_ptr, Uint32 index);
136
137 // Add new root map and increase level
138 bool add_root();
139
140 // Add new non-root map.
141 bool add_map(Ptr<Map>& map_ptr, Ptr<Map> parent_ptr, Uint32 digit);
142
143 // Subroutine to initialize map free lists.
144 void init_free(Ptr<Map> map_ptr);
145
146 // Add entry at given free position.
147 void add_entry(Ptr<Map> map_ptr, Uint32 digit, PtrI ptr_i);
148
149 // Remove entry and map if it becomes empty.
150 void remove_entry(Ptr<Map> map_ptr, Uint32 digit);
151
152 // Remove map and all parents which become empty.
153 void remove_map(Ptr<Map> map_ptr);
154
155 // Add map to available list.
156 void add_avail(Ptr<Map> map_ptr);
157
158 // Remove map from available list.
159 void remove_avail(Ptr<Map> map_ptr);
160
161 // Verify available lists
162 void verify_avail();
163
164 // Verify map (recursive).
165 void verify_map(Ptr<Map> map_ptr, Uint32 level, Uint32* count);
166
167 RecordPool<T> m_records;
168 RecordPool<Map> m_maps;
169 Uint32 m_levels; // 0 means empty pool
170 PtrI m_root;
171 PtrI m_avail[MaxLevels];
172 };
173
174 template <class T, Uint32 LogBase>
175 inline
LinearPool(GroupPool & gp)176 LinearPool<T, LogBase>::LinearPool(GroupPool& gp) :
177 m_records(gp),
178 m_maps(gp),
179 m_levels(0),
180 m_root(RNIL)
181 {
182 Uint32 n;
183 for (n = 0; n < MaxLevels; n++)
184 m_avail[n] = RNIL;
185 }
186
187 template <class T, Uint32 LogBase>
188 inline
~LinearPool()189 LinearPool<T, LogBase>::~LinearPool()
190 {
191 }
192
193 template <class T, Uint32 LogBase>
194 inline void
getPtr(Ptr<T> & ptr)195 LinearPool<T, LogBase>::getPtr(Ptr<T>& ptr)
196 {
197 Uint32 index = ptr.i;
198 // get level 0 map
199 Ptr<Map> map_ptr;
200 get_map(map_ptr, index);
201 // get record
202 Ptr<T> rec_ptr;
203 Uint32 digit = index & DigitMask;
204 assert(BitmaskImpl::get(BitmaskSize, map_ptr.p->m_bitmask, digit));
205 rec_ptr.i = map_ptr.p->m_entry[digit];
206 m_records.getPtr(rec_ptr);
207 ptr.p = rec_ptr.p;
208 }
209
210 template <class T, Uint32 LogBase>
211 inline bool
seize(Ptr<T> & ptr)212 LinearPool<T, LogBase>::seize(Ptr<T>& ptr)
213 {
214 // look for free list on some level
215 Ptr<Map> map_ptr;
216 map_ptr.i = RNIL;
217 Uint32 n = 0;
218 while (n < m_levels) {
219 if ((map_ptr.i = m_avail[n]) != RNIL)
220 break;
221 n++;
222 }
223 if (map_ptr.i == RNIL) {
224 // add new level with available maps
225 if (! add_root())
226 return false;
227 assert(n < m_levels);
228 map_ptr.i = m_avail[n];
229 }
230 m_maps.getPtr(map_ptr);
231 // walk down creating missing levels and using an entry on each
232 Uint32 digit;
233 Ptr<Map> new_ptr;
234 new_ptr.i = RNIL;
235 while (true) {
236 digit = map_ptr.p->m_firstfree;
237 if (n == 0)
238 break;
239 Ptr<Map> child_ptr;
240 if (! add_map(child_ptr, map_ptr, digit)) {
241 if (new_ptr.i != RNIL)
242 remove_map(new_ptr);
243 return false;
244 }
245 new_ptr = child_ptr;
246 map_ptr = child_ptr;
247 n--;
248 }
249 // now on level 0
250 assert(map_ptr.p->m_level == 0);
251 Ptr<T> rec_ptr;
252 if (! m_records.seize(rec_ptr)) {
253 if (new_ptr.i != RNIL)
254 remove_map(new_ptr);
255 return false;
256 }
257 add_entry(map_ptr, digit, rec_ptr.i);
258 ptr.i = digit + (map_ptr.p->m_index << LogBase);
259 ptr.p = rec_ptr.p;
260 return true;
261 }
262
263 template <class T, Uint32 LogBase>
264 inline int
seize_index(Ptr<T> & ptr,Uint32 index)265 LinearPool<T, LogBase>::seize_index(Ptr<T>& ptr, Uint32 index)
266 {
267 // extract all digits at least up to current root level
268 Uint32 digits[MaxLevels];
269 Uint32 n = 0;
270 Uint32 tmp = index;
271 do {
272 digits[n] = tmp & DigitMask;
273 tmp >>= LogBase;
274 } while (++n < m_levels || tmp != 0);
275 // add any new root levels
276 while (n > m_levels) {
277 if (! add_root())
278 return false;
279 }
280 // start from root
281 Ptr<Map> map_ptr;
282 map_ptr.i = m_root;
283 m_maps.getPtr(map_ptr);
284 // walk down creating or re-using existing levels
285 Uint32 digit;
286 bool used;
287 Ptr<Map> new_ptr;
288 new_ptr.i = RNIL;
289 while (true) {
290 digit = digits[--n];
291 used = BitmaskImpl::get(BitmaskSize, map_ptr.p->m_bitmask, digit);
292 if (n == 0)
293 break;
294 if (used) {
295 map_ptr.i = map_ptr.p->m_entry[digit];
296 m_maps.getPtr(map_ptr);
297 } else {
298 Ptr<Map> child_ptr;
299 if (! add_map(child_ptr, map_ptr, digit)) {
300 if (new_ptr.i != RNIL)
301 remove_map(new_ptr);
302 }
303 new_ptr = child_ptr;
304 map_ptr = child_ptr;
305 }
306 }
307 // now at level 0
308 assert(map_ptr.p->m_level == 0);
309 Ptr<T> rec_ptr;
310 if (used || ! m_records.seize(rec_ptr)) {
311 if (new_ptr.i != RNIL)
312 remove_map(new_ptr);
313 return used ? -1 : false;
314 }
315 add_entry(map_ptr, digit, rec_ptr.i);
316 assert(index == digit + (map_ptr.p->m_index << LogBase));
317 ptr.i = index;
318 ptr.p = rec_ptr.p;
319 return true;
320 }
321
322 template <class T, Uint32 LogBase>
323 inline void
release(Ptr<T> & ptr)324 LinearPool<T, LogBase>::release(Ptr<T>& ptr)
325 {
326 Uint32 index = ptr.i;
327 // get level 0 map
328 Ptr<Map> map_ptr;
329 get_map(map_ptr, index);
330 // release record
331 Ptr<T> rec_ptr;
332 Uint32 digit = index & DigitMask;
333 rec_ptr.i = map_ptr.p->m_entry[digit];
334 m_records.release(rec_ptr);
335 // remove entry
336 remove_entry(map_ptr, digit);
337 // null pointer
338 ptr.i = RNIL;
339 ptr.p = 0;
340 }
341
342 template <class T, Uint32 LogBase>
343 inline Uint32
count()344 LinearPool<T, LogBase>::count()
345 {
346 SuperPool& sp = m_records.m_superPool;
347 Uint32 count1 = sp.getRecUseCount(m_records.m_recInfo);
348 return count1;
349 }
350
351 template <class T, Uint32 LogBase>
352 inline void
verify()353 LinearPool<T, LogBase>::verify()
354 {
355 verify_avail();
356 if (m_root == RNIL) {
357 assert(m_levels == 0);
358 return;
359 }
360 assert(m_levels != 0);
361 Ptr<Map> map_ptr;
362 map_ptr.i = m_root;
363 m_maps.getPtr(map_ptr);
364 Uint32 count1 = count();
365 Uint32 count2 = 0;
366 verify_map(map_ptr, m_levels - 1, &count2);
367 assert(count1 == count2);
368 }
369
370 // private methods
371
372 template <class T, Uint32 LogBase>
373 inline void
get_map(Ptr<Map> & map_ptr,Uint32 index)374 LinearPool<T, LogBase>::get_map(Ptr<Map>& map_ptr, Uint32 index)
375 {
376 // root map must exist
377 Ptr<Map> tmp_ptr;
378 tmp_ptr.i = m_root;
379 m_maps.getPtr(tmp_ptr);
380 assert(tmp_ptr.p->m_level + 1 == m_levels);
381 // extract index digits up to current root level
382 Uint32 digits[MaxLevels];
383 Uint32 n = 0;
384 do {
385 digits[n] = index & DigitMask;
386 index >>= LogBase;
387 } while (++n < m_levels);
388 assert(index == 0);
389 // walk down indirect levels
390 while (--n > 0) {
391 tmp_ptr.i = tmp_ptr.p->m_entry[digits[n]];
392 m_maps.getPtr(tmp_ptr);
393 }
394 // level 0 map
395 assert(tmp_ptr.p->m_level == 0);
396 map_ptr = tmp_ptr;
397 }
398
399 template <class T, Uint32 LogBase>
400 inline bool
add_root()401 LinearPool<T, LogBase>::add_root()
402 {
403 // new root
404 Ptr<Map> map_ptr;
405 if (! m_maps.seize(map_ptr))
406 return false;
407 Uint32 n = m_levels++;
408 assert(n < MaxLevels);
409 // set up
410 map_ptr.p->m_level = n;
411 map_ptr.p->m_parent = RNIL;
412 map_ptr.p->m_index = 0;
413 init_free(map_ptr);
414 // on level > 0 digit 0 points to old root
415 if (n > 0) {
416 Ptr<Map> old_ptr;
417 old_ptr.i = m_root;
418 m_maps.getPtr(old_ptr);
419 assert(old_ptr.p->m_parent == RNIL);
420 old_ptr.p->m_parent = map_ptr.i;
421 add_entry(map_ptr, 0, old_ptr.i);
422 }
423 // set new root
424 m_root = map_ptr.i;
425 return true;
426 }
427
428 template <class T, Uint32 LogBase>
429 inline bool
add_map(Ptr<Map> & map_ptr,Ptr<Map> parent_ptr,Uint32 digit)430 LinearPool<T, LogBase>::add_map(Ptr<Map>& map_ptr, Ptr<Map> parent_ptr, Uint32 digit)
431 {
432 if (! m_maps.seize(map_ptr))
433 return false;
434 assert(parent_ptr.p->m_level != 0);
435 // set up
436 map_ptr.p->m_level = parent_ptr.p->m_level - 1;
437 map_ptr.p->m_parent = parent_ptr.i;
438 map_ptr.p->m_index = digit + (parent_ptr.p->m_index << LogBase);
439 init_free(map_ptr);
440 add_entry(parent_ptr, digit, map_ptr.i);
441 return true;
442 }
443
444 template <class T, Uint32 LogBase>
445 inline void
init_free(Ptr<Map> map_ptr)446 LinearPool<T, LogBase>::init_free(Ptr<Map> map_ptr)
447 {
448 map_ptr.p->m_occup = 0;
449 map_ptr.p->m_firstfree = 0;
450 // freelist
451 Uint32 j;
452 Uint16 back = ZNIL;
453 for (j = 0; j < Base - 1; j++) {
454 map_ptr.p->m_entry[j] = back | ((j + 1) << 16);
455 back = j;
456 }
457 map_ptr.p->m_entry[j] = back | (ZNIL << 16);
458 // bitmask
459 BitmaskImpl::clear(BitmaskSize, map_ptr.p->m_bitmask);
460 // add to available
461 add_avail(map_ptr);
462 }
463
464 template <class T, Uint32 LogBase>
465 inline void
add_entry(Ptr<Map> map_ptr,Uint32 digit,PtrI ptr_i)466 LinearPool<T, LogBase>::add_entry(Ptr<Map> map_ptr, Uint32 digit, PtrI ptr_i)
467 {
468 assert(map_ptr.p->m_occup < Base && digit < Base);
469 assert(! BitmaskImpl::get(BitmaskSize, map_ptr.p->m_bitmask, digit));
470 // unlink from freelist
471 Uint32 val = map_ptr.p->m_entry[digit];
472 Uint16 back = val & ZNIL;
473 Uint16 forw = val >> 16;
474 if (back != ZNIL) {
475 assert(back < Base);
476 map_ptr.p->m_entry[back] &= ZNIL;
477 map_ptr.p->m_entry[back] |= (forw << 16);
478 }
479 if (forw != ZNIL) {
480 assert(forw < Base);
481 map_ptr.p->m_entry[forw] &= (ZNIL << 16);
482 map_ptr.p->m_entry[forw] |= back;
483 }
484 if (back == ZNIL) {
485 map_ptr.p->m_firstfree = forw;
486 }
487 // set new value
488 map_ptr.p->m_entry[digit] = ptr_i;
489 map_ptr.p->m_occup++;
490 BitmaskImpl::set(BitmaskSize, map_ptr.p->m_bitmask, digit);
491 if (map_ptr.p->m_occup == Base)
492 remove_avail(map_ptr);
493 }
494
495 template <class T, Uint32 LogBase>
496 inline void
remove_entry(Ptr<Map> map_ptr,Uint32 digit)497 LinearPool<T, LogBase>::remove_entry(Ptr<Map> map_ptr, Uint32 digit)
498 {
499 assert(map_ptr.p->m_occup != 0 && digit < Base);
500 assert(BitmaskImpl::get(BitmaskSize, map_ptr.p->m_bitmask, digit));
501 // add to freelist
502 Uint32 firstfree = map_ptr.p->m_firstfree;
503 map_ptr.p->m_entry[digit] = ZNIL | (firstfree << 16);
504 if (firstfree != ZNIL) {
505 assert(firstfree < Base);
506 map_ptr.p->m_entry[firstfree] &= (ZNIL << 16);
507 map_ptr.p->m_entry[firstfree] |= digit;
508 }
509 map_ptr.p->m_firstfree = digit;
510 map_ptr.p->m_occup--;
511 BitmaskImpl::clear(BitmaskSize, map_ptr.p->m_bitmask, digit);
512 if (map_ptr.p->m_occup + 1 == Base)
513 add_avail(map_ptr);
514 else if (map_ptr.p->m_occup == 0)
515 remove_map(map_ptr);
516 }
517
518 template <class T, Uint32 LogBase>
519 inline void
remove_map(Ptr<Map> map_ptr)520 LinearPool<T, LogBase>::remove_map(Ptr<Map> map_ptr)
521 {
522 assert(map_ptr.p->m_occup == 0);
523 remove_avail(map_ptr);
524 Ptr<Map> parent_ptr;
525 parent_ptr.i = map_ptr.p->m_parent;
526 Uint32 digit = map_ptr.p->m_index & DigitMask;
527 PtrI map_ptr_i = map_ptr.i;
528 m_maps.release(map_ptr);
529 if (m_root == map_ptr_i) {
530 assert(parent_ptr.i == RNIL);
531 Uint32 used = count();
532 assert(used == 0);
533 m_root = RNIL;
534 m_levels = 0;
535 }
536 if (parent_ptr.i != RNIL) {
537 m_maps.getPtr(parent_ptr);
538 // remove child entry (recursive)
539 remove_entry(parent_ptr, digit);
540 }
541 }
542
543 template <class T, Uint32 LogBase>
544 inline void
add_avail(Ptr<Map> map_ptr)545 LinearPool<T, LogBase>::add_avail(Ptr<Map> map_ptr)
546 {
547 Uint32 n = map_ptr.p->m_level;
548 assert(n < m_levels);
549 map_ptr.p->m_nextavail = m_avail[n];
550 if (map_ptr.p->m_nextavail != RNIL) {
551 Ptr<Map> next_ptr;
552 next_ptr.i = map_ptr.p->m_nextavail;
553 m_maps.getPtr(next_ptr);
554 next_ptr.p->m_prevavail = map_ptr.i;
555 }
556 map_ptr.p->m_prevavail = RNIL;
557 m_avail[n] = map_ptr.i;
558 }
559
560 template <class T, Uint32 LogBase>
561 inline void
remove_avail(Ptr<Map> map_ptr)562 LinearPool<T, LogBase>::remove_avail(Ptr<Map> map_ptr)
563 {
564 Uint32 n = map_ptr.p->m_level;
565 assert(n < m_levels);
566 if (map_ptr.p->m_nextavail != RNIL) {
567 Ptr<Map> next_ptr;
568 next_ptr.i = map_ptr.p->m_nextavail;
569 m_maps.getPtr(next_ptr);
570 next_ptr.p->m_prevavail = map_ptr.p->m_prevavail;
571 }
572 if (map_ptr.p->m_prevavail != RNIL) {
573 Ptr<Map> prev_ptr;
574 prev_ptr.i = map_ptr.p->m_prevavail;
575 m_maps.getPtr(prev_ptr);
576 prev_ptr.p->m_nextavail = map_ptr.p->m_nextavail;
577 }
578 if (map_ptr.p->m_prevavail == RNIL) {
579 m_avail[n] = map_ptr.p->m_nextavail;
580 }
581 map_ptr.p->m_nextavail = RNIL;
582 map_ptr.p->m_prevavail = RNIL;
583 }
584
585 template <class T, Uint32 LogBase>
586 inline void
verify_avail()587 LinearPool<T, LogBase>::verify_avail()
588 {
589 // check available lists
590 for (Uint32 n = 0; n < MaxLevels; n++) {
591 Ptr<Map> map_ptr;
592 map_ptr.i = m_avail[n];
593 Uint32 back = RNIL;
594 while (map_ptr.i != RNIL) {
595 m_maps.getPtr(map_ptr);
596 assert(map_ptr.p->m_occup < Base);
597 assert(back == map_ptr.p->m_prevavail);
598 back = map_ptr.i;
599 map_ptr.i = map_ptr.p->m_nextavail;
600 }
601 }
602 }
603
604 template <class T, Uint32 LogBase>
605 inline void
verify_map(Ptr<Map> map_ptr,Uint32 level,Uint32 * count)606 LinearPool<T, LogBase>::verify_map(Ptr<Map> map_ptr, Uint32 level, Uint32* count)
607 {
608 assert(level < MaxLevels);
609 assert(map_ptr.p->m_level == level);
610 // check freelist
611 {
612 Uint32 nused = BitmaskImpl::count(BitmaskSize, map_ptr.p->m_bitmask);
613 assert(nused <= Base);
614 assert(map_ptr.p->m_occup == nused);
615 Uint32 nfree = 0;
616 Uint32 j = map_ptr.p->m_firstfree;
617 Uint16 back = ZNIL;
618 while (j != ZNIL) {
619 assert(j < Base);
620 assert(! BitmaskImpl::get(BitmaskSize, map_ptr.p->m_bitmask, j));
621 Uint32 val = map_ptr.p->m_entry[j];
622 assert(back == (val & ZNIL));
623 back = j;
624 j = (val >> 16);
625 nfree++;
626 }
627 assert(nused + nfree == Base);
628 }
629 // check entries
630 {
631 for (Uint32 j = 0; j < Base; j++) {
632 bool free = ! BitmaskImpl::get(BitmaskSize, map_ptr.p->m_bitmask, j);
633 if (free)
634 continue;
635 if (level != 0) {
636 Ptr<Map> child_ptr;
637 child_ptr.i = map_ptr.p->m_entry[j];
638 m_maps.getPtr(child_ptr);
639 assert(child_ptr.p->m_parent == map_ptr.i);
640 assert(child_ptr.p->m_index == j + (map_ptr.p->m_index << LogBase));
641 verify_map(child_ptr, level - 1, count);
642 } else {
643 Ptr<T> rec_ptr;
644 rec_ptr.i = map_ptr.p->m_entry[j];
645 m_records.getPtr(rec_ptr);
646 (*count)++;
647 }
648 }
649 }
650 // check membership on available list
651 {
652 Ptr<Map> avail_ptr;
653 avail_ptr.i = m_avail[map_ptr.p->m_level];
654 bool found = false;
655 while (avail_ptr.i != RNIL) {
656 if (avail_ptr.i == map_ptr.i) {
657 found = true;
658 break;
659 }
660 m_maps.getPtr(avail_ptr);
661 avail_ptr.i = avail_ptr.p->m_nextavail;
662 }
663 assert(found == (map_ptr.p->m_occup < Base));
664 }
665 }
666
667
668 #undef JAM_FILE_ID
669
670 #endif
671