slotfile.cpp

来自「开放源码的编译器open watcom 1.6.0版的源代码」· C++ 代码 · 共 843 行 · 第 1/2 页

CPP
843
字号




/*
 *
 *          Copyright (C) 1994, M. A. Sridhar
 *  
 *
 *     This software is Copyright M. A. Sridhar, 1994. You are free
 *     to copy, modify or distribute this software  as you see fit,
 *     and to use  it  for  any  purpose, provided   this copyright
 *     notice and the following   disclaimer are included  with all
 *     copies.
 *
 *                        DISCLAIMER
 *
 *     The author makes no warranties, either expressed or implied,
 *     with respect  to  this  software, its  quality, performance,
 *     merchantability, or fitness for any particular purpose. This
 *     software is distributed  AS IS.  The  user of this  software
 *     assumes all risks  as to its quality  and performance. In no
 *     event shall the author be liable for any direct, indirect or
 *     consequential damages, even if the  author has been  advised
 *     as to the possibility of such damages.
 *
 */



// This may look like C code, but it is really -*- C++ -*-

#include "base/binding.h"
#include "base/bitset.h"
#include "base/bytestrm.h"
#include "io/slotfile.h"


#define NEW_OP new

// Notes on the SlottedFile implementation:
// 
// The  slotted file is viewed  as a tree  with three levels: the root, its
// children and its  grandchildren. The  root  has 2**14 =  16384 children,
// among which the  the leftmost 2**12 = 4096  are actual records, and  the
// remaining are bitmaps (instances of  CL_BitSet), called block    bitmaps.
// At  the root are  stored  two bitmaps, each  containing  16384 bits: the
// fullMap, in which a bit is on if the corresponding child  of the root is
// full, and  the nonemptyMap, in which  a bit is  on  if the corresponding
// child  of the root is  not empty. Note  that every element in the bitset
// fullMap also  occurs in nonemptyMap.    The former bitset is  useful for
// fast allocation, while the latter is useful for fast iteration.
// 
// This tree is stored in pre-order on the disk file.



#define ROOT_WIDTH  14           // 2**(ROOT_WIDTH) is the number of
                                 // children of the root; thus ROOT_WIDTH is
                                 // the number of bits needed to specify a
                                 // block bitmap
#define WIDTH       16           // #bits needed for specifying a
                                 // record in a block

#define RECORDS_PER_BLOCK  (((ulong) 1) << WIDTH)
                                 // #records in a block

#define UNUSED_BITS 4096         // #children of the root that are records
                                 // and not blocks.

// CL_SlottedFileHandle has the following layout:
//
//    ---------------------------------------------------------------
//    |01|<------   Block bitmap # ------>|<-- Record # in block -->|
//    ---------------------------------------------------------------
//        ^                                ^                       ^
//      Bit 29                           Bit 15                  Bit 0
//
// The 1 in bit 30 is to ensure that every handle is nonzero.
//


class CL_SlottedFileHeader: public CL_Object {

    // This is a private class encapsulating the header of a SlottedFile.
public:
    CL_SlottedFileHeader ();
    long StorableFormWidth () const;
    bool ReadFrom (const CL_Stream&);
    bool WriteTo (CL_Stream&) const;
    static long StoreWidth (); // Provided for the purpose of efficiency 

    const char* ClassName () const {return "CL_SlottedFileHeader";};

    void operator= (const CL_SlottedFileHeader& hdr);

    void operator= (const CL_Object& o)
        {*this = (const CL_SlottedFileHeader&) o;};

    // Instance data:
    CL_BitSet fullMap;
    CL_BitSet nonemptyMap;
    long allocCount; // #allocated slots in the file
    long slotSize;   // Size of each slot, in bytes
    short userHeaderSize;
};

void CL_SlottedFileHeader::operator= (const CL_SlottedFileHeader& hdr)
{
    fullMap        = hdr.fullMap;
    nonemptyMap    = hdr.nonemptyMap;
    allocCount     = hdr.allocCount;
    slotSize       = hdr.slotSize;
    userHeaderSize = hdr.userHeaderSize;
}

long CL_SlottedFileHeader::StoreWidth ()
{
    static long width = -1;
    if (width == -1) {
        CL_SlottedFileHeader hdr;
        width = hdr.StorableFormWidth();
    }
    return width;
}



