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;
 | |
| }
 | |
| 
 | |
| 
 |