 
#ifndef _slotfile_h_
#define _slotfile_h_
 


/*
 *
 *          Copyright (C) 1995, M. A. Sridhar
 *  
 *
 *     This software is Copyright M. A. Sridhar, 1995. 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.
 *
 */

 

// SlottedFile is a binary file that manages fixed-length records called {\it
// slots.} Each slot contains  a fixed-length byte  string whose length is
// determined  the  first  time  the  file  is  created.  Also, each   slot
// associated with a distinct file-specific {\it  handle\/} (a long value).
// SlottedFile is an abstract class, and particular concrete versions
// of it may be constructed; {\tt PrivateSlottedFile} is one such.
// 
// The SlottedFile class provides for adding  and deleting records as well
// as storing and retrieving the associated  data.  When a record is added,
// a file-specific  handle is  returned which  can  be used for  subsequent
// reference to that record. The returned handle is always nonzero.
//
// The class also provides methods for access and modification of a
// header area in the file  where a client of this class may maintain
// data. The size of this header area is determined by the derived
// class, such as {\tt PrivateSlottedFile}.
//
// A    user  of this   object  may   delete  a  record,   thus freeing the
// corresponding slot;  when this is done,  the  handle associated with the
// slot is invalid, but this does not affect the handles of other allocated
// slots. Once a slot  is freed, however,  it is illegal  to attempt to use
// its handle.
// 
// The implementation    uses a 2-level   bitmap-based  representation, and
// therefore  limits  the total  number of  records  that a SlottedFile can
// contain to  about   768,000,000. This   representation   is designed  to
// minimize I/O  on  the stream; it  guarantees  that every  method of this
// class requires  at most two  read and three write  operations (including
// read/write of  the record itself), regardless  of file  size.  For small
// files (with  fewer than 4096 records),  each method requires at most one
// read and two write operations. This class is  therefore  well suited for
// storing large tables in relational databases.
//
//    The implementation also  ensures  that the  file occupies  as  little
// additional  space  as  possible, by  truncating   the  file  after every
// deletion so that there is no unused space at its end.





#if defined(__GNUC__)
#pragma interface
#endif


#include "io/iodefs.h"

#include "io/binfile.h"
#include "base/bitset.h"

 
typedef ulong CL_SlottedFileHandle;
class YACL_IO CL_SlottedFileHeader;

class YACL_IO CL_SlottedFile: public CL_Object {
 
public:
    
    ~CL_SlottedFile ();
    // Destructor: Close the SlottedFile.
    
    // ---------------------- Querying --------------------------
    
    virtual long SlotSize () const
        {return _slotSize;};
    // Return the slot size for this slotted file, in bytes.
 
    virtual bool IsValid (CL_SlottedFileHandle h) const;
    // Is the given handle a valid (allocated) one?

    virtual long SlotsAllocated () const;
    // Return the total number of slots currently allocated.

    short HeaderSize () const {return _userHeaderSize;};
    // Return the size (in bytes) of the user-defined header segment.
    
    // --------------------- Slot manipulation --------------------

    virtual CL_SlottedFileHandle AllocateSlot ();
    // Allocate a slot and return its handle. The contents of the slot
    // are undefined.
    
    virtual CL_SlottedFileHandle AddRecord (uchar* record);
    // Allocate a slot, copy the given record into the slot, and return
    // the handle for the slot. Return a zero handle on error, e.g. I/O error
    // or unavailability of slots. You could use a ByteString as
    // this parameter; e.g., if {\tt store} is a ByteStringStore, you can write
    // \par\begin{verbatim}
    //            CL_ByteString strg (store.SlotSize());
    //            // ... Set up contents of strg ...
    //            CL_SlottedFileHandle h = store.AddRecord (strg);
    // \end{verbatim}
    // \par\noindent because the ByteString's operator {\tt uchar*} is called
    // automatically.

    virtual bool RetrieveRecord (CL_SlottedFileHandle h,
                                 uchar* record) const;
    // Retrieve the record associated with handle {\tt h} and return it in
    // {\tt record}. The latter is assumed to point to a byte block of size
    // at least as large as the record length. 
    // 
    // The function returns TRUE on
    // success and FALSE if an invalid handle was specified or an I/O error
    // occurred.

