/* $Id: bbuddy.C,v 1.10 1999/11/11 22:44:41 dm Exp $ */ /* * * Copyright (C) 1998 David Mazieres (dm@uun.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA * */ #include "bbuddy.h" #include "bitvec.h" #include "msb.h" #undef setbit #undef clrbit /* Population count (number of bits set) */ const char bytepop[0x100] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, }; class bbfree : private bitvec { size_t hint; // Possibly the index of a non-zero map_t size_t cnt; // # of 1 bits size_t nmaps; public: enum { mapbits = bitvec::mapbits }; explicit bbfree (size_t nb = 0) : hint (0), cnt (0) { init (nb); } void init (size_t nb) { assert (nb >= nbits); size_t obits = nbits; setsize (nb); nmaps = (nb + mapbits - 1) / mapbits; range_clr (obits, nmaps * mapbits); } size_t getsize () const { return nbits; } bool getbit (u_long pos) const { return at (pos); } void setbit (u_long pos) { const size_t mi = pos / mapbits; const map_t mask = map_t (1) << pos % mapbits; #ifdef CHECK_BOUNDS assert (pos < nbits); assert (!(map[mi] & mask)); #endif /* CHECK_BOUNDS */ map[mi] |= mask; if (!map[hint]) hint = mi; cnt++; } void clrbit (u_long pos) { const size_t mi = pos / mapbits; const map_t mask = map_t (1) << pos % mapbits; #ifdef CHECK_BOUNDS assert (pos < nbits); assert (map[mi] & mask); #endif /* CHECK_BOUNDS */ map[mi] &= ~mask; cnt--; } bool findbit (size_t *posp); void _check () const; }; bool bbfree::findbit (size_t *posp) { if (!cnt || !nbits) return false; if (map_t v = map[hint]) { *posp = hint * mapbits + ffs (v) - 1; return true; } for (size_t i = 0; i < nmaps; i++) if (map_t v = map[i]) { hint = i; *posp = i * mapbits + ffs (v) - 1; return true; } panic ("bbfree::findbit: cnt was wrong!\n"); } void bbfree::_check () const { size_t sum = 0; for (u_char *cp = reinterpret_cast (map), *end = cp + nbytes (nbits); cp < end; cp++) sum += bytepop[*cp]; assert (sum == cnt); } inline bbfree & bbuddy::fm (size_t sn) { assert (sn >= log2minalloc && sn <= log2maxalloc); return freemaps[sn - log2minalloc]; } bbuddy::bbuddy (totsize_t ts, size_t minalloc, size_t maxalloc) : totsize (0), log2minalloc (log2c (minalloc)), log2maxalloc (log2c (maxalloc)), freemaps (New bbfree[1 + log2maxalloc - log2minalloc]), spaceleft (0) { assert (log2maxalloc >= log2minalloc); settotsize (ts); } void bbuddy::settotsize (totsize_t ts) { const size_t maxinc = 1 << log2maxalloc; ts = ts & ~(maxinc - 1); assert (ts >= totsize); totsize_t o = ts >> log2minalloc; for (u_int sn = log2minalloc; sn <= log2maxalloc; sn++) { fm (sn).init (o); o >>= 1; } o = totsize; if (o < ts) { for (;;) { size_t n = ffs (o) - 1; if (n >= log2maxalloc) break; size_t inc = 1 << n; dealloc (o, inc); o += inc; } while (o < ts) { dealloc (o, maxinc); o += maxinc; } } totsize = ts; } bbuddy::~bbuddy () { delete[] freemaps; } off_t bbuddy::alloc (size_t n) { u_int sn = log2c (n); if (sn < log2minalloc) sn = log2minalloc; if (sn > log2maxalloc) return -1; size_t pos; if (fm (sn).findbit (&pos)) { fm (sn).clrbit (pos); spaceleft -= 1 << sn; return (off_t) (pos) << sn; } u_int sni = sn; do { if (++sni > log2maxalloc) return -1; } while (!fm (sni).findbit (&pos)); fm (sni).clrbit (pos); while (--sni >= sn) { pos <<= 1; fm (sni).setbit (pos + 1); } spaceleft -= 1 << sn; return (off_t) (pos) << sn; } void bbuddy::dealloc (off_t off, size_t len) { u_int sn = log2c (len); if (sn < log2minalloc) sn = log2minalloc; if (sn > log2maxalloc) panic ("bbuddy::dealloc: invalid len %lu\n", (u_long) len); size_t pos = off >> sn; assert (off == (off_t) (pos) << sn); spaceleft += 1 << sn; u_int sni; for (sni = sn; sni < log2maxalloc; sni++, pos >>= 1) { bbfree &bf = fm (sni); if (bf.getbit (pos ^ 1)) bf.clrbit (pos ^ 1); else { bf.setbit (pos); return; } } fm (log2maxalloc).setbit (pos); } bool bbuddy::_check_pos (u_int sn, size_t pos, bool set) { bool ret = fm (sn).getbit (pos); if (ret) { if (set) panic ("bbuddy::_check_pos: bit should not be set!\n"); set = true; } if (sn > log2minalloc) { sn--; pos <<= 1; bool ret1 = _check_pos (sn, pos, set); bool ret2 = _check_pos (sn, pos + 1, set); assert (!ret1 || !ret2); } return ret; } void bbuddy::_check () { for (u_int sn = log2minalloc; sn <= log2maxalloc; sn++) fm (sn)._check (); for (size_t pos = 0, lim = fm (log2maxalloc).getsize (); pos < lim; pos++) _check_pos (log2maxalloc, pos, false); }