Root/
Source at commit 1322 created 12 years 8 months ago. By meklort, Add doxygen to utils folder | |
---|---|
1 | /******************************************************************************␊ |
2 | *␊ |
3 | * $Id:$␊ |
4 | *␊ |
5 | *␊ |
6 | * Copyright (C) 1997-2011 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 = portable_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 | ␊ |
406 |