    virtual bool ModifyRecord (CL_SlottedFileHandle h, uchar* record);
    // Overwrite the record associated with {\tt h} with the value specified in
    // {\tt record}. The latter is assumed to point to a byte block
    // of size at least as large as the record length. The function returns
    // TRUE on success and FALSE if an invalid handle was specified or an
    // I/O error occurred.
    
    virtual bool DeleteRecord (CL_SlottedFileHandle h);
    // Delete the record associated with handle {\tt h}. The handle must be
    // currently allocated. Returns TRUE on success, FALSE on error (e.g.,
    // I/O error or
    // invalid handle). After the deletion, the handle becomes available for
    // allocation to a subsequent {\tt AddRecord} or {\tt AllocateSlot}
    // request.

    // ------------------- Manipulating user headers ------------------

    virtual bool ReadHeader (uchar* header) const;
    // Read and return the user-defined header in the file. The
    // parameter must point to a segment of memory at least as large as the
    // user header size. (You can use a ByteString for this.) Returns
    // TRUE on success, FALSE on I/O error.

    virtual bool WriteHeader (uchar* header);
    // Write the parameter into the user-defined header. Returns
    // TRUE on success, FALSE on I/O error.

    // ------------------------ Iteration ---------------------------
    
    virtual CL_SlottedFileHandle FirstRecord (uchar* record) const;
    // This and the {\tt NextRecord} method provide a means of iteration
    // over the SlottedFile. This method returns the first record in the
    // SlottedFile in the parameter, and its handle as the function return
    // value.

    virtual CL_SlottedFileHandle NextRecord (CL_SlottedFileHandle h,
                                             uchar* record) const;
    // Return the next record in series after the one with the given handle,
    // in the parameter, and
    // return its handle as the function value. Return 0 when there are no
    // more records. 

    // ------------------------ Static functions -----------------------

    static long Valid (const char* path);
    // Determine whether the given path specifies a valid slotted file,
    // and if so, return its slot size; if not, return 0.
    

    // ------------------ Basic functions ------------------------------
    
    const char* ClassName () const {return "CL_SlottedFile";};

    CL_ClassId ClassId () const { return _CL_SlottedFile_CLASSID;};

    // ------------------- End public protocol ------------------------ 
 

protected:

    CL_SlottedFile (CL_Stream& data_store, CL_SlottedFileHeader* hdr,
                    bool report_errors = TRUE);
    // Constructor: specifies the stream to use. This stream must
    // exist at the time this constructor is executed. The second parameter
    // must have been created, and is owned, by the derived class. The third
    // parameter specifies whether errors should be reported via the
    // {\tt CL_Error} class.
    //
    // The first parameter is borrowed from the caller, who must ensure that
    // {\tt data_store}  exists for as long as this SlottedFile does. You
    // can create at most one SlottedFile on a given Stream, because the
    // SlottedFile needs to use the initial portion of the Stream for
    // file-specific information. However you can pass an object of any
    // class derived from Stream as  the {\tt data_store} parameter; this
    // lets you create, for example, a SlottedFile in shared memory, if you
    // have a Stream that is implemented on shared memory.


    // ----- Instance variables -------
    
    CL_SlottedFileHeader* _header;
    CL_Stream&            _file;           // The file we use
    long                  _slotSize;       // Slot size: set by derived class
    short                 _userHeaderSize; // User-defined header's size:
                                           // set by derived class
    bool                  _reportErrors;

    
    // ------ Protected methods ----
    
    CL_Offset             _BlockBitmapOffset (ushort index) const;

    CL_Offset             _RecordOffset (CL_SlottedFileHandle) const;

    CL_SlottedFileHandle  _GetSlot();

    void                  _DoError (const char*,
                                    CL_SlottedFileHandle h = 0) const;

    virtual bool         _ReadHeader  (CL_SlottedFileHeader& hdr) const = 0;
    // Read and write the header: for derived classes to override. These are
    // const methods because they are used in most methods, but do not
    // affect the abstract object's state, since they are only used for
    // bookkeeping.

    virtual bool         _WriteHeader (const CL_SlottedFileHeader&) const = 0;
    
    friend class CL_SlottedFileIterator;
};



// SlottedFileIterator allows iteration over a particular SlottedFile,
// examining all the records in it.