CL_SlottedFileHeader::CL_SlottedFileHeader ()
: fullMap (1L << ROOT_WIDTH), nonemptyMap (1L << ROOT_WIDTH)
{
    userHeaderSize = 0;
}

long CL_SlottedFileHeader::StorableFormWidth() const
{
    return sizeof (short) + 2*sizeof (long) + 2*fullMap.StorableFormWidth();
}

bool CL_SlottedFileHeader::WriteTo (CL_Stream& s) const
{
    return s.Write (userHeaderSize) && s.Write (allocCount) && s.Write
        (slotSize) && fullMap.WriteTo (s) && nonemptyMap.WriteTo (s);
}

bool CL_SlottedFileHeader::ReadFrom (const CL_Stream& s)
{
    return s.Read (userHeaderSize) && s.Read (allocCount) && s.Read
        (slotSize) && fullMap.ReadFrom (s) && nonemptyMap.ReadFrom (s);
}    



// ------------------------ IO_Iter structure ---------------------
// 
// struct IO_Iter {
//     IO_Iter () : _rootIter (root), _blockIter (block) {};
//     CL_BitSet root, block;
//     CL_BitSetIterator _rootIter, _blockIter;
//     short _blockIndex;
// };



/* ---------------------- Handle manipulation functions ------------- */

inline CL_SlottedFileHandle __MakeHandle (long record_no,
                                          long block_map_index)
{
    return record_no | (((ulong) block_map_index) <<  WIDTH)
        | (((ulong) 1) << (ROOT_WIDTH + WIDTH));
}

inline long __RecordNumber (CL_SlottedFileHandle h)
{
    return ((((ulong) 1) << WIDTH) - 1) & h;
}

inline long __BlockMapIndex (CL_SlottedFileHandle h)
{
    return (h  >> WIDTH) & ((((ulong) 1) << ROOT_WIDTH) - 1);
}





/* ------------------------ CL_SlottedFile methods --------------------- */


CL_SlottedFile::CL_SlottedFile (CL_Stream& s, bool report)
: _file (s), _reportErrors (report)
{
    _header = new CL_SlottedFileHeader;
}

/* ------------------------------------------------------------------ */

CL_SlottedFile::~CL_SlottedFile ()
{
    if (_header) {
        delete _header;
    }
}


/* ------------------------------------------------------------------ */

CL_SlottedFileHandle CL_SlottedFile::AllocateSlot ()
{
    if (!PrepareToChange())
        return 0;
    CL_SlottedFileHandle h =  _GetSlot ();
    Notify();
    return h;
}

/* ------------------------------------------------------------------ */

CL_SlottedFileHandle CL_SlottedFile::AddRecord (uchar* record)
{
    if (!PrepareToChange())
        return 0;
    CL_SlottedFileHandle h = _GetSlot ();
    if (h)
        if (!_file.Write (_RecordOffset (h), record, _slotSize))
            _DoError ("AddRecord", h);
    Notify ();
    return h;
}


/* ------------------------------------------------------------------ */


bool CL_SlottedFile::IsValid (CL_SlottedFileHandle h) const
{
    if (!_ReadHeader (*_header)) {
        _DoError ("IsValid: header read");
        return FALSE;
    }
    long rec   = __RecordNumber (h);
    long index = __BlockMapIndex (h);
    if (index < UNUSED_BITS) {
        ulong hi_bits = h & (3L << (WIDTH + ROOT_WIDTH));
        return (rec == index)
            && (hi_bits == (1L << (WIDTH + ROOT_WIDTH)))
            && _header->fullMap.Includes (rec);
    }
    CL_BitSet block_map;
    if (!_file.Read (_BlockBitmapOffset (index), block_map)) {
        _DoError ("IsValid: block map read");
        return FALSE;
    }
    return block_map.Includes (rec);
}


/* ------------------------------------------------------------------ */

long CL_SlottedFile::SlotsAllocated () const
{
    if (!_ReadHeader (*_header)) {
        _DoError ("SlotsAllocated: header read");
        return 0;
    }
    return _header->allocCount;
}


/* ------------------------------------------------------------------ */


