store.cpp

Go to the documentation of this file.
00001 /******************************************************************************
00002  *
00003  * $Id:$
00004  *
00005  *
00006  * Copyright (C) 1997-2008 by Dimitri van Heesch.
00007  *
00008  * Permission to use, copy, modify, and distribute this software and its
00009  * documentation under the terms of the GNU General Public License is hereby 
00010  * granted. No representations are made about the suitability of this software 
00011  * for any purpose. It is provided "as is" without express or implied warranty.
00012  * See the GNU General Public License for more details.
00013  *
00014  * Documents produced by Doxygen are derivative works derived from the
00015  * input used in their production; they are not affected by this license.
00016  *
00017  */
00018 
00019 #include "store.h"
00020 #include "portable.h"
00021 
00022 
00023 #include <stdio.h>
00024 #include <stdlib.h>
00025 #include <errno.h>
00026 #include <string.h>
00027 #include <assert.h>
00028 
00029 #define BLOCK_SIZE         512 // should be >8 and a multiple of 8
00030 #define BLOCK_POINTER_SIZE sizeof(portable_off_t)
00031 
00032 
00033 #define ASSERTS_ENABLED
00034 
00035 #ifdef ASSERTS_ENABLED
00036 #define STORE_ASSERT(x) assert(x)
00037 #else
00038 #define STORE_ASSERT(x)
00039 #endif
00040 
00041 //------------------------------------------------------------------------------------
00042 
00043 Store::Store() 
00044 {
00045   m_file       = 0;
00046   m_front      = 0;
00047   m_head       = 0;
00048   m_state      = Init;
00049   m_reads      = 0;
00050   m_writes     = 0;
00051 }
00052 
00053 Store::~Store()
00054 {
00055   if (m_file)   fclose(m_file);
00056 
00057   // clean up free list
00058   while (m_head)
00059   {
00060     Node *node = m_head;
00061     m_head = node->next;
00062     delete node;
00063   }
00064 }
00065 
00066 int Store::open(const char *name)
00067 {
00068   int i;
00069   STORE_ASSERT(m_state==Init);
00070   if (m_file) return 0; // already open
00071   m_file = fopen(name,"w+b");
00072   if (m_file==0) return -1;
00073 
00074   // first block serves as header, so offset=0 can be used as the end of the list.
00075   for (i=0;i<BLOCK_SIZE/8;i++)
00076   {
00077     fputc('D',m_file);
00078     fputc('O',m_file);
00079     fputc('X',m_file);
00080     fputc('Y',m_file);
00081     fputc('G',m_file);
00082     fputc('E',m_file);
00083     fputc('N',m_file);
00084     fputc(0,m_file);
00085   }
00086   m_front  = BLOCK_SIZE;
00087   m_head   = 0;
00088   m_state  = Reading;
00089   return 0;
00090 }
00091 
00092 void Store::close()
00093 {
00094   if (m_file) fclose(m_file);
00095   m_file=0;
00096   m_state  = Init;
00097 }
00098      
00099 portable_off_t Store::alloc()
00100 {
00101   STORE_ASSERT(m_state==Reading);
00102   m_state=Writing;
00103   portable_off_t pos;
00104   if (m_head==0) // allocate new block
00105   {
00106     //printf("alloc: new block\n");
00107     if (portable_fseek(m_file,0,SEEK_END)==-1) // go to end of the file
00108     {
00109       fprintf(stderr,"Store::alloc: Error seeking to end of file: %s\n",strerror(errno));
00110       exit(1);
00111     }
00112     pos = portable_ftell(m_file);
00113     STORE_ASSERT( (pos & (BLOCK_SIZE-1))==0 );
00114     m_front = pos + BLOCK_SIZE; // move front to end of this block
00115   }
00116   else // reuse freed block
00117   {
00118     //printf("alloc: reuse block: m_head=%d\n",(int)m_head);
00119     Node *node = m_head;
00120     pos = node->pos;
00121     // point head to next free item
00122     m_head = node->next;
00123     delete node;
00124     // move to start of the block
00125     if (portable_fseek(m_file,pos,SEEK_SET)==-1)
00126     {
00127       fprintf(stderr,"Store::alloc: Error seeking to position %d: %s\n",
00128           (int)pos,strerror(errno));
00129       exit(1);
00130     }
00131     STORE_ASSERT( (pos & (BLOCK_SIZE-1))==0 );
00132   }
00133   //printf("%x: Store::alloc\n",(int)pos);
00134   return pos;
00135 }
00136 
00137 int Store::write(const char *buf,uint size)
00138 {
00139   STORE_ASSERT(m_state==Writing);
00140   //printf("%x: Store::write\n",(int)portable_ftell(m_file));
00141   do
00142   {
00143     portable_off_t curPos     = portable_ftell(m_file);
00144     int bytesInBlock = BLOCK_SIZE - BLOCK_POINTER_SIZE - (curPos & (BLOCK_SIZE-1));
00145     int bytesLeft    = bytesInBlock<(int)size ? (int)size-bytesInBlock : 0;
00146     int numBytes     = size - bytesLeft;
00147     STORE_ASSERT(bytesInBlock>=0);
00148     STORE_ASSERT(numBytes<=(int)(BLOCK_SIZE-BLOCK_POINTER_SIZE));
00149     if (numBytes>0)
00150     {
00151       if ((int)fwrite(buf,1,numBytes,m_file)!=numBytes)
00152       {
00153         fprintf(stderr,"Error writing: %s\n",strerror(errno));
00154         exit(1);
00155       }
00156       m_writes++;
00157     }
00158     if (bytesLeft>0) // still more bytes to write
00159     {
00160       STORE_ASSERT(((portable_ftell(m_file)+BLOCK_POINTER_SIZE)&(BLOCK_SIZE-1))==0);
00161       // allocate new block
00162       if (m_head==0) // no free blocks to reuse
00163       {
00164         //printf("%x: Store::write: new: pos=%x\n",(int)m_front,(int)portable_ftell(m_file));
00165         // write pointer to next block
00166         if (fwrite(&m_front,BLOCK_POINTER_SIZE,1,m_file)!=1)
00167         {
00168           fprintf(stderr,"Error writing to store: %s\n",strerror(errno));
00169           exit(1);
00170         }
00171         STORE_ASSERT(portable_ftell(m_file)==(curPos&~(BLOCK_SIZE-1))+BLOCK_SIZE);
00172 
00173         // move to next block
00174         if (portable_fseek(m_file,0,SEEK_END)==-1) // go to end of the file
00175         {
00176           fprintf(stderr,"Store::alloc: Error seeking to end of file: %s\n",strerror(errno));
00177           exit(1);
00178         }
00179         STORE_ASSERT(portable_ftell(m_file)==m_front);
00180         // move front to the next of the block
00181         m_front+=BLOCK_SIZE;
00182       }
00183       else // reuse block from the free list
00184       {
00185         // write pointer to next block
00186         if (fwrite(&m_head->pos,BLOCK_POINTER_SIZE,1,m_file)!=1)
00187         {
00188           fprintf(stderr,"Error writing to store: %s\n",strerror(errno));
00189           exit(1);
00190         }
00191         Node *node = m_head;
00192         portable_off_t pos = node->pos;
00193         // point head to next free item
00194         m_head = node->next;
00195         delete node;
00196         // move to start of the block
00197         if (portable_fseek(m_file,pos,SEEK_SET)==-1)
00198         {
00199           fprintf(stderr,"Store::write: Error seeking to position %d: %s\n",
00200               (int)pos,strerror(errno));
00201           exit(1);
00202         }
00203         //printf("%x: Store::write: reuse\n",(int)pos);
00204       }
00205     }
00206     size-=numBytes;
00207     buf+=numBytes;
00208   }
00209   while (size>0);
00210   return size;
00211 }
00212 
00213 void Store::end()
00214 {
00215   STORE_ASSERT(m_state==Writing);
00216   portable_off_t curPos     = portable_ftell(m_file);
00217   int bytesInBlock = BLOCK_SIZE - (curPos & (BLOCK_SIZE-1));
00218   //printf("%x: Store::end erasing %x bytes\n",(int)curPos&~(BLOCK_SIZE-1),bytesInBlock);
00219   //printf("end: bytesInBlock=%x\n",bytesInBlock);
00220   // zero out rest of the block
00221   int i;
00222   for (i=0;i<bytesInBlock;i++)
00223   {
00224     fputc(0,m_file);
00225   }
00226   m_state=Reading;
00227 }
00228 
00229 void Store::release(portable_off_t pos)
00230 {
00231   STORE_ASSERT(m_state==Reading);
00232   //printf("%x: Store::release\n",(int)pos);
00233   STORE_ASSERT(pos>0 && (pos & (BLOCK_SIZE-1))==0);
00234   // goto end of the block
00235   portable_off_t cur = pos, next;
00236   while (1)
00237   {
00238     // add new node to the free list
00239     Node *node = new Node;
00240     node->next = m_head;
00241     node->pos = cur;
00242 
00243     m_head = node;
00244     // goto the end of cur block
00245     if (portable_fseek(m_file,cur+BLOCK_SIZE-BLOCK_POINTER_SIZE,SEEK_SET)==-1)
00246     {
00247       fprintf(stderr,"Store::release: Error seeking to position %d: %s\n",
00248           (int)(cur+BLOCK_SIZE-BLOCK_POINTER_SIZE),strerror(errno));
00249       exit(1);
00250     }
00251     // read pointer to next block
00252     if (fread(&next,BLOCK_POINTER_SIZE,1,m_file)!=1)
00253     {
00254       fprintf(stderr,"Store::release: Error reading from store: %s\n",strerror(errno));
00255       exit(1);
00256     }
00257     if (next==0) break; // found end of list -> cur is last element
00258     STORE_ASSERT((next & (BLOCK_SIZE-1))==0);
00259     cur = next;
00260     //printf("%x: Store::release\n",(int)cur);
00261   }
00262 }
00263 
00264 void Store::seek(portable_off_t pos)
00265 {
00266   STORE_ASSERT(m_state==Reading);
00267   //printf("%x: Store::seek\n",(int)pos);
00268   if (portable_fseek(m_file,pos,SEEK_SET)==-1)
00269   {
00270     fprintf(stderr,"Store::seek: Error seeking to position %d: %s\n",
00271         (int)pos,strerror(errno));
00272     exit(1);
00273   }
00274   STORE_ASSERT((pos&(BLOCK_SIZE-1))==0);
00275 }
00276 
00277 int Store::read(char *buf,uint size)
00278 {
00279   STORE_ASSERT(m_state==Reading);
00280   //printf("%x: Store::read total=%d\n",(int)portable_ftell(m_file),size);
00281   do
00282   {
00283     portable_off_t curPos     = portable_ftell(m_file);
00284     int bytesInBlock = BLOCK_SIZE - BLOCK_POINTER_SIZE - (curPos & (BLOCK_SIZE-1));
00285     int bytesLeft    = bytesInBlock<(int)size ? (int)size-bytesInBlock : 0;
00286     int numBytes     = size - bytesLeft;
00287     //printf("  Store::read: pos=%x num=%d left=%d\n",(int)curPos,numBytes,bytesLeft);
00288 
00289     if (numBytes>0)
00290     {
00291       //printf("%x: Store::read: %d out of %d bytes\n",(int)portable_ftell(m_file),numBytes,size);
00292       if ((int)fread(buf,1,numBytes,m_file)!=numBytes)
00293       {
00294         fprintf(stderr,"Error reading from store: %s\n",strerror(errno));
00295         exit(1);
00296       }
00297       m_reads++;
00298     }
00299     if (bytesLeft>0)
00300     {
00301       portable_off_t newPos;
00302       // read offset of the next block
00303       STORE_ASSERT(((portable_ftell(m_file)+BLOCK_POINTER_SIZE)&(BLOCK_SIZE-1))==0);
00304       if (fread((char *)&newPos,BLOCK_POINTER_SIZE,1,m_file)!=1)
00305       {
00306         fprintf(stderr,"Error reading from store: %s\n",strerror(errno));
00307         exit(1);
00308       }
00309       //printf("%x: Store::read: continue in next block, %d bytes to go\n",(int)newPos,bytesLeft);
00310       //printf("  Store::read: next block=%x\n",(int)newPos);
00311       STORE_ASSERT(newPos!=0);
00312       STORE_ASSERT((newPos&(BLOCK_SIZE-1))==0);
00313       curPos = newPos;
00314       // move to next block
00315       if (portable_fseek(m_file,curPos,SEEK_SET)==-1)
00316       {
00317         fprintf(stderr,"Store::read: Error seeking to position %d: %s\n",
00318             (int)curPos,strerror(errno));
00319         exit(1);
00320       }
00321     }
00322 
00323     size-=numBytes;
00324     buf+=numBytes;
00325   }
00326   while (size>0);
00327   return size;
00328 }
00329 
00330 void Store::printFreeList()
00331 {
00332   printf("FreeList: ");
00333   portable_off_t pos = m_head->pos;
00334   while (pos)
00335   {
00336     printf("%x ",(int)pos);
00337     m_head = m_head->next;
00338   }
00339   printf("\n");
00340 }
00341 
00342 void Store::printStats()
00343 {
00344   printf("ObjStore: block size %d bytes, total size %ld blocks, wrote %d blocks, read %d blocks\n",
00345       BLOCK_SIZE,(long)(m_front/BLOCK_SIZE),m_reads,m_writes);
00346 }
00347 
00348 #ifdef  STORE_TEST
00349 
00350 int main()
00351 {
00352   printf("sizeof(portable_off_t)=%d\n",(int)sizeof(portable_off_t));
00353   Store s;
00354   if (s.open("test.db")==0)
00355   {
00356     const char *str1 = "This is a test message... ";
00357     const char *str2 = "Another message. ";
00358 
00359     int i,j;
00360     for (j=0;j<5;j++)
00361     {
00362       char buf[100];
00363 
00364       portable_off_t handle = s.alloc();
00365       for (i=0;i<1000000000;i++)
00366       {
00367         s.write(str1,strlen(str1)+1);
00368       }
00369       s.end();
00370       portable_off_t handle2 = s.alloc();
00371       for (i=0;i<10;i++)
00372       {
00373         s.write(str2,strlen(str2)+1);
00374       }
00375       s.end();
00376 
00377       s.seek(handle);
00378       for (i=0;i<3;i++)
00379       {
00380         s.read(buf,strlen(str1)+1);
00381         printf("i=%d Read: %s\n",i,buf);
00382       }
00383 
00384       s.release(handle);
00385 
00386       s.seek(handle2);
00387       for (i=0;i<3;i++)
00388       {
00389         s.read(buf,strlen(str2)+1);
00390         printf("i=%d Read: %s\n",i,buf);
00391       }
00392 
00393       s.release(handle2);
00394     }
00395 
00396     s.close();
00397   }
00398   else
00399   {
00400     printf("Open failed! %s\n",strerror(errno));
00401   }
00402 }
00403 
00404 #endif
00405 



Generated on Mon Mar 31 10:58:44 2008 by  doxygen 1.5.1