2
** This program and library is free software; you can redistribute it and/or
3
** modify it under the terms of the GNU (Library) General Public License
4
** as published by the Free Software Foundation; either version 2
5
** of the License, or any later version.
7
** This program is distributed in the hope that it will be useful,
8
** but WITHOUT ANY WARRANTY; without even the implied warranty of
9
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
** GNU (Library) General Public License for more details.
12
** You should have received a copy of the GNU (Library) General Public License
13
** along with this program; if not, write to the Free Software
14
** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15
**-----------------------------------------------------------------
17
** Virtual Array Code.
18
** 11/2001 jmruiz - The intention of this routines is storing and reading
19
** elemnts of arrays of long numbers avoiding the
20
** allocation in memory of the total array. In other words,
21
** if we need to read only 10 elements of the array, we
22
** will must try to make the minimal I/O memory and disk
25
** To do that, the data is stored in aligned pages in disk
26
** Also, a simple cache system is used to speed I/O file
29
** The virtual array is extensible. In other words, you can
30
** add elements whenever you want
34
** ARRAY *ARRAY_Create(FILE *fp)
35
** Creates a virtual array. Returns the handle of the array
37
** ARRAY *ARRAY_Open(FILE *fp, unsigned long root_page)
38
** Opens an existent Virtual Array. root_page is de value returned by
39
** Array_Close. Returns de handle of the array.
41
** unsigned long ARRAY_Close(ARRAY *arr)
42
** Closes and frees memory. arr is the value returned by ARRAY_Create or
43
** ARRAY_Open. Returns the root page of the array. This value must be
45
** int ARRAY_Put(ARRAY *arr, int index, unsigned long value)
46
** Writes the array element arr[index]=value to the virtual array
48
** unsigned long ARRAY_Get(ARRAY *arr, int index)
49
** Reads the array element index. Returns de value arr[index]
63
/* A ARRAY page size */
64
#define ARRAY_PageSize 4096
68
/* Round to ARRAY_PageSize */
69
#define ARRAY_RoundPageSize(n) (((n) + ARRAY_PageSize - 1) & (~(ARRAY_PageSize - 1)))
71
#define ARRAY_PageHeaderSize (1 * SizeInt32)
73
#define ARRAY_PageData(pg) ((pg)->data + ARRAY_PageHeaderSize)
74
#define ARRAY_Data(pg,i) (ARRAY_PageData((pg)) + (i) * SizeInt32)
76
#define ARRAY_SetNextPage(pg,num) ( *(int *)((pg)->data + 0 * SizeInt32) = PACKLONG(num))
78
#define ARRAY_GetNextPage(pg,num) ( (num) = UNPACKLONG(*(int *)((pg)->data + 0 * SizeInt32)))
81
int ARRAY_WritePageToDisk(FILE *fp, ARRAY_Page *pg)
83
ARRAY_SetNextPage(pg,pg->next);
84
fseek(fp,pg->page_number * ARRAY_PageSize,SEEK_SET);
85
return fwrite(pg->data,ARRAY_PageSize,1,fp);
88
int ARRAY_WritePage(ARRAY *b, ARRAY_Page *pg)
90
int hash = pg->page_number % ARRAY_CACHE_SIZE;
93
if((tmp = b->cache[hash]))
97
if(tmp->page_number == pg->page_number)
101
tmp = tmp->next_cache;
104
pg->next_cache = b->cache[hash];
109
int ARRAY_FlushCache(ARRAY *b)
112
ARRAY_Page *tmp, *next;
113
for(i = 0; i < ARRAY_CACHE_SIZE; i++)
115
if((tmp = b->cache[i]))
122
printf("DEBUG Error in FlushCache: Page in use\n");
126
next = tmp->next_cache;
129
ARRAY_WritePageToDisk(b->fp, tmp);
132
if(tmp != b->cache[i])
137
b->cache[i]->next_cache = NULL;
143
int ARRAY_CleanCache(ARRAY *b)
146
ARRAY_Page *tmp,*next;
147
for(i = 0; i < ARRAY_CACHE_SIZE; i++)
149
if((tmp = b->cache[i]))
153
next = tmp->next_cache;
163
ARRAY_Page *ARRAY_ReadPageFromDisk(FILE *fp, unsigned long page_number)
165
ARRAY_Page *pg = (ARRAY_Page *)emalloc(sizeof(ARRAY_Page) + ARRAY_PageSize);
167
fseek(fp,page_number * ARRAY_PageSize,SEEK_SET);
168
fread(pg->data,ARRAY_PageSize, 1, fp);
170
ARRAY_GetNextPage(pg,pg->next);
172
pg->page_number = page_number;
177
ARRAY_Page *ARRAY_ReadPage(ARRAY *b, unsigned long page_number)
179
int hash = page_number % ARRAY_CACHE_SIZE;
181
if((tmp = b->cache[hash]))
185
if(tmp->page_number == page_number)
189
tmp = tmp->next_cache;
193
tmp = ARRAY_ReadPageFromDisk(b->fp, page_number);
196
tmp->next_cache = b->cache[hash];
197
b->cache[hash] = tmp;
201
ARRAY_Page *ARRAY_NewPage(ARRAY *b)
207
int size = ARRAY_PageSize;
209
/* Get file pointer */
210
if(fseek(fp,0,SEEK_END) !=0)
211
progerrno("Failed to seek to eof: ");
214
/* Round up file pointer */
215
offset = ARRAY_RoundPageSize(offset);
217
/* Set new file pointer - data will be aligned */
218
if(fseek(fp,offset, SEEK_SET)!=0 || offset != ftell(fp))
219
progerrno("Failed during seek: ");
222
pg = (ARRAY_Page *)emalloc(sizeof(ARRAY_Page) + size);
223
memset(pg,0,sizeof(ARRAY_Page) + size);
224
/* Reserve space in file */
225
if(fwrite(pg->data,1,size,fp)!=size || ((long)size + offset) != ftell(fp))
226
progerrno("Failed to write ARRAY_page: ");
230
pg->page_number = offset/ARRAY_PageSize;
235
hash = pg->page_number % ARRAY_CACHE_SIZE;
236
pg->next_cache = b->cache[hash];
241
void ARRAY_FreePage(ARRAY *b, ARRAY_Page *pg)
243
int hash = pg->page_number % ARRAY_CACHE_SIZE;
246
tmp = b->cache[hash];
249
if(!(tmp = b->cache[hash]))
251
/* This should never happen!!!! */
252
printf("Error in FreePage\n");
259
if (tmp->page_number != pg->page_number)
260
tmp = tmp->next_cache;
269
ARRAY *ARRAY_New(FILE *fp, unsigned int size)
273
b = (ARRAY *) emalloc(sizeof(ARRAY));
276
for(i = 0; i < ARRAY_CACHE_SIZE; i++)
282
ARRAY *ARRAY_Create(FILE *fp)
286
int size = ARRAY_PageSize;
288
b = ARRAY_New(fp , size);
289
root = ARRAY_NewPage(b);
291
b->root_page = root->page_number;
293
ARRAY_WritePage(b, root);
294
ARRAY_FreePage(b, root);
300
ARRAY *ARRAY_Open(FILE *fp, unsigned long root_page)
303
int size = ARRAY_PageSize;
305
b = ARRAY_New(fp , size);
307
b->root_page = root_page;
312
unsigned long ARRAY_Close(ARRAY *bt)
314
unsigned long root_page = bt->root_page;
315
ARRAY_FlushCache(bt);
316
ARRAY_CleanCache(bt);
322
int ARRAY_Put(ARRAY *b, int index, unsigned long value)
324
unsigned long next_page;
325
ARRAY_Page *root_page, *tmp = NULL, *prev;
326
int i, hash, page_reads, page_index;
328
page_reads = index / ((ARRAY_PageSize - ARRAY_PageHeaderSize) / SizeInt32);
329
hash = page_reads % ((ARRAY_PageSize - ARRAY_PageHeaderSize) / SizeInt32);
330
page_reads /= ((ARRAY_PageSize - ARRAY_PageHeaderSize) / SizeInt32);
331
page_index = index % ((ARRAY_PageSize - ARRAY_PageHeaderSize) / SizeInt32);
333
root_page = ARRAY_ReadPage(b, b->root_page);
334
next_page = UNPACKLONG(*(unsigned long *)ARRAY_Data(root_page, hash));
337
for(i = 0; i <= page_reads; i++)
341
tmp = ARRAY_NewPage(b);
342
ARRAY_WritePage(b,tmp);
345
*(unsigned long *)ARRAY_Data(root_page,hash) = PACKLONG(tmp->page_number);
346
ARRAY_WritePage(b,root_page);
350
prev->next = tmp->page_number;
351
ARRAY_WritePage(b,prev);
356
tmp = ARRAY_ReadPage(b, next_page);
359
ARRAY_FreePage(b,prev);
361
next_page = tmp->next;
363
*(unsigned long *)ARRAY_Data(tmp,page_index) = PACKLONG(value);
364
ARRAY_WritePage(b,tmp);
365
ARRAY_FreePage(b,tmp);
366
ARRAY_FreePage(b,root_page);
372
unsigned long ARRAY_Get(ARRAY *b, int index)
374
unsigned long next_page, value;
375
ARRAY_Page *root_page, *tmp;
376
int i, hash, page_reads, page_index;
378
page_reads = index / ((ARRAY_PageSize - ARRAY_PageHeaderSize) / SizeInt32);
379
hash = page_reads % ((ARRAY_PageSize - ARRAY_PageHeaderSize) / SizeInt32);
380
page_reads /= ((ARRAY_PageSize - ARRAY_PageHeaderSize) / SizeInt32);
381
page_index = index % ((ARRAY_PageSize - ARRAY_PageHeaderSize) / SizeInt32);
383
root_page = ARRAY_ReadPage(b, b->root_page);
384
next_page = UNPACKLONG(*(unsigned long *)ARRAY_Data(root_page, hash));
387
for(i = 0; i <= page_reads; i++)
390
ARRAY_FreePage(b, tmp);
393
ARRAY_FreePage(b,root_page);
398
tmp = ARRAY_ReadPage(b, next_page);
400
next_page = tmp->next;
402
value = UNPACKLONG(*(unsigned long *)ARRAY_Data(tmp,page_index));
403
ARRAY_FreePage(b,tmp);
404
ARRAY_FreePage(b,root_page);
415
#define N_TEST 50000000
417
#define F_READ_BINARY "rb"
418
#define F_WRITE_BINARY "wb"
419
#define F_READWRITE_BINARY "rb+"
421
#define F_READ_TEXT "r"
422
#define F_WRITE_TEXT "w"
423
#define F_READWRITE_TEXT "r+"
430
static unsigned long nums[N_TEST];
431
unsigned long root_page;
436
fp = fopen("kkkkk",F_WRITE_BINARY);
438
fp = fopen("kkkkk",F_READWRITE_BINARY);
440
fwrite("aaa",1,3,fp);
442
printf("\n\nIndexing\n\n");
444
bt = ARRAY_Create(fp);
445
for(i=0;i<N_TEST;i++)
449
ARRAY_Put(bt,i,nums[i]);
450
if(nums[i]!= ARRAY_Get(bt,i))
451
printf("\n\nmal %d\n\n",i);
454
ARRAY_FlushCache(bt);
459
root_page = ARRAY_Close(bt);
462
printf("\n\nUnfreed %d\n\n",num);
463
printf("\n\nSearching\n\n");
465
fp = fopen("kkkkk",F_READ_BINARY);
466
bt = ARRAY_Open(fp, root_page);
468
for(i=0;i<N_TEST;i++)
470
if(nums[i] != ARRAY_Get(bt,i))
471
printf("\n\nmal %d\n\n",i);
476
root_page = ARRAY_Close(bt);
479
printf("\n\nUnfreed %d\n\n",num);