home *** CD-ROM | disk | FTP | other *** search
- // ------------------------------- //
- // -------- Start of File -------- //
- // ------------------------------- //
- // ----------------------------------------------------------- //
- // C++ Source Code File Name: vbdfile.cpp
- // Compiler Used: MSVC40, DJGPP 2.7.2.1, GCC 2.7.2.1, HP CPP 10.24
- // Produced By: Doug Gaer
- // File Creation Date: 02/04/1997
- // Date Last Modified: 03/18/1999
- // Copyright (c) 1997 Douglas M. Gaer
- // ----------------------------------------------------------- //
- // ------------- Program Description and Details ------------- //
- // ----------------------------------------------------------- //
- /*
- The VBD C++ classes are copyright (c) 1997, by Douglas M. Gaer.
- All those who put this code or its derivatives in a commercial
- product MUST mention this copyright in their documentation for
- users of the products in which this code or its derivative
- classes are used. Otherwise, you have the freedom to redistribute
- verbatim copies of this source code, adapt it to your specific
- needs, or improve the code and release your improvements to the
- public provided that the modified files carry prominent notices
- stating that you changed the files and the date of any change.
-
- THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND.
- THE ENTIRE RISK OF THE QUALITY AND PERFORMANCE OF THIS SOFTWARE
- IS WITH YOU. SHOULD ANY ELEMENT OF THIS SOFTWARE PROVE DEFECTIVE,
- YOU WILL ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR, OR
- CORRECTION.
-
- The VBD file manager class is responsible for handling all
- low-level file operations through the use of VBDFile objects
- or by inheriting the VBDFile class. Low-level file operations
- refer to functions such as: Create(), Open(), Read(), Write(),
- Alloc(), Delete(), and Close(). These functions contain all the
- routines needed to create and maintain VBD files in accordance
- with the VBD File Format. VBDFile objects are reference counted
- and must be created dynamically due to the way reference
- counting is implemented.
- */
- // ----------------------------------------------------------- //
- #include <string.h>
- #include <stdlib.h>
- #include <errno.h>
- #include "vbdfile.h"
-
- // Define the __DOS_INCLUDES__ macro to use DOS path separators
- #ifdef __DOS_INCLUDES__
- #include <sys\types.h>
- #include <sys\stat.h>
- #else
- #include <sys/types.h>
- #include <sys/stat.h>
- #endif
-
- // Init all the static data members
- static char ClosedFileName[MaxNameLength] = "closed.vbd";
-
- // Current VBD file manager version number.
- // 07/22/1998 changed to version 1025 from version 1024.
- // 08/31/1998 changed to version 1027 from version 1025.
- // 02/10/1999 changed to version 1029 from version 1027.
- // 03/18/1999 changed to from version 1029 to final release version 1031.
- __LWORD__ VBDFile::VBDVersion = 1031;
-
- // 08/31/1998 added revision letter 'A' to signature. Revision 'A'
- // adds a 32-bit checksum routines, not included in previous versions.
- __SBYTE__ VBDFile::VBDSignature[8] = {'V', 'B', 'D', 'F', 'I', 'L', 'E', 'A'};
-
- VBDFile::VBDFile()
- // Creates a VBD file object, with refcount of one.
- {
- strcpy(FileName, ClosedFileName); // Set the initial file name
- fp = 0;
- Status = 0x01; // good, read-only, and closed
- }
-
- VBDFile::~VBDFile()
- {
- Close();
- }
-
- int VBDFile::Create(const char *FName, FAU StaticSize)
- // Creates and opens a new file named FName, truncating it
- // if it already exists. The area at the front of the file
- // of length StaticSize + sizeof(FileHeader) is reserved.
- {
- // First, close the current file if open.
- Close();
-
- Status = 0x05; // Set read/write bit and good bit
-
- // Create, truncate if already exists
- fp = ::fopen(FName, "w+b");
-
- if(fp == 0) {
- #ifdef CPP_EXCEPTIONS
- throw CFileCreationError();
- #else
- Error->SignalException(EHandler::FileCreationError);
- #endif
- }
- else {
- Status |= 2; // Set open bit
- strcpy(FileName, FName);
- Header.HeapStart = StaticSize + sizeof(FileHeader);
- LastOperation = READ; // So Write() works right the first time
- InitHdr();
- }
-
- // Returns 1 if the file was successfully created and opened.
- return IsOpen();
- }
-
- void VBDFile::InitHdr()
- // Sets up the header area in the file. If there's
- // statically allocated data beyond the header, write
- // a 0 to the last byte of it, so that there will be no
- // unexpected end of file errors. The rest of the static
- // area stays uninitialized.
- {
- Header.FreeSpace = 0;
- Header.EndOfFile = Header.HeapStart;
- Header.HighestVB = 0; // No blocks allocated in the file
- memcpy(Header.Signature, VBDSignature, 8); // Copy signature
- Header.Version = VBDVersion;
-
- // Set the revision letter
- char revision[8];
- memmove(revision, VBDSignature, 8);
- rev_letter = revision[7];
-
- // 03/13/1998: Write the VBD file header and flush the disk
- // buffers to maintain file integrity during multiple file
- // access.
- WriteHdr();
-
- if(Header.HeapStart > sizeof(FileHeader)) {
- __SBYTE__ zero_byte = 0;
- Write(&zero_byte, 1, Header.HeapStart-1);
- }
- }
-
- int VBDFile::Open(const char *FName, AccessMode Mode)
- // Opens the FName file. File must exist and be a VBDFile type
- // or error occurs. This function will check the revsion letter
- // when opening an existing file.
- {
- char *mode_str;
-
- // First, close the current file if open.
- Close();
-
- if(Mode == READONLY) {
- mode_str = "rb";
- Status = 0x01; // set good bit, reset read/write bit
- }
- else {
- mode_str = "r+b";
- Status = 0x05; // Set good bit and read/write bit
- }
-
- fp = ::fopen(FName, mode_str);
-
- if(fp == 0) {
- #ifdef CPP_EXCEPTIONS
- throw CFileOpenError();
- #else
- Error->SignalException(EHandler::FileOpenError);
- #endif
- }
- else {
- Status |= 2; // Set open bit
- strcpy(FileName, FName);
- LastOperation = WRITE; // So Read() works right the first time
-
- ReadHdr();
- // Test file type, checking the revision letter
- if(memcmp(Header.Signature, VBDSignature, 7)) {
- #ifdef CPP_EXCEPTIONS
- throw CWrongFileType();
- #else
- Error->SignalException(EHandler::WrongFileType);
- #endif
- }
-
- // Set the revision letter according to the file header
- char revision[8];
- memmove(revision, Header.Signature, 8);
- rev_letter = revision[7];
- }
-
- // 03/11/1998: This code was added to ensure that true end of
- // file is stored in the VBD file header.
- INT32 filesize;
- filesize = FileSize(FName);
- if(Header.EndOfFile < filesize) {
- Header.EndOfFile = filesize;
- Flush();
- }
-
- return IsOpen(); // Returns 1 if file opened successfully, else 0.
- }
-
- int VBDFile::ReOpen(const char *FName, AccessMode Mode)
- // Reopens the FName file. File must exist and be a VBD file type
- // or error occurs. This function will check the revsion letter
- // when reopening an existing file.
- {
- char *mode_str;
-
- if(Mode == READONLY) {
- mode_str = "rb";
- Status = 0x01; // set good bit, reset read/write bit
- }
- else {
- mode_str = "r+b";
- Status = 0x05; // Set good bit and read/write bit
- }
-
- // Close the file and flush all buffers
- ::rewind(fp);
- if(!ReadOnly()) ::fflush(fp);
- ::fclose(fp);
-
- // Reopen after closing the file
- fp = ::fopen(FName, mode_str);
-
- if(fp == 0) {
- #ifdef CPP_EXCEPTIONS
- throw CFileOpenError();
- #else
- Error->SignalException(EHandler::FileOpenError);
- #endif
- }
- else {
- Status |= 2; // Set open bit
- strcpy(FileName, FName);
- LastOperation = WRITE; // So Read() works right the first time
-
- ReadHdr();
- // Test file type, checking the revision letter
- if(memcmp(Header.Signature, VBDSignature, 7)) {
- #ifdef CPP_EXCEPTIONS
- throw CWrongFileType();
- #else
- Error->SignalException(EHandler::WrongFileType);
- #endif
- }
-
- // Set the revision letter according to the file header
- char revision[8];
- memmove(revision, Header.Signature, 8);
- rev_letter = revision[7];
- }
-
- // 03/11/1998: This code was added to ensure that true end of
- // file is stored in the VBD file header.
- INT32 filesize;
- filesize = FileSize(FName);
- if(Header.EndOfFile < filesize) {
- Header.EndOfFile = filesize;
- Flush();
- }
-
- return IsOpen(); // Returns 1 if file opened successfully, else 0.
- }
-
- void VBDFile::Close(int flush)
- // Closes the file if not already closed, flushing the
- // basic header if flush = 1. Does nothing if in the
- // error state. Checks for dangling references to
- // this file.
- {
- if(IsOK()) {
- if(refcount > 1) // Check the object pointer's reference count
- #ifdef CPP_EXCEPTIONS
- throw CDanglingPtr();
- #else
- Error->SignalException(EHandler::DanglingPtr);
- #endif
-
- if(flush && !ReadOnly())
- WriteHdr();
- if(::fclose(fp) != 0)
- #ifdef CPP_EXCEPTIONS
- throw CFileCloseError();
- #else
- Error->SignalException(EHandler::FileCloseError);
- #endif
- strcpy(FileName, ClosedFileName); // Set the initial file name
- }
- Status &= 0xfd; // Reset open bit
- }
-
- void VBDFile::Flush()
- // Writes the VBDFile header to the file, and then flushes
- // any internal buffers the file might have.
- {
- if(ReadyForWriting()) {
- WriteHdr();
- if(fflush(fp) != 0)
- #ifdef CPP_EXCEPTIONS
- throw CFileWriteError();
- #else
- Error->SignalException(EHandler::FileWriteError);
- #endif
- Seek(0, SEEK_CUR); // So that the LastOperation logic works right
- }
- }
-
- void VBDFile::Seek(FAU Offset, int SeekMode)
- // Moves the file pointer to the byte Offset,
- // using SeekMode, (which should be either SEEK_SET,
- // SEEK_CUR, or SEEK_END).
- {
- if(IsOK()) {
- int rv = ::fseek(fp, Offset, SeekMode);
- if(rv != 0)
- #ifdef CPP_EXCEPTIONS
- throw CFileSeekError();
- #else
- Error->SignalException(EHandler::FileSeekError);
- #endif
- LastOperation = SEEK;
- }
- else
- #ifdef CPP_EXCEPTIONS
- throw CFileNotReady();
- #else
- Error->SignalException(EHandler::FileNotReady);
- #endif
- }
-
- FAU VBDFile::SeekTo(FAU Address)
- // Seek to the specified address, optimizing the seek
- // operation by moving the file position indicator based
- // on the current stream position. Returns the current
- // file position after performing the seek operation.
- {
- // Get the current stream position
- StreamPos pos = FilePosition();
-
- if(Address == CurrAddress) { // Do not perform a seek operation
- return pos;
- }
- else if(Address > pos) { // Seek forward to the specified address
- StreamPos offset = Address - pos;
- Seek(offset, SEEK_CUR);
- }
- else if(Address < pos) { // Seek backward to the specified address
- if((__LWORD__)Address == 0) {
- Rewind();
- }
- else {
- fpos_t f_offset = (__LWORD__)pos - (__LWORD__)Address;
- fpos_t fpos = GetPosition();
- fpos -= f_offset;
- SetPosition(fpos);
- }
- }
- else { // Current file position equals the specified address
- // Ensure seeks between intervening reads and writes
- Seek(0, SEEK_CUR);
- }
-
- return FilePosition(); // Return current file position after seeking
- }
-
- void VBDFile::Rewind()
- // Repositions the file pointer to the beginning of the file.
- {
- ::rewind(fp);
- LastOperation = REWIND;
- }
-
- fpos_t VBDFile::GetPosition()
- // Returns the current value of the file-position indicator.
- // The SetPosition() function can later use information to reset
- // the file pointer to its position at the time GetPosition() was
- // called. The fpos_t type is an internal format and is intended
- // for use only by the GetPosition() and SetPosition() functions
- // to record information for uniquely specifying positions within
- // a file.
- {
- fpos_t pos;
-
- if(IsOK()) {
- int rv = ::fgetpos(fp, &pos);
- if(rv != 0)
- #ifdef CPP_EXCEPTIONS
- throw CFileSeekError();
- #else
- Error->SignalException(EHandler::FileSeekError);
- #endif
- LastOperation = SEEK;
- }
- else
- #ifdef CPP_EXCEPTIONS
- throw CFileNotReady();
- #else
- Error->SignalException(EHandler::FileNotReady);
- #endif
-
- return pos;
- }
-
- void VBDFile::SetPosition(const fpos_t pos)
- // Set the file-position indicator for stream to the value of
- // pos, which is obtained in a prior call to the GetPosition()
- // function. The fpos_t type is an internal format and is intended
- // for use only by the GetPosition() and SetPosition() functions to
- // record information for uniquely specifying positions within a
- // file.
- {
- if(IsOK()) {
- int rv = ::fsetpos(fp, &pos);
- if(rv != 0)
- #ifdef CPP_EXCEPTIONS
- throw CFileSeekError();
- #else
- Error->SignalException(EHandler::FileSeekError);
- #endif
- LastOperation = SEEK;
- }
- else
- #ifdef CPP_EXCEPTIONS
- throw CFileNotReady();
- #else
- Error->SignalException(EHandler::FileNotReady);
- #endif
- }
-
- void VBDFile::Read(void *buf, __UWORD__ Bytes, FAU Address)
- // Reads Bytes from Address into buf. The address
- // is always interpreted to be from the beginning of the
- // file, unless it's CurrAddress, which means from the
- // current position.
- {
- if(IsOK()) {
- // Ensure seeks between intervening reads and writes,
- // and optimize for sequential reads
- if(Address == CurrAddress) {
- if(LastOperation == WRITE) Seek(0, SEEK_CUR);
- }
- else
- Seek(Address, SEEK_SET);
-
- __UWORD__ bytesmoved = ::fread(buf, 1, Bytes, fp);
- if(bytesmoved != Bytes) {
-
- if(feof(fp)) {
- // 03/17/1998: Modified to throw CAccessViolation exception
- // if an end of file error occurs during multiple file access
- // over an NFS mount.
- #ifdef CPP_EXCEPTIONS
- throw CAccessViolation();
- #else
- Error->SignalException(EHandler::AccessViolation);
- #endif
- }
- else
- #ifdef CPP_EXCEPTIONS
- throw CFileReadError();
- #else
- Error->SignalException(EHandler::FileReadError);
- #endif
- }
- LastOperation = READ;
- }
- else
- #ifdef CPP_EXCEPTIONS
- throw CFileNotReady();
- #else
- Error->SignalException(EHandler::FileNotReady);
- #endif
- }
-
- void VBDFile::Write(const void *buf, __UWORD__ Bytes, FAU Address,
- int flush, int bit_test)
- // Stores Bytes from buf to Address. The Address is
- // always interpreted to be from the beginning of the
- // file, unless it's CurrAddress, which means from the
- // current position. If flush equals one, the file
- // buffers will be flushed to disk with each write
- // operation. If bit_test equals one, the CRC of the
- // of the buffer will be compared to the CRC of the
- // actual bytes written to disk.
- {
- FAU block_address;
-
- if(ReadyForWriting()) {
- // Ensure seeks between intervening reads and writes,
- // and optimize for sequential writes
- if(Address == CurrAddress) {
- if(LastOperation == READ) Seek(0, SEEK_CUR);
- }
- else
- Seek(Address, SEEK_SET);
-
- block_address = FilePosition();
- __UWORD__ bytesmoved = ::fwrite(buf, 1, Bytes, fp);
-
- if(bytesmoved != Bytes) {
- if(feof(fp))
- #ifdef CPP_EXCEPTIONS
- throw CEOFError();
- #else
- Error->SignalException(EHandler::EOFError);
- #endif
- else
- #ifdef CPP_EXCEPTIONS
- throw CFileWriteError();
- #else
- Error->SignalException(EHandler::FileWriteError);
- #endif
- }
- LastOperation = WRITE;
- }
- else
- #ifdef CPP_EXCEPTIONS
- throw CFileNotWriteable();
- #else
- Error->SignalException(EHandler::FileNotWriteable);
- #endif
-
- // 03/13/1998: Allow application to flush disk buffers after
- // each write operation to ensure the file data stays in sync
- // during multiple file access.
- if(flush) {
- if(fflush(fp) != 0)
- #ifdef CPP_EXCEPTIONS
- throw CFileWriteError();
- #else
- Error->SignalException(EHandler::FileWriteError);
- #endif
- Seek(0, SEEK_CUR); // So that the LastOperation logic works right
- }
-
- if(bit_test) {
- __ULWORD__ w_csum = calcCRC32((char *)buf, Bytes);
- __ULWORD__ r_csum = CalcChecksum(Bytes, block_address);
-
- if(w_csum ^ r_csum)
- #ifdef CPP_EXCEPTIONS
- throw CChecksumError();
- #else
- Error->SignalException(EHandler::ChecksumError);
- #endif
- }
- }
-
- UINT32 VBDFile::WriteObjectChecksum(FAU Address)
- // Used to write a 32-bit checksum for the object at the
- // end of a block. The Address variable must be set to the
- // file address of the block data, not the block header.
- // This function assumes that the data has already been
- // written to the block. Returns the 32-bit CRC checksum
- // value for the object stored in the block.
- {
- // Perform operation according to the revision letter
- switch(rev_letter) {
- case 'A': case 'a' : // Version 1027, rev A
- // 9/24/1998: Revision 'A' reserves four bytes at the end
- // of each block for a 32-bit CRC checksum.
- break;
-
- default: // Always return zero for any file below revision 'A'
- return 0;
- }
-
- UINT32 CRC;
- __UWORD__ Bytes;
- VBHeader vb;
-
- // Calculate the address of the block header
- FAU block_address = Address - sizeof(VBHeader);
-
- if(IsOK()) {
- // Make sure that the this is a pre-alloacted block
- // Will throw a CSyncError if this is a bad file address
- Read(&vb, sizeof(VBHeader), block_address);
- if(vb.CkWord != CheckWord)
- #ifdef CPP_EXCEPTIONS
- throw CSyncError();
- #else
- Error->SignalException(EHandler::SyncError);
- #endif
-
- // Calculate a checksum based on the block data
- Bytes = (vb.Length - sizeof(VBHeader)) - sizeof(vbChecksum);
- CRC = CalcChecksum(Bytes, Address);
-
- // Offset address to point to the checksum field located
- // at the end of the block.
- Address += Bytes;
-
- // Write the CRC for the block header and the block data.
- Write(&CRC, sizeof(CRC), Address);
- }
-
- return CRC;
- }
-
- int VBDFile::ReadObjectChecksum(FAU Address, __ULWORD__ *object_crc,
- __ULWORD__ *calc_crc)
- // Tests the object's CRC value stored on disk against
- // the actual CRC of the bytes stored on disk. The Address
- // variable must be set to the file address of the block
- // data, not the block header. This function assumes that
- // the data has already been written to the block. Returns
- // true if the object's CRC test good or false if the CRC
- // tests bad. Passes back the object's CRC stored on disk
- // in the object_crc variable and the calculated CRC value
- // in the calc_crc variable.
- {
- // Perform operation according to the revision letter
- switch(rev_letter) {
- case 'A': case 'a' : // Version 1027, rev A
- // 9/24/1998: Revision 'A' reserves four bytes at the end
- // of each block for a 32-bit CRC checksum.
- break;
-
- default: // Always return true for any file below revision 'A'
- return 1;
- }
-
- UINT32 CRC, objectCRC;
- VBHeader vb;
- __UWORD__ Bytes;
-
- // Calculate the address of the block header
- FAU block_address = Address - sizeof(VBHeader);
-
- if(IsOK()) {
- // Make sure that the this is a pre-alloacted block
- // Will throw a CSyncError if this is a bad file address
- Read(&vb, sizeof(VBHeader), block_address);
- if(vb.CkWord != CheckWord)
- #ifdef CPP_EXCEPTIONS
- throw CSyncError();
- #else
- Error->SignalException(EHandler::SyncError);
- #endif
-
- // Calculate a checksum based on the block data
- Bytes = (vb.Length - sizeof(VBHeader)) - sizeof(vbChecksum);
- CRC = CalcChecksum(Bytes, Address);
-
- // Offset address to point to the checksum field located
- // at the end of the block.
- Address += Bytes;
-
- // Read the CRC value stored on disk
- Read(&objectCRC, sizeof(objectCRC), Address);
- }
-
- if(object_crc) *object_crc = objectCRC;
- if(calc_crc) *calc_crc = CRC;
-
- if(CRC ^ objectCRC) return 0; // Return false if CRC check fails
-
- return 1; // Return true if the CRC values match
- }
-
- __ULWORD__ VBDFile::CalcChecksum(__UWORD__ Bytes, FAU Address, int mem_alloc)
- // Calculate a 32-bit CRC checksum for a given number
- // of bytes starting at the specified address. Returns a
- // 32-bit CRC value. If the mem_alloc variable is true, a
- // buffer equal to the specified number of bytes will be
- // created in memory. If the mem_alloc variable is false
- // or memory allocation fails, the CRC will be calculated
- // byte by byte starting at the specified address.
- {
- __ULWORD__ CRC;
- __UWORD__ len = Bytes;
- unsigned char data;
- char *buf = 0;
-
- // Create a buffer equal to the object length
- if(mem_alloc) buf = new char[Bytes];
-
- if(buf) {
- if(IsOK()) {
- Read(buf, Bytes, Address);
- CRC = calcCRC32(buf, Bytes);
- delete buf;
- }
- }
- else {
- if(IsOK()) {
- SeekTo(Address); // Seek to the specified file address
- CRC = 0xffffffffL;
- while(len--) {
- Read(&data, sizeof(data));
- CRC = calcCRC32(data, CRC);
- }
- CRC ^= 0xffffffffL;
- }
- }
-
- return CRC;
- }
-
- void VBDFile::ReadVBHdr(VBHeader &hdr, FAU Address)
- // Reads in VB header, and tests check word to make
- // sure the file is still in sync.
- {
- Read(&hdr, sizeof(VBHeader), Address);
- if(hdr.CkWord != CheckWord)
- #ifdef CPP_EXCEPTIONS
- throw CSyncError();
- #else
- Error->SignalException(EHandler::SyncError);
- #endif
- }
-
- void VBDFile::WriteVBHdr(const VBHeader &hdr, FAU Address)
- // Writes the block header to disk.
- {
- Write(&hdr, sizeof(VBHeader), Address);
- }
-
- void VBDFile::ReadHdr()
- // Reads the VBD file header from disk.
- {
- Read(&Header, sizeof(FileHeader), StartOfFile);
- }
-
- void VBDFile::WriteHdr()
- // Writes the VBD file header to disk.
- {
- Write(&Header, sizeof(FileHeader), StartOfFile);
- }
-
- FAU VBDFile::Alloc(__UWORD__ Bytes)
- // Allocates a Variable Data Block of x Bytes of data from either
- // the free space list, or from the end of the file. The number
- // of bytes allocated is adjusted to hold a VBHeader plus the
- // object. Only the VBHeader is written to the allocated space.
- // Alloc() returns the location of the space allocated for the
- // object, or returns a 0 if an error occurred.
- {
- FAU Address = 0;
- VBHeader vb;
-
- if(ReadyForWriting()) {
-
- // Adjust the number of bytes to allocate space of VB header
- // according to the revision letter.
- switch(rev_letter) {
- case 'A': case 'a' : // Version 1027, rev A
- // 9/24/1998: Revision 'A' adjusts the number of bytes to
- // include a 32-bit CRC. Space for a four byte checksum will
- // be reserved at the end of the block.
- Bytes += (sizeof(vbChecksum) + sizeof(VBHeader));
- break;
-
- default: // Default to revision zero
- Bytes += sizeof(VBHeader);
- break;
- }
-
- // Try to reclaim a block if the free space list is not empty
- if(Header.FreeSpace != 0) Address = Reclaim(Bytes);
-
- if(IsOK() && Address == 0) { // Extend the file
- Address = Header.EndOfFile;
- Header.EndOfFile += Bytes;
-
- // Write to the last byte to avoid possible end of file errors
- __SBYTE__ zero = 0;
- Write(&zero, sizeof(__SBYTE__), Header.EndOfFile-1);
- Header.HighestVB = Address;
- }
-
- vb.Status = NormalVB; // Mark VB with normal attribute
- vb.NextDeletedVB = 0; // Always 0 unless block is marked deleted
- vb.CkWord = CheckWord; // Assign the check word value
- vb.Length = Bytes; // Total number of bytes for this VB
-
- WriteVBHdr(vb, Address); // Write header for this VB
- }
- else
- #ifdef CPP_EXCEPTIONS
- throw CFileNotWriteable();
- #else
- Error->SignalException(EHandler::FileNotWriteable);
- #endif
- if(Address > Header.HighestVB) Header.HighestVB = Address;
-
- // 03/13/1998: Ensure that the VBD file header stays in sync
- // during multiple file access.
- WriteHdr();
-
- return SeekTo(Address + sizeof(VBHeader));
- }
-
- int VBDFile::Remove(FAU Address, int mem_alloc)
- // Deletes the VB and removes the object by setting all
- // the data bytes to zero. The VB is marked removed,
- // indicating that the object cannot be undeleted. If
- // the mem_alloc variable is true, a buffer equal to
- // the object length of the object will be created in
- // memory. If the mem_alloc variable is false or memory
- // allocation fails, a single byte value, equal to zero,
- // is written to the file byte by byte for the length of
- // the object.
- {
- // Return false if VB is already deleted or marked bad
- if(!Delete(Address)) return 0;
-
- VBHeader vb;
- FAU VBAddress = Address - sizeof(VBHeader); // Address of VB header
- ReadVBHdr(vb, VBAddress);
- vb.Status = RemovedVB; // Mark VB removed
- __UWORD__ Bytes = vb.Length - sizeof(VBHeader);
-
- const unsigned char data = 0;
- const __UWORD__ len = Bytes;
- char *buf = 0;
-
- // Create a buffer equal to the object length
- if(mem_alloc) buf = new char[len];
-
- if(buf) {
- for(__UWORD__ i = 0; i < len; i++) // Zero out the buffer
- buf[i] = 0;
-
- if(ReadyForWriting()) {
- WriteVBHdr(vb, VBAddress);
- Write(buf, Bytes, Address, 1, 0);
- delete buf;
- return 1; // Return true if successful
- }
- else
- #ifdef CPP_EXCEPTIONS
- throw CFileNotWriteable();
- #else
- Error->SignalException(EHandler::FileNotWriteable);
- #endif
- }
- else {
- if(ReadyForWriting()) {
- WriteVBHdr(vb, VBAddress);
- while(Bytes--) // Write all zeros in place of the object
- Write(&data, sizeof(data), CurrAddress, 0, 0);
- return 1; // Return true if successful
- }
- else
- #ifdef CPP_EXCEPTIONS
- throw CFileNotWriteable();
- #else
- Error->SignalException(EHandler::FileNotWriteable);
- #endif
- }
-
- return 0; // Ensure all paths return a value
- }
-
- int VBDFile::Delete(FAU Address)
- // Marks the VB at location Address deleted and leaves the
- // object unchanged, allowing it to be undeleted. The deleted
- // VB is placed on the front of the free space list.
- {
- VBHeader vb;
-
- FAU VBAddress = Address - sizeof(VBHeader); // Address of VB header
- ReadVBHdr(vb, VBAddress);
-
- // Return false if VB is already deleted
- if((vb.Status & 0xff) != NormalVB) return 0;
-
- vb.Status = DeletedVB; // Mark VB deleted
-
- if(ReadyForWriting()) {
- vb.NextDeletedVB = Header.FreeSpace; // VB to become head of free list
- WriteVBHdr(vb, VBAddress);
-
- // Make sure the free space list is not corrupt
- if(Header.FreeSpace != FSListCorrupt) {
- Header.FreeSpace = VBAddress;
- WriteHdr();
- }
- return 1; // Return true if successful
- }
- else
- #ifdef CPP_EXCEPTIONS
- throw CFileNotWriteable();
- #else
- Error->SignalException(EHandler::FileNotWriteable);
- #endif
- return 0; // Ensure all paths return a value
- }
-
- StreamPos VBDFile::FilePosition()
- // Returns the current file posistion
- {
- if(!IsOK())
- #ifdef CPP_EXCEPTIONS
- throw CFileNotReady();
- #else
- Error->SignalException(EHandler::FileNotReady);
- #endif
-
- return ::ftell(fp);
- }
-
- const char *VBDFile::GetSignature() const
- // Return the VBD file signature with no revision letter
- {
- size_t len = sizeof(Header.Signature);
- char *s = new char[len];
- memcpy(s, Header.Signature, len);
- s[len-1] = 0;
- return (const char *)s;
- }
-
- char *VBDFile::GetSignature()
- // Return the VBD file signature with no revision letter
- {
- size_t len = sizeof(Header.Signature);
- char *s = new char[len];
- memcpy(s, Header.Signature, len);
- s[len-1] = 0;
- return s;
- }
-
- __UWORD__ VBDFile::VBTotal()
- // Returns the total number of valid VBs in the file
- {
- // 03/13/1998: Ensure that the VBD file header stays in sync
- // during multiple file access
- TestVBDHeader();
-
- VBHeader vb;
- FAU Address = FindFirstVB(0); // Search the entire file
- __UWORD__ i = 0;
-
- while(Address) { // Until a block of a valid size is found
- if(Address >= Header.EndOfFile) break;
- Read(&vb, sizeof(VBHeader), Address);
- if(!IsOK()) break;
-
- // If this is not a valid block, find the next one
- if(vb.CkWord != CheckWord) {
- Address = FindFirstVB(Address);
- }
- else {
- Address += vb.Length;
- i++;
- }
- }
- return i;
- }
-
- INT32 VBDFile::VBDeleted(__UWORD__ *d, __UWORD__ *r)
- // Returns the total number of removed and deleted VBs
- {
- // 03/13/1998: Ensure that the VBD file header stays in sync
- // during multiple file access
- TestVBDHeader();
-
- VBHeader vb;
- FAU addr = Header.FreeSpace;
- __UWORD__ i = 0;
-
- // Set the deleted and removed pointers to zero
- if(d) *d = 0; if(r) *r = 0;
-
- if(addr == FSListCorrupt) return FSListCorrupt;
-
- while(addr) { // Until a block of a valid size is found
- Read(&vb, sizeof(VBHeader), addr);
- if(!IsOK()) break;
-
- // If this is not a valid block, the free space list is corrupt
- if(vb.CkWord != CheckWord) {
- Header.FreeSpace = FSListCorrupt;
- WriteHdr();
- return FSListCorrupt;
- }
-
- // Added: 09/04/1998 to prevent an infinite loop.
- // If the block is not marked deleted or removed, the
- // Next deleted VBD pointer is bad. This will cause
- // an infinite loop if the end of the free space
- // list is pointing to valid block.
- switch((__SBYTE__)(vb.Status & 0xff)) {
- case DeletedVB :
- // Make sure the block is not pointing to itself
- if(addr == vb.NextDeletedVB) {
- Header.FreeSpace = FSListCorrupt;
- WriteHdr();
- return 0;
- }
- break;
-
- case RemovedVB :
- // Make sure the block is not pointing to itself
- if(addr == vb.NextDeletedVB) {
- Header.FreeSpace = FSListCorrupt;
- WriteHdr();
- return 0;
- }
- break;
-
- default :
- Header.FreeSpace = FSListCorrupt;
- WriteHdr();
- return 0;
- }
-
- addr = vb.NextDeletedVB;
-
- if(d) {
- if((vb.Status & 0xff) == DeletedVB)
- *d = *d+1;
- }
-
- if(r) {
- if((vb.Status & 0xff) == RemovedVB)
- *r = *r+1;
- }
-
- i++;
- }
- return i;
- }
-
- __UWORD__ VBDFile::ObjectLength(FAU Address)
- // Returns the object length in bytes at the specified address
- {
- VBHeader vb;
- FAU VBAddress;
-
- // Calculate the address of the block header
- if(Address == CurrAddress)
- VBAddress = FilePosition() - sizeof(VBHeader);
- else
- VBAddress = Address - sizeof(VBHeader);
-
- ReadVBHdr(vb, VBAddress);
-
- __UWORD__ len;
-
- // Adjust the number of bytes to allocate space of VB header
- // according to the revision letter.
- switch(rev_letter) {
- case 'A': case 'a' : // Version 1027, rev A
- // 9/24/1998: Revision 'A' adjusts the number of bytes to
- // include a 32-bit CRC. Space for a four byte checksum is
- // reserved at the end of the block.
- len = vb.Length - sizeof(VBHeader);
- len -= sizeof(vbChecksum);
- break;
-
- default: // Default to revision zero
- len = vb.Length - sizeof(VBHeader);
- break;
- }
- return len;
- }
-
- __UWORD__ VBDFile::VBLength(FAU Address)
- // Returns the total VB length in bytes at the specified address
- {
- VBHeader vb;
- FAU VBAddress;
-
- // Calculate the address of the block header
- if(Address == CurrAddress)
- VBAddress = FilePosition() - sizeof(VBHeader);
- else
- VBAddress = Address - sizeof(VBHeader);
-
- ReadVBHdr(vb, VBAddress);
- return vb.Length;
- }
-
- int VBDFile::UnDelete(FAU Address)
- // Undeletes the VB if it has not been removed or reclaimed
- {
- VBHeader vb, prev_VB;
- FAU addr, vb_addr, prev_addr;
- addr = Header.FreeSpace;
-
- vb_addr = Address - sizeof(VBHeader); // Address of VB header
- Read(&vb, sizeof(VBHeader), vb_addr);
-
- // Return false if VB is not marked deleted
- if((vb.Status & 0xff) != DeletedVB) return 0;
-
- // Loop until the block is found in the free space list
- while(addr) {
- Read(&vb, sizeof(VBHeader), addr);
- if(!IsOK()) break;
-
- // Added: 09/04/1998 to signal not to use the free space list.
- // If this is not a valid block, the free space list is corrupt
- if(vb.CkWord != CheckWord) {
- Header.FreeSpace = FSListCorrupt;
- WriteHdr();
- return 0;
- }
-
- // Added: 09/04/1998 to prevent an infinite loop.
- // If the block is not marked deleted or removed, the
- // Next deleted VBD pointer is bad. This will cause
- // an infinite loop if the end of the free space
- // list is pointing to valid block.
- switch((__SBYTE__)(vb.Status & 0xff)) {
- case DeletedVB :
- // Make sure the block is not pointing to itself
- if(addr == vb.NextDeletedVB) {
- Header.FreeSpace = FSListCorrupt;
- WriteHdr();
- return 0;
- }
- break;
-
- case RemovedVB :
- // Make sure the block is not pointing to itself
- if(addr == vb.NextDeletedVB) {
- Header.FreeSpace = FSListCorrupt;
- WriteHdr();
- return 0;
- }
- break;
-
- default :
- Header.FreeSpace = FSListCorrupt;
- WriteHdr();
- return 0;
- }
-
- // Found the block in the free space list
- if(addr == vb_addr) {
- if(prev_addr == 0) { // Adjust the free space list
- // At the head of freespace list, so make a new head
- Header.FreeSpace = vb.NextDeletedVB;
- WriteHdr();
- }
- else {
- // In the middle of free space, so link prev to Next
- prev_VB.NextDeletedVB = vb.NextDeletedVB;
- WriteVBHdr(prev_VB, prev_addr);
- }
-
- // Undelete the specified block
- vb.Status = NormalVB; // Mark VB with normal attribute
- vb.NextDeletedVB = 0; // Always 0 unless VB is marked deleted
- WriteVBHdr(vb, addr); // Write header for this VB
-
- return 1; // Return true if successful
- }
-
- // Keep looping through the free space list
- prev_addr = addr;
- prev_VB = vb;
- addr = vb.NextDeletedVB;
- }
-
- return 0; // Return false if block was not undeleted
- }
-
- FAU VBDFile::ReAlloc(FAU Address, __UWORD__ Bytes)
- // Reallocates a Variable Data Block of x Bytes of data from either
- // the free space list, or from the end of the file. ReAlloc()
- // returns the location of the space reallocated for the object,
- // or returns a 0 if an error occurred.
- {
- // Delete the object before allocating space for it
- if(!Delete(Address)) return 0;
- return Alloc(Bytes);
- }
-
- FAU VBDFile::FindFirstVB(FAU Offset)
- // Search through the VBD file until a valid VB is found.
- // The search starts at the heap start or the offset value.
- // Returns 0 if no valid VB is found in the file.
- {
- VBHeader vb;
- FAU Address = 0;
-
- // 03/13/1998: Ensure that the VBD file header stays in sync
- // during multiple file access
- TestVBDHeader();
-
- // No VBs have been allocated yet
- if(Header.HeapStart == Header.EndOfFile) return 0;
-
- if(!Offset)
- Address = Header.HeapStart; // If no Offset, start at heap
- else {
- Address = Address + Offset; // Offset the starting address
-
- if(Address >= Header.EndOfFile) // Prevent offsetting past EOF
- return 0; // Invalid address
- }
-
- while(1) {
- if(Address + sizeof(VBHeader) >= Header.EndOfFile || !IsOK()) return 0;
- Read(&vb, sizeof(VBHeader), Address);
- if(vb.CkWord != CheckWord)
- Address++; // Loop through the file byte by byte
- else
- break; // Found valid VB
- }
- return SeekTo(Address);
- }
-
- FAU VBDFile::FindFirstObject(FAU Offset)
- // Search through the VBD file until a valid VB is found and
- // then return the object's address. If the VB is marked deleted
- // continue searching until the first normal VB is found. The
- // search starts at the heap start or the offset. Returns 0 if
- // no valid VB is found in the file.
- {
- VBHeader vb;
- FAU Address = FindFirstVB(Offset);
- if(!Address) return 0;
-
- while(1) { // Loop until a normal VB status is found
- ReadVBHdr(vb, Address);
- if((vb.Status & 0xff) == NormalVB) break;
- Address = FindFirstVB(Address+vb.Length);
- if(!Address) return 0;
- }
-
- return Address + sizeof(VBHeader);
- }
-
- FAU VBDFile::FindNextVB(FAU Offset)
- // Search through the VBD file until the next valid VB after the
- // first valid VB is found. The search starts at the heap start
- // or the offset value. Returns 0 if no valid VB is found.
- {
- VBHeader vb;
-
- // No VBs have been allocated yet
- if(Header.HeapStart == Header.EndOfFile) return 0;
-
- FAU Address = FindFirstVB(Offset);
-
- if(!Address) return 0; // No Vaild VB found
-
- ReadVBHdr(vb, Address);
- FAU NextVB = Address + vb.Length;
-
- if(NextVB >= Header.EndOfFile) return Address; // This is last the VB
-
- ReadVBHdr(vb, NextVB); // Ensure VB header is valid
-
- return NextVB;
- }
-
- FAU VBDFile::FindNextObject(FAU Offset)
- // Search through the VBD file until the next valid VB after the
- // first valid VB is found and then return the object's address.
- // If the VB is marked deleted continue searching until the next
- // normal VB is found. The search starts at the heap start or the
- // offset value. Returns 0 if no valid VB is found.
- {
- VBHeader vb;
- FAU Address = FindNextVB(Offset);
- if(!Address) return 0;
-
- while(1) { // Loop until a normal VB status is found
- ReadVBHdr(vb, Address);
- if((vb.Status & 0xff) == NormalVB) break;
- Address = FindNextVB(Address+vb.Length);
- if(!Address) return 0;
- }
-
- return Address + sizeof(VBHeader);
- }
-
- FAU VBDFile::GetFreeSpace()
- {
- FileHeader fh;
- Read(&fh, sizeof(FileHeader), StartOfFile);
-
- // Ensure the in memory copy and the disk copy are the same
- if(fh.FreeSpace != Header.FreeSpace) {
- ReadHdr();
- }
-
- return Header.FreeSpace;
- }
-
- FAU VBDFile::GetVBDFreeSpace()
- // Use this version of GetFreeSpace for wxWindows programs
- {
- FileHeader fh;
- Read(&fh, sizeof(FileHeader), StartOfFile);
-
- // Ensure the in memory copy and the disk copy are the same
- if(fh.FreeSpace != Header.FreeSpace) {
- ReadHdr();
- }
-
- return Header.FreeSpace;
- }
-
- FAU VBDFile::GetEOF()
- {
- FileHeader fh;
- Read(&fh, sizeof(FileHeader), StartOfFile);
-
- // Ensure the in memory copy and the disk copy are the same
- if(fh.EndOfFile != Header.EndOfFile) {
- ReadHdr();
- }
-
- return Header.EndOfFile;
- }
-
- FAU VBDFile::GetHeapStart()
- {
- FileHeader fh;
- Read(&fh, sizeof(FileHeader), StartOfFile);
-
- // Ensure the in memory copy and the disk copy are the same
- if(fh.HeapStart != Header.HeapStart) {
- ReadHdr();
- }
-
- return Header.HeapStart;
- }
-
- FAU VBDFile::GetHighestVB()
- {
- FileHeader fh;
- Read(&fh, sizeof(FileHeader), StartOfFile);
-
- // Ensure the in memory copy and the disk copy are the same
- if(fh.HighestVB != Header.HighestVB) {
- ReadHdr();
- }
-
- return Header.HighestVB;
- }
-
- __LWORD__ VBDFile::StaticArea()
- {
- FileHeader fh;
- Read(&fh, sizeof(FileHeader), StartOfFile);
-
- // Ensure the in memory copy and the disk copy are the same
- if(fh.HeapStart != Header.HeapStart) {
- ReadHdr();
- }
-
- return Header.HeapStart - sizeof(FileHeader);
- }
-
-
- int VBDFile::TestVBDHeader()
- // This function is used to ensure that the in memory copy
- // of the VBD file header and the disk copy stay in sync
- // during multiple file access.
- {
- FileHeader fh;
- int errors = 0;
-
- Read(&fh, sizeof(FileHeader), StartOfFile);
-
- if(fh.FreeSpace != Header.FreeSpace) {
- ReadHdr();
- errors++;
- }
-
- if(fh.EndOfFile != Header.EndOfFile) {
- ReadHdr();
- errors++;
- }
-
- if(fh.HeapStart != Header.HeapStart) {
- ReadHdr();
- errors++;
- }
-
- if(fh.HighestVB != Header.HighestVB) {
- ReadHdr();
- errors++;
- }
-
- if(fh.HeapStart != Header.HeapStart) {
- ReadHdr();
- errors++;
- }
-
- return errors;
- }
-
- // ==============================================================
- // General purpose file utilites (BEGIN HERE)
- // ==============================================================
- int VBDFile::Exists(const char *FName)
- // Returns true if the file exists
- {
- FILE *tmp; // Temporary file pointer
- tmp = ::fopen(FName, "rb");
-
- if(!tmp) return 0; // Return 0 if file does not exist
-
- fclose(tmp);
- return 1;
- }
-
- __LWORD__ VBDFile::FileSize(const char *FName)
- // Returns the file size. Use after file has been closed
- // and re-opened to ensure that all the buffers are flushed
- // to disk.
- {
- struct stat buf;
- int result = stat(FName, &buf);
- if(result != 0)
- #ifdef CPP_EXCEPTIONS
- throw CFileOpenError();
- #else
- Error->SignalException(EHandler::FileOpenError);
- #endif
- return buf.st_size;
- }
- // ==============================================================
- // General purpose file utilites (BEGIN HERE)
- // ==============================================================
-
- // ==============================================================
- // Debug VBD File manager: revisions 01/20/1997 (BEGIN HERE)
- // ==============================================================
- int VBDFile::BlindOpen(const char *FName, AccessMode Mode)
- // Opens the FName file without checking the file type.
- {
- char *mode_str;
-
- // First, close the current file if open.
- Close();
-
- if(Mode == READONLY) {
- mode_str = "rb";
- Status = 0x01; // set good bit, reset read/write bit
- }
- else {
- mode_str = "r+b";
- Status = 0x05; // Set good bit and read/write bit
- }
-
- fp = ::fopen(FName, mode_str);
-
- if(fp == 0) {
- #ifdef CPP_EXCEPTIONS
- throw CFileOpenError();
- #else
- Error->SignalException(EHandler::FileOpenError);
- #endif
- }
- else {
- Status |= 2; // Set open bit
- strcpy(FileName, FName);
- LastOperation = WRITE; // So Read() works right the first time
- ReadHdr();
-
- // Set the revision letter according to the file header
- if(memcmp(Header.Signature, VBDSignature, 7) == 0) {
- char revision[8];
- memmove(revision, Header.Signature, 8);
- rev_letter = revision[7];
- }
- }
-
- return IsOpen(); // Returns 1 if file opened successfully, else 0.
- }
-
- FAU VBDFile::VBSearch(FAU Offset)
- // Search through the VBD file until a valid VB is found.
- // The search starts at the beginning of the file or the
- // offset value. Returns 0 if no valid VB is found in the
- // file.
- {
- VBHeader vb;
- FAU Address = 0;
-
- FAU StaticEOF = FileSize(VBDFileName());
-
- if(!Offset)
- Address = sizeof(FileHeader); // If no Offset, start after VBD header
- else {
- Address = Address + Offset; // Offset the starting address
-
- if(Address >= StaticEOF) // Prevent offsetting past EOF
- return 0; // Invalid address
- }
-
- while(1) {
- if(Address + sizeof(VBHeader) >= StaticEOF || !IsOK()) return 0;
- Read(&vb, sizeof(VBHeader), Address);
- if(vb.CkWord != CheckWord)
- Address++; // Loop through the file byte by byte
- else
- break; // Found valid VB
- }
- return Address;
- }
- // ==============================================================
- // Debug VBD File manager: revisions 01/20/1997 (END HERE)
- // ==============================================================
-
- // ==============================================================
- // Reclaim function: revisions 09/11/1998 (BEGIN HERE)
- // ==============================================================
- // Define the __RECLAIM_BEST_FIT__ macro to use the best-fit routine
- // or the __RECLAIM_FIRST_FIT__ macro to use the first-fit routine.
- // Will default to first-fit. The best-fit method will prevent
- // fragmentation as much as possible but is costly in terms of speed.
- // The first-fit method will not prevent fragmentation as well as the
- // best-fit method but offers a tremendous speed advantage.
-
- #ifdef __RECLAIM_BEST_FIT__ // Defaults to first-fit
- FAU VBDFile::Reclaim(__UWORD__ Bytes)
- // Searches the free space list for a block that can be reuesd.
- // This function will search the free space list for an "exact-
- // fit" first and then try to find the "best-fit" for the number
- // of bytes requested. NOTE: The byte size is adjusted by the
- // Alloc() function to allocate space for the VB header plus
- // the object. Returns address of the reclaimed space, or zero
- // if a deleted or removed block of the appropriate size is not
- // found. An exact-fit is a block that matches the exact number
- // of bytes requested. If an exact-fit cannot be found, the next
- // block big enough to hold number of bytes requested plus the
- // size a block header with a least one byte left over becomes
- // a best-fit block. The search continues until a best-fit
- // block with the least number of unsed bytes is found. The
- // used bytes in the best-fit block are used to create a new
- // block that will be put back on the free space list. This
- // will keep the gaps between the blocks as small as possible,
- // with the smallest gap being as large as a single block header
- // plus one byte.
- {
- // Cannot reuse any blocks if the free space list is corrupt
- if(Header.FreeSpace == FSListCorrupt) return 0;
-
- VBHeader vb, prev_VB, new_VB;
- VBHeader best_fit_prev_VB, best_fit_VB;
- FAU addr, prev_addr, new_addr;
- FAU best_fit_addr, best_fit_prev_addr;
- __UWORD__ avail_len, unused_len, best_fit_unused_len = 0;
-
- // Constants for the best-fit criteria. NOTE: The maximum length
- // of a block to reuse equals: (max_limit * byte_multiple) * Bytes
- const unsigned max_limit = 10; // Maximum number of byte multiples
- const double byte_multiple = .25; // Byte multiples
-
- double best_byte_len, byte_percent = 0;
- double bytes_requested = (double)Bytes;
- unsigned i;
- unsigned best_length[max_limit];
-
- // Calculate the best-fit byte values
- for(i = 0; i < max_limit; i++) {
- byte_percent += byte_multiple;
- best_byte_len = bytes_requested * byte_percent;
- best_length[i] = (unsigned)best_byte_len;// + sizeof(VBHeader);
- }
-
- addr = Header.FreeSpace;
- prev_addr = best_fit_addr = 0;
-
- // Search the entire free space list until an exact-fit
- // or a best-fit block is found.
- while(addr) {
- Read(&vb, sizeof(VBHeader), addr);
- if(!IsOK()) break;
-
- // Added: 09/04/1998 to signal not to use the free space list.
- // If this is not a valid block, the free space list is corrupt
- if(vb.CkWord != CheckWord) {
- Header.FreeSpace = FSListCorrupt;
- WriteHdr();
- return 0;
- }
-
- // Added: 09/04/1998 to prevent an infinite loop.
- // If the block is not marked deleted or removed, the
- // Next deleted VBD pointer is bad. This will cause
- // an infinite loop if the end of the free space
- // list is pointing to valid block.
- switch((__SBYTE__)(vb.Status & 0xff)) {
- case DeletedVB :
- // Make sure the block is not pointing to itself
- if(addr == vb.NextDeletedVB) {
- Header.FreeSpace = FSListCorrupt;
- WriteHdr();
- return 0;
- }
- break;
-
- case RemovedVB :
- // Make sure the block is not pointing to itself
- if(addr == vb.NextDeletedVB) {
- Header.FreeSpace = FSListCorrupt;
- WriteHdr();
- return 0;
- }
- break;
-
- default :
- Header.FreeSpace = FSListCorrupt;
- WriteHdr();
- return 0;
- }
-
- avail_len = vb.Length; // Length of object plus sizeof VB header
-
- // Unused length must be big enough to hold a two VB headers
- // plus the object
- if(avail_len > Bytes + sizeof(VBHeader))
- unused_len = avail_len - Bytes;
- else
- unused_len = 0;
-
- if(avail_len == Bytes) {
- // Block is an exact fit, so link prev link to Next link
- if(prev_addr == 0) {
- // At the head of freespace list, so make a new head
- Header.FreeSpace = vb.NextDeletedVB;
- WriteHdr();
- }
- else {
- // In the middle of free space, so link prev to Next
- prev_VB.NextDeletedVB = vb.NextDeletedVB;
- WriteVBHdr(prev_VB, prev_addr);
- }
- return IsOK() ? (__LWORD__)addr : 0;
- }
-
- if(unused_len > 0) { // Found bigger block with room for header
- for(i = 0; i < max_limit; i++) {
- if(unused_len <= best_length[i]) {
- // Use the block matching the best-fit criteria
- if(best_fit_unused_len > best_length[i]) {
- // Use the block if it is a better then the current one
- best_fit_addr = addr;
- best_fit_prev_addr = prev_addr;
- best_fit_VB = vb;
- best_fit_prev_VB = prev_VB;
- best_fit_unused_len = unused_len;
- }
- }
- }
- }
-
- // Block not big enough, so try Next block
- prev_addr = addr;
- prev_VB = vb;
- addr = vb.NextDeletedVB;
-
- } // End of block search
-
- // Could not find a best fit
- if(best_fit_addr == 0) return 0;
-
- // Reuse the block and any remaining bytes
- new_addr = best_fit_addr + Bytes;
- new_VB.CkWord = CheckWord;
- new_VB.Status = RemovedVB;
- new_VB.NextDeletedVB = best_fit_VB.NextDeletedVB;
- new_VB.Length = best_fit_unused_len;
- WriteVBHdr(new_VB, new_addr);
-
- // Adjust the free space list
- if(best_fit_prev_addr == 0) {
- // At the head of freespace, so this is the new head
- Header.FreeSpace = new_addr;
- WriteHdr();
- }
- else { // In the middle of freespace
- best_fit_prev_VB.NextDeletedVB = new_addr;
- WriteVBHdr(best_fit_prev_VB, best_fit_prev_addr);
- }
-
- return IsOK() ? (__LWORD__)best_fit_addr : 0;
- }
-
- #else // __RECLAIM_FIRST_FIT__
- FAU VBDFile::Reclaim(__UWORD__ Bytes)
- // Searchs the free space list for the first block that can
- // be reused. This function will search the free space list
- // for a "first-fit" big enough to hold the number of bytes
- // requested. NOTE: The byte size is adjusted by the Alloc()
- // function to allocate space for the VB header plus the object.
- // Returns address of the reclaimed space, or zero if a deleted
- // or removed block of the appropriate size is not found. If an
- // "exact-fit" is found (a block that matches the exact number
- // of bytes requested) the address of that block is returned.
- // Otherwise the address of the frist block big enough to hold
- // number of bytes requested plus the size a block header with a
- // least one byte left over is returned. The used bytes in the
- // frist-fit block are used to create a new block that will be
- // put back on the free space list.
- {
- // Cannot reuse any blocks if the free space list is corrupt
- if(Header.FreeSpace == FSListCorrupt) return 0;
-
- VBHeader vb, prev_VB, new_VB;
- FAU addr, prev_addr, new_addr;
- __UWORD__ avail_len, unused_len;
-
- addr = Header.FreeSpace; prev_addr = 0;
-
- // Search the free space list until a first-fit is found
- while(addr) {
- Read(&vb, sizeof(VBHeader), addr);
- if(!IsOK()) break;
-
- // Added: 09/04/1998 to signal not to use the free space list.
- // If this is not a valid block, the free space list is corrupt
- if(vb.CkWord != CheckWord) {
- Header.FreeSpace = FSListCorrupt;
- WriteHdr();
- return 0;
- }
-
- // Added: 09/04/1998 to prevent an infinite loop.
- // If the block is not marked deleted or removed, the
- // Next deleted VBD pointer is bad. This will cause
- // an infinite loop if the end of the free space
- // list is pointing to valid block.
- switch((__SBYTE__)(vb.Status & 0xff)) {
- case DeletedVB :
- // Make sure the block is not pointing to itself
- if(addr == vb.NextDeletedVB) {
- Header.FreeSpace = FSListCorrupt;
- WriteHdr();
- return 0;
- }
- break;
-
- case RemovedVB :
- // Make sure the block is not pointing to itself
- if(addr == vb.NextDeletedVB) {
- Header.FreeSpace = FSListCorrupt;
- WriteHdr();
- return 0;
- }
- break;
-
- default :
- Header.FreeSpace = FSListCorrupt;
- WriteHdr();
- return 0;
- }
-
- avail_len = vb.Length; // Length of object plus sizeof VB header
-
- // Unused length must be big enough to hold a two VB headers
- // plus the object
- if(avail_len > Bytes + sizeof(VBHeader))
- unused_len = avail_len - Bytes;
- else
- unused_len = 0;
-
- if(avail_len == Bytes) {
- // Block is an exact fit, so link prev link to Next link
- if(prev_addr == 0) {
- // At the head of freespace list, so make a new head
- Header.FreeSpace = vb.NextDeletedVB;
- WriteHdr();
- }
- else {
- // In the middle of free space, so link prev to Next
- prev_VB.NextDeletedVB = vb.NextDeletedVB;
- WriteVBHdr(prev_VB, prev_addr);
- }
- break;
- }
-
- if(unused_len > 0) {
- // Block too big, and there's room for a VB header
- // in the unused portion, so reuse any remaining bytes.
- new_addr = addr + Bytes;
- new_VB.CkWord = CheckWord;
- new_VB.Status = RemovedVB;
- new_VB.NextDeletedVB = vb.NextDeletedVB;
- new_VB.Length = unused_len;
- WriteVBHdr(new_VB, new_addr);
- if(prev_addr == 0) {
- // At the head of freespace, so this is the new head
- Header.FreeSpace = new_addr;
- WriteHdr();
- }
- else { // In the middle of freespace
- prev_VB.NextDeletedVB = new_addr;
- WriteVBHdr(prev_VB, prev_addr);
- }
- break;
- }
-
- // Block not big enough, so try Next block
- prev_addr = addr;
- prev_VB = vb;
- addr = vb.NextDeletedVB;
-
- } // End of block search
-
- return IsOK() ? (__LWORD__)addr : 0;
- }
- #endif //__RECLAIM_FIRST_FIT__, __RECLAIM_BEST_FIT__
- // ==============================================================
- // Reclaim function: revisions 09/11/1998 (END HERE)
- // ==============================================================
-
- // ----------------------------------------------------------- //
- // ------------------------------- //
- // --------- End of File --------- //
- // ------------------------------- //
-