home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 22 gnu / 22-gnu.zip / db02_src.zip / rserv.cc < prev    next >
C/C++ Source or Header  |  1994-02-22  |  13KB  |  500 lines

  1. /**************************************************************************
  2.  * Source Id :
  3.  *
  4.  * $Id: rserv.cc,v 1.30 1993/07/19 11:58:50 kevinl Exp $
  5.  *-------------------------------------------------------------------------
  6.  * Project Notes :
  7.  *
  8.  *  Diamond Base
  9.  *  ============
  10.  *    A solid database implementation, spurred on by the continuing
  11.  *  Metal (Lead) Base saga.
  12.  *
  13.  *  Project Team :
  14.  *      A. Davison
  15.  *      K. Lentin
  16.  *      D. Platt
  17.  *
  18.  *    Project Commenced : 05-02-1993
  19.  *
  20.  *-------------------------------------------------------------------------
  21.  *  Module Notes :
  22.  *
  23.  *  recServer module
  24.  *
  25.  *  Original Author : Darren Platt
  26.  *
  27.  *-------------------------------------------------------------------------
  28.  * Revision History:
  29.  *
  30.  * $Log: rserv.cc,v $
  31. // Revision 1.30  1993/07/19  11:58:50    kevinl
  32. // Added a cast for bitPoolRecs
  33. //
  34. // Revision 1.29  1993/06/23  05:21:22    kevinl
  35. // Mallocs are now in angular brackets
  36. //
  37. // Revision 1.28  1993/05/26  01:01:39    kevinl
  38. // MALLOC_H_MISSING
  39. //
  40. // Revision 1.27  1993/05/25  12:59:27    kevinl
  41. // Fixed #elif
  42. //
  43. // Revision 1.26  1993/05/06  04:01:10    kevinl
  44. // SASC define for malloc.h, unistd.h is now stdio.h and BINARY mode opens
  45. //
  46. // Revision 1.25  1993/05/03  23:35:22    kevinl
  47. // theData not data
  48. //
  49. // Revision 1.24  1993/05/01  14:43:17    kevinl
  50. // Got rid of ints
  51. //
  52. // Revision 1.23  1993/04/28  14:45:52    darrenp
  53. // Tidied up debuggin info.
  54. //
  55. // Revision 1.22  1993/04/28  14:42:40    darrenp
  56. // Made delRec more paranoid - can now return db_nfound
  57. //
  58. // Revision 1.21  1993/04/27  06:59:47    kevinl
  59. // Comments
  60. //
  61. // Revision 1.20  1993/04/15  04:21:52    kevinl
  62. // Moved malloc.h
  63. //
  64. // Revision 1.19  1993/04/09  03:11:03    darrenp
  65. // fixed a bug in db extension which made it break after 47,509 additions.
  66. //
  67. // Revision 1.18  1993/04/05  07:50:39    kevinl
  68. // Put new records retrieved from file into cache
  69. //
  70. // Revision 1.17  1993/04/05  01:29:01    kevinl
  71. // Rmeoved midnless debug ouput
  72. //
  73. // Revision 1.16  1993/04/04  23:59:57    kevinl
  74. // Added cache support
  75. //
  76. // Revision 1.15  1993/03/29  08:20:09    darrenp
  77. // Added new malloc library support
  78. //
  79. // Revision 1.14  1993/03/28  11:51:24    kevinl
  80. // Modified for dbErr
  81. //
  82. // Revision 1.13  1993/03/28  19:08:35    davison
  83. // Changed references to pError to dbErr().
  84. //
  85. // Revision 1.12  1993/03/26  06:16:38    darrenp
  86. // standardized error codes.
  87. //
  88. // Revision 1.11  1993/03/24  06:22:09    kevinl
  89. // Added better error handling code
  90. //
  91. // Revision 1.10  1993/03/20  14:14:00    kevinl
  92. // Check return code of putBitPull in createDb
  93. //
  94. // Revision 1.9  1993/03/15  01:06:37  darrenp
  95. // delRec added
  96. //
  97.  **************************************************************************/
  98.  
  99. #include <fcntl.h>
  100. #if defined(__BORLANDC__) || defined(__SASC) || defined(__EMX__)
  101. #   ifndef __SASC
  102. #      include <io.h>
  103. #   endif
  104. #   include <stdio.h>
  105. #else
  106. #include <unistd.h>
  107. #endif
  108. #include <stdlib.h>
  109. #include <assert.h>
  110. #include <errno.h>
  111. #ifndef MALLOC_H_MISSING
  112. #include <malloc.h>
  113. #endif
  114. #include <iostream.h>
  115. #include <rserv.h>
  116.  
  117. //
  118. // A record server which just retrieves records from a file,
  119. // monitoring where the free slots occur.
  120. //
  121. // Turn on RSERV_DEBUG for crude debugging info
  122. //
  123. recServer::recServer(const char *name, long offset, long cSize) : cache()
  124. {
  125.     commonInit();
  126.  
  127. #ifdef __BORLANDC__
  128.     fd = open(name,O_RDWR | O_BINARY);
  129. #elif defined(__SASC)
  130.     fd = open((char*)name,O_RDWR | O_BINARY);
  131. #else
  132.     fd = open(name,O_RDWR);
  133. #endif
  134.     if (fd==-1) {
  135.         // Failed to open.
  136.         isOpen = false;
  137.     } else {
  138.         headerOffset = offset;
  139.         dataOffset = headerOffset + sizeof header;
  140.  
  141.         if (headerOffset) {
  142.             // Seek to the header position if non-zero.
  143.             if (lseek(fd,headerOffset,SEEK_SET)!=headerOffset) {
  144.                 dbErr("Seeking to header:");
  145.                 exit(1);
  146.             }
  147.         }
  148.         // Now read in the header structure.
  149.         if (!readHeader(header)) {
  150.             // Eeek - can't read in header.
  151.             dbErr("Failed reading header because:");
  152.             exit(1);
  153.         }
  154.  
  155.         blockLength = bitPoolBytes + header.recLength * bitPoolRecs;
  156.  
  157.         // Read in the starting bitPool;
  158.         if (!getBitPool(currentPool,indelPool)) {
  159.             isOpen = false;
  160.             return;
  161.         }
  162.         // Create a cache to speed things up
  163.         createCache(header.recLength, cSize);
  164.         isOpen = true;
  165.     }
  166. }
  167.  
  168. // Just create a recServer without opening any files
  169.  
  170. recServer::recServer(void) : cache()
  171. {
  172.     commonInit();
  173.     isOpen = false;
  174. }
  175.  
  176. recServer::~recServer()
  177. {
  178.     if (isOpen) close(fd);
  179. }
  180.  
  181. // Create a new recServer file
  182.  
  183. dbError recServer::createDb(const char *name, long recLen, long offset, long cSize)
  184. {
  185.     // Create a new db.
  186.     if (isOpen) return dbErr(db_alreadyopen);
  187.     createCache(recLen, cSize);
  188.  
  189.     headerOffset = offset;
  190.     dataOffset = headerOffset + sizeof header;
  191.  
  192. #ifdef __BORLANDC__
  193.     fd = open(name,O_CREAT | O_RDWR | O_BINARY, 0644);
  194. #elif defined(__SASC)
  195.     fd = open((char*)name,O_CREAT | O_RDWR | O_BINARY, 0644);
  196. #else
  197.     fd = open(name,O_CREAT | O_RDWR , 0644);
  198. #endif
  199.     if (fd==-1) {
  200.         // Failed to open.
  201.         isOpen = false;
  202.         return dbErr(db_err);
  203.     } else {
  204.  
  205.         headerOffset = offset;
  206.  
  207.         // Setup the header details.
  208.         header.recLength = recLen;
  209.         header.totalRecs = bitPoolRecs;
  210.         header.recsUsed = 0;
  211.  
  212.         blockLength = bitPoolBytes + header.recLength * bitPoolRecs;
  213.  
  214.         indelPool.zero();
  215.         if (!putBitPool(0,indelPool)) return dbErr(db_err);
  216.  
  217.         if (!writeHeader(header)) return dbErr(db_err);
  218.  
  219.         isOpen = true;
  220.         return dbErr(db_ok);
  221.     }
  222. }
  223.  
  224. dbError recServer::getRec(long idx,void *buffer)
  225. {
  226.     if (!isOpen) return dbErr(db_notopen);
  227.  
  228.     // Can we get it from the cache?
  229.     if (getCache((char*)buffer, idx))
  230.     {
  231. //        cout << "Let's go to the video tape" << endl;
  232.         return dbErr(db_ok);
  233.     }
  234.  
  235.     // Not in the cache, read it in and put it in the cache
  236.  
  237.     if (lseek(fd,idx2offset(idx),SEEK_SET)!=idx2offset(idx)) return dbErr(db_err);
  238.     if (read(fd,buffer,header.recLength)!=header.recLength) return dbErr(db_err);
  239.     putCache((char*)buffer, idx);
  240.     return dbErr(db_ok);
  241. }
  242.  
  243. dbError recServer::putRec(long idx,void *buffer)
  244. {
  245.     if (!isOpen) return dbErr(db_notopen);
  246.  
  247.     // Save to the file and put it in the cache
  248.  
  249.     if (lseek(fd,idx2offset(idx),SEEK_SET)!=idx2offset(idx)) return dbErr(db_err);
  250.     if (write(fd,buffer,header.recLength)!=header.recLength) return dbErr(db_err);
  251.     putCache((char*)buffer, idx);
  252.     return dbErr(db_ok);
  253. }
  254.  
  255. // Open up a new spot in the recServer
  256.  
  257. dbError recServer::newRec(long &newIdx)
  258. {
  259.     if (!isOpen) return dbErr(db_notopen);
  260.  
  261.     long i;
  262.     long currNumBlocks = header.totalRecs >> bitPoolBits;
  263.  
  264.     // If the db gets over 80% full, extend it.
  265.  
  266.     if (header.recsUsed > (long)(0.8*header.totalRecs)) {
  267.         // time to build a bigger house.
  268.         // Extend the database by 25% of its current size.
  269.  
  270. #ifdef RSERV_DEBUG
  271.         cout << "Expanding database" << endl;
  272. #endif
  273.         long newNumBlocks = (long)(currNumBlocks * 1.25);
  274.         if (newNumBlocks==currNumBlocks) newNumBlocks++;
  275.  
  276.         // Create a whole lot of zero bit blocks.
  277.         // record where the end of the file is, since
  278.         // that's a good place to continue insertion after extending
  279.         // the file.
  280.         currentPool = currNumBlocks;
  281.  
  282.         bitPool dummy;
  283.         for(i=0;i<(newNumBlocks-currNumBlocks);i++) {
  284. #ifdef RSERV_DEBUG
  285.             cout << "Adding block" << endl;
  286. #endif
  287.             putBitPool(currNumBlocks+i,dummy);
  288.         }
  289.         header.totalRecs+=i*bitPoolRecs;
  290.         // put it back on disk like a good boy - or we could
  291.         // wait for the inevitable update below. nah.
  292.         if (!writeHeader(header)) return dbErr(db_err);
  293.  
  294.         // we know that the pool at the end of the file is
  295.         // empty, so don't fetch it, just use an empty one.
  296.         indelPool.zero();
  297.     }
  298.     // Ok, there is enough space now.
  299.  
  300.     do { // Loop till we find an empty bitPool.
  301.         // Is there enough space on the current bitpool.
  302.         if (indelPool.bitsUsed<bitPoolRecs) {
  303.             // Ok, this sucker is not full yet.
  304.  
  305.             // First find the byte where the zero bit is living.
  306.             for(i=0;indelPool.map[i]==255;i++);
  307.             assert(i<bitPoolRecs);
  308.  
  309.             unsigned char t=indelPool.map[i],mask = 0x80;
  310.  
  311.             // convert x to an index offset.
  312.             // This is really psychedelic, the arrangement of >>s and //s
  313.             unsigned long x=i << 3;
  314.             while(t&mask) { mask >>= 1; x++; };
  315.  
  316.             t |= mask; // it was clear to start with, so set it.
  317.  
  318.             indelPool.map[i] = t; // put it back into the bitPool.
  319.  
  320.             // x is the index of the record we want into the block.
  321.  
  322.             indelPool.bitsUsed++;
  323.             putBitPool(currentPool,indelPool);
  324.  
  325.             header.recsUsed++;
  326.             if (!writeHeader(header)) return dbErr(db_err);
  327.  
  328.             // set the variable they passed in.
  329.             newIdx = x + (currentPool << bitPoolBits);
  330. #ifdef RSERV_DEBUG
  331.             cout << "+++++ allocating bucket (" << fd << "," << newIdx
  332.                 << ")" << endl;
  333. #endif
  334.             return dbErr(db_ok);
  335.         }
  336.         // Ok, this bitPool was full, so move to the next one.
  337.         currentPool++;
  338.         if (currentPool == currNumBlocks) currentPool = 0;
  339.         if (!getBitPool(currentPool,indelPool)) return dbErr(db_err);
  340. #ifdef RSERV_DEBUG
  341.         cout << "Moving to bitpool #" << currentPool << endl;
  342. #endif
  343.     } while (1);
  344. }
  345.  
  346. // Delete a record from the recServer
  347.  
  348. dbError recServer::delRec(long idx)
  349. {
  350. #ifdef RSERV_DEBUG
  351.     cout << "+++++ attempting delete of (" << fd << "," << idx << ")" << endl;
  352. #endif
  353.     if (!isOpen) return dbErr(db_notopen);
  354.  
  355.     // First locate the appropriate bitPool, then the appropriate
  356.     // bit within it, then zero it, update the number of records used
  357.     // and update the bitPool and header.
  358.  
  359.     static char bitTable[8] = {128,64,32,16,8,4,2,1};
  360.     currentPool = idx >> bitPoolBits;
  361.  
  362.     // Fetch that bit pool;
  363.     if (!getBitPool(currentPool,indelPool)) return dbErr(db_err);
  364.  
  365.     // Find out which bit number to delete.
  366.     long whichBit = idx & ((1<<bitPoolBits) -1);
  367.  
  368.     // Find out which byte that occurs in.
  369.     long whichByte = whichBit >> 3;
  370.  
  371.     // And which bit within the byte.
  372.     whichBit &=7;
  373.  
  374.     if (!(indelPool.map[whichByte] & bitTable[whichBit])) {
  375.         // Blow the whistle - deleting a non-existant record.
  376.         return dbErr(db_nfound);
  377.     }
  378.  
  379.     // Zero that bit
  380.     indelPool.map[whichByte] ^= bitTable[whichBit];
  381.     indelPool.bitsUsed--;
  382.     header.recsUsed--;
  383.  
  384.     if (!writeHeader(header)) return dbErr(db_err);
  385.     if (!putBitPool(currentPool,indelPool)) return dbErr(db_err);
  386. #ifdef RSERV_DEBUG
  387.     cout << "+++++ succeeded " << endl;
  388. #endif
  389.     return dbErr(db_ok);
  390. }
  391.  
  392. // Read in the header of the record storage file.
  393.  
  394. bool recServer::readHeader(rsHeader &h)
  395. {
  396.     // Seek to the right part of the file.
  397.     if (lseek(fd,headerOffset,SEEK_SET)!=headerOffset) return false;
  398.  
  399.     // Now read in the header structure.
  400.     if (read(fd,&h,sizeof h)!=(sizeof h)) {
  401.         // Eeek - can't read in header.
  402.         return false;
  403.     }
  404.     return true;
  405. }
  406.  
  407. // Write out the header
  408.  
  409. bool recServer::writeHeader(rsHeader &h)
  410. {
  411.     // Seek to the right part of the file.
  412.     if (lseek(fd,headerOffset,SEEK_SET)!=headerOffset) return false;
  413.  
  414.     // Now write the header structure.
  415.     if (write(fd,&h,sizeof h)!=(sizeof h)) {
  416.         // Eeek - can't write the header.
  417.         return false;
  418.     }
  419.     return true;
  420. }
  421.  
  422. // take an index and calculate where to look for it
  423. // in the file - need to allow for the regular insertion
  424. // of free bitpools.
  425.  
  426. long recServer::idx2offset(long idx)
  427. {
  428.     // every 2^bitPoolBits records, there is a bit pool of size
  429.     // 2^(bitPoolBits / 8) to record which buckets are free in the
  430.     // next partition.
  431.  
  432.     long whichBlock = idx >> bitPoolBits;
  433.     long whichWithinBlock = idx & (bitPoolRecs - 1);
  434.  
  435.     return (whichBlock * blockLength) + whichWithinBlock * header.recLength
  436.         + dataOffset+sizeof(bitPool);
  437. }
  438.  
  439. // Performs any initialization common to all constructors.
  440.  
  441. void recServer::commonInit(void)
  442. {
  443.     bitPoolBits = 12; // magic number. 4096 records between bitPools
  444.     bitPoolRecs = (short)(1 << bitPoolBits);
  445.  
  446.     // The number of bytes in the bitPool is the number of bits
  447.     // needed to map the following block of records, plus a short
  448.     // to say how many are used in this block.
  449.  
  450.     bitPoolBytes = sizeof(bitPool);
  451.  
  452.     // currentPool is used to keep track of where we are inserting/
  453.     // looking for spare buckets at the moment. It is the number of
  454.     // the bitPool - insertions/ deletions occur from this block
  455.     // until an operation can't be performed, and then we rotate
  456.     // through the file until we find a suitable pool and the stop
  457.     // there. If there is no more space in the file, then it gets
  458.     // extended.
  459.     currentPool = 0;
  460.  
  461.     dbErr(db_ok);
  462. }
  463.  
  464. // fetch a particular bitpool.
  465.  
  466. bool recServer::getBitPool(long idx,bitPool &bp)
  467. {
  468.     if (lseek(fd,bpIdx2Offset(idx),SEEK_SET)!=bpIdx2Offset(idx)) return false;
  469.     if (read(fd,&bp,sizeof bp)!=(sizeof bp)) return false;
  470.     return true;
  471. }
  472.  
  473. // Write back the bitpool.
  474.  
  475. bool recServer::putBitPool(long idx,bitPool &bp)
  476. {
  477.     if (lseek(fd,bpIdx2Offset(idx),SEEK_SET)!=bpIdx2Offset(idx)) return false;
  478.     if (write(fd,&bp,sizeof bp)!=(sizeof bp)) return false;
  479.     return true;
  480. }
  481.  
  482. long recServer::bpIdx2Offset(long idx)
  483. {
  484.     return dataOffset + idx * blockLength;
  485. }
  486.  
  487. bool recServer::readData(long offset,void *theData,long length)
  488. {
  489.     if (lseek(fd,offset,SEEK_SET)!=offset) return false;
  490.     if (read(fd,theData,length)!=length) return false;
  491.     return true;
  492. }
  493.  
  494. bool recServer::writeData(long offset,void *theData,long length)
  495. {
  496.     if (lseek(fd,offset,SEEK_SET)!=offset) return false;
  497.     if (write(fd,theData,length)!=length) return false;
  498.     return true;
  499. }
  500.