home *** CD-ROM | disk | FTP | other *** search
/ Visual Basic Source Code / Visual Basic Source Code.iso / vbsource / vbdatabs / vbdfile.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1999-03-18  |  52.3 KB  |  1,819 lines

  1. // ------------------------------- //
  2. // -------- Start of File -------- //
  3. // ------------------------------- //
  4. // ----------------------------------------------------------- // 
  5. // C++ Source Code File Name: vbdfile.cpp 
  6. // Compiler Used: MSVC40, DJGPP 2.7.2.1, GCC 2.7.2.1, HP CPP 10.24
  7. // Produced By: Doug Gaer  
  8. // File Creation Date: 02/04/1997 
  9. // Date Last Modified: 03/18/1999
  10. // Copyright (c) 1997 Douglas M. Gaer
  11. // ----------------------------------------------------------- // 
  12. // ------------- Program Description and Details ------------- // 
  13. // ----------------------------------------------------------- // 
  14. /*
  15. The VBD C++ classes are copyright (c) 1997, by Douglas M. Gaer.
  16. All those who put this code or its derivatives in a commercial
  17. product MUST mention this copyright in their documentation for
  18. users of the products in which this code or its derivative
  19. classes are used. Otherwise, you have the freedom to redistribute
  20. verbatim copies of this source code, adapt it to your specific
  21. needs, or improve the code and release your improvements to the
  22. public provided that the modified files carry prominent notices
  23. stating that you changed the files and the date of any change.
  24.  
  25. THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND.
  26. THE ENTIRE RISK OF THE QUALITY AND PERFORMANCE OF THIS SOFTWARE
  27. IS WITH YOU. SHOULD ANY ELEMENT OF THIS SOFTWARE PROVE DEFECTIVE,
  28. YOU WILL ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR, OR
  29. CORRECTION.
  30.  
  31. The VBD file manager class is responsible for handling all
  32. low-level file operations through the use of VBDFile objects
  33. or by inheriting the VBDFile class. Low-level file operations
  34. refer to functions such as: Create(), Open(), Read(), Write(),
  35. Alloc(), Delete(), and Close(). These functions contain all the
  36. routines needed to create and maintain VBD files in accordance
  37. with the VBD File Format. VBDFile objects are reference counted
  38. and must be created dynamically due to the way reference
  39. counting is implemented. 
  40. */
  41. // ----------------------------------------------------------- // 
  42. #include <string.h>
  43. #include <stdlib.h>
  44. #include <errno.h>
  45. #include "vbdfile.h"
  46.  
  47. // Define the __DOS_INCLUDES__ macro to use DOS path separators
  48. #ifdef __DOS_INCLUDES__
  49. #include <sys\types.h>
  50. #include <sys\stat.h>
  51. #else
  52. #include <sys/types.h>
  53. #include <sys/stat.h>
  54. #endif
  55.  
  56. // Init all the static data members
  57. static char ClosedFileName[MaxNameLength] = "closed.vbd";
  58.  
  59. // Current VBD file manager version number.
  60. // 07/22/1998 changed to version 1025 from version 1024.
  61. // 08/31/1998 changed to version 1027 from version 1025.
  62. // 02/10/1999 changed to version 1029 from version 1027.
  63. // 03/18/1999 changed to from version 1029 to final release version 1031.
  64. __LWORD__ VBDFile::VBDVersion = 1031;
  65.  
  66. // 08/31/1998 added revision letter 'A' to signature. Revision 'A'
  67. // adds a 32-bit checksum routines, not included in previous versions.
  68. __SBYTE__ VBDFile::VBDSignature[8] = {'V', 'B', 'D', 'F', 'I', 'L', 'E', 'A'};
  69.  
  70. VBDFile::VBDFile()
  71. // Creates a VBD file object, with refcount of one.
  72. {
  73.   strcpy(FileName, ClosedFileName); // Set the initial file name
  74.   fp = 0;
  75.   Status = 0x01; // good, read-only, and closed
  76. }
  77.  
  78. VBDFile::~VBDFile()
  79. {
  80.   Close();
  81. }
  82.  
  83. int VBDFile::Create(const char *FName, FAU StaticSize)
  84. // Creates and opens a new file named FName, truncating it
  85. // if it already exists. The area at the front of the file
  86. // of length StaticSize + sizeof(FileHeader) is reserved.
  87. {
  88.   // First, close the current file if open.
  89.   Close();
  90.  
  91.   Status = 0x05; // Set read/write bit and good bit
  92.  
  93.   // Create, truncate if already exists
  94.   fp = ::fopen(FName, "w+b");
  95.  
  96.   if(fp == 0) {
  97. #ifdef CPP_EXCEPTIONS
  98.     throw CFileCreationError();
  99. #else
  100.     Error->SignalException(EHandler::FileCreationError);
  101. #endif
  102.   }
  103.   else {
  104.     Status |= 2; // Set open bit
  105.     strcpy(FileName, FName);
  106.     Header.HeapStart = StaticSize + sizeof(FileHeader);
  107.     LastOperation = READ; // So Write() works right the first time
  108.     InitHdr();
  109.   }
  110.  
  111.   // Returns 1 if the file was successfully created and opened.
  112.   return IsOpen(); 
  113. }
  114.  
  115. void VBDFile::InitHdr()
  116. // Sets up the header area in the file. If there's
  117. // statically allocated data beyond the header, write
  118. // a 0 to the last byte of it, so that there will be no
  119. // unexpected end of file errors. The rest of the static
  120. // area stays uninitialized.
  121. {
  122.   Header.FreeSpace = 0;
  123.   Header.EndOfFile = Header.HeapStart;
  124.   Header.HighestVB = 0; // No blocks allocated in the file 
  125.   memcpy(Header.Signature, VBDSignature, 8); // Copy signature
  126.   Header.Version = VBDVersion;
  127.  
  128.   // Set the revision letter 
  129.   char revision[8];
  130.   memmove(revision, VBDSignature, 8);
  131.   rev_letter = revision[7];
  132.  
  133.   // 03/13/1998: Write the VBD file header and flush the disk
  134.   // buffers to maintain file integrity during multiple file
  135.   // access.
  136.   WriteHdr();
  137.   
  138.   if(Header.HeapStart > sizeof(FileHeader)) {
  139.      __SBYTE__ zero_byte = 0;
  140.      Write(&zero_byte, 1, Header.HeapStart-1);
  141.   }
  142. }
  143.  
  144. int VBDFile::Open(const char *FName, AccessMode Mode)
  145. // Opens the FName file. File must exist and be a VBDFile type 
  146. // or error occurs. This function will check the revsion letter
  147. // when opening an existing file.
  148. {
  149.   char *mode_str;
  150.  
  151.   // First, close the current file if open.
  152.   Close();
  153.   
  154.   if(Mode == READONLY) {
  155.     mode_str = "rb";
  156.     Status = 0x01; // set good bit, reset read/write bit
  157.   }
  158.   else {
  159.     mode_str = "r+b";
  160.     Status = 0x05; // Set good bit and read/write bit
  161.   }
  162.  
  163.   fp = ::fopen(FName, mode_str);
  164.   
  165.   if(fp == 0) {
  166. #ifdef CPP_EXCEPTIONS
  167.     throw CFileOpenError();
  168. #else
  169.     Error->SignalException(EHandler::FileOpenError);
  170. #endif
  171.   }
  172.   else {
  173.     Status |= 2; // Set open bit
  174.     strcpy(FileName, FName);
  175.     LastOperation = WRITE; // So Read() works right the first time
  176.     
  177.     ReadHdr();
  178.     // Test file type, checking the revision letter
  179.     if(memcmp(Header.Signature, VBDSignature, 7)) { 
  180. #ifdef CPP_EXCEPTIONS
  181.        throw CWrongFileType();
  182. #else
  183.        Error->SignalException(EHandler::WrongFileType);
  184. #endif
  185.     }
  186.  
  187.     // Set the revision letter according to the file header
  188.     char revision[8];
  189.     memmove(revision, Header.Signature, 8);
  190.     rev_letter = revision[7];
  191.   }
  192.  
  193.   // 03/11/1998: This code was added to ensure that true end of
  194.   // file is stored in the VBD file header. 
  195.   INT32 filesize;
  196.   filesize = FileSize(FName);
  197.   if(Header.EndOfFile < filesize) {
  198.     Header.EndOfFile = filesize;
  199.     Flush();
  200.   }
  201.   
  202.   return IsOpen(); // Returns 1 if file opened successfully, else 0.
  203. }
  204.  
  205. int VBDFile::ReOpen(const char *FName, AccessMode Mode)
  206. // Reopens the FName file. File must exist and be a VBD file type 
  207. // or error occurs. This function will check the revsion letter
  208. // when reopening an existing file.
  209. {
  210.   char *mode_str;
  211.   
  212.   if(Mode == READONLY) {
  213.     mode_str = "rb";
  214.     Status = 0x01; // set good bit, reset read/write bit
  215.   }
  216.   else {
  217.     mode_str = "r+b";
  218.     Status = 0x05; // Set good bit and read/write bit
  219.   }
  220.  
  221.   // Close the file and flush all buffers 
  222.   ::rewind(fp);
  223.   if(!ReadOnly()) ::fflush(fp);
  224.   ::fclose(fp);
  225.  
  226.   // Reopen after closing the file
  227.   fp = ::fopen(FName, mode_str);
  228.  
  229.   if(fp == 0) {
  230. #ifdef CPP_EXCEPTIONS
  231.     throw CFileOpenError();
  232. #else
  233.     Error->SignalException(EHandler::FileOpenError);
  234. #endif
  235.   }
  236.   else {
  237.     Status |= 2; // Set open bit
  238.     strcpy(FileName, FName);
  239.     LastOperation = WRITE; // So Read() works right the first time
  240.     
  241.     ReadHdr();
  242.     // Test file type, checking the revision letter
  243.     if(memcmp(Header.Signature, VBDSignature, 7)) { 
  244. #ifdef CPP_EXCEPTIONS
  245.        throw CWrongFileType();
  246. #else
  247.        Error->SignalException(EHandler::WrongFileType);
  248. #endif
  249.     }
  250.  
  251.     // Set the revision letter according to the file header
  252.     char revision[8];
  253.     memmove(revision, Header.Signature, 8);
  254.     rev_letter = revision[7];
  255.   }
  256.  
  257.   // 03/11/1998: This code was added to ensure that true end of
  258.   // file is stored in the VBD file header. 
  259.   INT32 filesize;
  260.   filesize = FileSize(FName);
  261.   if(Header.EndOfFile < filesize) {
  262.     Header.EndOfFile = filesize;
  263.     Flush();
  264.   }
  265.   
  266.   return IsOpen(); // Returns 1 if file opened successfully, else 0.
  267. }
  268.  
  269. void VBDFile::Close(int flush)
  270. // Closes the file if not already closed, flushing the
  271. // basic header if flush = 1. Does nothing if in the
  272. // error state. Checks for dangling references to
  273. // this file.
  274. {
  275.   if(IsOK()) {
  276.     if(refcount > 1) // Check the object pointer's reference count
  277. #ifdef CPP_EXCEPTIONS
  278.       throw CDanglingPtr();
  279. #else
  280.     Error->SignalException(EHandler::DanglingPtr);
  281. #endif
  282.     
  283.     if(flush && !ReadOnly())
  284.       WriteHdr();
  285.     if(::fclose(fp) != 0)
  286. #ifdef CPP_EXCEPTIONS
  287.       throw CFileCloseError();
  288. #else
  289.       Error->SignalException(EHandler::FileCloseError);
  290. #endif
  291.     strcpy(FileName, ClosedFileName); // Set the initial file name
  292.   }
  293.   Status &= 0xfd; // Reset open bit
  294. }
  295.  
  296. void VBDFile::Flush()
  297. // Writes the VBDFile header to the file, and then flushes
  298. // any internal buffers the file might have.
  299. {
  300.   if(ReadyForWriting()) {
  301.     WriteHdr();
  302.     if(fflush(fp) != 0)
  303. #ifdef CPP_EXCEPTIONS
  304.       throw CFileWriteError();
  305. #else
  306.     Error->SignalException(EHandler::FileWriteError);
  307. #endif
  308.     Seek(0, SEEK_CUR); // So that the LastOperation logic works right
  309.   }
  310. }
  311.  
  312. void VBDFile::Seek(FAU Offset, int SeekMode)
  313. // Moves the file pointer to the byte Offset,
  314. // using SeekMode, (which should be either SEEK_SET,
  315. // SEEK_CUR, or SEEK_END).
  316. {
  317.   if(IsOK()) {
  318.      int rv = ::fseek(fp, Offset, SeekMode);
  319.      if(rv != 0)
  320. #ifdef CPP_EXCEPTIONS
  321.        throw CFileSeekError();
  322. #else
  323.        Error->SignalException(EHandler::FileSeekError);
  324. #endif
  325.      LastOperation = SEEK;
  326.   }
  327.   else
  328. #ifdef CPP_EXCEPTIONS
  329.     throw CFileNotReady();
  330. #else
  331.     Error->SignalException(EHandler::FileNotReady);
  332. #endif
  333. }
  334.  
  335. FAU VBDFile::SeekTo(FAU Address)
  336. // Seek to the specified address, optimizing the seek
  337. // operation by moving the file position indicator based
  338. // on the current stream position. Returns the current
  339. // file position after performing the seek operation.
  340. {
  341.   // Get the current stream position
  342.   StreamPos pos = FilePosition();
  343.  
  344.   if(Address == CurrAddress) { // Do not perform a seek operation
  345.     return pos;
  346.   }
  347.   else if(Address > pos) { // Seek forward to the specified address
  348.     StreamPos offset = Address - pos;
  349.     Seek(offset, SEEK_CUR);
  350.   }
  351.   else if(Address < pos) { // Seek backward to the specified address
  352.     if((__LWORD__)Address == 0) {
  353.       Rewind();
  354.     }
  355.     else {
  356.       fpos_t f_offset = (__LWORD__)pos - (__LWORD__)Address; 
  357.       fpos_t fpos = GetPosition();
  358.       fpos -= f_offset;
  359.       SetPosition(fpos);
  360.     }
  361.   }
  362.   else { // Current file position equals the specified address
  363.     // Ensure seeks between intervening reads and writes
  364.     Seek(0, SEEK_CUR);
  365.   }
  366.   
  367.   return FilePosition(); // Return current file position after seeking
  368. }
  369.  
  370. void VBDFile::Rewind()
  371. // Repositions the file pointer to the beginning of the file.
  372. {
  373.   ::rewind(fp);
  374.   LastOperation = REWIND;
  375. }
  376.  
  377. fpos_t VBDFile::GetPosition()
  378. // Returns the current value of the file-position indicator.
  379. // The SetPosition() function can later use information to reset
  380. // the file pointer to its position at the time GetPosition() was
  381. // called. The fpos_t type is an internal format and is intended
  382. // for use only by the GetPosition() and SetPosition() functions 
  383. // to record information for uniquely specifying positions within 
  384. // a file.
  385. {
  386.   fpos_t pos;
  387.   
  388.   if(IsOK()) {
  389.     int rv = ::fgetpos(fp, &pos); 
  390.     if(rv != 0)
  391. #ifdef CPP_EXCEPTIONS
  392.       throw CFileSeekError();
  393. #else
  394.     Error->SignalException(EHandler::FileSeekError);
  395. #endif
  396.     LastOperation = SEEK;
  397.   }
  398.   else
  399. #ifdef CPP_EXCEPTIONS
  400.     throw CFileNotReady();
  401. #else
  402.   Error->SignalException(EHandler::FileNotReady);
  403. #endif
  404.  
  405.   return pos;
  406. }
  407.  
  408. void VBDFile::SetPosition(const fpos_t pos)
  409. // Set the file-position indicator for stream to the value of
  410. // pos, which is obtained in a prior call to the GetPosition()
  411. // function. The fpos_t type is an internal format and is intended
  412. // for use only by the GetPosition() and SetPosition() functions to
  413. // record information for uniquely specifying positions within a
  414. // file.
  415. {
  416.   if(IsOK()) {
  417.     int rv = ::fsetpos(fp, &pos); 
  418.     if(rv != 0)
  419. #ifdef CPP_EXCEPTIONS
  420.       throw CFileSeekError();
  421. #else
  422.     Error->SignalException(EHandler::FileSeekError);
  423. #endif
  424.     LastOperation = SEEK;
  425.   }
  426.   else
  427. #ifdef CPP_EXCEPTIONS
  428.     throw CFileNotReady();
  429. #else
  430.   Error->SignalException(EHandler::FileNotReady);
  431. #endif
  432. }
  433.  
  434. void VBDFile::Read(void *buf, __UWORD__ Bytes, FAU Address)
  435. // Reads Bytes from Address into buf. The address 
  436. // is always interpreted to be from the beginning of the
  437. // file, unless it's CurrAddress, which means from the
  438. // current position. 
  439. {
  440.   if(IsOK()) {
  441.      // Ensure seeks between intervening reads and writes,
  442.      // and optimize for sequential reads
  443.     if(Address == CurrAddress) {
  444.       if(LastOperation == WRITE) Seek(0, SEEK_CUR);
  445.     }
  446.     else
  447.       Seek(Address, SEEK_SET);
  448.     
  449.     __UWORD__ bytesmoved = ::fread(buf, 1, Bytes, fp);
  450.     if(bytesmoved != Bytes) {
  451.       
  452.       if(feof(fp)) {
  453.     // 03/17/1998: Modified to throw CAccessViolation exception
  454.     // if an end of file error occurs during multiple file access
  455.     // over an NFS mount.
  456. #ifdef CPP_EXCEPTIONS
  457.     throw CAccessViolation();
  458. #else
  459.       Error->SignalException(EHandler::AccessViolation);
  460. #endif
  461.       }
  462.       else
  463. #ifdef CPP_EXCEPTIONS
  464.     throw CFileReadError();
  465. #else
  466.       Error->SignalException(EHandler::FileReadError);
  467. #endif
  468.     }
  469.     LastOperation = READ;
  470.   }
  471.   else
  472. #ifdef CPP_EXCEPTIONS
  473.     throw CFileNotReady();
  474. #else
  475.   Error->SignalException(EHandler::FileNotReady);
  476. #endif
  477. }
  478.  
  479. void VBDFile::Write(const void *buf, __UWORD__ Bytes, FAU Address,
  480.                     int flush, int bit_test)
  481. // Stores Bytes from buf to Address. The Address is
  482. // always interpreted to be from the beginning of the
  483. // file, unless it's CurrAddress, which means from the
  484. // current position. If flush equals one, the file
  485. // buffers will be flushed to disk with each write
  486. // operation. If bit_test equals one, the CRC of the
  487. // of the buffer will be compared to the CRC of the
  488. // actual bytes written to disk.
  489. {
  490.   FAU block_address;
  491.   
  492.   if(ReadyForWriting()) {
  493.      // Ensure seeks between intervening reads and writes,
  494.      // and optimize for sequential writes
  495.      if(Address == CurrAddress) {
  496.        if(LastOperation == READ) Seek(0, SEEK_CUR);
  497.      }
  498.      else 
  499.        Seek(Address, SEEK_SET);
  500.      
  501.      block_address = FilePosition(); 
  502.      __UWORD__ bytesmoved = ::fwrite(buf, 1, Bytes, fp);
  503.      
  504.      if(bytesmoved != Bytes) {
  505.        if(feof(fp))
  506. #ifdef CPP_EXCEPTIONS
  507.      throw CEOFError();
  508. #else
  509.        Error->SignalException(EHandler::EOFError);
  510. #endif
  511.        else
  512. #ifdef CPP_EXCEPTIONS
  513.      throw CFileWriteError();
  514. #else
  515.        Error->SignalException(EHandler::FileWriteError);
  516. #endif
  517.      }
  518.      LastOperation = WRITE;
  519.   }
  520.   else
  521. #ifdef CPP_EXCEPTIONS
  522.     throw CFileNotWriteable();
  523. #else
  524.   Error->SignalException(EHandler::FileNotWriteable);
  525. #endif
  526.   
  527.   // 03/13/1998: Allow application to flush disk buffers after
  528.   // each write operation to ensure the file data stays in sync
  529.   // during multiple file access.
  530.   if(flush) {  
  531.     if(fflush(fp) != 0)
  532. #ifdef CPP_EXCEPTIONS
  533.       throw CFileWriteError();
  534. #else
  535.     Error->SignalException(EHandler::FileWriteError);
  536. #endif
  537.     Seek(0, SEEK_CUR); // So that the LastOperation logic works right
  538.   }
  539.  
  540.   if(bit_test) {
  541.     __ULWORD__ w_csum = calcCRC32((char *)buf, Bytes);
  542.     __ULWORD__ r_csum = CalcChecksum(Bytes, block_address);
  543.  
  544.     if(w_csum ^ r_csum)
  545. #ifdef CPP_EXCEPTIONS
  546.       throw CChecksumError();
  547. #else
  548.     Error->SignalException(EHandler::ChecksumError);
  549. #endif
  550.   }
  551. }
  552.  
  553. UINT32 VBDFile::WriteObjectChecksum(FAU Address)
  554. // Used to write a 32-bit checksum for the object at the
  555. // end of a block. The Address variable must be set to the
  556. // file address of the block data, not the block header.
  557. // This function assumes that the data has already been
  558. // written to the block. Returns the 32-bit CRC checksum
  559. // value for the object stored in the block.
  560. {
  561.   // Perform operation according to the revision letter
  562.   switch(rev_letter) {
  563.     case 'A': case 'a' : // Version 1027, rev A
  564.       // 9/24/1998: Revision 'A' reserves four bytes at the end
  565.       // of each block for a 32-bit CRC checksum.
  566.       break;
  567.       
  568.     default: // Always return zero for any file below revision 'A'
  569.       return 0; 
  570.   }
  571.   
  572.   UINT32 CRC;
  573.   __UWORD__ Bytes;
  574.   VBHeader vb;
  575.  
  576.   // Calculate the address of the block header
  577.   FAU block_address = Address - sizeof(VBHeader);
  578.   
  579.   if(IsOK()) {
  580.     // Make sure that the this is a pre-alloacted block
  581.     // Will throw a CSyncError if this is a bad file address
  582.     Read(&vb, sizeof(VBHeader), block_address);
  583.     if(vb.CkWord != CheckWord)
  584. #ifdef CPP_EXCEPTIONS
  585.       throw CSyncError();
  586. #else
  587.     Error->SignalException(EHandler::SyncError);
  588. #endif
  589.  
  590.     // Calculate a checksum based on the block data
  591.     Bytes = (vb.Length - sizeof(VBHeader)) - sizeof(vbChecksum);
  592.     CRC = CalcChecksum(Bytes, Address);
  593.  
  594.     // Offset address to point to the checksum field located
  595.     // at the end of the block.
  596.     Address += Bytes;
  597.  
  598.     // Write the CRC for the block header and the block data.
  599.     Write(&CRC, sizeof(CRC), Address);
  600.   }
  601.  
  602.   return CRC;
  603. }
  604.  
  605. int VBDFile::ReadObjectChecksum(FAU Address, __ULWORD__ *object_crc,
  606.                 __ULWORD__ *calc_crc)
  607. // Tests the object's CRC value stored on disk against
  608. // the actual CRC of the bytes stored on disk. The Address
  609. // variable must be set to the file address of the block
  610. // data, not the block header. This function assumes that
  611. // the data has already been written to the block. Returns
  612. // true if the object's CRC test good or false if the CRC
  613. // tests bad. Passes back the object's CRC stored on disk
  614. // in the object_crc variable and the calculated CRC value
  615. // in the calc_crc variable.
  616. {
  617.   // Perform operation according to the revision letter
  618.   switch(rev_letter) {
  619.     case 'A': case 'a' : // Version 1027, rev A
  620.       // 9/24/1998: Revision 'A' reserves four bytes at the end
  621.       // of each block for a 32-bit CRC checksum.
  622.       break;
  623.       
  624.     default: // Always return true for any file below revision 'A'
  625.       return 1; 
  626.   }
  627.  
  628.   UINT32 CRC, objectCRC;
  629.   VBHeader vb;
  630.   __UWORD__ Bytes;
  631.  
  632.   // Calculate the address of the block header
  633.   FAU block_address = Address - sizeof(VBHeader);
  634.   
  635.   if(IsOK()) {
  636.     // Make sure that the this is a pre-alloacted block
  637.     // Will throw a CSyncError if this is a bad file address
  638.     Read(&vb, sizeof(VBHeader), block_address);
  639.     if(vb.CkWord != CheckWord)
  640. #ifdef CPP_EXCEPTIONS
  641.       throw CSyncError();
  642. #else
  643.     Error->SignalException(EHandler::SyncError);
  644. #endif
  645.  
  646.     // Calculate a checksum based on the block data
  647.     Bytes = (vb.Length - sizeof(VBHeader)) - sizeof(vbChecksum);
  648.     CRC = CalcChecksum(Bytes, Address);
  649.  
  650.     // Offset address to point to the checksum field located
  651.     // at the end of the block.
  652.     Address += Bytes;
  653.  
  654.     // Read the CRC value stored on disk
  655.     Read(&objectCRC, sizeof(objectCRC), Address);
  656.   }
  657.  
  658.   if(object_crc) *object_crc = objectCRC;
  659.   if(calc_crc) *calc_crc = CRC;
  660.      
  661.   if(CRC ^ objectCRC) return 0; // Return false if CRC check fails
  662.  
  663.   return 1; // Return true if the CRC values match
  664. }
  665.  
  666. __ULWORD__ VBDFile::CalcChecksum(__UWORD__ Bytes, FAU Address, int mem_alloc)
  667. // Calculate a 32-bit CRC checksum for a given number
  668. // of bytes starting at the specified address. Returns a
  669. // 32-bit CRC value. If the mem_alloc variable is true, a
  670. // buffer equal to the specified number of bytes will be
  671. // created in memory. If the mem_alloc variable is false
  672. // or memory allocation fails, the CRC will be calculated
  673. // byte by byte starting at the specified address.
  674. {
  675.   __ULWORD__ CRC;
  676.   __UWORD__ len = Bytes;
  677.   unsigned char data;
  678.   char *buf = 0;  
  679.  
  680.   // Create a buffer equal to the object length
  681.   if(mem_alloc) buf = new char[Bytes]; 
  682.  
  683.   if(buf) {
  684.     if(IsOK()) {
  685.       Read(buf, Bytes, Address);
  686.       CRC = calcCRC32(buf, Bytes);
  687.       delete buf;
  688.     }
  689.   }
  690.   else {
  691.     if(IsOK()) {
  692.       SeekTo(Address); // Seek to the specified file address
  693.       CRC = 0xffffffffL;
  694.       while(len--) {
  695.     Read(&data, sizeof(data));
  696.     CRC = calcCRC32(data, CRC);
  697.       }
  698.       CRC ^= 0xffffffffL;
  699.     }
  700.   }
  701.  
  702.   return CRC; 
  703. }
  704.  
  705. void VBDFile::ReadVBHdr(VBHeader &hdr, FAU Address)
  706. // Reads in VB header, and tests check word to make
  707. // sure the file is still in sync.
  708. {
  709.   Read(&hdr, sizeof(VBHeader), Address);
  710.   if(hdr.CkWord != CheckWord)
  711. #ifdef CPP_EXCEPTIONS
  712.     throw CSyncError();
  713. #else
  714.     Error->SignalException(EHandler::SyncError);
  715. #endif
  716. }
  717.  
  718. void VBDFile::WriteVBHdr(const VBHeader &hdr, FAU Address)
  719. // Writes the block header to disk.
  720. {
  721.   Write(&hdr, sizeof(VBHeader), Address);
  722. }
  723.  
  724. void VBDFile::ReadHdr()
  725. // Reads the VBD file header from disk.
  726. {
  727.   Read(&Header, sizeof(FileHeader), StartOfFile);
  728. }
  729.  
  730. void VBDFile::WriteHdr()
  731. // Writes the VBD file header to disk.
  732. {
  733.   Write(&Header, sizeof(FileHeader), StartOfFile);
  734. }
  735.  
  736. FAU VBDFile::Alloc(__UWORD__ Bytes)
  737. // Allocates a Variable Data Block of x Bytes of data from either
  738. // the free space list, or from the end of the file. The number
  739. // of bytes allocated is adjusted to hold a VBHeader plus the
  740. // object. Only the VBHeader is written to the allocated space.
  741. // Alloc() returns the location of the space allocated for the
  742. // object, or returns a 0 if an error occurred.
  743. {
  744.   FAU Address = 0;
  745.   VBHeader vb;
  746.   
  747.   if(ReadyForWriting()) {
  748.  
  749.     // Adjust the number of bytes to allocate space of VB header
  750.     // according to the revision letter.
  751.     switch(rev_letter) {
  752.       case 'A': case 'a' : // Version 1027, rev A
  753.     // 9/24/1998: Revision 'A' adjusts the number of bytes to
  754.     // include a 32-bit CRC. Space for a four byte checksum will
  755.     // be reserved at the end of the block.
  756.     Bytes += (sizeof(vbChecksum) + sizeof(VBHeader));
  757.     break;
  758.  
  759.       default: // Default to revision zero
  760.     Bytes += sizeof(VBHeader);
  761.     break;
  762.     }
  763.     
  764.     // Try to reclaim a block if the free space list is not empty
  765.     if(Header.FreeSpace != 0) Address = Reclaim(Bytes); 
  766.  
  767.     if(IsOK() && Address == 0) { // Extend the file 
  768.       Address = Header.EndOfFile;
  769.       Header.EndOfFile += Bytes;
  770.  
  771.       // Write to the last byte to avoid possible end of file errors
  772.       __SBYTE__ zero = 0;
  773.       Write(&zero, sizeof(__SBYTE__), Header.EndOfFile-1);
  774.       Header.HighestVB = Address;  
  775.     }
  776.  
  777.     vb.Status = NormalVB;  // Mark VB with normal attribute
  778.     vb.NextDeletedVB = 0;  // Always 0 unless block is marked deleted
  779.     vb.CkWord = CheckWord; // Assign the check word value
  780.     vb.Length = Bytes;     // Total number of bytes for this VB
  781.     
  782.     WriteVBHdr(vb, Address); // Write header for this VB
  783.   }
  784.   else
  785. #ifdef CPP_EXCEPTIONS
  786.     throw CFileNotWriteable();
  787. #else
  788.     Error->SignalException(EHandler::FileNotWriteable);
  789. #endif
  790.   if(Address > Header.HighestVB) Header.HighestVB = Address;  
  791.  
  792.   // 03/13/1998: Ensure that the VBD file header stays in sync
  793.   // during multiple file access.
  794.   WriteHdr();
  795.  
  796.   return SeekTo(Address + sizeof(VBHeader));
  797. }
  798.  
  799. int VBDFile::Remove(FAU Address, int mem_alloc)
  800. // Deletes the VB and removes the object by setting all
  801. // the data bytes to zero. The VB is marked removed,
  802. // indicating that the object cannot be undeleted. If
  803. // the mem_alloc variable is true, a buffer equal to
  804. // the object length of the object will be created in
  805. // memory. If the mem_alloc variable is false or memory
  806. // allocation fails, a single byte value, equal to zero,
  807. // is written to the file byte by byte for the length of
  808. // the object. 
  809. {
  810.   // Return false if VB is already deleted or marked bad
  811.   if(!Delete(Address)) return 0;
  812.     
  813.   VBHeader vb;
  814.   FAU VBAddress = Address - sizeof(VBHeader); // Address of VB header
  815.   ReadVBHdr(vb, VBAddress);
  816.   vb.Status = RemovedVB; // Mark VB removed
  817.   __UWORD__ Bytes = vb.Length - sizeof(VBHeader);
  818.  
  819.   const unsigned char data = 0;
  820.   const __UWORD__ len = Bytes;
  821.   char *buf = 0;
  822.   
  823.   // Create a buffer equal to the object length
  824.   if(mem_alloc) buf = new char[len]; 
  825.  
  826.   if(buf) {
  827.     for(__UWORD__ i = 0; i < len; i++)  // Zero out the buffer
  828.       buf[i] = 0;
  829.   
  830.     if(ReadyForWriting()) {
  831.       WriteVBHdr(vb, VBAddress);
  832.       Write(buf, Bytes, Address, 1, 0); 
  833.       delete buf;
  834.       return 1; // Return true if successful
  835.     }
  836.     else
  837. #ifdef CPP_EXCEPTIONS
  838.       throw CFileNotWriteable();
  839. #else
  840.     Error->SignalException(EHandler::FileNotWriteable);
  841. #endif
  842.   }
  843.   else {
  844.     if(ReadyForWriting()) {
  845.       WriteVBHdr(vb, VBAddress);
  846.       while(Bytes--) // Write all zeros in place of the object
  847.     Write(&data, sizeof(data), CurrAddress, 0, 0); 
  848.       return 1; // Return true if successful
  849.     }
  850.     else
  851. #ifdef CPP_EXCEPTIONS
  852.       throw CFileNotWriteable();
  853. #else
  854.     Error->SignalException(EHandler::FileNotWriteable);
  855. #endif
  856.   }
  857.  
  858.   return 0; // Ensure all paths return a value
  859. }
  860.  
  861. int VBDFile::Delete(FAU Address)
  862. // Marks the VB at location Address deleted and leaves the
  863. // object unchanged, allowing it to be undeleted. The deleted
  864. // VB is placed on the front of the free space list. 
  865. {
  866.   VBHeader vb;
  867.   
  868.   FAU VBAddress = Address - sizeof(VBHeader); // Address of VB header
  869.   ReadVBHdr(vb, VBAddress);
  870.  
  871.   // Return false if VB is already deleted
  872.   if((vb.Status & 0xff) != NormalVB) return 0; 
  873.   
  874.   vb.Status = DeletedVB; // Mark VB deleted
  875.   
  876.   if(ReadyForWriting()) {
  877.     vb.NextDeletedVB = Header.FreeSpace; // VB to become head of free list
  878.     WriteVBHdr(vb, VBAddress);
  879.     
  880.     // Make sure the free space list is not corrupt
  881.     if(Header.FreeSpace != FSListCorrupt) {
  882.       Header.FreeSpace = VBAddress;
  883.       WriteHdr();
  884.     }
  885.     return 1; // Return true if successful
  886.   }
  887.   else
  888. #ifdef CPP_EXCEPTIONS
  889.     throw CFileNotWriteable();
  890. #else
  891.     Error->SignalException(EHandler::FileNotWriteable);
  892. #endif
  893.     return 0; // Ensure all paths return a value
  894. }
  895.  
  896. StreamPos VBDFile::FilePosition()
  897. // Returns the current file posistion
  898. {
  899.   if(!IsOK())
  900. #ifdef CPP_EXCEPTIONS
  901.     throw CFileNotReady();
  902. #else
  903.     Error->SignalException(EHandler::FileNotReady);
  904. #endif
  905.  
  906.   return ::ftell(fp);
  907. }
  908.  
  909. const char *VBDFile::GetSignature() const
  910. // Return the VBD file signature with no revision letter
  911. {
  912.   size_t len = sizeof(Header.Signature);
  913.   char *s = new char[len];
  914.   memcpy(s, Header.Signature, len);
  915.   s[len-1] = 0; 
  916.   return (const char *)s;
  917. }
  918.  
  919. char *VBDFile::GetSignature() 
  920. // Return the VBD file signature with no revision letter
  921. {
  922.   size_t len = sizeof(Header.Signature);
  923.   char *s = new char[len];
  924.   memcpy(s, Header.Signature, len);
  925.   s[len-1] = 0;
  926.   return s;
  927. }
  928.  
  929. __UWORD__ VBDFile::VBTotal()
  930. // Returns the total number of valid VBs in the file
  931. {
  932.   // 03/13/1998: Ensure that the VBD file header stays in sync
  933.   // during multiple file access
  934.   TestVBDHeader();
  935.  
  936.   VBHeader vb;
  937.   FAU Address = FindFirstVB(0); // Search the entire file
  938.   __UWORD__ i = 0;
  939.  
  940.   while(Address) { // Until a block of a valid size is found
  941.     if(Address >= Header.EndOfFile) break;
  942.     Read(&vb, sizeof(VBHeader), Address);
  943.     if(!IsOK()) break;
  944.  
  945.     // If this is not a valid block, find the next one
  946.     if(vb.CkWord != CheckWord) {
  947.       Address = FindFirstVB(Address);
  948.     }
  949.     else {
  950.       Address += vb.Length;
  951.       i++;
  952.     }
  953.   }
  954.   return i;
  955. }
  956.  
  957. INT32 VBDFile::VBDeleted(__UWORD__ *d, __UWORD__ *r)
  958. // Returns the total number of removed and deleted VBs
  959. {
  960.   // 03/13/1998: Ensure that the VBD file header stays in sync
  961.   // during multiple file access
  962.   TestVBDHeader();
  963.  
  964.   VBHeader vb;
  965.   FAU addr = Header.FreeSpace; 
  966.   __UWORD__ i = 0;
  967.  
  968.   // Set the deleted and removed pointers to zero
  969.   if(d) *d = 0; if(r) *r = 0;
  970.   
  971.   if(addr == FSListCorrupt) return FSListCorrupt;
  972.  
  973.   while(addr) { // Until a block of a valid size is found
  974.     Read(&vb, sizeof(VBHeader), addr);
  975.     if(!IsOK()) break;
  976.  
  977.     // If this is not a valid block, the free space list is corrupt
  978.     if(vb.CkWord != CheckWord) {
  979.       Header.FreeSpace = FSListCorrupt;
  980.       WriteHdr(); 
  981.       return FSListCorrupt;
  982.     }
  983.  
  984.     // Added: 09/04/1998 to prevent an infinite loop. 
  985.     // If the block is not marked deleted or removed, the
  986.     // Next deleted VBD pointer is bad. This will cause
  987.     // an infinite loop if the end of the free space
  988.     // list is pointing to valid block.
  989.     switch((__SBYTE__)(vb.Status & 0xff)) {
  990.       case DeletedVB :
  991.     // Make sure the block is not pointing to itself
  992.     if(addr == vb.NextDeletedVB) { 
  993.       Header.FreeSpace = FSListCorrupt;
  994.       WriteHdr(); 
  995.       return 0;
  996.     }
  997.     break;
  998.  
  999.       case RemovedVB :
  1000.     // Make sure the block is not pointing to itself
  1001.     if(addr == vb.NextDeletedVB) { 
  1002.       Header.FreeSpace = FSListCorrupt;
  1003.       WriteHdr(); 
  1004.       return 0;
  1005.     }
  1006.     break;
  1007.  
  1008.       default :
  1009.     Header.FreeSpace = FSListCorrupt;
  1010.     WriteHdr(); 
  1011.     return 0;
  1012.     }
  1013.  
  1014.     addr = vb.NextDeletedVB;
  1015.  
  1016.     if(d) {
  1017.       if((vb.Status & 0xff) == DeletedVB)
  1018.     *d = *d+1;
  1019.     }
  1020.  
  1021.     if(r) {
  1022.       if((vb.Status & 0xff) == RemovedVB)
  1023.     *r = *r+1;
  1024.     }
  1025.  
  1026.     i++;
  1027.   }
  1028.   return i;
  1029. }
  1030.  
  1031. __UWORD__ VBDFile::ObjectLength(FAU Address)
  1032. // Returns the object length in bytes at the specified address
  1033. {
  1034.   VBHeader vb;
  1035.   FAU VBAddress;
  1036.  
  1037.   // Calculate the address of the block header
  1038.   if(Address == CurrAddress)
  1039.     VBAddress = FilePosition() - sizeof(VBHeader); 
  1040.   else
  1041.     VBAddress = Address - sizeof(VBHeader); 
  1042.   
  1043.   ReadVBHdr(vb, VBAddress);
  1044.  
  1045.   __UWORD__ len;
  1046.   
  1047.   // Adjust the number of bytes to allocate space of VB header
  1048.   // according to the revision letter.
  1049.   switch(rev_letter) {
  1050.     case 'A': case 'a' : // Version 1027, rev A
  1051.       // 9/24/1998: Revision 'A' adjusts the number of bytes to
  1052.       // include a 32-bit CRC. Space for a four byte checksum is
  1053.       // reserved at the end of the block.
  1054.       len = vb.Length - sizeof(VBHeader);
  1055.       len -= sizeof(vbChecksum);
  1056.       break;
  1057.  
  1058.     default: // Default to revision zero
  1059.       len = vb.Length - sizeof(VBHeader);
  1060.       break;
  1061.   }
  1062.   return len;
  1063. }
  1064.  
  1065. __UWORD__ VBDFile::VBLength(FAU Address)
  1066. // Returns the total VB length in bytes at the specified address
  1067. {
  1068.   VBHeader vb;
  1069.   FAU VBAddress;
  1070.   
  1071.   // Calculate the address of the block header
  1072.   if(Address == CurrAddress)
  1073.     VBAddress = FilePosition() - sizeof(VBHeader); 
  1074.   else
  1075.     VBAddress = Address - sizeof(VBHeader); 
  1076.  
  1077.   ReadVBHdr(vb, VBAddress);
  1078.   return vb.Length;
  1079. }
  1080.  
  1081. int VBDFile::UnDelete(FAU Address)
  1082. // Undeletes the VB if it has not been removed or reclaimed
  1083. {
  1084.   VBHeader vb, prev_VB; 
  1085.   FAU addr, vb_addr, prev_addr;
  1086.   addr = Header.FreeSpace; 
  1087.  
  1088.   vb_addr = Address - sizeof(VBHeader); // Address of VB header
  1089.   Read(&vb, sizeof(VBHeader), vb_addr);
  1090.   
  1091.   // Return false if VB is not marked deleted 
  1092.   if((vb.Status & 0xff) != DeletedVB) return 0; 
  1093.  
  1094.   // Loop until the block is found in the free space list
  1095.   while(addr) { 
  1096.     Read(&vb, sizeof(VBHeader), addr);
  1097.     if(!IsOK()) break;
  1098.     
  1099.     // Added: 09/04/1998 to signal not to use the free space list.
  1100.     // If this is not a valid block, the free space list is corrupt
  1101.     if(vb.CkWord != CheckWord) {
  1102.       Header.FreeSpace = FSListCorrupt;
  1103.       WriteHdr(); 
  1104.       return 0;
  1105.     }
  1106.     
  1107.     // Added: 09/04/1998 to prevent an infinite loop. 
  1108.     // If the block is not marked deleted or removed, the
  1109.     // Next deleted VBD pointer is bad. This will cause
  1110.     // an infinite loop if the end of the free space
  1111.     // list is pointing to valid block.
  1112.     switch((__SBYTE__)(vb.Status & 0xff)) {
  1113.       case DeletedVB :
  1114.     // Make sure the block is not pointing to itself 
  1115.     if(addr == vb.NextDeletedVB) { 
  1116.       Header.FreeSpace = FSListCorrupt;
  1117.       WriteHdr(); 
  1118.       return 0;
  1119.     }
  1120.     break;
  1121.  
  1122.       case RemovedVB :
  1123.     // Make sure the block is not pointing to itself 
  1124.     if(addr == vb.NextDeletedVB) { 
  1125.       Header.FreeSpace = FSListCorrupt;
  1126.       WriteHdr(); 
  1127.       return 0;
  1128.     }
  1129.     break;
  1130.  
  1131.       default :
  1132.     Header.FreeSpace = FSListCorrupt;
  1133.     WriteHdr(); 
  1134.     return 0;
  1135.     }
  1136.  
  1137.     // Found the block in the free space list
  1138.     if(addr == vb_addr) { 
  1139.       if(prev_addr == 0) { // Adjust the free space list
  1140.     // At the head of freespace list, so make a new head
  1141.     Header.FreeSpace = vb.NextDeletedVB;
  1142.     WriteHdr(); 
  1143.       }
  1144.       else {
  1145.     // In the middle of free space, so link prev to Next
  1146.     prev_VB.NextDeletedVB = vb.NextDeletedVB;
  1147.     WriteVBHdr(prev_VB, prev_addr);
  1148.       }
  1149.  
  1150.       // Undelete the specified block
  1151.       vb.Status = NormalVB;  // Mark VB with normal attribute
  1152.       vb.NextDeletedVB = 0;  // Always 0 unless VB is marked deleted
  1153.       WriteVBHdr(vb, addr);  // Write header for this VB
  1154.  
  1155.       return 1; // Return true if successful
  1156.     }
  1157.     
  1158.     // Keep looping through the free space list
  1159.     prev_addr = addr;
  1160.     prev_VB = vb;
  1161.     addr = vb.NextDeletedVB;
  1162.   } 
  1163.  
  1164.   return 0; // Return false if block was not undeleted
  1165. }
  1166.  
  1167. FAU VBDFile::ReAlloc(FAU Address, __UWORD__ Bytes)
  1168. // Reallocates a Variable Data Block of x Bytes of data from either
  1169. // the free space list, or from the end of the file. ReAlloc()
  1170. // returns the location of the space reallocated for the object,
  1171. // or returns a 0 if an error occurred.
  1172. {
  1173.   // Delete the object before allocating space for it
  1174.   if(!Delete(Address)) return 0; 
  1175.   return Alloc(Bytes);
  1176. }
  1177.  
  1178. FAU VBDFile::FindFirstVB(FAU Offset)
  1179. // Search through the VBD file until a valid VB is found.
  1180. // The search starts at the heap start or the offset value.
  1181. // Returns 0 if no valid VB is found in the file.
  1182. {
  1183.   VBHeader vb;
  1184.   FAU Address = 0;
  1185.  
  1186.   // 03/13/1998: Ensure that the VBD file header stays in sync
  1187.   // during multiple file access
  1188.   TestVBDHeader();
  1189.  
  1190.   // No VBs have been allocated yet
  1191.   if(Header.HeapStart == Header.EndOfFile) return 0;
  1192.      
  1193.   if(!Offset)
  1194.     Address = Header.HeapStart; // If no Offset, start at heap
  1195.   else {
  1196.     Address = Address + Offset; // Offset the starting address 
  1197.  
  1198.     if(Address >= Header.EndOfFile) // Prevent offsetting past EOF
  1199.       return 0; // Invalid address
  1200.   }
  1201.   
  1202.   while(1) {
  1203.     if(Address + sizeof(VBHeader) >= Header.EndOfFile || !IsOK()) return 0;
  1204.     Read(&vb, sizeof(VBHeader), Address);
  1205.     if(vb.CkWord != CheckWord)
  1206.       Address++; // Loop through the file byte by byte
  1207.     else
  1208.       break; // Found valid VB
  1209.   }
  1210.   return SeekTo(Address);
  1211. }
  1212.  
  1213. FAU VBDFile::FindFirstObject(FAU Offset)
  1214. // Search through the VBD file until a valid VB is found and
  1215. // then return the object's address. If the VB is marked deleted
  1216. // continue searching until the first normal VB is found. The
  1217. // search starts at the heap start or the offset. Returns 0 if
  1218. // no valid VB is found in the file.
  1219. {
  1220.   VBHeader vb;
  1221.   FAU Address = FindFirstVB(Offset);
  1222.   if(!Address) return 0;
  1223.  
  1224.   while(1) { // Loop until a normal VB status is found
  1225.     ReadVBHdr(vb, Address);
  1226.     if((vb.Status & 0xff) == NormalVB) break;
  1227.     Address = FindFirstVB(Address+vb.Length);
  1228.     if(!Address) return 0;
  1229.   }
  1230.  
  1231.   return Address + sizeof(VBHeader);
  1232. }
  1233.  
  1234. FAU VBDFile::FindNextVB(FAU Offset)
  1235. // Search through the VBD file until the next valid VB after the
  1236. // first valid VB is found. The search starts at the heap start
  1237. // or the offset value. Returns 0 if no valid VB is found.
  1238. {
  1239.   VBHeader vb;
  1240.  
  1241.   // No VBs have been allocated yet
  1242.   if(Header.HeapStart == Header.EndOfFile) return 0;
  1243.  
  1244.   FAU Address = FindFirstVB(Offset);
  1245.  
  1246.   if(!Address) return 0; // No Vaild VB found
  1247.  
  1248.   ReadVBHdr(vb, Address);
  1249.   FAU NextVB = Address + vb.Length;
  1250.  
  1251.   if(NextVB >= Header.EndOfFile) return Address; // This is last the VB
  1252.  
  1253.   ReadVBHdr(vb, NextVB); // Ensure VB header is valid
  1254.  
  1255.   return NextVB;
  1256. }
  1257.  
  1258. FAU VBDFile::FindNextObject(FAU Offset)
  1259. // Search through the VBD file until the next valid VB after the
  1260. // first valid VB is found and then return the object's address.
  1261. // If the VB is marked deleted continue searching until the next
  1262. // normal VB is found. The search starts at the heap start or the
  1263. // offset value. Returns 0 if no valid VB is found.
  1264. {
  1265.   VBHeader vb;
  1266.   FAU Address = FindNextVB(Offset);
  1267.   if(!Address) return 0;
  1268.   
  1269.   while(1) { // Loop until a normal VB status is found
  1270.     ReadVBHdr(vb, Address);
  1271.     if((vb.Status & 0xff) == NormalVB) break;
  1272.     Address = FindNextVB(Address+vb.Length);
  1273.     if(!Address) return 0;
  1274.   }
  1275.  
  1276.   return Address + sizeof(VBHeader);
  1277. }
  1278.  
  1279. FAU VBDFile::GetFreeSpace() 
  1280. {
  1281.   FileHeader fh;
  1282.   Read(&fh, sizeof(FileHeader), StartOfFile);
  1283.  
  1284.   // Ensure the in memory copy and the disk copy are the same
  1285.   if(fh.FreeSpace != Header.FreeSpace) { 
  1286.     ReadHdr();
  1287.   }
  1288.  
  1289.   return Header.FreeSpace;
  1290. }
  1291.   
  1292. FAU VBDFile::GetVBDFreeSpace() 
  1293. // Use this version of GetFreeSpace for wxWindows programs
  1294. {
  1295.   FileHeader fh;
  1296.   Read(&fh, sizeof(FileHeader), StartOfFile);
  1297.  
  1298.   // Ensure the in memory copy and the disk copy are the same
  1299.   if(fh.FreeSpace != Header.FreeSpace) { 
  1300.     ReadHdr();
  1301.   }
  1302.  
  1303.   return Header.FreeSpace;
  1304. }
  1305.  
  1306. FAU VBDFile::GetEOF() 
  1307. {
  1308.   FileHeader fh;
  1309.   Read(&fh, sizeof(FileHeader), StartOfFile);
  1310.  
  1311.   // Ensure the in memory copy and the disk copy are the same
  1312.   if(fh.EndOfFile != Header.EndOfFile) { 
  1313.     ReadHdr();
  1314.   }
  1315.  
  1316.   return Header.EndOfFile;
  1317. }
  1318.  
  1319. FAU VBDFile::GetHeapStart() 
  1320. {
  1321.   FileHeader fh;
  1322.   Read(&fh, sizeof(FileHeader), StartOfFile);
  1323.  
  1324.   // Ensure the in memory copy and the disk copy are the same
  1325.   if(fh.HeapStart != Header.HeapStart) { 
  1326.     ReadHdr();
  1327.   }
  1328.  
  1329.   return Header.HeapStart;
  1330. }
  1331.  
  1332. FAU VBDFile::GetHighestVB()
  1333. {
  1334.   FileHeader fh;
  1335.   Read(&fh, sizeof(FileHeader), StartOfFile);
  1336.  
  1337.   // Ensure the in memory copy and the disk copy are the same
  1338.   if(fh.HighestVB != Header.HighestVB) { 
  1339.     ReadHdr();
  1340.   }
  1341.  
  1342.   return Header.HighestVB;
  1343. }
  1344.  
  1345. __LWORD__ VBDFile::StaticArea()
  1346. {
  1347.   FileHeader fh;
  1348.   Read(&fh, sizeof(FileHeader), StartOfFile);
  1349.  
  1350.   // Ensure the in memory copy and the disk copy are the same
  1351.   if(fh.HeapStart != Header.HeapStart) { 
  1352.     ReadHdr();
  1353.   }
  1354.  
  1355.   return Header.HeapStart - sizeof(FileHeader);
  1356. }   
  1357.  
  1358.  
  1359. int VBDFile::TestVBDHeader()
  1360. // This function is used to ensure that the in memory copy
  1361. // of the VBD file header and the disk copy stay in sync
  1362. // during multiple file access.
  1363. {
  1364.   FileHeader fh;
  1365.   int errors = 0;
  1366.   
  1367.   Read(&fh, sizeof(FileHeader), StartOfFile);
  1368.  
  1369.   if(fh.FreeSpace != Header.FreeSpace) { 
  1370.     ReadHdr();
  1371.     errors++;
  1372.   }
  1373.  
  1374.   if(fh.EndOfFile != Header.EndOfFile) { 
  1375.     ReadHdr();
  1376.     errors++;
  1377.   }
  1378.  
  1379.   if(fh.HeapStart != Header.HeapStart) { 
  1380.     ReadHdr();
  1381.     errors++;
  1382.   }
  1383.  
  1384.   if(fh.HighestVB != Header.HighestVB) { 
  1385.     ReadHdr();
  1386.     errors++;
  1387.   }
  1388.  
  1389.   if(fh.HeapStart != Header.HeapStart) { 
  1390.     ReadHdr();
  1391.     errors++;
  1392.   }
  1393.  
  1394.   return errors;
  1395. }
  1396.  
  1397. // ==============================================================
  1398. // General purpose file utilites (BEGIN HERE)
  1399. // ==============================================================
  1400. int VBDFile::Exists(const char *FName)
  1401. // Returns true if the file exists
  1402. {
  1403.   FILE *tmp; // Temporary file pointer
  1404.   tmp = ::fopen(FName, "rb");
  1405.  
  1406.   if(!tmp) return 0; // Return 0 if file does not exist
  1407.   
  1408.   fclose(tmp);
  1409.   return 1;
  1410. }
  1411.  
  1412. __LWORD__ VBDFile::FileSize(const char *FName)
  1413. // Returns the file size. Use after file has been closed
  1414. // and re-opened to ensure that all the buffers are flushed
  1415. // to disk. 
  1416. {
  1417.   struct stat buf;
  1418.   int result = stat(FName, &buf);
  1419.   if(result != 0)
  1420. #ifdef CPP_EXCEPTIONS
  1421.     throw CFileOpenError();
  1422. #else
  1423.     Error->SignalException(EHandler::FileOpenError);
  1424. #endif
  1425.   return buf.st_size;
  1426. }
  1427. // ==============================================================
  1428. // General purpose file utilites (BEGIN HERE)
  1429. // ==============================================================
  1430.  
  1431. // ==============================================================
  1432. // Debug VBD File manager:  revisions 01/20/1997 (BEGIN HERE)
  1433. // ==============================================================
  1434. int VBDFile::BlindOpen(const char *FName, AccessMode Mode)
  1435. // Opens the FName file without checking the file type.
  1436. {
  1437.   char *mode_str;
  1438.  
  1439.   // First, close the current file if open.
  1440.   Close();
  1441.  
  1442.   if(Mode == READONLY) {
  1443.      mode_str = "rb";
  1444.      Status = 0x01; // set good bit, reset read/write bit
  1445.   }
  1446.   else {
  1447.      mode_str = "r+b";
  1448.      Status = 0x05; // Set good bit and read/write bit
  1449.   }
  1450.  
  1451.   fp = ::fopen(FName, mode_str);
  1452.  
  1453.   if(fp == 0) {
  1454. #ifdef CPP_EXCEPTIONS
  1455.     throw CFileOpenError();
  1456. #else
  1457.     Error->SignalException(EHandler::FileOpenError);
  1458. #endif
  1459.   }
  1460.   else {
  1461.     Status |= 2; // Set open bit
  1462.     strcpy(FileName, FName);
  1463.     LastOperation = WRITE; // So Read() works right the first time
  1464.     ReadHdr();
  1465.  
  1466.     // Set the revision letter according to the file header
  1467.     if(memcmp(Header.Signature, VBDSignature, 7) == 0) { 
  1468.       char revision[8];
  1469.       memmove(revision, Header.Signature, 8);
  1470.       rev_letter = revision[7];
  1471.     }    
  1472.   }
  1473.  
  1474.   return IsOpen(); // Returns 1 if file opened successfully, else 0.
  1475. }
  1476.  
  1477. FAU VBDFile::VBSearch(FAU Offset)
  1478. // Search through the VBD file until a valid VB is found.
  1479. // The search starts at the beginning  of the file or the
  1480. // offset value. Returns 0 if no valid VB is found in the
  1481. // file.
  1482. {
  1483.   VBHeader vb;
  1484.   FAU Address = 0;
  1485.  
  1486.   FAU StaticEOF = FileSize(VBDFileName());
  1487.  
  1488.   if(!Offset)
  1489.     Address = sizeof(FileHeader); // If no Offset, start after VBD header
  1490.   else {
  1491.     Address = Address + Offset; // Offset the starting address 
  1492.  
  1493.     if(Address >= StaticEOF) // Prevent offsetting past EOF
  1494.       return 0; // Invalid address
  1495.   }
  1496.   
  1497.   while(1) {
  1498.     if(Address + sizeof(VBHeader) >= StaticEOF || !IsOK()) return 0;
  1499.     Read(&vb, sizeof(VBHeader), Address);
  1500.     if(vb.CkWord != CheckWord)
  1501.       Address++; // Loop through the file byte by byte
  1502.     else
  1503.       break; // Found valid VB
  1504.   }
  1505.   return Address;
  1506. }
  1507. // ==============================================================
  1508. // Debug VBD File manager:  revisions 01/20/1997 (END HERE)
  1509. // ==============================================================
  1510.  
  1511. // ==============================================================
  1512. // Reclaim function:  revisions 09/11/1998 (BEGIN HERE)
  1513. // ==============================================================
  1514. // Define the __RECLAIM_BEST_FIT__ macro to use the best-fit routine
  1515. // or the __RECLAIM_FIRST_FIT__ macro to use the first-fit routine.
  1516. // Will default to first-fit. The best-fit method will prevent
  1517. // fragmentation as much as possible but is costly in terms of speed.
  1518. // The first-fit method will not prevent fragmentation as well as the
  1519. // best-fit method but offers a tremendous speed advantage.
  1520.  
  1521. #ifdef __RECLAIM_BEST_FIT__ // Defaults to first-fit
  1522. FAU VBDFile::Reclaim(__UWORD__ Bytes)
  1523. // Searches the free space list for a block that can be reuesd.
  1524. // This function will search the free space list for an "exact- 
  1525. // fit" first and then try to find the "best-fit" for the number
  1526. // of bytes requested. NOTE: The byte size is adjusted by the
  1527. // Alloc() function to allocate space for the VB header plus
  1528. // the object. Returns address of the reclaimed space, or zero
  1529. // if a deleted or removed block of the appropriate size is not
  1530. // found. An exact-fit is a block that matches the exact number
  1531. // of bytes requested. If an exact-fit cannot be found, the next
  1532. // block big enough to hold number of bytes requested plus the
  1533. // size a block header with a least one byte left over becomes
  1534. // a best-fit block. The search continues until a best-fit
  1535. // block with the least number of unsed bytes is found. The
  1536. // used bytes in the best-fit block are used to create a new
  1537. // block that will be put back on the free space list. This
  1538. // will keep the gaps between the blocks as small as possible,
  1539. // with the smallest gap being as large as a single block header
  1540. // plus one byte.
  1541. {
  1542.   // Cannot reuse any blocks if the free space list is corrupt
  1543.   if(Header.FreeSpace == FSListCorrupt) return 0;
  1544.  
  1545.   VBHeader vb, prev_VB, new_VB;
  1546.   VBHeader best_fit_prev_VB, best_fit_VB;
  1547.   FAU addr, prev_addr, new_addr;
  1548.   FAU best_fit_addr, best_fit_prev_addr;
  1549.   __UWORD__ avail_len, unused_len, best_fit_unused_len = 0;
  1550.   
  1551.   // Constants for the best-fit criteria. NOTE: The maximum length
  1552.   // of a block to reuse equals: (max_limit * byte_multiple) * Bytes 
  1553.   const unsigned max_limit = 10;    // Maximum number of byte multiples
  1554.   const double byte_multiple = .25; // Byte multiples  
  1555.  
  1556.   double best_byte_len, byte_percent = 0;
  1557.   double bytes_requested = (double)Bytes;
  1558.   unsigned i;
  1559.   unsigned best_length[max_limit];
  1560.   
  1561.   // Calculate the best-fit byte values
  1562.   for(i = 0; i < max_limit; i++) {
  1563.     byte_percent += byte_multiple;
  1564.     best_byte_len = bytes_requested * byte_percent;
  1565.     best_length[i] = (unsigned)best_byte_len;// + sizeof(VBHeader);
  1566.   }
  1567.  
  1568.   addr = Header.FreeSpace;
  1569.   prev_addr = best_fit_addr = 0;
  1570.   
  1571.   // Search the entire free space list until an exact-fit 
  1572.   // or a best-fit block is found.
  1573.   while(addr) { 
  1574.     Read(&vb, sizeof(VBHeader), addr);
  1575.     if(!IsOK()) break;
  1576.     
  1577.     // Added: 09/04/1998 to signal not to use the free space list.
  1578.     // If this is not a valid block, the free space list is corrupt
  1579.     if(vb.CkWord != CheckWord) {
  1580.       Header.FreeSpace = FSListCorrupt;
  1581.       WriteHdr(); 
  1582.       return 0;
  1583.     }
  1584.  
  1585.     // Added: 09/04/1998 to prevent an infinite loop. 
  1586.     // If the block is not marked deleted or removed, the
  1587.     // Next deleted VBD pointer is bad. This will cause
  1588.     // an infinite loop if the end of the free space
  1589.     // list is pointing to valid block.
  1590.     switch((__SBYTE__)(vb.Status & 0xff)) {
  1591.       case DeletedVB :
  1592.     // Make sure the block is not pointing to itself 
  1593.     if(addr == vb.NextDeletedVB) { 
  1594.       Header.FreeSpace = FSListCorrupt;
  1595.       WriteHdr(); 
  1596.       return 0;
  1597.     }
  1598.     break;
  1599.  
  1600.       case RemovedVB :
  1601.     // Make sure the block is not pointing to itself 
  1602.     if(addr == vb.NextDeletedVB) { 
  1603.       Header.FreeSpace = FSListCorrupt;
  1604.       WriteHdr(); 
  1605.       return 0;
  1606.     }
  1607.     break;
  1608.  
  1609.       default :
  1610.     Header.FreeSpace = FSListCorrupt;
  1611.     WriteHdr(); 
  1612.     return 0;
  1613.     }
  1614.  
  1615.     avail_len = vb.Length; // Length of object plus sizeof VB header
  1616.  
  1617.     // Unused length must be big enough to hold a two VB headers
  1618.     // plus the object
  1619.     if(avail_len > Bytes + sizeof(VBHeader)) 
  1620.       unused_len = avail_len - Bytes;
  1621.     else
  1622.       unused_len = 0;
  1623.     
  1624.     if(avail_len == Bytes) {
  1625.       // Block is an exact fit, so link prev link to Next link
  1626.       if(prev_addr == 0) {
  1627.     // At the head of freespace list, so make a new head
  1628.     Header.FreeSpace = vb.NextDeletedVB;
  1629.     WriteHdr(); 
  1630.       }
  1631.       else {
  1632.     // In the middle of free space, so link prev to Next
  1633.     prev_VB.NextDeletedVB = vb.NextDeletedVB;
  1634.     WriteVBHdr(prev_VB, prev_addr);
  1635.       }
  1636.       return IsOK() ? (__LWORD__)addr : 0;
  1637.     }
  1638.  
  1639.     if(unused_len > 0) { // Found bigger block with room for header
  1640.       for(i = 0; i < max_limit; i++) {
  1641.     if(unused_len <= best_length[i]) { 
  1642.       // Use the block matching the best-fit criteria
  1643.       if(best_fit_unused_len > best_length[i]) { 
  1644.         // Use the block if it is a better then the current one
  1645.         best_fit_addr = addr;
  1646.         best_fit_prev_addr = prev_addr;
  1647.         best_fit_VB = vb;
  1648.         best_fit_prev_VB = prev_VB;
  1649.         best_fit_unused_len = unused_len;
  1650.       }
  1651.     }
  1652.       }
  1653.     }
  1654.     
  1655.     // Block not big enough, so try Next block
  1656.     prev_addr = addr;
  1657.     prev_VB = vb;
  1658.     addr = vb.NextDeletedVB;
  1659.   
  1660.   } // End of block search
  1661.  
  1662.   // Could not find a best fit
  1663.   if(best_fit_addr == 0) return 0; 
  1664.  
  1665.   // Reuse the block and any remaining bytes
  1666.   new_addr = best_fit_addr + Bytes;
  1667.   new_VB.CkWord = CheckWord;
  1668.   new_VB.Status = RemovedVB;
  1669.   new_VB.NextDeletedVB = best_fit_VB.NextDeletedVB;
  1670.   new_VB.Length = best_fit_unused_len;
  1671.   WriteVBHdr(new_VB, new_addr);
  1672.  
  1673.   // Adjust the free space list
  1674.   if(best_fit_prev_addr == 0) {
  1675.     // At the head of freespace, so this is the new head
  1676.     Header.FreeSpace = new_addr;
  1677.     WriteHdr(); 
  1678.   }
  1679.   else { // In the middle of freespace
  1680.     best_fit_prev_VB.NextDeletedVB = new_addr;
  1681.     WriteVBHdr(best_fit_prev_VB, best_fit_prev_addr);
  1682.   }
  1683.   
  1684.   return IsOK() ? (__LWORD__)best_fit_addr : 0;
  1685. }
  1686.  
  1687. #else // __RECLAIM_FIRST_FIT__ 
  1688. FAU VBDFile::Reclaim(__UWORD__ Bytes)
  1689. // Searchs the free space list for the first block that can
  1690. // be reused. This function will search the free space list
  1691. // for a "first-fit" big enough to hold the number of bytes
  1692. // requested. NOTE: The byte size is adjusted by the Alloc()
  1693. // function to allocate space for the VB header plus the object.
  1694. // Returns address of the reclaimed space, or zero if a deleted
  1695. // or removed block of the appropriate size is not found. If an
  1696. // "exact-fit" is found (a block that matches the exact number
  1697. // of bytes requested) the address of that block is returned.
  1698. // Otherwise the address of the frist block big enough to hold
  1699. // number of bytes requested plus the size a block header with a
  1700. // least one byte left over is returned. The used bytes in the
  1701. // frist-fit block are used to create a new block that will be
  1702. // put back on the free space list. 
  1703. {
  1704.   // Cannot reuse any blocks if the free space list is corrupt
  1705.   if(Header.FreeSpace == FSListCorrupt) return 0;
  1706.  
  1707.   VBHeader vb, prev_VB, new_VB;
  1708.   FAU addr, prev_addr, new_addr;
  1709.   __UWORD__ avail_len, unused_len;
  1710.  
  1711.   addr = Header.FreeSpace; prev_addr = 0;
  1712.  
  1713.   // Search the free space list until a first-fit is found
  1714.   while(addr) { 
  1715.     Read(&vb, sizeof(VBHeader), addr);
  1716.     if(!IsOK()) break;
  1717.     
  1718.     // Added: 09/04/1998 to signal not to use the free space list.
  1719.     // If this is not a valid block, the free space list is corrupt
  1720.     if(vb.CkWord != CheckWord) {
  1721.       Header.FreeSpace = FSListCorrupt;
  1722.       WriteHdr(); 
  1723.       return 0;
  1724.     }
  1725.  
  1726.     // Added: 09/04/1998 to prevent an infinite loop. 
  1727.     // If the block is not marked deleted or removed, the
  1728.     // Next deleted VBD pointer is bad. This will cause
  1729.     // an infinite loop if the end of the free space
  1730.     // list is pointing to valid block.
  1731.     switch((__SBYTE__)(vb.Status & 0xff)) {
  1732.       case DeletedVB :
  1733.     // Make sure the block is not pointing to itself 
  1734.     if(addr == vb.NextDeletedVB) { 
  1735.       Header.FreeSpace = FSListCorrupt;
  1736.       WriteHdr(); 
  1737.       return 0;
  1738.     }
  1739.     break;
  1740.  
  1741.       case RemovedVB :
  1742.     // Make sure the block is not pointing to itself 
  1743.     if(addr == vb.NextDeletedVB) { 
  1744.       Header.FreeSpace = FSListCorrupt;
  1745.       WriteHdr(); 
  1746.       return 0;
  1747.     }
  1748.     break;
  1749.  
  1750.       default :
  1751.     Header.FreeSpace = FSListCorrupt;
  1752.     WriteHdr(); 
  1753.     return 0;
  1754.     }
  1755.  
  1756.     avail_len = vb.Length; // Length of object plus sizeof VB header
  1757.  
  1758.     // Unused length must be big enough to hold a two VB headers
  1759.     // plus the object
  1760.     if(avail_len > Bytes + sizeof(VBHeader)) 
  1761.       unused_len = avail_len - Bytes;
  1762.     else
  1763.       unused_len = 0;
  1764.     
  1765.     if(avail_len == Bytes) {
  1766.       // Block is an exact fit, so link prev link to Next link
  1767.       if(prev_addr == 0) {
  1768.     // At the head of freespace list, so make a new head
  1769.     Header.FreeSpace = vb.NextDeletedVB;
  1770.     WriteHdr(); 
  1771.       }
  1772.       else {
  1773.     // In the middle of free space, so link prev to Next
  1774.     prev_VB.NextDeletedVB = vb.NextDeletedVB;
  1775.     WriteVBHdr(prev_VB, prev_addr);
  1776.       }
  1777.       break;
  1778.     }
  1779.  
  1780.     if(unused_len > 0) {
  1781.       // Block too big, and there's room for a VB header
  1782.       // in the unused portion, so reuse any remaining bytes.
  1783.       new_addr = addr + Bytes;
  1784.       new_VB.CkWord = CheckWord;
  1785.       new_VB.Status = RemovedVB;
  1786.       new_VB.NextDeletedVB = vb.NextDeletedVB;
  1787.       new_VB.Length = unused_len;
  1788.       WriteVBHdr(new_VB, new_addr);
  1789.       if(prev_addr == 0) {
  1790.     // At the head of freespace, so this is the new head
  1791.     Header.FreeSpace = new_addr;
  1792.     WriteHdr(); 
  1793.       }
  1794.       else { // In the middle of freespace
  1795.     prev_VB.NextDeletedVB = new_addr;
  1796.     WriteVBHdr(prev_VB, prev_addr);
  1797.       }
  1798.       break;
  1799.     }
  1800.  
  1801.     // Block not big enough, so try Next block
  1802.     prev_addr = addr;
  1803.     prev_VB = vb;
  1804.     addr = vb.NextDeletedVB;
  1805.  
  1806.   } // End of block search
  1807.   
  1808.   return IsOK() ? (__LWORD__)addr : 0;
  1809. }
  1810. #endif //__RECLAIM_FIRST_FIT__, __RECLAIM_BEST_FIT__
  1811. // ==============================================================
  1812. // Reclaim function:  revisions 09/11/1998 (END HERE)
  1813. // ==============================================================
  1814.  
  1815. // ----------------------------------------------------------- //
  1816. // ------------------------------- //
  1817. // --------- End of File --------- //
  1818. // ------------------------------- //
  1819.