bool CL_SlottedFile::RetrieveRecord (CL_SlottedFileHandle h,
                                     uchar* record) const
{
    if (!IsValid (h))
        return FALSE;
    if (_file.Read (_RecordOffset (h), record, _slotSize) != _slotSize) {
        _DoError ("RetrieveRecord: record read failed", h);
        return FALSE;
    }
    return TRUE;
}


/* ------------------------------------------------------------------ */


bool CL_SlottedFile::ModifyRecord (CL_SlottedFileHandle h,
                                   uchar* record)
{
    if (!PrepareToChange())
        return FALSE;
    if (!IsValid (h))
        return FALSE;
    if (!_file.Write (_RecordOffset (h), record, _slotSize)) {
        _DoError ("ModifyRecord: record write failed", h);
        return FALSE;
    }
    Notify ();
    return TRUE;
}




/* ------------------------------------------------------------------ */



bool CL_SlottedFile::DeleteRecord (CL_SlottedFileHandle handle)
    // Free a previously-allocated slot
{
    if (!PrepareToChange())
        return FALSE;
    ulong hi_bits = handle & (3L << (WIDTH + ROOT_WIDTH));
    if (hi_bits != (1L << (WIDTH + ROOT_WIDTH)))
        return FALSE;
    long rec = __RecordNumber (handle);
    long index = __BlockMapIndex (handle);
    long new_file_size = -1L; // Will be changed appropriately if the file
                              // needs to be truncated
    CL_BitSet block_map;
    if (!_ReadHeader (*_header))
        return FALSE;
    if (index < UNUSED_BITS) {
        // The index specifies a record, not a bitmap
        _header->fullMap.Remove (index);
        _header->nonemptyMap.Remove (index);
        if (_header->nonemptyMap.IsEmpty()) {
            // There are no more records in the file after this deletion
            new_file_size = CL_SlottedFileHeader::StoreWidth();
        }
        else {
            long l = _header->nonemptyMap.Largest();
            if (l < index) {
                // We're deleting the record at the end of the file
                new_file_size = _BlockBitmapOffset (l) + _slotSize;
            }
        }
    }
    else {
        // The index specifies a bitmap
        CL_Offset block_offset = _BlockBitmapOffset (index);
        long file_size = _file.Size();
        if (block_offset < 0 || block_offset >= file_size)
            return FALSE;
        if (!_file.Read (block_offset, block_map)) {
            _DoError ("DeleteRecord: Level-1 map read failed");
            return FALSE;
        }
        block_map.Remove (rec);
        _header->fullMap.Remove (index);
        register long rec_end = _RecordOffset (handle) + _slotSize;
        if (block_map.IsEmpty()) {
            _header->nonemptyMap.Remove (index);
            if (file_size <= rec_end) {
                // We're removing the only allocated record in the block
                new_file_size = block_offset;
            }
        }
        else {
            if (file_size <= rec_end) {
                // We're removing the record at the end of the file
                new_file_size = rec_end - _slotSize;
            }
        }
        if (new_file_size != block_offset &&
            !_file.Write (block_offset, block_map)) {
            _DoError ("DeleteRecord: block map write failed", handle);
            return FALSE;
        }
    }
    _header->allocCount--;
    if (!_WriteHeader (*_header))
        return FALSE;
    if (new_file_size != -1L && !_file.ChangeSize (new_file_size)) {
        _DoError ("DeleteRecord: ChangeSize failed");
        return FALSE;
    }
    Notify ();
    return TRUE;
}



/* ------------------------------------------------------------------ */
bool CL_SlottedFile::ReadHeader (uchar* header) const
{
    return (_userHeaderSize > 0)
        ? _file.Read (CL_SlottedFileHeader::StoreWidth(), header,
                      _userHeaderSize)
        : FALSE;
}

        

// Write the parameter into the user-defined header. Returns
// TRUE on success, FALSE on i/o error.
bool CL_SlottedFile::WriteHeader (uchar* header)
{
    return (_userHeaderSize > 0)
        ? _file.Write (CL_SlottedFileHeader::StoreWidth(), header,
                       _userHeaderSize)
        : FALSE;
}






CL_SlottedFileHandle CL_SlottedFile::FirstRecord (uchar* record) const
{
    if (!_ReadHeader (*_header)) {
        _DoError ("FirstRecord: header read");
        return 0;
    }
    if (_header->allocCount <= 0)
        return 0;
    long index = _header->nonemptyMap.Smallest ();

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?