class YACL_IO CL_SlottedFileIterator {

public:
    CL_SlottedFileIterator (const CL_SlottedFile& file);
    // Constructor: tell which SlottedFile to inspect.

    CL_SlottedFileIterator (const CL_SlottedFileIterator& iter);
    // Copy constructor.

    ~CL_SlottedFileIterator ();
    // Destructor.
    
    virtual void Reset ();
    // Reset the iteration to the beginning of the file.

    virtual CL_SlottedFileHandle Next (uchar* record);
    // Return the next record in sequence, in the parameter, and
    // return its handle as the function value. Return 0 when there are no
    // more records. The records are returned in order of their occurrence
    // in the file; this order has nothing to do with the order in which
    // they were added or removed.

protected:
    CL_BitSet               _root, _block;
    CL_BitSetIterator       _rootIter, _blockIter;
    short                   _blockIndex;
    const CL_SlottedFile&   _sfile;

};



// The PrivateSlottedFile is an implementation of the SlottedFile that
// assumes that the file will be used exclusively by one process and not
// shared by multiple processes.

class YACL_IO CL_PrivateSlottedFile: public CL_SlottedFile {

public:
    // ---------------- Construction and destruction -------------------
    
    CL_PrivateSlottedFile (const char* path, bool report_errors = TRUE);
    // Open an existing slotted file with the given path name. This
    // constructor exits with a fatal error if either the file does not
    // exist or does not contain the correct headers; therefore, it is
    // advisable to use the {\tt Valid} method before invoking it.
    //
    //     The second parameter specifies what to to do in case of I/O
    // errors. The default is to use the {\tt CL_Error::Warning} call to issue
    // error messages; if reportErrors is FALSE, such warnings will not
    // be issued.
 
    CL_PrivateSlottedFile (const char* path, long record_size,
                           short user_header_size = 0,
                           bool report_errors = TRUE);
    // Create a new slotted file with specified path name and
    // record size. Overwrite any existing file with the same path name.
    // The third parameter specifies the size of the user header area to
    // be set apart in the file. The {\tt report_errors} parameter specifies
    // whether errors must be reported immediately via the {\tt
    // CL_Error::Warning} method.

    CL_PrivateSlottedFile (CL_Stream& file, bool report_errors = TRUE);
    // Alternate constructors: similar to the above, except that the given
    // stream is used as data repository; this stream is {\it not\/}
    // owned by the SlottedFile, and will not be destroyed by the
    // SlottedFile's destructor.
 
    CL_PrivateSlottedFile (CL_Stream& file, long record_size,
                           short user_header_size = 0,
                           bool report_errors = TRUE);

    virtual ~CL_PrivateSlottedFile();
    // Destructor.
 

protected:
    CL_BinaryFile*        _ourFile; // Created by the first two constructors
    bool                  _weOwnIt; // TRUE if one of the first two
                                    // constructors was used

    bool _ReadHeader  (CL_SlottedFileHeader&) const;
    // Override the inherited pure virtual method.
    
    bool _WriteHeader (const CL_SlottedFileHeader&) const;
    // Override the inherited pure virtual method.

};




// The SlottedFileHeader class encapsulates the header of a SlottedFile. It
// is not intended for use be clients of the SlottedFile and its derived
// classes, but only by classes derived from SlottedFile.

class YACL_IO CL_SlottedFileHeader: public CL_Object {

    
public:
    CL_SlottedFileHeader (long slotSiz = 0, long userHdrSize = 0);
    
    ~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;};

    long SlotSize() const {return slotSize;};

    long UserHeaderSize () const {return userHeaderSize;};

    long AllocCount () const {return allocCount;};

    const CL_BitSet& NonemptyMap () const {return nonemptyMap;};

    const CL_BitSet& FullMap () const {return fullMap;};
    
    
protected:
    // 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;
    
    friend CL_SlottedFile;
};


// class CL_SharedSlottedFile:  public CL_SlottedFile {
// 
// public:
//     CL_SharedSlottedFile () {NotImplemented ("SharedSlottedFile");};
// 
//     ~CL_SharedSlottedFile ();
// 
//     char* ClassName () const {return "CL_SharedSlottedFile";};
//     
// protected:
//     bool _ReadHeader  () const;
// 
//     bool _WriteHeader () const;
// 
// };

    
#endif
