/*
 * DEBUG: section 08    Swap File Bitmap
 * AUTHOR: Harvest Derived
 *
 * SQUID Web Proxy Cache          http://www.squid-cache.org/
 * ----------------------------------------------------------
 *
 *  Squid is the result of efforts by numerous individuals from
 *  the Internet community; see the CONTRIBUTORS file for full
 *  details.   Many organizations have provided support for Squid's
 *  development; see the SPONSORS file for full details.  Squid is
 *  Copyrighted (C) 2001 by the Regents of the University of
 *  California; see the COPYRIGHT file for full details.  Squid
 *  incorporates software developed and/or copyrighted by other
 *  sources; see the CREDITS file for full details.
 *
 *  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 of the License, 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, USA.
 *
 */

#include "squid.h"
#include "Debug.h"
#include "FileMap.h"

/* Number of bits in a long */
#if SIZEOF_LONG == 8
#define LONG_BIT_SHIFT 6
#define BITS_IN_A_LONG 0x40
#define LONG_BIT_MASK  0x3F
#define ALL_ONES (unsigned long) 0xFFFFFFFFFFFFFFFF
#elif SIZEOF_LONG == 4
#define LONG_BIT_SHIFT 5
#define BITS_IN_A_LONG 0x20
#define LONG_BIT_MASK  0x1F
#define ALL_ONES (unsigned long) 0xFFFFFFFF
#else
#define LONG_BIT_SHIFT 5
#define BITS_IN_A_LONG 0x20
#define LONG_BIT_MASK  0x1F
#define ALL_ONES (unsigned long) 0xFFFFFFFF
#endif

#define FM_INITIAL_NUMBER (1<<14)

FileMap::FileMap() :
        capacity_(FM_INITIAL_NUMBER), usedSlots_(0),
        nwords(capacity_ >> LONG_BIT_SHIFT)
{
    debugs(8, 3, HERE << "creating space for " << capacity_ << " files");
    debugs(8, 5, "--> " << nwords << " words of " << sizeof(*bitmap) << " bytes each");
    bitmap = (unsigned long *)xcalloc(nwords, sizeof(*bitmap));
}

void
FileMap::grow()
{
    int old_sz = nwords * sizeof(*bitmap);
    void *old_map = bitmap;
    capacity_ <<= 1;
    assert(capacity_ <= (1 << 24));	/* swap_filen is 25 bits, signed */
    nwords = capacity_ >> LONG_BIT_SHIFT;
    debugs(8, 3, HERE << " creating space for " << capacity_ << " files");
    debugs(8, 5, "--> " << nwords << " words of " << sizeof(*bitmap) << " bytes each");
    bitmap = (unsigned long *)xcalloc(nwords, sizeof(*bitmap));
    debugs(8, 3, "copying " << old_sz << " old bytes");
    memcpy(bitmap, old_map, old_sz);
    xfree(old_map);
    /* XXX account fm->bitmap */
}

bool
FileMap::setBit(sfileno file_number)
{
    unsigned long bitmask = (1L << (file_number & LONG_BIT_MASK));

    while (file_number >= capacity_)
        grow();

    bitmap[file_number >> LONG_BIT_SHIFT] |= bitmask;

    ++usedSlots_;

    return file_number;
}

/*
 * WARNING: clearBit does not perform array bounds
 * checking!  It assumes that 'file_number' is valid, and that the
 * bit is already set.  The caller must verify both of those
 * conditions by calling testBit
 * () first.
 */
void
FileMap::clearBit(sfileno file_number)
{
    unsigned long bitmask = (1L << (file_number & LONG_BIT_MASK));
    bitmap[file_number >> LONG_BIT_SHIFT] &= ~bitmask;
    --usedSlots_;
}

bool
FileMap::testBit(sfileno file_number) const
{
    unsigned long bitmask = (1L << (file_number & LONG_BIT_MASK));

    if (file_number >= capacity_)
        return 0;

    /* be sure the return value is an int, not a u_long */
    return (bitmap[file_number >> LONG_BIT_SHIFT] & bitmask ? 1 : 0);
}

sfileno
FileMap::allocate(sfileno suggestion)
{
    int word;

    if (suggestion >= capacity_)
        suggestion = 0;

    if (!testBit(suggestion))
        return suggestion;

    word = suggestion >> LONG_BIT_SHIFT;

    for (unsigned int count = 0; count < nwords; ++count) {
        if (bitmap[word] != ALL_ONES)
            break;

        word = (word + 1) % nwords;
    }

    for (unsigned char bit = 0; bit < BITS_IN_A_LONG; ++bit) {
        suggestion = ((unsigned long) word << LONG_BIT_SHIFT) | bit;

        if (!testBit(suggestion)) {
            return suggestion;
        }
    }

    grow();
    return allocate(capacity_ >> 1);
}

FileMap::~FileMap()
{
    safe_free(bitmap);
}