/*
 * STORAGE.H
 *
 *  Header file for ArchiveLib 1.0
 *
 *  Copyright (c) 1994 Greenleaf Software, Inc.
 *  All Rights Reserved
 *
 * DESCRIPTION
 *
 *  This file contains the class declaration for the very important
 *  base class ALStorage.
 * 
 * CLASS DEFINITIONS:
 *
 *  ALStorage
 *
 * FUNCTIONS:
 *
 *  ALStorage::ReadChar()
 *  ALStorage::WriteChar()
 * 
 * REVISION HISTORY
 *
 *  May 26, 1994  1.0A  : First release
 *
 */

#ifndef _STORAGE_H
#define _STORAGE_H

#if defined( __cplusplus )

#include <stddef.h>   /* need for size_t */

#include "timedate.h"
#include "fileattr.h"

/*
 * class ALStorage
 *
 * DESCRIPTION
 *
 * ALStorage is a base class that defines the different types
 * of storage objects used by Archive Library.  The two most
 * commonly used storage object types are file objects and memory
 * objects, defined by the derived classes ALFile and
 * ALMemory.
 *
 * ALStorage objects are used to store and retrieve objects from archives.
 * They are also used to store and retrieve the archives themselves,
 * allowing archives to be stored in files or directly in memory.
 *
 * The ALStorage adds buffering to the storage object, allowing for
 * fast access to data presently cached in memory.  This is very similar to
 * the buffering provided for FILE types in stdio.h.  Note that this
 * buffering is generally only efficient/useful if lots of sequential
 * reads or writes are being done, as opposed to random accessess.
 *
 * ALStorage objects give up a lot of flexibility in order to provide
 * quick and efficient access to data.  The primary way this affects
 * use of the class is that the I/O buffer can only be used for reading
 * or writing, but not both simultaneously.  The class doesn't check
 * for this at run time, so programmers need to enforce it themselves.
 *
 * When a read is initiated for the first time, the buffer is loaded up,
 * and subsequent reads are performed out of the I/O buffer.  To switch
 * to writing mode, a call to FlushBuffer needs to be performed, which
 * will reset the input and output indices.  Likewise, when, done writing,
 * a call to FlushBuffer() can be performed to clear the indices.  A
 * read can be done subsequently.
 *
 * DATA MEMBERS
 *
 *  mpcBuffer            : This is the I/O buffer.  I read big blocks of
 *                         data into this buffer, then I can perform
 *                         character reads from an inline functin that
 *                         doesn't have to access any virtual fns.  Speeds
 *                         things up tremendously.  Likewise, I write
 *                         to this buffer using inline functions until it
 *                         it is full.  Only then do I call a virtual
 *                         to flush it to disk, memory, or whatever.
 *
 *  muBufferValidData    : This keeps track of the end of valid data,
 *                         both when reading and writing.  When re
 *                         read in a block of data, this index is set
 *                         to the end of the data.  When writing, this
 *                         index is continually updated to reflect the
 *                         end of the user written data.
 *                         
 *  muWriteIndex         : The index in the I/O buffer where the next byte
 *                         is going to be written.
 *
 *  muReadIndex          : The index in the I/O buffer where the next read
 *                         will come from.
 *
 *  mlFilePointer        : The current location of the read/write pointer
 *                         in the underlying object, e.g. a file.  This
 *                         is the location where the data will be written
 *                         out of the I/O buffer when a FlushBuffer() call
 *                         is made.  Or, if reading, it is where the next
 *                         LoadBuffer() will read data from.
 *
 *  mlSize               : The size of the file/object.  This will ordinarily
 *                         be set to -1 when we create an object, because
 *                         we don't know the size yet.  When you call Open()
 *                         for an existing object, the value will usually
 *                         be loaded using some sort of system call.  We
 *                         also can figure out what the size is when we do
 *                         a ReadDirectory call on an archive.
 *
 *  mlCrc32              : The CRC-32 for the object.  This value normally
 *                         won't be known until an object has been placed
 *                         in an archive, or when the information has
 *                         been read out using in ReadDirectory().
 *
 *  miUpdateCrcFlag      : This flag is set to indicate that we are in the
 *                         process of calculating the CRC while the file
 *                         is being compressed.
 *
 *  miCreated            : This flag will be set if the file was opened
 *                         using Create(), clear if it was opened using
 *                         Open().  When miCreated is set, we will try
 *                         to set the file time, date and attributes when
 *                         we close the file.  This is so we can set these
 *                         attributes when we are recreating a file that
 *                         was stored in an archive.
 *
 *  miStorageObjectType  : An integer that is assigned when the object was
 *                         constructed.  Usually one of the enumerated
 *                         constants found in ALDEFS.H.  This is the number
 *                         that gets stored in the Archive directory with 
 *                         the object, so we can figure out what type of 
 *                         object to create when extracting.
 *
 *  muBufferSize         : The size of the I/O buffer.
 *
 *  mpMonitor            : A pointer to the monitor attached to this object.
 *                         During the archiving process, this pointer gets
 *                         set by the archive routine for each storage object
 *                         as it is being processes.  A value of 0 just
 *                         means no monitor is watching this object at the
 *                         moment.
 *
 *  mTimeDate            : The time and date stamp for the file, this usually
 *                         gets set when the object is opened using Open(),
 *                         it is also set when we read in a storage object's
 *                         information using ReadDirectory().
 *
 *  mAttributes          : The attributes associated with the file. R/H/S/A.
 *
 *  mName                : The name of the storage object.
 *
 *  mStatus              : The current status of the object.
 *
 * MEMBER FUNCTIONS
 *
 *  ALStorage()          : The constructor, creates the object, but doesn't
 *                         necessarily create the file/whatever.
 *  operator=()          : Assignment operator.
 *  operator new()       : The memory allocation operator.  This is only
 *                         used if the library is in a DLL.
 *  ~ALStorage()         : Virtual destructor.
 *  UpdateCrc()          : Protected function used internally when the
 *                         crc is being calculated
 *  ReadString()         : Read a string in ArchiveLib's proprietary format.
 *  WriteStorageObjectData() : Protected function to write custom data needed
 *                             for a particular derived classe.
 *  ReadStorageObjectData()  : Protected function read that data back in.
 *  ReadChar()           : Superfast inline function to read a bytee
 *  WriteChar()          : Fast inline function to write a byte.
 *  ReadBuffer()         : Function to read blocks of data.
 *  WriteBuffer()        : Function to write blocks of data.
 *  Open()               : Open() used to prepare an existing object for I/O.
 *  Create()             : Create a new underlying object for I/O.
 *  Close()              : Called when I/O is complete.
 *  LoadBuffer()         : Called to reload the I/O buffer, used internally
 *                         when ReadChar() runs out of stuff to read.
 *  FlushBuffer()        : Called to flush the I/O buffer to the underlying
 *                         object.  Called when WriteChar() has gone too far.
 *  Seek()               : Called to reposition the I/O pointer of the 
 *                         underlying object.
 *  YieldTime()          : Called whenever a FlushBuffer() or LoadBuffer()
 *                         takes place.  Used to update the Monitor attached
 *                         to the file, and to yield time to the O/S.
 *  Compare()            : Compare two storage objects.
 *  InitCrc32()          : Called to start calculating the CRC for an object.
 *  WritePortableShort() : Write 16 bit integer in little endian format.
 *  WritePortableLong()  : Write 32 bit integer in little endian format.
 *  ReadPortableShort()  : Read 16 bit integer in little endian format.
 *  ReadPortableLong()   : Read 32 bit integer in little endian format.
 *  WriteString()        : Write string in ArchiveLib format.
 *  Rename()             : Rename the underlying object.
 *  UnRename()           : Undo a rename operation.
 *  RenameToBackup()     : Rename to a special backup name.
 *  Delete()             : Delete an underlying storage object.
 *  GetCrc32()           : Return value of the CRC member.
 *  GetSize()            : Reeturn value of the size member.
 *  IsOpen()             : Indicate if the file is open.
 *  Tell()               : Indicate where the next read or write will 
 *                         take place.
 *
 * REVISION HISTORY
 *
 *  May 26, 1994  1.0A  : First release
 *
 */


