Orb/Doxygen/src/store.cpp
changeset 0 42188c7ea2d9
child 4 468f4c8d3d5b
equal deleted inserted replaced
-1:000000000000 0:42188c7ea2d9
       
     1 /******************************************************************************
       
     2  *
       
     3  * 
       
     4  *
       
     5  *
       
     6  * Copyright (C) 1997-2008 by Dimitri van Heesch.
       
     7  *
       
     8  * Permission to use, copy, modify, and distribute this software and its
       
     9  * documentation under the terms of the GNU General Public License is hereby 
       
    10  * granted. No representations are made about the suitability of this software 
       
    11  * for any purpose. It is provided "as is" without express or implied warranty.
       
    12  * See the GNU General Public License for more details.
       
    13  *
       
    14  * Documents produced by Doxygen are derivative works derived from the
       
    15  * input used in their production; they are not affected by this license.
       
    16  *
       
    17  */
       
    18 
       
    19 #include "store.h"
       
    20 #include "portable.h"
       
    21 
       
    22 
       
    23 #include <stdio.h>
       
    24 #include <stdlib.h>
       
    25 #include <errno.h>
       
    26 #include <string.h>
       
    27 #include <assert.h>
       
    28 
       
    29 #define BLOCK_SIZE         512 // should be >8 and a multiple of 8
       
    30 #define BLOCK_POINTER_SIZE sizeof(portable_off_t)
       
    31 
       
    32 
       
    33 #define ASSERTS_ENABLED
       
    34 
       
    35 #ifdef ASSERTS_ENABLED
       
    36 #define STORE_ASSERT(x) assert(x)
       
    37 #else
       
    38 #define STORE_ASSERT(x)
       
    39 #endif
       
    40 
       
    41 //------------------------------------------------------------------------------------
       
    42 
       
    43 Store::Store() 
       
    44 {
       
    45   m_file       = 0;
       
    46   m_front      = 0;
       
    47   m_head       = 0;
       
    48   m_state      = Init;
       
    49   m_reads      = 0;
       
    50   m_writes     = 0;
       
    51 }
       
    52 
       
    53 Store::~Store()
       
    54 {
       
    55   if (m_file)   fclose(m_file);
       
    56 
       
    57   // clean up free list
       
    58   while (m_head)
       
    59   {
       
    60     Node *node = m_head;
       
    61     m_head = node->next;
       
    62     delete node;
       
    63   }
       
    64 }
       
    65 
       
    66 int Store::open(const char *name)
       
    67 {
       
    68   int i;
       
    69   STORE_ASSERT(m_state==Init);
       
    70   if (m_file) return 0; // already open
       
    71   m_file = fopen(name,"w+b");
       
    72   if (m_file==0) return -1;
       
    73 
       
    74   // first block serves as header, so offset=0 can be used as the end of the list.
       
    75   for (i=0;i<BLOCK_SIZE/8;i++)
       
    76   {
       
    77     fputc('D',m_file);
       
    78     fputc('O',m_file);
       
    79     fputc('X',m_file);
       
    80     fputc('Y',m_file);
       
    81     fputc('G',m_file);
       
    82     fputc('E',m_file);
       
    83     fputc('N',m_file);
       
    84     fputc(0,m_file);
       
    85   }
       
    86   m_front  = BLOCK_SIZE;
       
    87   m_head   = 0;
       
    88   m_state  = Reading;
       
    89   return 0;
       
    90 }
       
    91 
       
    92 void Store::close()
       
    93 {
       
    94   if (m_file) fclose(m_file);
       
    95   m_file=0;
       
    96   m_state  = Init;
       
    97 }
       
    98      
       
    99 portable_off_t Store::alloc()
       
   100 {
       
   101   STORE_ASSERT(m_state==Reading);
       
   102   m_state=Writing;
       
   103   portable_off_t pos;
       
   104   if (m_head==0) // allocate new block
       
   105   {
       
   106     //printf("alloc: new block\n");
       
   107     if (portable_fseek(m_file,0,SEEK_END)==-1) // go to end of the file
       
   108     {
       
   109       fprintf(stderr,"Store::alloc: Error seeking to end of file: %s\n",strerror(errno));
       
   110       exit(1);
       
   111     }
       
   112     pos = portable_ftell(m_file);
       
   113     STORE_ASSERT( (pos & (BLOCK_SIZE-1))==0 );
       
   114     m_front = pos + BLOCK_SIZE; // move front to end of this block
       
   115   }
       
   116   else // reuse freed block
       
   117   {
       
   118     //printf("alloc: reuse block: m_head=%d\n",(int)m_head);
       
   119     Node *node = m_head;
       
   120     pos = node->pos;
       
   121     // point head to next free item
       
   122     m_head = node->next;
       
   123     delete node;
       
   124     // move to start of the block
       
   125     if (portable_fseek(m_file,pos,SEEK_SET)==-1)
       
   126     {
       
   127       fprintf(stderr,"Store::alloc: Error seeking to position %d: %s\n",
       
   128           (int)pos,strerror(errno));
       
   129       exit(1);
       
   130     }
       
   131     STORE_ASSERT( (pos & (BLOCK_SIZE-1))==0 );
       
   132   }
       
   133   //printf("%x: Store::alloc\n",(int)pos);
       
   134   return pos;
       
   135 }
       
   136 
       
   137 int Store::write(const char *buf,uint size)
       
   138 {
       
   139   STORE_ASSERT(m_state==Writing);
       
   140   //printf("%x: Store::write\n",(int)portable_ftell(m_file));
       
   141   do
       
   142   {
       
   143     portable_off_t curPos     = portable_ftell(m_file);
       
   144     int bytesInBlock = BLOCK_SIZE - BLOCK_POINTER_SIZE - (curPos & (BLOCK_SIZE-1));
       
   145     int bytesLeft    = bytesInBlock<(int)size ? (int)size-bytesInBlock : 0;
       
   146     int numBytes     = size - bytesLeft;
       
   147     STORE_ASSERT(bytesInBlock>=0);
       
   148     STORE_ASSERT(numBytes<=(int)(BLOCK_SIZE-BLOCK_POINTER_SIZE));
       
   149     if (numBytes>0)
       
   150     {
       
   151       if ((int)fwrite(buf,1,numBytes,m_file)!=numBytes)
       
   152       {
       
   153         fprintf(stderr,"Error writing: %s\n",strerror(errno));
       
   154         exit(1);
       
   155       }
       
   156       m_writes++;
       
   157     }
       
   158     if (bytesLeft>0) // still more bytes to write
       
   159     {
       
   160       STORE_ASSERT(((portable_ftell(m_file)+BLOCK_POINTER_SIZE)&(BLOCK_SIZE-1))==0);
       
   161       // allocate new block
       
   162       if (m_head==0) // no free blocks to reuse
       
   163       {
       
   164         //printf("%x: Store::write: new: pos=%x\n",(int)m_front,(int)portable_ftell(m_file));
       
   165         // write pointer to next block
       
   166         if (fwrite(&m_front,BLOCK_POINTER_SIZE,1,m_file)!=1)
       
   167         {
       
   168           fprintf(stderr,"Error writing to store: %s\n",strerror(errno));
       
   169           exit(1);
       
   170         }
       
   171         STORE_ASSERT(portable_ftell(m_file)==(curPos&~(BLOCK_SIZE-1))+BLOCK_SIZE);
       
   172 
       
   173         // move to next block
       
   174         if (portable_fseek(m_file,0,SEEK_END)==-1) // go to end of the file
       
   175         {
       
   176           fprintf(stderr,"Store::alloc: Error seeking to end of file: %s\n",strerror(errno));
       
   177           exit(1);
       
   178         }
       
   179         STORE_ASSERT(portable_ftell(m_file)==m_front);
       
   180         // move front to the next of the block
       
   181         m_front+=BLOCK_SIZE;
       
   182       }
       
   183       else // reuse block from the free list
       
   184       {
       
   185         // write pointer to next block
       
   186         if (fwrite(&m_head->pos,BLOCK_POINTER_SIZE,1,m_file)!=1)
       
   187         {
       
   188           fprintf(stderr,"Error writing to store: %s\n",strerror(errno));
       
   189           exit(1);
       
   190         }
       
   191         Node *node = m_head;
       
   192         portable_off_t pos = node->pos;
       
   193         // point head to next free item
       
   194         m_head = node->next;
       
   195         delete node;
       
   196         // move to start of the block
       
   197         if (portable_fseek(m_file,pos,SEEK_SET)==-1)
       
   198         {
       
   199           fprintf(stderr,"Store::write: Error seeking to position %d: %s\n",
       
   200               (int)pos,strerror(errno));
       
   201           exit(1);
       
   202         }
       
   203         //printf("%x: Store::write: reuse\n",(int)pos);
       
   204       }
       
   205     }
       
   206     size-=numBytes;
       
   207     buf+=numBytes;
       
   208   }
       
   209   while (size>0);
       
   210   return size;
       
   211 }
       
   212 
       
   213 void Store::end()
       
   214 {
       
   215   STORE_ASSERT(m_state==Writing);
       
   216   portable_off_t curPos     = portable_ftell(m_file);
       
   217   int bytesInBlock = BLOCK_SIZE - (curPos & (BLOCK_SIZE-1));
       
   218   //printf("%x: Store::end erasing %x bytes\n",(int)curPos&~(BLOCK_SIZE-1),bytesInBlock);
       
   219   //printf("end: bytesInBlock=%x\n",bytesInBlock);
       
   220   // zero out rest of the block
       
   221   int i;
       
   222   for (i=0;i<bytesInBlock;i++)
       
   223   {
       
   224     fputc(0,m_file);
       
   225   }
       
   226   m_state=Reading;
       
   227 }
       
   228 
       
   229 void Store::release(portable_off_t pos)
       
   230 {
       
   231   STORE_ASSERT(m_state==Reading);
       
   232   //printf("%x: Store::release\n",(int)pos);
       
   233   STORE_ASSERT(pos>0 && (pos & (BLOCK_SIZE-1))==0);
       
   234   // goto end of the block
       
   235   portable_off_t cur = pos, next;
       
   236   while (1)
       
   237   {
       
   238     // add new node to the free list
       
   239     Node *node = new Node;
       
   240     node->next = m_head;
       
   241     node->pos = cur;
       
   242 
       
   243     m_head = node;
       
   244     // goto the end of cur block
       
   245     if (portable_fseek(m_file,cur+BLOCK_SIZE-BLOCK_POINTER_SIZE,SEEK_SET)==-1)
       
   246     {
       
   247       fprintf(stderr,"Store::release: Error seeking to position %d: %s\n",
       
   248           (int)(cur+BLOCK_SIZE-BLOCK_POINTER_SIZE),strerror(errno));
       
   249       exit(1);
       
   250     }
       
   251     // read pointer to next block
       
   252     if (fread(&next,BLOCK_POINTER_SIZE,1,m_file)!=1)
       
   253     {
       
   254       fprintf(stderr,"Store::release: Error reading from store: %s\n",strerror(errno));
       
   255       exit(1);
       
   256     }
       
   257     if (next==0) break; // found end of list -> cur is last element
       
   258     STORE_ASSERT((next & (BLOCK_SIZE-1))==0);
       
   259     cur = next;
       
   260     //printf("%x: Store::release\n",(int)cur);
       
   261   }
       
   262 }
       
   263 
       
   264 void Store::seek(portable_off_t pos)
       
   265 {
       
   266   STORE_ASSERT(m_state==Reading);
       
   267   //printf("%x: Store::seek\n",(int)pos);
       
   268   if (portable_fseek(m_file,pos,SEEK_SET)==-1)
       
   269   {
       
   270     fprintf(stderr,"Store::seek: Error seeking to position %d: %s\n",
       
   271         (int)pos,strerror(errno));
       
   272     exit(1);
       
   273   }
       
   274   STORE_ASSERT((pos&(BLOCK_SIZE-1))==0);
       
   275 }
       
   276 
       
   277 int Store::read(char *buf,uint size)
       
   278 {
       
   279   STORE_ASSERT(m_state==Reading);
       
   280   //printf("%x: Store::read total=%d\n",(int)portable_ftell(m_file),size);
       
   281   do
       
   282   {
       
   283     portable_off_t curPos     = portable_ftell(m_file);
       
   284     int bytesInBlock = BLOCK_SIZE - BLOCK_POINTER_SIZE - (curPos & (BLOCK_SIZE-1));
       
   285     int bytesLeft    = bytesInBlock<(int)size ? (int)size-bytesInBlock : 0;
       
   286     int numBytes     = size - bytesLeft;
       
   287     //printf("  Store::read: pos=%x num=%d left=%d\n",(int)curPos,numBytes,bytesLeft);
       
   288 
       
   289     if (numBytes>0)
       
   290     {
       
   291       //printf("%x: Store::read: %d out of %d bytes\n",(int)portable_ftell(m_file),numBytes,size);
       
   292       if ((int)fread(buf,1,numBytes,m_file)!=numBytes)
       
   293       {
       
   294         fprintf(stderr,"Error reading from store: %s\n",strerror(errno));
       
   295         exit(1);
       
   296       }
       
   297       m_reads++;
       
   298     }
       
   299     if (bytesLeft>0)
       
   300     {
       
   301       portable_off_t newPos;
       
   302       // read offset of the next block
       
   303       STORE_ASSERT(((portable_ftell(m_file)+BLOCK_POINTER_SIZE)&(BLOCK_SIZE-1))==0);
       
   304       if (fread((char *)&newPos,BLOCK_POINTER_SIZE,1,m_file)!=1)
       
   305       {
       
   306         fprintf(stderr,"Error reading from store: %s\n",strerror(errno));
       
   307         exit(1);
       
   308       }
       
   309       //printf("%x: Store::read: continue in next block, %d bytes to go\n",(int)newPos,bytesLeft);
       
   310       //printf("  Store::read: next block=%x\n",(int)newPos);
       
   311       STORE_ASSERT(newPos!=0);
       
   312       STORE_ASSERT((newPos&(BLOCK_SIZE-1))==0);
       
   313       curPos = newPos;
       
   314       // move to next block
       
   315       if (portable_fseek(m_file,curPos,SEEK_SET)==-1)
       
   316       {
       
   317         fprintf(stderr,"Store::read: Error seeking to position %d: %s\n",
       
   318             (int)curPos,strerror(errno));
       
   319         exit(1);
       
   320       }
       
   321     }
       
   322 
       
   323     size-=numBytes;
       
   324     buf+=numBytes;
       
   325   }
       
   326   while (size>0);
       
   327   return size;
       
   328 }
       
   329 
       
   330 void Store::printFreeList()
       
   331 {
       
   332   printf("FreeList: ");
       
   333   portable_off_t pos = m_head->pos;
       
   334   while (pos)
       
   335   {
       
   336     printf("%x ",(int)pos);
       
   337     m_head = m_head->next;
       
   338   }
       
   339   printf("\n");
       
   340 }
       
   341 
       
   342 void Store::printStats()
       
   343 {
       
   344   printf("ObjStore: block size %d bytes, total size %ld blocks, wrote %d blocks, read %d blocks\n",
       
   345       BLOCK_SIZE,(long)(m_front/BLOCK_SIZE),m_reads,m_writes);
       
   346 }
       
   347 
       
   348 #ifdef  STORE_TEST
       
   349 
       
   350 int main()
       
   351 {
       
   352   printf("sizeof(portable_off_t)=%d\n",(int)sizeof(portable_off_t));
       
   353   Store s;
       
   354   if (s.open("test.db")==0)
       
   355   {
       
   356     const char *str1 = "This is a test message... ";
       
   357     const char *str2 = "Another message. ";
       
   358 
       
   359     int i,j;
       
   360     for (j=0;j<5;j++)
       
   361     {
       
   362       char buf[100];
       
   363 
       
   364       portable_off_t handle = s.alloc();
       
   365       for (i=0;i<1000000000;i++)
       
   366       {
       
   367         s.write(str1,strlen(str1)+1);
       
   368       }
       
   369       s.end();
       
   370       portable_off_t handle2 = s.alloc();
       
   371       for (i=0;i<10;i++)
       
   372       {
       
   373         s.write(str2,strlen(str2)+1);
       
   374       }
       
   375       s.end();
       
   376 
       
   377       s.seek(handle);
       
   378       for (i=0;i<3;i++)
       
   379       {
       
   380         s.read(buf,strlen(str1)+1);
       
   381         printf("i=%d Read: %s\n",i,buf);
       
   382       }
       
   383 
       
   384       s.release(handle);
       
   385 
       
   386       s.seek(handle2);
       
   387       for (i=0;i<3;i++)
       
   388       {
       
   389         s.read(buf,strlen(str2)+1);
       
   390         printf("i=%d Read: %s\n",i,buf);
       
   391       }
       
   392 
       
   393       s.release(handle2);
       
   394     }
       
   395 
       
   396     s.close();
       
   397   }
       
   398   else
       
   399   {
       
   400     printf("Open failed! %s\n",strerror(errno));
       
   401   }
       
   402 }
       
   403 
       
   404 #endif
       
   405