Fix for UBSan build
[platform/upstream/doxygen.git] / src / store.cpp
1 /******************************************************************************
2  *
3  * $Id:$
4  *
5  *
6  * Copyright (C) 1997-2012 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
50 Store::Store() 
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
61 Store::~Store()
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;
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;
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   }
94   m_front  = BLOCK_SIZE;
95   m_cur    = BLOCK_SIZE;
96   m_head   = 0;
97   m_state  = Reading;
98   return 0;
99 }
100
101 void Store::close()
102 {
103   if (m_file) fclose(m_file);
104   m_file=0;
105   m_state  = Init;
106 }
107      
108 portable_off_t Store::alloc()
109 {
110   STORE_ASSERT(m_state==Reading);
111   m_state=Writing;
112   portable_off_t pos;
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 {
156   STORE_ASSERT(m_state==Writing);
157   //printf("%x: Store::write\n",(int)portable_ftell(m_file));
158   do
159   {
160 #if USE_FTELL
161     portable_off_t curPos = portable_ftell(m_file);
162 #else
163     portable_off_t curPos = m_cur;
164 #endif
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));
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
183       STORE_ASSERT(((portable_ftell(m_file)+BLOCK_POINTER_SIZE)&(BLOCK_SIZE-1))==0);
184 #else
185       STORE_ASSERT(((m_cur+BLOCK_POINTER_SIZE)&(BLOCK_SIZE-1))==0);
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         }
197         m_cur+=BLOCK_POINTER_SIZE;
198 #if USE_FTELL
199         STORE_ASSERT(portable_ftell(m_file)==(curPos&~(BLOCK_SIZE-1))+BLOCK_SIZE);
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
211         STORE_ASSERT(portable_ftell(m_file)==m_front);
212 #else
213         STORE_ASSERT(m_cur==m_front);
214 #endif
215         // move front to the next of the block
216         m_front+=BLOCK_SIZE;
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
249 void Store::end()
250 {
251   STORE_ASSERT(m_state==Writing);
252 #if USE_FTELL
253   portable_off_t curPos = portable_ftell(m_file);
254 #else
255   portable_off_t curPos = m_cur;
256 #endif
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
261   int i;
262   for (i=0;i<bytesInBlock;i++)
263   {
264     fputc(0,m_file);
265   }
266   m_state=Reading;
267 }
268
269 void Store::release(portable_off_t pos)
270 {
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;
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
305 void Store::seek(portable_off_t pos)
306 {
307   STORE_ASSERT(m_state==Reading);
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 {
321   STORE_ASSERT(m_state==Reading);
322   //printf("%x: Store::read total=%d\n",(int)portable_ftell(m_file),size);
323   do
324   {
325 #if USE_FTELL
326     portable_off_t curPos = portable_ftell(m_file);
327 #else
328     portable_off_t curPos = m_cur;
329 #endif
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);
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
351       STORE_ASSERT(((portable_ftell(m_file)+BLOCK_POINTER_SIZE)&(BLOCK_SIZE-1))==0);
352 #else
353       STORE_ASSERT(((m_cur+BLOCK_POINTER_SIZE)&(BLOCK_SIZE-1))==0);
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
382 void Store::printFreeList()
383 {
384   printf("FreeList: ");
385   portable_off_t pos = m_head->pos;
386   while (pos)
387   {
388     printf("%x ",(int)pos);
389     m_head = m_head->next;
390   }
391   printf("\n");
392 }
393
394 void Store::printStats()
395 {
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);
398 }
399
400 void Store::dumpBlock(portable_off_t s,portable_off_t e)
401 {
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);
406   int i,j;
407   for (i=0;i<size;i+=16)
408   {
409     printf("%08x: ",(int)s+i);
410     for (j=i;j<QMIN(size,i+16);j++)
411     {
412       printf("%02x ",buf[i+j]);
413     }
414     printf("  ");
415     for (j=i;j<QMIN(size,i+16);j++)
416     {
417       printf("%c",(buf[i+j]>=32 && buf[i+j]<128)?buf[i+j]:'.');
418     }
419     printf("\n");
420   }
421   delete[] buf;
422   portable_fseek(m_file,m_cur,SEEK_SET);
423 }
424
425 #ifdef  STORE_TEST
426
427 int main()
428 {
429   printf("sizeof(portable_off_t)=%d\n",(int)sizeof(portable_off_t));
430   Store s;
431   if (s.open("test.db")==0)
432   {
433     const char *str1 = "This is a test message... ";
434     const char *str2 = "Another message. ";
435
436     int i,j;
437     for (j=0;j<5;j++)
438     {
439       char buf[100];
440
441       portable_off_t handle = s.alloc();
442       for (i=0;i<1000000000;i++)
443       {
444         s.write(str1,strlen(str1)+1);
445       }
446       s.end();
447       portable_off_t handle2 = s.alloc();
448       for (i=0;i<10;i++)
449       {
450         s.write(str2,strlen(str2)+1);
451       }
452       s.end();
453
454       s.seek(handle);
455       for (i=0;i<3;i++)
456       {
457         s.read(buf,strlen(str1)+1);
458         printf("i=%d Read: %s\n",i,buf);
459       }
460
461       s.release(handle);
462
463       s.seek(handle2);
464       for (i=0;i<3;i++)
465       {
466         s.read(buf,strlen(str2)+1);
467         printf("i=%d Read: %s\n",i,buf);
468       }
469
470       s.release(handle2);
471     }
472
473     s.close();
474   }
475   else
476   {
477     printf("Open failed! %s\n",strerror(errno));
478   }
479 }
480
481 #endif
482