/*
 * Forward declaration
 */

class AL_CLASS_TYPE ALMonitor;

class AL_CLASS_TYPE ALStorage {
    public :
/*
 * Classes I trust
 */
    friend class AL_CLASS_TYPE ALArchiveBase;
    friend class AL_CLASS_TYPE ALCompressedObject;
/*
 * Constructors, destructors, assignment operator
 */
    protected :
        AL_PROTO ALStorage( const char AL_DLL_FAR *file_name,
                            size_t buffer_size,
                            const ALStorageType storage_type,
                            ALCase name_case = AL_MIXED );
        ALStorage AL_DLL_FAR & AL_PROTO operator=( const ALStorage AL_DLL_FAR & );
#if defined( AL_USING_DLL ) || defined( AL_BUILDING_DLL )
        void AL_DLL_FAR * AL_PROTO operator new( size_t size );
#endif
    public :
        virtual AL_PROTO ~ALStorage();
/*
 * I don't want to allow the copy constructor to exist.
 */
    protected :
        AL_PROTO ALStorage( const ALStorage AL_DLL_FAR & );

/*
 * Member functions, grouped somewhat
 *
 *
 * Private member manipulation
 */
    protected :
        void AL_PROTO UpdateCrc( size_t count );
/*
 * This is private, because it allocates memory in the DLL, so it
 * must be deleted in the DLL as well.
 */
    private :
        char AL_DLL_FAR * AL_PROTO ReadString();
        virtual int AL_PROTO 
        WriteStorageObjectData( ALStorage AL_DLL_FAR * archive );
        virtual int AL_PROTO 
        ReadStorageObjectData( ALStorage AL_DLL_FAR * archive );
/*
 * The file I/O access public interface
 */
    public :
        int AL_PROTO ReadChar();
        int AL_PROTO WriteChar( int c );
        size_t AL_PROTO ReadBuffer( unsigned char AL_DLL_FAR *buffer, 
                                    size_t length );
        /* Please keep this arg const, breaks WriteString o/w */
        size_t AL_PROTO WriteBuffer( const unsigned char AL_DLL_FAR *buffer, 
                                     size_t length );
        virtual int AL_PROTO Open();
        virtual int AL_PROTO Create();
        virtual int AL_PROTO Close();
        virtual int AL_PROTO LoadBuffer( long address ) = 0;
        virtual int AL_PROTO FlushBuffer() = 0;
        virtual int AL_PROTO Seek( long address ) = 0;
        virtual void AL_PROTO YieldTime();
        int AL_PROTO Compare( ALStorage AL_DLL_FAR &test_object );
        void AL_PROTO InitCrc32( unsigned long seed = 0xffffffffl );
        int AL_PROTO WritePortableShort( short int short_data );
        int AL_PROTO WritePortableLong( long long_data );
        int AL_PROTO ReadPortableShort( short int AL_DLL_FAR &short_data );
        int AL_PROTO ReadPortableLong( long AL_DLL_FAR &long_data );
        int AL_PROTO WriteString( const char AL_DLL_FAR *string_data );
/*
 * File manipulation public interface
 */
    public :
        virtual int AL_PROTO Rename( const char AL_DLL_FAR *new_name = 0, 
                                     int delete_on_clash = 1 ) = 0;
        virtual int AL_PROTO UnRename( int delete_on_clash = 1 ) = 0;
        virtual int AL_PROTO RenameToBackup( int delete_on_clash = 1 ) = 0;
        virtual int AL_PROTO Delete() = 0;
/*
 * Access functions
 */
    public :
        long AL_PROTO GetCrc32();
        long AL_PROTO GetSize() const { return mlSize; }
        int AL_PROTO IsOpen(){ return mpcBuffer != 0; }
        long AL_PROTO Tell();
/*
 * Data members
 */
    protected :
        unsigned char AL_DLL_FAR *mpcBuffer;
        size_t muBufferValidData;
        size_t muWriteIndex;
        size_t muReadIndex;
        long mlFilePointer;
        long mlSize;
        long mlCrc32;
        short int miUpdateCrcFlag;
        short int miCreated;
/*
 * Public members
 */
    public :
        const ALStorageType miStorageObjectType;
        const size_t muBufferSize;
        ALMonitor AL_DLL_FAR *mpMonitor;
        ALTimeDate mTimeDate;
        ALFileAttributes mAttributes;
        ALName mName;
        ALStatus mStatus;
        AL_CLASS_TAG( _ALStorageTag );
};

