// // MEMSTD.CPP // // Source file for ArchiveLib 2.0 // // Copyright (c) Greenleaf Software, Inc. 1994-1996 // All Rights Reserved // // CONTENTS // // ALMemory::operator new() // ALMemory::ALMemory() // newALMemory() // ALMemory::~ALMemory() // ALMemory::_LoadBuffer() // ALMemory::Delete() // ALMemory::GrowUserBuffer() // ALMemory::_FlushBuffer() // ALMemory::Close() // ALMemory::Create() // ALMemory::Open() // ALMemory::Clone() // // DESCRIPTION // // This file contains the C++ member functions to support class // ALMemory. This class works very closely with the parent class, // ALMemoryBase, found in MEMSTORE.CPP. // // REVISION HISTORY // // May 22, 1994 1.0A : First release // // July 7, 1994 1.0B : Had to make a lot of changes to support file // management under UNIX. // // February 14, 1996 2.0A : New Release // #include "arclib.h" #if !defined( AL_IBM ) #pragma hdrstop #endif #include "memstore.h" #include // might be using malloc()! const size_t max_alloc = (size_t) ~0; // // NAME // // ALMemory::operator new() // // PLATFORMS/ENVIRONMENTS // // Console Windows PM // C++ // // SHORT DESCRIPTION // // Memory allocator used when ArchiveLib resides in a 16 bit DLL. // // C++ SYNOPSIS // // #include "arclib.h" // #include "memstore.h" // // void * ALMemory::operator new( size_t size ) // // C SYNOPSIS // // None. // // VB SYNOPSIS // // None. // // DELPHI SYNOPSIS // // None. // // ARGUMENTS // // size : The number of bytes that the compiler has decided will be // necessary to construct a new ALMemory object. // // DESCRIPTION // // When using a DLL, it is easy to get into a dangerous situation when // creating objects whose ctor and dtor are both in the DLL. The problem // arises because when you create an object using new, the memory for // the object will be allocated from the EXE. However, when you destroy // the object using delete, the memory is freed inside the DLL. Since // the DLL doesn't really own that memory, bad things can happen. // // But, you say, won't the space just go back to the Windows heap regardless // of who tries to free it? Maybe, but maybe not. If the DLL is using // a subsegment allocation scheme, it might do some sort of local free // before returning the space to the windows heap. That is the point where // you could conceivably cook your heap. // // By providing our own version of operator new inside this class, we // ensure that all memory allocation for the class will be done from // inside the DLL, not the EXE calling the DLL. // // Incidentally, I suspect that this function never gets called. If an // object of a derived archive class is being created, it should use // its own new operator, rendering this one useless. // // RETURNS // // A pointer to some memory that should have been pulled out of the // heap for the DLL. // // EXAMPLE // // SEE ALSO // // REVISION HISTORY // // February 14, 1996 2.0A : New Release // #if defined( AL_BUILDING_DLL ) void AL_DLL_FAR * AL_PROTO ALMemory::operator new( size_t size ) /* Tag internal function */ { return ::new char[ size ]; } #endif // // NAME // // ALMemory::ALMemory() // // PLATFORMS/ENVIRONMENTS // // Console Windows PM // C++ C VB Delphi // // SHORT DESCRIPTION // // Constructs a new ALMemory object. // // C++ SYNOPSIS // // #include "arclib.h" // #include "memstore.h" // // ALMemory::ALMemory( const char *buffer_name = "", // char *user_buffer = 0 , // size_t user_buffer_size = 0, // ALCase name_case = AL_MIXED ); // // C SYNOPSIS // // #include "arclib.h" // #include "memstore.h" // // hALStorage newALMemory( char *buffer_name, // char *user_buffer, // int user_buffer_size ); // // VB SYNOPSIS // // None, VB should use newALWinMemory(); // // DELPHI SYNOPSIS // // None, Delphi should use newALWinMemory(); // // ARGUMENTS // // buffer_name : An arbitrary name assigned to the buffer. Buffer // names don't have to be unique, because buffers aren't // named at the operating system level. But if you are // going to insert the storage object into an archive, the // name needs to be unique so that you will be able to // extract it properly. // // user_buffer : If you want the ALMemory class to automatically // allocate a buffer for you, and grow it as necessary, just // leave this pointer set to 0. If you want to use your own // buffer, which won't have the ability to grow, pass a // pointer to it in this parameter. // // user_buffer_size : If you are passing a pointer to your own buffer, // you need to indicate how large it is here. Since // we are dealing with standard buffers here, this is of // type size_t. // // name_case : This decides whether you want the file name to be // case sensitive when making comparisons. MS-DOS // file names are case-insensitive. You can make memory // buffers either mixed case, forced upper, or forced // lower. The default of mixed case means that comparisons // will be case sensitive, which is fine. // // Note that I don't even give the option of using name_case // to C users. They are going to get the default // value assigned to their OS/environment. // // DESCRIPTION // // This constructor calls the base class constructor in an initializer // list, which takes care of most of the dirty work right away. After that // is done, all the constructor has to do is initialize a few data members. // That should be self-explanatory. Remember that if the user doesn't // supply a buffer, we are going to allocate it for her, but not until // there is actually a demand for memory. // // RETURNS // // If memory allocation is being done dynamically, this function returns // a pointer/handle to a new ALMemory object. If it is called as // a static or automatic constructor (only possible in C++), it returns // nothing. // // EXAMPLE // // SEE ALSO // // ALMemoryBase::ALMemoryBase(), ALStorage::ALStorage(), // ALMemory::~ALMemory() // // REVISION HISTORY // // February 14, 1996 2.0A : New Release // AL_PROTO ALMemory::ALMemory( const char AL_DLL_FAR *buffer_name /* = "" */, /* Tag public function */ char AL_DLL_FAR *user_buffer /* = 0 */ , size_t user_buffer_size /* = 0 */, ALCase name_case /* = AL_MIXED */ ) : ALMemoryBase( buffer_name, name_case ) { if ( user_buffer != 0 ) { mpcUserBuffer = user_buffer; mfUserOwnsBuffer = 1; mlUserBufferSize = user_buffer_size; } else { mfUserOwnsBuffer = 0; mpcUserBuffer = 0; mlUserBufferSize = 0; } } #if !defined( AL_NO_C ) extern "C" AL_LINKAGE hALStorage AL_FUNCTION newALMemory( char AL_DLL_FAR *buffer_name, /* Tag public function */ char AL_DLL_FAR *user_buffer, size_t user_buffer_size ) { if ( user_buffer_size == 0 ) return (hALStorage) new ALMemory( buffer_name ); else return (hALStorage) new ALMemory( buffer_name, user_buffer, user_buffer_size ); } #endif // // NAME // // ALMemory::~ALMemory() // // PLATFORMS/ENVIRONMENTS // // Console Windows PM // C++ // // SHORT DESCRIPTION // // Destroy an ALMemory object. // // C++ SYNOPSIS // // #include "arclib.h" // #include "memstore.h" // // ALMemory::~ALMemory(); // // C SYNOPSIS // // None, use the base class destructor: deleteALStorage(). // // VB SYNOPSIS // // None, use the base class destructor: deleteALStorage(). // // DELPHI SYNOPSIS // // None, use the base class destructor: deleteALStorage(). // // ARGUMENTS // // None, you don't get any for a destructor. // // DESCRIPTION // // The destructor has just one thing it has to do before this object // goes away. If the buffer that it has been using all along doesn't // belong to the user, then it is the class's responsibility to get // rid of it. We do so here, using the standard version of free. // // RETURNS // // Nothing. // // EXAMPLE // // SEE ALSO // // ALMemory::ALMemory() // // REVISION HISTORY // // February 14, 1996 2.0A : New Release // AL_PROTO ALMemory::~ALMemory() /* Tag public function */ { AL_ASSERT( GoodTag(), "~ALMemory: attempting to delete invalid object" ); if ( !mfUserOwnsBuffer ) { if ( mpcUserBuffer ) { free( mpcUserBuffer ); mpcUserBuffer = 0; } } AL_ASSERT( GoodTag(), "~ALMemory: attempting to delete invalid object" ); } // // NAME // // ALMemory::_LoadBuffer() // // PLATFORMS/ENVIRONMENTS // // Console Windows PM // C++ // // SHORT DESCRIPTION // // Read memory from the big buffer into the local I/O buffer. // // C++ SYNOPSIS // // #include "arclib.h" // #include "memstore.h" // // void ALMemory::_LoadBuffer( long address ); // // C SYNOPSIS // // None, internal protected function. // // VB SYNOPSIS // // None, internal protected function. // // DELPHI SYNOPSIS // // None, internal protected functoin. // // ARGUMENTS // // address : The offset in the memory object that is going to be // loaded. // // DESCRIPTION // // External users of an ALStorage class perform all of their access via // a local I/O buffer. Functions such as ReadChar() and WriteChar() look // at a thing called mpcBuffer for their data. When reading from // mpcBuffer, you are going to run out of data from time to time. When // this happens, you will generate a call to the virtual function // LoadBuffer(). // // As it happens, all of the ALMemory objects share a common version // of LoadBuffer(). LoadBuffer() still has to call something a little // more specialized though, and that's where this version of _LoadBuffer() // comes into play. It just performs a memcpy() routine to actually move // data out of the big memory buffer and into the local I/O buffer // used by ReadChar() et. al. // // RETURNS // // Nothing. // // EXAMPLE // // SEE ALSO // // REVISION HISTORY // // February 14, 1996 2.0A : New Release // void AL_PROTO ALMemory::_LoadBuffer( long address ) /* Tag protected function */ { memcpy( mpcBuffer, mpcUserBuffer + (size_t) address, muBufferValidData ); } // // NAME // // ALMemory::Delete() // // PLATFORMS/ENVIRONMENTS // // Console Windows PM // C++ // // SHORT DESCRIPTION // // Delete the underlying buffer for the memory object. // // C++ SYNOPSIS // // #include "arclib.h" // #include "memstore.h" // // int ALMemory::Delete(); // // C SYNOPSIS // // None, C programs use the base class function deleteALStorage(). // // VB SYNOPSIS // // None, VB programs use the base class function deleteALStorage(). // // DELPHI SYNOPSIS // // None, Delphi programs use the base class function deleteALStorage(). // // ARGUMENTS // // None. // // DESCRIPTION // // This function is analogous to the unlink() RTL function for files. It // has to close the file, and get rid of its big buffer. This is fairly // easy with memory buffers, we just call the appropriate version of // free() to delete the buffer. // // RETURNS // // Nothing. // // EXAMPLE // // SEE ALSO // // ALMemory::Create(), ALStorage::Delete() // // REVISION HISTORY // // February 14, 1996 2.0A : New Release // int AL_PROTO ALMemory::Delete() /* Tag public function */ { if ( !mfUserOwnsBuffer ) { free( mpcUserBuffer ); mpcUserBuffer = 0; } return AL_SUCCESS; } // // NAME // // ALMemory::GrowUserBuffer() // // PLATFORMS/ENVIRONMENTS // // Console Windows PM // C++ // // SHORT DESCRIPTION // // Enlarge the user buffer. // // C++ SYNOPSIS // // #include "arclib.h" // #include "memstore.h" // // int ALMemory::GrowUserBuffer( long minimum_new_size ); // // C SYNOPSIS // // None, this is an internal protected C++ function. // // VB SYNOPSIS // // None, this is an internal protected C++ function. // // DELPHI SYNOPSIS // // None, this is an internal protected C++ function. // // ARGUMENTS // // minimum_new_size : This is the size that the caller absolutely must // have to successfully perform a write. Anything // less than this won't do. // // DESCRIPTION // // Sometimes a write to a memory object goes past the current end of the // buffer. When this happens, code in the base class calls this // function to attempt to enlarge the buffer. // // Enlarging the buffer is tricky, because you have to allocate new space, // then copy the old buffer into the new buffer. This means you // temporarily need a boot-load of space. If you are lucky, the realloc() // function might be able to attempt to avoid this situation. // // We try to enlarge things by a fixed amount, large enough to prevent // thrashing. But if that doesn't fly, we can fall back and try to // enlarge to the minimum acceptable size. // // RETURNS // // AL_SUCCESS if all went well, some error code < AL_SUCCESS if not. // // EXAMPLE // // SEE ALSO // // REVISION HISTORY // // February 14, 1996 2.0A : New Release // int AL_PROTO ALMemory::GrowUserBuffer( long minimum_new_size ) /* Tag protected function */ { if ( mStatus < AL_SUCCESS ) return mStatus; if ( mfUserOwnsBuffer ) return mStatus.SetError( AL_CANT_ALLOCATE_MEMORY, "Attempt to write past the end of a " "user owned buffer for ALMemory " "%s", mName.GetSafeName() ); if ( minimum_new_size >= max_alloc ) return mStatus.SetError( AL_CANT_ALLOCATE_MEMORY, "Attempt to allocate a buffer " "of %ld bytes for ALMemory " "%s", minimum_new_size, mName.GetSafeName() ); long trial_size = mlUserBufferSize + 16384; if ( trial_size >= max_alloc ) trial_size = max_alloc - 16; if ( trial_size >= minimum_new_size ) { char *new_buf = (char *) realloc( mpcUserBuffer, (size_t) trial_size ); if ( new_buf ) { mpcUserBuffer = new_buf; mlUserBufferSize = (size_t) trial_size; return AL_SUCCESS; } } char *new_buf = (char *) realloc( mpcUserBuffer, (size_t) minimum_new_size ); if ( new_buf ) { mpcUserBuffer = new_buf; mlUserBufferSize = (size_t) minimum_new_size; return AL_SUCCESS; } return mStatus.SetError( AL_CANT_ALLOCATE_MEMORY, "Allocation failure when attempting to " "allocate a buffer " "of %ld bytes for ALMemory " "%s", minimum_new_size, mName.GetSafeName() ); } // // NAME // // ALMemory::_FlushBuffer() // // PLATFORMS/ENVIRONMENTS // // Console Windows PM // C++ // // SHORT DESCRIPTION // // Flush data to the big buffer. // // C++ SYNOPSIS // // #include "arclib.h" // #include "memstore.h" // // void ALMemory::_FlushBuffer( long address ) // // C SYNOPSIS // // None, internal protected C++ function. // // VB SYNOPSIS // // None, internal protected C++ function. // // DELPHI SYNOPSIS // // None, internal protected C++ function. // // ARGUMENTS // // address : The address in the big buffer where the flush should write // to. // // DESCRIPTION // // When performing WriteChar() or WriteBuffer() operations, ALStorage // causes output to be directed to a small I/O buffer. When this I/O // buffer gets full, a call to ALFlushBuffer() is generated, which is // supposed to dump that memory to a physical device. // // When ALMemoryBase gets a call to FlushBuffer(), it handles almost // everything on its own. The one thing it can't handle, however, is // the routine to copy the I/O buffer out to the big memory object. // It has to really on this dinky virtual function to do the job. // // RETURNS // // Nothing. // // EXAMPLE // // SEE ALSO // // REVISION HISTORY // // February 14, 1996 2.0A : New Release // void AL_PROTO ALMemory::_FlushBuffer( long address ) /* Tag protected function */ { memcpy( mpcUserBuffer + (size_t) address, mpcBuffer, muWriteIndex ); } // // NAME // // ALMemory::Close() // // PLATFORMS/ENVIRONMENTS // // Console Windows PM // C++ // // SHORT DESCRIPTION // // Close an open memory buffer object // // C++ SYNOPSIS // // #include "arclib.h" // #include "memstore.h" // // int ALMemory::Close() // // C SYNOPSIS // // C programs should use the base class function ALStorageClose(). // // VB SYNOPSIS // // VB programs should use the base class function ALStorageClose(). // // DELPHI SYNOPSIS // // Delphi programs should use the base class function ALStorageClose(). // // ARGUMENTS // // None. // // DESCRIPTION // // Close() is supposed to do the same thing to a memory buffer as fclose() // in the RTL does to a file. The most important thing we are concerned // about is that the I/O buffer gets freed up by the base class, so this // suddenly might not be a giant heavyweight object any more. // // After freeing things up in the base class, we check to see if // we have allocated more space than we really need. If so, we do // a realloc() of some sort to give space back to the O/S. // // RETURNS // // Nothing. // // EXAMPLE // // SEE ALSO // // ALMemory::Open(), ALStorage::Close() // // REVISION HISTORY // // February 14, 1996 2.0A : New Release // int AL_PROTO ALMemory::Close() /* Tag public function */ { ALMemoryBase::Close(); if ( mStatus < AL_SUCCESS ) return mStatus; if ( ! mfUserOwnsBuffer ) { if ( mlSize != 0 ) { char *new_buf = (char *) realloc( mpcUserBuffer, (size_t) mlSize ); if ( new_buf ) { mpcUserBuffer = new_buf; mlUserBufferSize = mlSize; } } else { free( mpcUserBuffer ); mpcUserBuffer = 0; } } return AL_SUCCESS; } // // NAME // // ALMemory::Create() // // PLATFORMS/ENVIRONMENTS // // Console Windows PM // C++ // // SHORT DESCRIPTION // // Create the memory storage object big buffer. // // C++ SYNOPSIS // // #include "arclib.h" // #include "memstore.h" // // int ALMemory::Create( long init_size ); // // C SYNOPSIS // // C programs should use the base class function ALStorageCreate(). // // VB SYNOPSIS // // VB programs should use the base class function ALStorageCreate(). // // DELPHI SYNOPSIS // // Delphi programs should use the base class function ALStorageCreate(). // // ARGUMENTS // // init_size : When you create an ALMemory object of any kind, you can // write out data to it at your own pace, without having any // idea how much space you will need. The storage object // tries to increase its size every time you fill up // the current huge buffer. Well, if you know in advance how // much space you are going to need, you can allocate the // whole buffer at once, and avoid all that extra work. So // some calls to Create() now pass on an initial size using // this argument. // // DESCRIPTION // // This is like creating a new file. If there isn't a memory buffer // already assigned to this object, we create one, with an initial // allocation of 16Kbytes, or more if requested. // // RETURNS // // Either AL_SUCCESS, or an unfriendly error code. // // EXAMPLE // // SEE ALSO // // ALStorage::Create(), ALMemory::Open() // // REVISION HISTORY // // February 14, 1996 2.0A : New Release // int AL_PROTO ALMemory::Create( long init_size ) /* Tag public function */ { ALMemoryBase::Create(); if ( mStatus < AL_SUCCESS ) return mStatus; if ( mpcUserBuffer ) return AL_SUCCESS; //If a buffer was already created somewhere down the //line, we won't do it again. if ( init_size == -1L ) init_size = 16384; if ( init_size > max_alloc ) return mStatus.SetError( AL_CANT_ALLOCATE_MEMORY, "Allocation failure when attempting to " "initialize a buffer " "of %ld bytes for ALMemory " "%s in _Create()", init_size, mName.GetSafeName() ); mpcUserBuffer = (char *) malloc( (size_t) init_size ); mlUserBufferSize = init_size; if ( mpcUserBuffer == 0 ) return mStatus.SetError( AL_CANT_ALLOCATE_MEMORY, "Allocation failure when attempting to " "create a buffer " "of %ld bytes for ALMemory " "%s in _Create()", mlUserBufferSize, mName.GetSafeName() ); return AL_SUCCESS; } // // NAME // // ALMemory::Open() // // PLATFORMS/ENVIRONMENTS // // Console Windows PM // C++ // // SHORT DESCRIPTION // // Open an existing memory storage object. // // C++ SYNOPSIS // // #include "arclib.h" // #include "memstore.h" // // int ALMemory::Open(); // // C SYNOPSIS // // C programs should use the base class function ALStorageOpen(). // // VB SYNOPSIS // // VB programs should use the base class function ALStorageOpen(). // // DELPHI SYNOPSIS // // Delphi programs should use the base class function ALStorageOpen(). // // ARGUMENTS // // None. // // DESCRIPTION // // This is like opening an existing file. Since there is supposed to be // an existing memory buffer already, we gripe if we can't find one. // // RETURNS // // Either AL_SUCCESS, or an unfriendly error code. // // EXAMPLE // // SEE ALSO // // ALStorage::Open(), ALMemory::Create() // // REVISION HISTORY // // February 14, 1996 2.0A : New Release // int AL_PROTO ALMemory::Open() /* Tag public function */ { ALMemoryBase::Open(); if ( mStatus < AL_SUCCESS ) return mStatus; if ( mpcUserBuffer == 0 ) return mStatus.SetError( AL_CANT_OPEN_FILE, "Attempt to open ALMemory %s " "with no buffer allocated", mName.GetSafeName() ); else mlSize = mlUserBufferSize; return AL_SUCCESS; } // // NAME // // ALMemory::Clone() // // PLATFORMS/ENVIRONMENTS // // Console Windows PM // C++ // // SHORT DESCRIPTION // // Clone this memory based storage object. // // C++ SYNOPSIS // // #include "arclib.h" // #include "memstore.h" // // ALStorage ALMemory::Clone( const char *name, // int object_type ) const; // // C SYNOPSIS // // None, this is an internal C++ function // // VB SYNOPSIS // // None. // // DELPHI SYNOPSIS // // None. // // ARGUMENTS // // name : The desired name of the new object. Usually this will // be a name found in an Archive directory. // // object_type : The type of object we want to create. Only // AL_STORAGE_DEFAULT and AL_MEMORY_OBJECT will cause this // function to succeed. // // DESCRIPTION // // The virtual Clone() function is used by archiving programs to act // as a virtual constructor. When preparing to create storage objects // based on the contents of an Archive directory, the archiving code can // call Clone() for all the storage objects in its toolkit until it finds // one that responds to its object type. // // For example, if an archive contained an AL_MEMORY object, and we were // extracting, and an ALFile object was in the toolkit, it would call // ALFile::Clone() from the toolkit object, with an object type of // AL_MEMORY_OBJECT. This Clone() function would fail. Hopefully, there // would be a memory based storage object in the toolkit that would // respond properly to the Clone() call. // // Another object in the same archive might have an AL_FILE_OBJECT type. // When the archiving code called Clone() again with that object type, // we would successfully create the new File object in Clone(). // // RETURNS // // Either a pointer to a newly constructed ALHugeMemory object, or a zero // in case of error. // // EXAMPLE // // SEE ALSO // // REVISION HISTORY // // February 14, 1996 2.0A : New Release // ALStorage AL_DLL_FAR * ALMemory::Clone( const char AL_DLL_FAR *name, /* Tag public function */ int object_type ) const { switch ( object_type ) { case AL_STORAGE_DEFAULT : case AL_MEMORY_OBJECT : return new ALMemory( name ); } return 0; }