852 lines
27 KiB
C++
Executable File
852 lines
27 KiB
C++
Executable File
//
|
|
// MEMSTORE.CPP
|
|
//
|
|
// Source file for ArchiveLib 1.0
|
|
//
|
|
// Copyright (c) Greenleaf Software, Inc. 1994
|
|
// All Rights Reserved
|
|
//
|
|
// CONTENTS
|
|
//
|
|
// ALMemory::operator new()
|
|
// ALMemory::ALMemory()
|
|
// ALMemory::~ALMemory()
|
|
// ALMemory::LoadBuffer()
|
|
// ALMemory::Delete()
|
|
// ALMemory::Rename()
|
|
// ALMemory::RenameToBackup()
|
|
// ALMemory::UnRename()
|
|
// ALMemory::Seek()
|
|
// ALMemory::GrowUserBuffer()
|
|
// ALMemory::FlushBuffer()
|
|
// ALMemory::Close()
|
|
// ALMemory::Create()
|
|
// ALMemory::Open()
|
|
//
|
|
// DESCRIPTION
|
|
//
|
|
// This file contains all the C++ member functions to support the
|
|
// ALMemory class. ALMemory is conceptually pretty simple, but it suffers
|
|
// from a little bit of #ifdef'itis. The reason for this is that
|
|
// things change just a little bit when we are using Windows memory
|
|
// management. Not enough to create a new class, but enough to have
|
|
// to make a lot of code conditional.
|
|
//
|
|
// The big difference between the Windows and DOS code shows up in two
|
|
// areas. First, under Windows we have huge buffers that can support
|
|
// up to either 16Mbytes or 4GBytes, depending. Under MS-DOS real mode,
|
|
// out biggest buffer is 64Kb. Second, under MS-DOS we get new memory
|
|
// with malloc/realloc/free. Under Windows we use LocalAlloc etc.
|
|
//
|
|
// Other than that, the whole class is pretty straightforward. Try not to
|
|
// let the issue of naming the buffers throw you, it is basically
|
|
// irrelevant.
|
|
//
|
|
// REVISION HISTORY
|
|
//
|
|
// May 24, 1994 1.0A : First release
|
|
//
|
|
//
|
|
|
|
#include "arclib.h"
|
|
#pragma hdrstop
|
|
|
|
#include "memstore.h"
|
|
|
|
#include <stdlib.h> // might be using malloc()!
|
|
|
|
//
|
|
// void * ALMemory::operator new( size_t size )
|
|
//
|
|
// ARGUMENTS:
|
|
//
|
|
// size : The amount of storage that needs to be allocated for
|
|
// this object.
|
|
//
|
|
// RETURNS
|
|
//
|
|
// A pointer to the storage.
|
|
//
|
|
// DESCRIPTION
|
|
//
|
|
// When using the DLL version of ArchiveLib, it is a good idea to
|
|
// allocate the storage for objects from inside the DLL, since they
|
|
// will be freed inside the DLL. If we don't have the new operator
|
|
// for a class, its storage will be allocated from the EXE before
|
|
// the constructor code is called. Then, when it is time to free
|
|
// the storage, the delete operator will be called inside the DLL.
|
|
// Not good, right?
|
|
//
|
|
//
|
|
// Very important: this new operator is called to allocate the
|
|
// storage for the ALMemory object itself. This has nothing to do
|
|
// with the storage buffer that the memory object will be using
|
|
// later on. In other words, this new operator is responsible for
|
|
// no more than a couple of dozen bytes, not potentially hundreds
|
|
// of Kbytes.
|
|
//
|
|
// REVISION HISTORY
|
|
//
|
|
// May 24, 1994 1.0A : First release
|
|
//
|
|
|
|
#if defined( AL_BUILDING_DLL )
|
|
void AL_DLL_FAR * AL_PROTO ALMemory::operator new( size_t size )
|
|
{
|
|
return ::new char[ size ];
|
|
}
|
|
#endif
|
|
|
|
// WINDOWS version:
|
|
//
|
|
// ALMemory::ALMemory( const char *buffer_name = "",
|
|
// char AL_HUGE *user_buffer = 0,
|
|
// DWORD user_buffer_size = 0,
|
|
// ALCase name_case = AL_MIXED )
|
|
//
|
|
// MS-DOS real mode version :
|
|
//
|
|
// ALMemory::ALMemory( const char *buffer_name = "",
|
|
// char *user_buffer = 0,
|
|
// int user_buffer_size = 0,
|
|
// ALCase name_case = AL_MIXED )
|
|
//
|
|
// 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. Note that under Windows 16 this
|
|
// is a huge pointer, meaning it can span segments, and
|
|
// access potentially 16 Mbytes of memory.
|
|
//
|
|
// user_buffer_size : If you are passing a pointer to your own buffer,
|
|
// you need to indicate how large it is here. Under
|
|
// Windows this is a DWORD instead of a 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.
|
|
// RETURNS
|
|
//
|
|
// Nothing, it is a constructor.
|
|
//
|
|
// 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.
|
|
//
|
|
// REVISION HISTORY
|
|
//
|
|
// May 22, 1994 1.0A : First release
|
|
//
|
|
#if defined( AL_WINDOWS_MEMORY )
|
|
//
|
|
// The Windows and MS-DOS constructors are nearly identical.
|
|
//
|
|
AL_PROTO ALMemory::ALMemory( const char AL_DLL_FAR *buffer_name /* = "" */,
|
|
char AL_HUGE *user_buffer /* = 0 */,
|
|
DWORD user_buffer_size /* = 0 */,
|
|
ALCase name_case /* = AL_MIXED */)
|
|
: ALStorage( buffer_name, 4096, AL_MEMORY_OBJECT, name_case )
|
|
{
|
|
if ( user_buffer != 0 ) {
|
|
mpcUserBuffer = user_buffer;
|
|
mfUserOwnsBuffer = 1;
|
|
muUserBufferSize = user_buffer_size;
|
|
} else {
|
|
mfUserOwnsBuffer = 0;
|
|
mpcUserBuffer = 0;
|
|
muUserBufferSize = 0;
|
|
}
|
|
mhUserMemoryHandle = 0;
|
|
}
|
|
|
|
#else // #if defined( AL_WINDOWS_MEMORY )
|
|
|
|
AL_PROTO ALMemory::ALMemory( const char AL_DLL_FAR *buffer_name /* = "" */,
|
|
char AL_DLL_FAR *user_buffer /* = 0 */ ,
|
|
int user_buffer_size /* = 0 */,
|
|
ALCase name_case /* = AL_MIXED */ )
|
|
: ALStorage( buffer_name, 4096, AL_MEMORY_OBJECT, name_case )
|
|
{
|
|
if ( user_buffer != 0 ) {
|
|
mpcUserBuffer = user_buffer;
|
|
mfUserOwnsBuffer = 1;
|
|
muUserBufferSize = user_buffer_size;
|
|
} else {
|
|
mfUserOwnsBuffer = 0;
|
|
mpcUserBuffer = 0;
|
|
muUserBufferSize = 0;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// ALMemory::~ALMemory()
|
|
//
|
|
// ARGUMENTS:
|
|
//
|
|
// None, you don't get any for a destructor.
|
|
//
|
|
// RETURNS
|
|
//
|
|
// Nothing.
|
|
//
|
|
// 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 one of two methods, depending on
|
|
// whether we are under MS-DOS or Windows.
|
|
//
|
|
// Note also that we check the GoodTag() function when in Debug mode.
|
|
// That will help catch really bad mistakes, such as trying to delete
|
|
// an object that is not even an ALMemory object, maybe a beer can.
|
|
//
|
|
// REVISION HISTORY
|
|
//
|
|
// May 22, 1994 1.0A : First release
|
|
//
|
|
|
|
AL_PROTO ALMemory::~ALMemory()
|
|
{
|
|
AL_ASSERT( GoodTag(), "~ALMemory: attempting to delete invalid object" );
|
|
if ( !mfUserOwnsBuffer ) {
|
|
if ( mpcUserBuffer ) {
|
|
#if defined( AL_WINDOWS_MEMORY )
|
|
GlobalUnlock( (HGLOBAL) mhUserMemoryHandle );
|
|
GlobalFree( (HGLOBAL) mhUserMemoryHandle );
|
|
mhUserMemoryHandle= 0;
|
|
#else
|
|
free( mpcUserBuffer );
|
|
#endif
|
|
mpcUserBuffer = 0;
|
|
}
|
|
}
|
|
AL_ASSERT( GoodTag(), "~ALMemory: attempting to delete invalid object" );
|
|
}
|
|
|
|
//
|
|
// int ALMemory::LoadBuffer( long address )
|
|
//
|
|
// ARGUMENTS:
|
|
//
|
|
// address : An offset that we need to load data from.
|
|
//
|
|
// RETURNS
|
|
//
|
|
// AL_SEEK_ERROR if we try to read past the end of file. AL_END_OF_FILE
|
|
// if we just run out of data. Otherwise an int indicating how many bytes
|
|
// are now in the buffer.
|
|
//
|
|
// DESCRIPTION
|
|
//
|
|
// This is a virtual function the ALStorage functions rely on when reading
|
|
// data. Anytime someone tries to do a ReadChar() or ReadBuffer(), and
|
|
// it turns out that the I/O buffer has been exhausted, this function
|
|
// is called.
|
|
//
|
|
// The simple job of this function is to read as many bytes as possible out
|
|
// of the giant memory block allocated for the ALMemory object, and stick
|
|
// it into the I/O buffer, which caches it for calls to ReadChar()
|
|
// and friends.
|
|
//
|
|
// This works fine unless you try to go past the end of the buffer,
|
|
// since there is nothing there we flag that as an error.
|
|
//
|
|
// REVISION HISTORY
|
|
//
|
|
// May 22, 1994 1.0A : First release
|
|
//
|
|
|
|
int AL_PROTO ALMemory::LoadBuffer( long address )
|
|
{
|
|
if ( mStatus < AL_SUCCESS )
|
|
return mStatus;
|
|
if ( mlFilePointer != address ) {
|
|
if ( mlFilePointer > (long) muUserBufferSize )
|
|
return mStatus.SetError( AL_SEEK_ERROR,
|
|
"Attempt to read past end of the "
|
|
"buffer in ALMemory %s",
|
|
mName.GetName() );
|
|
}
|
|
long load = muUserBufferSize - address;
|
|
if ( load > (long) muBufferSize )
|
|
muBufferValidData = muBufferSize;
|
|
else
|
|
muBufferValidData = (size_t) load;
|
|
if ( muBufferValidData <= 0 )
|
|
return AL_END_OF_FILE;
|
|
#if defined( AL_WINDOWS_MEMORY )
|
|
//
|
|
// Some problems passing huge arrays to memcpy, got to do it inline instead
|
|
// I think Microsoft says memcpy() will work with huge pointers as long
|
|
// as you don't try to use the inline optimizations, but I say why take
|
|
// chances...
|
|
//
|
|
// Another note: AL_HUGE is _huge for win16, but blank for win32.
|
|
//
|
|
char AL_HUGE *temp = mpcUserBuffer + address;
|
|
for ( unsigned i = 0 ; i < muBufferValidData ; i++ )
|
|
mpcBuffer[ i ] = *temp++;
|
|
// memcpy( mpcBuffer, mpcUserBuffer + address, muBufferValidData );
|
|
#else
|
|
memcpy( mpcBuffer, mpcUserBuffer + (size_t) address, muBufferValidData );
|
|
#endif
|
|
if ( miUpdateCrcFlag )
|
|
UpdateCrc( muBufferValidData );
|
|
muReadIndex = 0; //Reading can resume at this location
|
|
mlFilePointer += muBufferValidData;
|
|
YieldTime();
|
|
return muBufferValidData;
|
|
}
|
|
|
|
//
|
|
// int ALMemory::Delete()
|
|
//
|
|
// ARGUMENTS:
|
|
//
|
|
// None.
|
|
//
|
|
// RETURNS
|
|
//
|
|
// Always returns AL_SUCCESS.
|
|
//
|
|
// DESCRIPTION
|
|
//
|
|
// This function is supposed to delete the underlying physical object.
|
|
// This makes a lot of sense with files, because you are essentially
|
|
// emulating the MS-DOS command line DEL function. With memory
|
|
// objects things aren't quite as clear. So we destroy the buffer,
|
|
// and that's that.
|
|
//
|
|
// REVISION HISTORY
|
|
//
|
|
// May 22, 1994 1.0A : First release
|
|
//
|
|
|
|
int AL_PROTO ALMemory::Delete()
|
|
{
|
|
if ( !mfUserOwnsBuffer ) {
|
|
#if defined( AL_WINDOWS_MEMORY )
|
|
GlobalUnlock( (HGLOBAL) mhUserMemoryHandle );
|
|
GlobalFree( (HGLOBAL) mhUserMemoryHandle );
|
|
mhUserMemoryHandle= 0;
|
|
#else
|
|
free( mpcUserBuffer );
|
|
#endif
|
|
mpcUserBuffer = 0;
|
|
}
|
|
return AL_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// int ALMemory::Rename( const char *new_name /* = 0 */,
|
|
// int /* delete_on_clash = 1 */ )
|
|
//
|
|
// ARGUMENTS:
|
|
//
|
|
// new_name : The new name of the buffer.
|
|
//
|
|
// delete_on_clash : This argument makes sense with files. What it says
|
|
// is that if you try to rename BOB.DAT to BOB.BAK,
|
|
// and it turns out that there is another BOB.BAK, should
|
|
// you delete the other one? With memory buffers,
|
|
// there is no clash, cause the OS doesn't care about
|
|
// unique names. So we ignore this parm.
|
|
//
|
|
// RETURNS
|
|
//
|
|
// Always returns AL_SUCCESS.
|
|
//
|
|
// DESCRIPTION
|
|
//
|
|
// This function is supposed to rename the underlying physical object.
|
|
// But in the case of memory buffers, the underlying physical object
|
|
// doesn't actually have a name, so this is really just a local rename.
|
|
//
|
|
// REVISION HISTORY
|
|
//
|
|
// May 22, 1994 1.0A : First release
|
|
//
|
|
|
|
int AL_PROTO ALMemory::Rename( const char AL_DLL_FAR *new_name /* = 0 */,
|
|
int /* delete_on_clash = 1 */ )
|
|
{
|
|
if ( new_name )
|
|
mName = new_name;
|
|
return AL_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// int ALMemory::RenameToBackup( int delete_on_clash /* = 1 */ )
|
|
//
|
|
// ARGUMENTS:
|
|
//
|
|
// delete_on_clash : This argument makes sense with files. What it says
|
|
// is that if you try to rename BOB.DAT to BOB.BAK,
|
|
// and it turns out that there is another BOB.BAK, should
|
|
// you delete the other one? With memory buffers,
|
|
// there is no clash, cause the OS doesn't care about
|
|
// unique names. So it doesn't matter what value you
|
|
// pass to the Rename() function, it is going to be
|
|
// ignored.
|
|
//
|
|
// RETURNS
|
|
//
|
|
// Always returns AL_SUCCESS.
|
|
//
|
|
// DESCRIPTION
|
|
//
|
|
// This function renames an object. But instead of making you sweat in
|
|
// order to come up with a new name, it just uses the default name
|
|
// that we use to assign a backup name.
|
|
//
|
|
// REVISION HISTORY
|
|
//
|
|
// May 22, 1994 1.0A : First release
|
|
//
|
|
|
|
int AL_PROTO ALMemory::RenameToBackup( int delete_on_clash /* = 1 */ )
|
|
{
|
|
mName.ChangeExtension();
|
|
return Rename( 0, delete_on_clash );
|
|
}
|
|
|
|
//
|
|
// int ALMemory::UnRename( int /* delete_on_clash = 1 */)
|
|
//
|
|
// ARGUMENTS:
|
|
//
|
|
// delete_on_clash : This argument makes sense with files. What it says
|
|
// is that if you try to rename BOB.DAT to BOB.BAK,
|
|
// and it turns out that there is another BOB.BAK, should
|
|
// you delete the other one? With memory buffers,
|
|
// there is no clash, cause the OS doesn't care about
|
|
// unique names. So we just ignore it here.
|
|
//
|
|
// RETURNS
|
|
//
|
|
// Always returns AL_SUCCESS.
|
|
//
|
|
// DESCRIPTION
|
|
//
|
|
// If you decide you didn't really want to rename an object after all, you
|
|
// can call this function to get the old name back!
|
|
//
|
|
// REVISION HISTORY
|
|
//
|
|
// May 22, 1994 1.0A : First release
|
|
//
|
|
|
|
int AL_PROTO ALMemory::UnRename( int /* delete_on_clash = 1 */)
|
|
{
|
|
AL_ASSERT( mName.GetName() != 0, "UnRename: trying to rename with a null name" );
|
|
AL_ASSERT( mName.GetOldName() != 0, "UnRename: trying to rename with a null old name" );
|
|
AL_ASSERT( strlen( mName ) > 0, "UnRename: trying to rename with a zero length name" );
|
|
AL_ASSERT( strlen( mName.GetOldName() ) > 0, "UnRename: trying to rename with a zero length old name" );
|
|
|
|
ALStorage::mName = mName.GetOldName();
|
|
return AL_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// int ALMemory::Seek( long address )
|
|
//
|
|
// ARGUMENTS:
|
|
//
|
|
// address : The address in the memory object to go to. The read and
|
|
// write pointers will now be repositioned to this point.
|
|
//
|
|
// RETURNS
|
|
//
|
|
// AL_SEEK_ERROR if we can't get to that point in the buffer. Otherwise
|
|
// AL_SUCCESS.
|
|
//
|
|
// DESCRIPTION
|
|
//
|
|
// This function acts just like the seek() function in the C runtime
|
|
// library. It flushes the current I/O buffers, and then moves the file
|
|
// read and write pointers to a new spot, specified by the address. if
|
|
// there is no memory there, you will get an error. Note that this
|
|
// makes it not quite like the C run time library, since it can create
|
|
// new space with a seek(). But I don't think we need that ability yet.
|
|
//
|
|
// REVISION HISTORY
|
|
//
|
|
// May 22, 1994 1.0A : First release
|
|
//
|
|
|
|
int AL_PROTO ALMemory::Seek( long address )
|
|
{
|
|
FlushBuffer();
|
|
if ( mStatus < 0 )
|
|
return mStatus;
|
|
|
|
if ( mlFilePointer != address ) {
|
|
if ( mlFilePointer > (long) muUserBufferSize )
|
|
return mStatus.SetError( AL_SEEK_ERROR,
|
|
"Attempt to read past end of the "
|
|
"buffer in ALMemory %s",
|
|
mName.GetName() );
|
|
}
|
|
mlFilePointer = address;
|
|
return AL_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// int ALMemory::GrowUserBuffer( long minimum_new_size )
|
|
//
|
|
// ARGUMENTS:
|
|
//
|
|
// minimum_new_size : The absolute minimum new size you need the buffer
|
|
// to grow to. This amount is usually determined by
|
|
// a pending I/O request. For example, if the current
|
|
// size of the buffer is 1000, and you have a 1 byte
|
|
// data block to write at 1000, the minimum new size
|
|
// will be 1001.
|
|
//
|
|
// RETURNS
|
|
//
|
|
// AL_CANT_ALLOCATE_MEMORY, if we just can't get it. AL_SUCCESS if we can.
|
|
//
|
|
// DESCRIPTION
|
|
//
|
|
// When you are trying to write to the memory object, and you have hit
|
|
// the end of the currently allocated area, it would seem like a good
|
|
// time to allocate more. When that situation occurs, this function is
|
|
// called. If the user owns the buffer, we don't have the option of asking
|
|
// the O/S or RTL for more memory, because we don't even know if the user
|
|
// memory is on the heap or what. But if we own the memory we know how
|
|
// to ask for more.
|
|
//
|
|
// The strategy for asking for more memory is pretty simple. Normally,
|
|
// we ask for another 16K. If that fails, we fall back to asking for
|
|
// just enough memory to cover our current I/O request. Asking for
|
|
// this memory is sufficiently different under real mode dos and protected
|
|
// mode windows that we have two completely different routines, separated
|
|
// only by #ifdefs.
|
|
//
|
|
// REVISION HISTORY
|
|
//
|
|
// May 22, 1994 1.0A : First release
|
|
//
|
|
|
|
#if defined( AL_WINDOWS_MEMORY )
|
|
int AL_PROTO ALMemory::GrowUserBuffer( long minimum_new_size )
|
|
{
|
|
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() );
|
|
long trial_size = muUserBufferSize + 16384;
|
|
GlobalUnlock( (HGLOBAL) mhUserMemoryHandle );
|
|
HGLOBAL new_handle = GlobalReAlloc( (HGLOBAL) mhUserMemoryHandle, trial_size, GMEM_MOVEABLE );
|
|
if ( new_handle == 0 ) {
|
|
trial_size = minimum_new_size;
|
|
new_handle = GlobalReAlloc( (HGLOBAL) mhUserMemoryHandle, trial_size, GMEM_MOVEABLE );
|
|
}
|
|
if ( new_handle == 0 ) {
|
|
mpcUserBuffer = (char AL_HUGE *) GlobalLock( (HGLOBAL) mhUserMemoryHandle );
|
|
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() );
|
|
}
|
|
mpcUserBuffer = (char AL_HUGE *) GlobalLock( new_handle );
|
|
mhUserMemoryHandle = new_handle;
|
|
muUserBufferSize = trial_size;
|
|
return AL_SUCCESS;
|
|
}
|
|
#else // #ifdef AL_WINDOWS_MEMORY
|
|
|
|
int AL_PROTO ALMemory::GrowUserBuffer( long minimum_new_size )
|
|
{
|
|
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 >= 65535L )
|
|
return mStatus.SetError( AL_CANT_ALLOCATE_MEMORY,
|
|
"Attempt to allocate a huge buffer "
|
|
"of %ld bytes for ALMemory "
|
|
"%s",
|
|
minimum_new_size,
|
|
mName.GetSafeName() );
|
|
long trial_size = muUserBufferSize + 16384;
|
|
if ( trial_size >= 65000U )
|
|
trial_size = 65000U;
|
|
if ( trial_size >= minimum_new_size ) {
|
|
char *new_buf = (char *) realloc( mpcUserBuffer, (size_t) trial_size );
|
|
if ( new_buf ) {
|
|
mpcUserBuffer = new_buf;
|
|
muUserBufferSize = (size_t) trial_size;
|
|
return AL_SUCCESS;
|
|
}
|
|
}
|
|
char *new_buf = (char *) realloc( mpcUserBuffer, (size_t) minimum_new_size );
|
|
if ( new_buf ) {
|
|
mpcUserBuffer = new_buf;
|
|
muUserBufferSize = (size_t) trial_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() );
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// int ALMemory::FlushBuffer()
|
|
//
|
|
// ARGUMENTS:
|
|
//
|
|
// None.
|
|
//
|
|
// RETURNS
|
|
//
|
|
// AL_CANT_ALLOCATE_MEMORY, if we run out. Otherwise, AL_SUCCESS.
|
|
//
|
|
// DESCRIPTION
|
|
//
|
|
// This routine is called when the I/O buffer is filled up. It means
|
|
// you have filled up the cache with what is usually 4K bytes of data.
|
|
// This routine is also called if you have hot data in the I/O buffer
|
|
// and you decide to do a seek(), or a read().
|
|
//
|
|
// All we have to do here is take the hot data in the I/O buffer and
|
|
// write it out to our massive memory object. The big complication is
|
|
// that sometimes the memory object isn't big enough, so while we are
|
|
// all busy trying to do this, we have to ask for more data at the
|
|
// same time.
|
|
//
|
|
// REVISION HISTORY
|
|
//
|
|
// May 22, 1994 1.0A : First release
|
|
//
|
|
|
|
int AL_PROTO ALMemory::FlushBuffer()
|
|
{
|
|
if ( mStatus < 0 )
|
|
return mStatus;
|
|
//
|
|
// If the write index is 0, we can skip all this stuff, because there
|
|
// is nothing in the buffer to flush out.
|
|
//
|
|
if ( muWriteIndex != 0 ) {
|
|
if ( miUpdateCrcFlag )
|
|
UpdateCrc( muWriteIndex );
|
|
if ( ( muWriteIndex + mlFilePointer ) > (long)muUserBufferSize )
|
|
if ( GrowUserBuffer( muWriteIndex + mlFilePointer ) < 0 )
|
|
return mStatus;
|
|
#if defined( AL_WINDOWS_MEMORY )
|
|
//
|
|
// Can't use memcpy with huge pointers, at least not with the optimized
|
|
// versions.
|
|
//
|
|
char AL_HUGE *temp = mpcUserBuffer + mlFilePointer;
|
|
for ( unsigned int i = 0 ; i < muWriteIndex ; i++ )
|
|
*temp++ = mpcBuffer[ i ];
|
|
// memcpy( mpcUserBuffer + mlFilePointer, mpcBuffer, muWriteIndex );
|
|
#else
|
|
memcpy( mpcUserBuffer + (size_t) mlFilePointer, mpcBuffer, muWriteIndex );
|
|
#endif
|
|
mlFilePointer += muWriteIndex;
|
|
muWriteIndex = 0;
|
|
if ( mlSize < mlFilePointer )
|
|
mlSize = mlFilePointer;
|
|
}
|
|
muReadIndex = 0;
|
|
muBufferValidData = 0;
|
|
YieldTime();
|
|
return AL_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// int ALMemory::Close()
|
|
//
|
|
// ARGUMENTS:
|
|
//
|
|
// None.
|
|
//
|
|
// RETURNS
|
|
//
|
|
// AL_SUCCESS, or various error codes that filter on down from other
|
|
// routines.
|
|
//
|
|
// DESCRIPTION
|
|
//
|
|
// Close() is supposed to do the same thing as fclose() in the run
|
|
// time library. 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.
|
|
//
|
|
// REVISION HISTORY
|
|
//
|
|
// May 22, 1994 1.0A : First release
|
|
//
|
|
|
|
int AL_PROTO ALMemory::Close()
|
|
{
|
|
if ( mpcBuffer == 0 )
|
|
return mStatus;
|
|
FlushBuffer();
|
|
ALStorage::Close();
|
|
//
|
|
// If we aren't using all our space, give back the extra.
|
|
//
|
|
if ( mlSize < (long) muUserBufferSize ) {
|
|
#if defined( AL_WINDOWS_MEMORY )
|
|
GlobalUnlock( (HGLOBAL) mhUserMemoryHandle );
|
|
HGLOBAL new_handle = GlobalReAlloc( (HGLOBAL) mhUserMemoryHandle, mlSize, GMEM_MOVEABLE );
|
|
if ( new_handle != 0 )
|
|
mhUserMemoryHandle = new_handle;
|
|
mpcUserBuffer = (char AL_HUGE *) GlobalLock( (HGLOBAL) mhUserMemoryHandle );
|
|
muUserBufferSize = mlSize;
|
|
#else
|
|
char *new_buf = (char *) realloc( mpcUserBuffer, (size_t) mlSize );
|
|
if ( new_buf )
|
|
mpcUserBuffer = new_buf;
|
|
muUserBufferSize = (size_t) mlSize;
|
|
#endif
|
|
}
|
|
return mStatus;
|
|
}
|
|
|
|
//
|
|
// int ALMemory::Create()
|
|
//
|
|
// ARGUMENTS:
|
|
//
|
|
// None.
|
|
//
|
|
// RETURNS
|
|
//
|
|
// AL_SUCCESS, AL_CANT_ALLOCATE_MEMORY, or various error codes that
|
|
// filter on down from other routines.
|
|
//
|
|
// 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.
|
|
//
|
|
// REVISION HISTORY
|
|
//
|
|
// May 22, 1994 1.0A : First release
|
|
//
|
|
|
|
int AL_PROTO ALMemory::Create()
|
|
{
|
|
ALStorage::Create();
|
|
if ( mStatus < AL_SUCCESS )
|
|
return mStatus;
|
|
if ( (char *) mName == 0 || strlen( mName ) == 0 )
|
|
mName = "AL.TMP";
|
|
if ( mfUserOwnsBuffer )
|
|
return AL_SUCCESS; //If the user supplied the buffer, we take what's available
|
|
#if defined( AL_WINDOWS_MEMORY )
|
|
mhUserMemoryHandle = GlobalAlloc( GMEM_MOVEABLE, 16384 );
|
|
if ( mhUserMemoryHandle ) {
|
|
mpcUserBuffer = (char AL_HUGE *) GlobalLock( (HGLOBAL) mhUserMemoryHandle );
|
|
muUserBufferSize = 16384;
|
|
} else {
|
|
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()",
|
|
16384,
|
|
mName.GetSafeName() );
|
|
}
|
|
#else
|
|
mpcUserBuffer = (char *) malloc( 16384 );
|
|
muUserBufferSize = 16384;
|
|
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()",
|
|
16384,
|
|
mName.GetSafeName() );
|
|
#endif
|
|
return AL_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// int ALMemory::Open()
|
|
//
|
|
// ARGUMENTS:
|
|
//
|
|
// None.
|
|
//
|
|
// RETURNS
|
|
//
|
|
// AL_SUCCESS, AL_CANT_OPEN_FILE, or various error codes that
|
|
// filter on down from other routines.
|
|
//
|
|
// 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.
|
|
//
|
|
// REVISION HISTORY
|
|
//
|
|
// May 22, 1994 1.0A : First release
|
|
//
|
|
|
|
int AL_PROTO ALMemory::Open()
|
|
{
|
|
ALStorage::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 = (long) muUserBufferSize;
|
|
return AL_SUCCESS;
|
|
}
|
|
|
|
|