/*
 * It is really important to keep these guys inline.
 */


/*
 * inline int ALStorage::ReadChar()
 *
 * ARGUMENTS:
 *
 *  None.
 *
 * RETURNS
 *
 *  Either the next character available from the I/O buffer, or
 *  AL_END_OF_FILE.
 *
 * DESCRIPTION
 *
 *  This is an inline function that is able to quickly do buffered I/O. 
 *  By utilizing an I/O buffer we can make this routine very fast, since
 *  it doesn't have to call a virtual function.  The virtual function
 *  only has to be called when LoadBuffer() gets called.
 *
 *  Different compilers have different abilities to make this code inline,
 *  so sometimes it needs to be tinkered with.  If you see anything in here
 *  that looks funny, that probably explains why.
 *
 * REVISION HISTORY
 *
 *   May 26, 1994  1.0A  : First release
 *
 */

inline int AL_PROTO ALStorage::ReadChar()
{
    int result;

    AL_ASSERT( muWriteIndex == 0, "ReadChar(): Attempt to read while in write mode" ); /*Can't read if I've done a write!*/
    result = muBufferValidData - muReadIndex;
    if ( result <= 0 )
        result = LoadBuffer( mlFilePointer );
    AL_ASSERT( mpcBuffer != 0, "ReadChar(): Attempt to read from closed file" );    /*Potential disaster*/
    if ( result < 0 )
        return result;
    else
        return mpcBuffer[ muReadIndex++ ] & 0xff;
}

