store.cpp
Go to the documentation of this file.
1 /******************************************************************************
2  *
3  *
4  *
5  *
6  * Copyright (C) 1997-2015 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 #include <ctype.h>
29 
30 #define BLOCK_SIZE 512 // should be >8 and a power of 2
31 #define BLOCK_POINTER_SIZE sizeof(portable_off_t)
32 
33 
34 #define ASSERTS_ENABLED
35 
36 #ifdef ASSERTS_ENABLED
37 #define STORE_ASSERT(x) assert(x)
38 #else
39 #define STORE_ASSERT(x)
40 #endif
41 
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.
46 #define USE_FTELL 0
47 
48 //------------------------------------------------------------------------------------
49 
51 {
52  m_file = 0;
53  m_front = 0;
54  m_cur = 0;
55  m_head = 0;
56  m_state = Init;
57  m_reads = 0;
58  m_writes = 0;
59 }
60 
62 {
63  if (m_file) fclose(m_file);
64 
65  // clean up free list
66  while (m_head)
67  {
68  Node *node = m_head;
69  m_head = node->next;
70  delete node;
71  }
72 }
73 
74 int Store::open(const char *name)
75 {
76  int i;
78  if (m_file) return 0; // already open
79  m_file = portable_fopen(name,"w+b");
80  if (m_file==0) return -1;
81 
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++)
84  {
85  fputc('D',m_file);
86  fputc('O',m_file);
87  fputc('X',m_file);
88  fputc('Y',m_file);
89  fputc('G',m_file);
90  fputc('E',m_file);
91  fputc('N',m_file);
92  fputc(0,m_file);
93  }
95  m_cur = BLOCK_SIZE;
96  m_head = 0;
97  m_state = Reading;
98  return 0;
99 }
100 
102 {
103  if (m_file) fclose(m_file);
104  m_file=0;
105  m_state = Init;
106 }
107 
109 {
113  if (m_head==0) // allocate new block
114  {
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
117  {
118  fprintf(stderr,"Store::alloc: Error seeking to end of file: %s\n",strerror(errno));
119  exit(1);
120  }
121 #if USE_FTELL
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
125 #else
126  m_cur = m_front;
127  pos = m_cur;
128  STORE_ASSERT( (pos & (BLOCK_SIZE-1))==0 );
129  m_front = pos + BLOCK_SIZE;
130 #endif
131  }
132  else // reuse freed block
133  {
134  //printf("alloc: reuse block: pos=%lld\n",(long long)m_head->pos);
135  Node *node = m_head;
136  pos = node->pos;
137  // point head to next free item
138  m_head = node->next;
139  delete node;
140  // move to start of the block
141  if (portable_fseek(m_file,pos,SEEK_SET)==-1)
142  {
143  fprintf(stderr,"Store::alloc: Error seeking to position %d: %s\n",
144  (int)pos,strerror(errno));
145  exit(1);
146  }
147  m_cur = pos;
148  STORE_ASSERT( (pos & (BLOCK_SIZE-1))==0 );
149  }
150  //printf("%x: Store::alloc\n",(int)pos);
151  return pos;
152 }
153 
154 int Store::write(const char *buf,uint size)
155 {
157  //printf("%x: Store::write\n",(int)portable_ftell(m_file));
158  do
159  {
160 #if USE_FTELL
162 #else
163  portable_off_t curPos = m_cur;
164 #endif
165  int bytesInBlock = (int)(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));
170  if (numBytes>0)
171  {
172  if ((int)fwrite(buf,1,numBytes,m_file)!=numBytes)
173  {
174  fprintf(stderr,"Error writing: %s\n",strerror(errno));
175  exit(1);
176  }
177  m_cur+=numBytes;
178  m_writes++;
179  }
180  if (bytesLeft>0) // still more bytes to write
181  {
182 #if USE_FTELL
184 #else
186 #endif
187  // allocate new block
188  if (m_head==0) // no free blocks to reuse
189  {
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)
193  {
194  fprintf(stderr,"Error writing to store: %s\n",strerror(errno));
195  exit(1);
196  }
198 #if USE_FTELL
200 #else
201  STORE_ASSERT(m_cur==(curPos&~(BLOCK_SIZE-1))+BLOCK_SIZE);
202 #endif
203  // move to next block
204  if (portable_fseek(m_file,0,SEEK_END)==-1) // go to end of the file
205  {
206  fprintf(stderr,"Store::alloc: Error seeking to end of file: %s\n",strerror(errno));
207  exit(1);
208  }
209  m_cur=m_front;
210 #if USE_FTELL
212 #else
214 #endif
215  // move front to the next of the block
217  }
218  else // reuse block from the free list
219  {
220  // write pointer to next block
221  if (fwrite(&m_head->pos,BLOCK_POINTER_SIZE,1,m_file)!=1)
222  {
223  fprintf(stderr,"Error writing to store: %s\n",strerror(errno));
224  exit(1);
225  }
226  Node *node = m_head;
227  portable_off_t pos = node->pos;
228  // point head to next free item
229  m_head = node->next;
230  delete node;
231  // move to start of the block
232  if (portable_fseek(m_file,pos,SEEK_SET)==-1)
233  {
234  fprintf(stderr,"Store::write: Error seeking to position %d: %s\n",
235  (int)pos,strerror(errno));
236  exit(1);
237  }
238  m_cur = pos;
239  //printf("%x: Store::write: reuse\n",(int)pos);
240  }
241  }
242  size-=numBytes;
243  buf+=numBytes;
244  }
245  while (size>0);
246  return size;
247 }
248 
250 {
252 #if USE_FTELL
254 #else
255  portable_off_t curPos = m_cur;
256 #endif
257  int bytesInBlock = (int)(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
261  int i;
262  for (i=0;i<bytesInBlock;i++)
263  {
264  fputc(0,m_file);
265  }
267 }
268 
270 {
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;
276  while (1)
277  {
278  // add new node to the free list
279  Node *node = new Node;
280  node->next = m_head;
281  node->pos = cur;
282 
283  m_head = node;
284  // goto the end of cur block
285  if (portable_fseek(m_file,cur+BLOCK_SIZE-BLOCK_POINTER_SIZE,SEEK_SET)==-1)
286  {
287  fprintf(stderr,"Store::release: Error seeking to position %d: %s\n",
288  (int)(cur+BLOCK_SIZE-BLOCK_POINTER_SIZE),strerror(errno));
289  exit(1);
290  }
291  // read pointer to next block
292  if (fread(&next,BLOCK_POINTER_SIZE,1,m_file)!=1)
293  {
294  fprintf(stderr,"Store::release: Error reading from store: %s\n",strerror(errno));
295  exit(1);
296  }
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);
300  cur = next;
301  //printf("%x: Store::release\n",(int)cur);
302  }
303 }
304 
306 {
308  //printf("%x: Store::seek\n",(int)pos);
309  if (portable_fseek(m_file,pos,SEEK_SET)==-1)
310  {
311  fprintf(stderr,"Store::seek: Error seeking to position %d: %s\n",
312  (int)pos,strerror(errno));
313  exit(1);
314  }
315  m_cur = pos;
316  STORE_ASSERT((pos&(BLOCK_SIZE-1))==0);
317 }
318 
319 int Store::read(char *buf,uint size)
320 {
322  //printf("%x: Store::read total=%d\n",(int)portable_ftell(m_file),size);
323  do
324  {
325 #if USE_FTELL
327 #else
328  portable_off_t curPos = m_cur;
329 #endif
330  int bytesInBlock = (int)(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);
334 
335  if (numBytes>0)
336  {
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)
339  {
340  fprintf(stderr,"Error reading from store: %s\n",strerror(errno));
341  exit(1);
342  }
343  m_cur+=numBytes;
344  m_reads++;
345  }
346  if (bytesLeft>0)
347  {
348  portable_off_t newPos;
349  // read offset of the next block
350 #if USE_FTELL
352 #else
354 #endif
355  if (fread((char *)&newPos,BLOCK_POINTER_SIZE,1,m_file)!=1)
356  {
357  fprintf(stderr,"Error reading from store: %s\n",strerror(errno));
358  exit(1);
359  }
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);
364  curPos = newPos;
365  // move to next block
366  if (portable_fseek(m_file,curPos,SEEK_SET)==-1)
367  {
368  fprintf(stderr,"Store::read: Error seeking to position %d: %s\n",
369  (int)curPos,strerror(errno));
370  exit(1);
371  }
372  m_cur = curPos;
373  }
374 
375  size-=numBytes;
376  buf+=numBytes;
377  }
378  while (size>0);
379  return size;
380 }
381 
383 {
384  printf("FreeList: ");
385  while (m_head)
386  {
388  printf("%x ",(int)pos);
389  m_head = m_head->next;
390  }
391  printf("\n");
392 }
393 
395 {
396  printf("ObjStore: block size %d bytes, total size %ld blocks, wrote %d blocks, read %d blocks\n",
398 }
399 
401 {
402  portable_fseek(m_file,s,SEEK_SET);
403  int size = (int)(e-s);
404  uchar *buf = new uchar[size];
405  if (fread(buf,size,1,m_file)==(size_t)size)
406  {
407  int i,j;
408  for (i=0;i<size;i+=16)
409  {
410  printf("%08x: ",(int)s+i);
411  for (j=i;j<QMIN(size,i+16);j++)
412  {
413  printf("%02x ",buf[i+j]);
414  }
415  printf(" ");
416  for (j=i;j<QMIN(size,i+16);j++)
417  {
418  printf("%c",(buf[i+j]>=32 && buf[i+j]<128)?buf[i+j]:'.');
419  }
420  printf("\n");
421  }
422  }
423  delete[] buf;
424  portable_fseek(m_file,m_cur,SEEK_SET);
425 }
426 
427 #ifdef STORE_TEST
428 
429 int main()
430 {
431  printf("sizeof(portable_off_t)=%d\n",(int)sizeof(portable_off_t));
432  Store s;
433  if (s.open("test.db")==0)
434  {
435  const char *str1 = "This is a test message... ";
436  const char *str2 = "Another message. ";
437 
438  int i,j;
439  for (j=0;j<5;j++)
440  {
441  char buf[100];
442 
443  portable_off_t handle = s.alloc();
444  for (i=0;i<1000000000;i++)
445  {
446  s.write(str1,strlen(str1)+1);
447  }
448  s.end();
449  portable_off_t handle2 = s.alloc();
450  for (i=0;i<10;i++)
451  {
452  s.write(str2,strlen(str2)+1);
453  }
454  s.end();
455 
456  s.seek(handle);
457  for (i=0;i<3;i++)
458  {
459  s.read(buf,strlen(str1)+1);
460  printf("i=%d Read: %s\n",i,buf);
461  }
462 
463  s.release(handle);
464 
465  s.seek(handle2);
466  for (i=0;i<3;i++)
467  {
468  s.read(buf,strlen(str2)+1);
469  printf("i=%d Read: %s\n",i,buf);
470  }
471 
472  s.release(handle2);
473  }
474 
475  s.close();
476  }
477  else
478  {
479  printf("Open failed! %s\n",strerror(errno));
480  }
481 }
482 
483 #endif
484 
static QCString name
Definition: declinfo.cpp:673
portable_off_t alloc()
Definition: store.cpp:108
State m_state
Definition: store.h:118
int read(char *buf, uint size)
Definition: store.cpp:319
#define BLOCK_POINTER_SIZE
Definition: store.cpp:31
QMapNodeBase Node
Definition: qmap.cpp:41
off_t portable_off_t
Definition: portable.h:11
void end()
Definition: store.cpp:249
portable_off_t pos
Definition: store.h:110
void printFreeList()
Definition: store.cpp:382
int errno
Contains the last error code.
Definition: structcmd.h:53
unsigned char uchar
Definition: nybbler.cc:11
Store()
Definition: store.cpp:50
portable_off_t pos() const
Definition: store.h:97
#define QMIN(a, b)
Definition: qglobal.h:391
decltype(auto) constexpr size(T &&obj)
ADL-aware version of std::size.
Definition: StdUtils.h:92
const double e
portable_off_t m_cur
Definition: store.h:116
The Store is a file based memory manager.
Definition: store.h:52
struct Node * next
Definition: store.h:111
portable_off_t portable_fseek(FILE *f, portable_off_t offset, int whence)
Definition: portable.cpp:322
portable_off_t portable_ftell(FILE *f)
Definition: portable.cpp:333
void dumpBlock(portable_off_t start, portable_off_t end)
Definition: store.cpp:400
int m_reads
Definition: store.h:119
int write(const char *buf, uint size)
Definition: store.cpp:154
void seek(portable_off_t handle)
Definition: store.cpp:305
void release(portable_off_t handle)
Definition: store.cpp:269
Node * m_head
Definition: store.h:117
portable_off_t m_front
Definition: store.h:115
#define STORE_ASSERT(x)
Definition: store.cpp:37
FILE * m_file
Definition: store.h:114
int m_writes
Definition: store.h:120
~Store()
Definition: store.cpp:61
int open(const char *name)
Definition: store.cpp:74
unsigned uint
Definition: qglobal.h:351
FILE * portable_fopen(const char *fileName, const char *mode)
Definition: portable.cpp:344
static QCString * s
Definition: config.cpp:1042
Portable versions of functions that are platform dependent.
void printStats()
Definition: store.cpp:394
void close()
Definition: store.cpp:101
#define BLOCK_SIZE
Definition: store.cpp:30