1 /******************************************************************************
6 * Copyright (C) 1997-2012 by Dimitri van Heesch.
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.
14 * Documents produced by Doxygen are derivative works derived from the
15 * input used in their production; they are not affected by this license.
30 #define BLOCK_SIZE 512 // should be >8 and a power of 2
31 #define BLOCK_POINTER_SIZE sizeof(portable_off_t)
34 #define ASSERTS_ENABLED
36 #ifdef ASSERTS_ENABLED
37 #define STORE_ASSERT(x) assert(x)
39 #define STORE_ASSERT(x)
42 // Decide to use ftell or keep track of the current file pointer ourselves.
43 // Since valgrind shows that calling ftell has the unwanted side-effect of
44 // writing some uninitialized bytes (!) it might be better (and faster) to keep track
45 // of the current pointer ourselves.
48 //------------------------------------------------------------------------------------
63 if (m_file) fclose(m_file);
74 int Store::open(const char *name)
77 STORE_ASSERT(m_state==Init);
78 if (m_file) return 0; // already open
79 m_file = portable_fopen(name,"w+b");
80 if (m_file==0) return -1;
82 // first block serves as header, so offset=0 can be used as the end of the list.
83 for (i=0;i<BLOCK_SIZE/8;i++)
103 if (m_file) fclose(m_file);
108 portable_off_t Store::alloc()
110 STORE_ASSERT(m_state==Reading);
113 if (m_head==0) // allocate new block
115 //printf("alloc: new block, pos=%lld\n",(long long)m_front);
116 if (portable_fseek(m_file,0,SEEK_END)==-1) // go to end of the file
118 fprintf(stderr,"Store::alloc: Error seeking to end of file: %s\n",strerror(errno));
122 pos = portable_ftell(m_file);
123 STORE_ASSERT( (pos & (BLOCK_SIZE-1))==0 );
124 m_front = pos + BLOCK_SIZE; // move front to end of this block
128 STORE_ASSERT( (pos & (BLOCK_SIZE-1))==0 );
129 m_front = pos + BLOCK_SIZE;
132 else // reuse freed block
134 //printf("alloc: reuse block: pos=%lld\n",(long long)m_head->pos);
137 // point head to next free item
140 // move to start of the block
141 if (portable_fseek(m_file,pos,SEEK_SET)==-1)
143 fprintf(stderr,"Store::alloc: Error seeking to position %d: %s\n",
144 (int)pos,strerror(errno));
148 STORE_ASSERT( (pos & (BLOCK_SIZE-1))==0 );
150 //printf("%x: Store::alloc\n",(int)pos);
154 int Store::write(const char *buf,uint size)
156 STORE_ASSERT(m_state==Writing);
157 //printf("%x: Store::write\n",(int)portable_ftell(m_file));
161 portable_off_t curPos = portable_ftell(m_file);
163 portable_off_t curPos = m_cur;
165 int bytesInBlock = BLOCK_SIZE - BLOCK_POINTER_SIZE - (curPos & (BLOCK_SIZE-1));
166 int bytesLeft = bytesInBlock<(int)size ? (int)size-bytesInBlock : 0;
167 int numBytes = size - bytesLeft;
168 STORE_ASSERT(bytesInBlock>=0);
169 STORE_ASSERT(numBytes<=(int)(BLOCK_SIZE-BLOCK_POINTER_SIZE));
172 if ((int)fwrite(buf,1,numBytes,m_file)!=numBytes)
174 fprintf(stderr,"Error writing: %s\n",strerror(errno));
180 if (bytesLeft>0) // still more bytes to write
183 STORE_ASSERT(((portable_ftell(m_file)+BLOCK_POINTER_SIZE)&(BLOCK_SIZE-1))==0);
185 STORE_ASSERT(((m_cur+BLOCK_POINTER_SIZE)&(BLOCK_SIZE-1))==0);
187 // allocate new block
188 if (m_head==0) // no free blocks to reuse
190 //printf("%x: Store::write: new: pos=%x\n",(int)m_front,(int)portable_ftell(m_file));
191 // write pointer to next block
192 if (fwrite(&m_front,BLOCK_POINTER_SIZE,1,m_file)!=1)
194 fprintf(stderr,"Error writing to store: %s\n",strerror(errno));
197 m_cur+=BLOCK_POINTER_SIZE;
199 STORE_ASSERT(portable_ftell(m_file)==(curPos&~(BLOCK_SIZE-1))+BLOCK_SIZE);
201 STORE_ASSERT(m_cur==(curPos&~(BLOCK_SIZE-1))+BLOCK_SIZE);
203 // move to next block
204 if (portable_fseek(m_file,0,SEEK_END)==-1) // go to end of the file
206 fprintf(stderr,"Store::alloc: Error seeking to end of file: %s\n",strerror(errno));
211 STORE_ASSERT(portable_ftell(m_file)==m_front);
213 STORE_ASSERT(m_cur==m_front);
215 // move front to the next of the block
218 else // reuse block from the free list
220 // write pointer to next block
221 if (fwrite(&m_head->pos,BLOCK_POINTER_SIZE,1,m_file)!=1)
223 fprintf(stderr,"Error writing to store: %s\n",strerror(errno));
227 portable_off_t pos = node->pos;
228 // point head to next free item
231 // move to start of the block
232 if (portable_fseek(m_file,pos,SEEK_SET)==-1)
234 fprintf(stderr,"Store::write: Error seeking to position %d: %s\n",
235 (int)pos,strerror(errno));
239 //printf("%x: Store::write: reuse\n",(int)pos);
251 STORE_ASSERT(m_state==Writing);
253 portable_off_t curPos = portable_ftell(m_file);
255 portable_off_t curPos = m_cur;
257 int bytesInBlock = BLOCK_SIZE - (curPos & (BLOCK_SIZE-1));
258 //printf("%x: Store::end erasing %x bytes\n",(int)curPos&~(BLOCK_SIZE-1),bytesInBlock);
259 //printf("end: bytesInBlock=%x\n",bytesInBlock);
260 // zero out rest of the block
262 for (i=0;i<bytesInBlock;i++)
269 void Store::release(portable_off_t pos)
271 STORE_ASSERT(m_state==Reading);
272 //printf("release: block pos=%lld\n",(long long)pos);
273 STORE_ASSERT(pos>0 && (pos & (BLOCK_SIZE-1))==0);
274 // goto end of the block
275 portable_off_t cur = pos, next;
278 // add new node to the free list
279 Node *node = new Node;
284 // goto the end of cur block
285 if (portable_fseek(m_file,cur+BLOCK_SIZE-BLOCK_POINTER_SIZE,SEEK_SET)==-1)
287 fprintf(stderr,"Store::release: Error seeking to position %d: %s\n",
288 (int)(cur+BLOCK_SIZE-BLOCK_POINTER_SIZE),strerror(errno));
291 // read pointer to next block
292 if (fread(&next,BLOCK_POINTER_SIZE,1,m_file)!=1)
294 fprintf(stderr,"Store::release: Error reading from store: %s\n",strerror(errno));
297 m_cur = cur+BLOCK_SIZE;
298 if (next==0) break; // found end of list -> cur is last element
299 STORE_ASSERT((next & (BLOCK_SIZE-1))==0);
301 //printf("%x: Store::release\n",(int)cur);
305 void Store::seek(portable_off_t pos)
307 STORE_ASSERT(m_state==Reading);
308 //printf("%x: Store::seek\n",(int)pos);
309 if (portable_fseek(m_file,pos,SEEK_SET)==-1)
311 fprintf(stderr,"Store::seek: Error seeking to position %d: %s\n",
312 (int)pos,strerror(errno));
316 STORE_ASSERT((pos&(BLOCK_SIZE-1))==0);
319 int Store::read(char *buf,uint size)
321 STORE_ASSERT(m_state==Reading);
322 //printf("%x: Store::read total=%d\n",(int)portable_ftell(m_file),size);
326 portable_off_t curPos = portable_ftell(m_file);
328 portable_off_t curPos = m_cur;
330 int bytesInBlock = BLOCK_SIZE - BLOCK_POINTER_SIZE - (curPos & (BLOCK_SIZE-1));
331 int bytesLeft = bytesInBlock<(int)size ? (int)size-bytesInBlock : 0;
332 int numBytes = size - bytesLeft;
333 //printf(" Store::read: pos=%x num=%d left=%d\n",(int)curPos,numBytes,bytesLeft);
337 //printf("%x: Store::read: %d out of %d bytes\n",(int)portable_ftell(m_file),numBytes,size);
338 if ((int)fread(buf,1,numBytes,m_file)!=numBytes)
340 fprintf(stderr,"Error reading from store: %s\n",strerror(errno));
348 portable_off_t newPos;
349 // read offset of the next block
351 STORE_ASSERT(((portable_ftell(m_file)+BLOCK_POINTER_SIZE)&(BLOCK_SIZE-1))==0);
353 STORE_ASSERT(((m_cur+BLOCK_POINTER_SIZE)&(BLOCK_SIZE-1))==0);
355 if (fread((char *)&newPos,BLOCK_POINTER_SIZE,1,m_file)!=1)
357 fprintf(stderr,"Error reading from store: %s\n",strerror(errno));
360 //printf("%x: Store::read: continue in next block, %d bytes to go\n",(int)newPos,bytesLeft);
361 //printf(" Store::read: next block=%x\n",(int)newPos);
362 STORE_ASSERT(newPos!=0);
363 STORE_ASSERT((newPos&(BLOCK_SIZE-1))==0);
365 // move to next block
366 if (portable_fseek(m_file,curPos,SEEK_SET)==-1)
368 fprintf(stderr,"Store::read: Error seeking to position %d: %s\n",
369 (int)curPos,strerror(errno));
382 void Store::printFreeList()
384 printf("FreeList: ");
385 portable_off_t pos = m_head->pos;
388 printf("%x ",(int)pos);
389 m_head = m_head->next;
394 void Store::printStats()
396 printf("ObjStore: block size %d bytes, total size %ld blocks, wrote %d blocks, read %d blocks\n",
397 BLOCK_SIZE,(long)(m_front/BLOCK_SIZE),m_reads,m_writes);
400 void Store::dumpBlock(portable_off_t s,portable_off_t e)
402 portable_fseek(m_file,s,SEEK_SET);
403 int size = (int)(e-s);
404 uchar *buf = new uchar[size];
405 (void)fread(buf,size,1,m_file);
407 for (i=0;i<size;i+=16)
409 printf("%08x: ",(int)s+i);
410 for (j=i;j<QMIN(size,i+16);j++)
412 printf("%02x ",buf[i+j]);
415 for (j=i;j<QMIN(size,i+16);j++)
417 printf("%c",(buf[i+j]>=32 && buf[i+j]<128)?buf[i+j]:'.');
422 portable_fseek(m_file,m_cur,SEEK_SET);
429 printf("sizeof(portable_off_t)=%d\n",(int)sizeof(portable_off_t));
431 if (s.open("test.db")==0)
433 const char *str1 = "This is a test message... ";
434 const char *str2 = "Another message. ";
441 portable_off_t handle = s.alloc();
442 for (i=0;i<1000000000;i++)
444 s.write(str1,strlen(str1)+1);
447 portable_off_t handle2 = s.alloc();
450 s.write(str2,strlen(str2)+1);
457 s.read(buf,strlen(str1)+1);
458 printf("i=%d Read: %s\n",i,buf);
466 s.read(buf,strlen(str2)+1);
467 printf("i=%d Read: %s\n",i,buf);
477 printf("Open failed! %s\n",strerror(errno));