/*
 * inline int ALStorage::WriteChar( int c )
 *
 * ARGUMENTS:
 *
 *  c  : The character that is going to be written.
 *
 * RETURNS
 *
 *  Either the character that we just wrote out, or an error < AL_SUCCESS.
 *
 * DESCRIPTION
 *
 *  This is an inline function that is able to quickly do buffered I/O. 
 *  By utilizing an I/O buffer we can make this routine very fast, since
 *  it doesn't have to call a virtual function.  The virtual function
 *  only has to be called when FlushBuffer() gets called.
 *
 *  Different compilers have different abilities to make this code inline,
 *  so sometimes it needs to be tinkered with.  If you see anything in here
 *  that looks funny, that probably explains why.
 *
 * REVISION HISTORY
 *
 *   May 26, 1994  1.0A  : First release
 *
 */

inline int AL_PROTO ALStorage::WriteChar( int c )
{
    int result;

/*    assert( muReadIndex == 0 ); */ /* Can't write if I've done a read */
    AL_ASSERT( mpcBuffer != 0, "WriteChar(): Attempt to write to closed file" );   /* Disaster! */
    result = muBufferSize - muWriteIndex;
    if ( result <= 0 )
        result = FlushBuffer();
    if ( result < 0 )
        return mStatus;
    else
        return mpcBuffer[ muWriteIndex++ ] = (char) c;
}

#endif /* #if defined( __cplusplus ) */

#endif /* #ifndef _STORAGE_H         */