Routino SVN Repository Browser

Check out the latest version of Routino: svn co http://routino.org/svn/trunk routino

ViewVC logotype

Annotation of /trunk/src/sorting.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 543 - (hide annotations) (download) (as text)
Sat Dec 18 19:12:03 2010 UTC (14 years, 3 months ago) by amb
File MIME type: text/x-csrc
File size: 16070 byte(s)
Handle the case where there is no data in the file.

1 amb 269 /***************************************
2 amb 543 $Header: /home/amb/CVS/routino/src/sorting.c,v 1.13 2010-12-18 19:12:03 amb Exp $
3 amb 269
4     Merge sort functions.
5    
6     Part of the Routino routing software.
7     ******************/ /******************
8 amb 449 This file Copyright 2009-2010 Andrew M. Bishop
9 amb 269
10     This program is free software: you can redistribute it and/or modify
11     it under the terms of the GNU Affero General Public License as published by
12     the Free Software Foundation, either version 3 of the License, or
13     (at your option) any later version.
14    
15     This program is distributed in the hope that it will be useful,
16     but WITHOUT ANY WARRANTY; without even the implied warranty of
17     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18     GNU Affero General Public License for more details.
19    
20     You should have received a copy of the GNU Affero General Public License
21     along with this program. If not, see <http://www.gnu.org/licenses/>.
22     ***************************************/
23    
24    
25     #include <stdio.h>
26     #include <stdlib.h>
27     #include <string.h>
28     #include <assert.h>
29    
30 amb 532 #include "types.h"
31    
32 amb 449 #include "files.h"
33 amb 532 #include "sorting.h"
34 amb 269
35    
36     /* Variables */
37    
38 amb 289 /*+ The command line '--tmpdir' option or its default value. +*/
39 amb 284 extern char *option_tmpdirname;
40 amb 269
41 amb 358 /*+ The amount of RAM to use for filesorting. +*/
42     extern size_t option_filesort_ramsize;
43 amb 269
44 amb 358
45 amb 269 /*++++++++++++++++++++++++++++++++++++++
46 amb 310 A function to sort the contents of a file of fixed length objects using a
47     limited amount of RAM.
48 amb 269
49     The data is sorted using a "Merge sort" http://en.wikipedia.org/wiki/Merge_sort
50     and in particular an "external sort" http://en.wikipedia.org/wiki/External_sorting.
51     The individual sort steps and the merge step both use a "Heap sort"
52     http://en.wikipedia.org/wiki/Heapsort. The combination of the two should work well
53     if the data is already partially sorted.
54    
55     int fd_in The file descriptor of the input file (opened for reading and at the beginning).
56    
57     int fd_out The file descriptor of the output file (opened for writing and empty).
58    
59     size_t itemsize The size of each item in the file that needs sorting.
60    
61     int (*compare)(const void*, const void*) The comparison function (identical to qsort if the
62     data to be sorted is an array of things not pointers).
63    
64 amb 274 int (*buildindex)(void *,index_t) If non-NULL then this function is called for each item, if it
65     returns 1 then it is written to the output file.
66 amb 269 ++++++++++++++++++++++++++++++++++++++*/
67    
68 amb 311 void filesort_fixed(int fd_in,int fd_out,size_t itemsize,int (*compare)(const void*,const void*),int (*buildindex)(void*,index_t))
69 amb 269 {
70     int *fds=NULL,*heap=NULL;
71     int nfiles=0,ndata=0;
72     index_t count=0,total=0;
73 amb 358 size_t nitems=option_filesort_ramsize/(itemsize+sizeof(void*));
74 amb 283 void *data=NULL,**datap=NULL;
75 amb 269 char *filename;
76     int i,more=1;
77    
78     /* Allocate the RAM buffer and other bits */
79    
80     data=malloc(nitems*itemsize);
81     datap=malloc(nitems*sizeof(void*));
82    
83 amb 284 filename=(char*)malloc(strlen(option_tmpdirname)+24);
84 amb 269
85     /* Loop around, fill the buffer, sort the data and write a temporary file */
86    
87     do
88     {
89     int fd,n=0;
90    
91     /* Read in the data and create pointers */
92    
93     for(i=0;i<nitems;i++)
94     {
95     datap[i]=data+i*itemsize;
96    
97     if(ReadFile(fd_in,datap[i],itemsize))
98     {
99     more=0;
100     break;
101     }
102    
103     total++;
104     }
105    
106     n=i;
107    
108 amb 543 /* No new data read in this time round */
109    
110 amb 269 if(n==0)
111     break;
112    
113 amb 543 /* Shortcut if there are no files (i.e. no data at all) */
114    
115     if(nfiles==0 && n==0)
116     goto tidy_and_exit;
117    
118 amb 269 /* Sort the data pointers using a heap sort */
119    
120 amb 503 filesort_heapsort(datap,n,compare);
121 amb 269
122     /* Shortcut if all read in and sorted at once */
123    
124 amb 310 if(nfiles==0 && !more)
125 amb 269 {
126     for(i=0;i<n;i++)
127     {
128 amb 274 if(!buildindex || buildindex(datap[i],count))
129     {
130     WriteFile(fd_out,datap[i],itemsize);
131     count++;
132     }
133 amb 269 }
134    
135 amb 283 goto tidy_and_exit;
136 amb 269 }
137    
138     /* Create a temporary file and write the result */
139    
140 amb 284 sprintf(filename,"%s/filesort.%d.tmp",option_tmpdirname,nfiles);
141 amb 269
142 amb 502 fd=OpenFileNew(filename);
143 amb 269
144     for(i=0;i<n;i++)
145     WriteFile(fd,datap[i],itemsize);
146    
147     CloseFile(fd);
148    
149     nfiles++;
150     }
151     while(more);
152    
153     /* Shortcut if only one file (unlucky for us there must have been exactly
154     nitems, lucky for us we still have the data in RAM) */
155    
156     if(nfiles==1)
157     {
158     for(i=0;i<nitems;i++)
159     {
160 amb 274 if(!buildindex || buildindex(datap[i],count))
161     {
162     WriteFile(fd_out,datap[i],itemsize);
163     count++;
164     }
165 amb 269 }
166    
167     DeleteFile(filename);
168    
169 amb 283 goto tidy_and_exit;
170 amb 269 }
171    
172     /* Check that number of files is less than file size */
173    
174     assert(nfiles<nitems);
175    
176     /* Open all of the temporary files */
177    
178     fds=(int*)malloc(nfiles*sizeof(int));
179    
180     for(i=0;i<nfiles;i++)
181     {
182 amb 284 sprintf(filename,"%s/filesort.%d.tmp",option_tmpdirname,i);
183 amb 269
184     fds[i]=ReOpenFile(filename);
185    
186     DeleteFile(filename);
187     }
188    
189     /* Perform an n-way merge using a binary heap */
190    
191     heap=(int*)malloc(nfiles*sizeof(int));
192    
193     /* Fill the heap to start with */
194    
195     for(i=0;i<nfiles;i++)
196     {
197     int index;
198    
199     datap[i]=data+i*itemsize;
200    
201     ReadFile(fds[i],datap[i],itemsize);
202    
203     heap[i]=i;
204    
205     index=i;
206    
207     /* Bubble up the new value */
208    
209     while(index>0 &&
210     compare(datap[heap[index]],datap[heap[(index-1)/2]])<0)
211     {
212     int newindex;
213     int temp;
214    
215     newindex=(index-1)/2;
216    
217     temp=heap[index];
218     heap[index]=heap[newindex];
219     heap[newindex]=temp;
220    
221     index=newindex;
222     }
223     }
224    
225     /* Repeatedly pull out the root of the heap and refill from the same file */
226    
227     ndata=nfiles;
228    
229     do
230     {
231     int index=0;
232    
233 amb 274 if(!buildindex || buildindex(datap[heap[0]],count))
234     {
235     WriteFile(fd_out,datap[heap[0]],itemsize);
236     count++;
237     }
238 amb 269
239     if(ReadFile(fds[heap[0]],datap[heap[0]],itemsize))
240     {
241     ndata--;
242     heap[0]=heap[ndata];
243     }
244    
245     /* Bubble down the new value */
246    
247     while((2*index+2)<ndata &&
248     (compare(datap[heap[index]],datap[heap[2*index+1]])>0 ||
249     compare(datap[heap[index]],datap[heap[2*index+2]])>0))
250     {
251     int newindex;
252     int temp;
253    
254     if(compare(datap[heap[2*index+1]],datap[heap[2*index+2]])<0)
255     newindex=2*index+1;
256     else
257     newindex=2*index+2;
258    
259     temp=heap[newindex];
260     heap[newindex]=heap[index];
261     heap[index]=temp;
262    
263     index=newindex;
264     }
265    
266     if((2*index+2)==ndata &&
267     compare(datap[heap[index]],datap[heap[2*index+1]])>0)
268     {
269     int newindex;
270     int temp;
271    
272     newindex=2*index+1;
273    
274     temp=heap[newindex];
275     heap[newindex]=heap[index];
276     heap[index]=temp;
277     }
278     }
279     while(ndata>0);
280    
281     /* Tidy up */
282    
283 amb 283 tidy_and_exit:
284 amb 269
285 amb 283 if(fds)
286     {
287     for(i=0;i<nfiles;i++)
288     CloseFile(fds[i]);
289     free(fds);
290     }
291    
292     if(heap)
293     free(heap);
294    
295 amb 269 free(data);
296     free(datap);
297     free(filename);
298     }
299    
300    
301     /*++++++++++++++++++++++++++++++++++++++
302 amb 310 A function to sort the contents of a file of variable length objects (each
303     preceded by its length in 2 bytes) using a limited amount of RAM.
304    
305     The data is sorted using a "Merge sort" http://en.wikipedia.org/wiki/Merge_sort
306     and in particular an "external sort" http://en.wikipedia.org/wiki/External_sorting.
307     The individual sort steps and the merge step both use a "Heap sort"
308     http://en.wikipedia.org/wiki/Heapsort. The combination of the two should work well
309     if the data is already partially sorted.
310    
311     int fd_in The file descriptor of the input file (opened for reading and at the beginning).
312    
313     int fd_out The file descriptor of the output file (opened for writing and empty).
314    
315     int (*compare)(const void*, const void*) The comparison function (identical to qsort if the
316     data to be sorted is an array of things not pointers).
317    
318     int (*buildindex)(void *,index_t) If non-NULL then this function is called for each item, if it
319     returns 1 then it is written to the output file.
320     ++++++++++++++++++++++++++++++++++++++*/
321    
322 amb 311 void filesort_vary(int fd_in,int fd_out,int (*compare)(const void*,const void*),int (*buildindex)(void*,index_t))
323 amb 310 {
324     int *fds=NULL,*heap=NULL;
325     int nfiles=0,ndata=0;
326     index_t count=0,total=0;
327 amb 311 FILESORT_VARINT nextitemsize,largestitemsize=0;
328 amb 310 void *data=NULL,**datap=NULL;
329     char *filename;
330     int i,more=1;
331    
332     /* Allocate the RAM buffer and other bits */
333    
334 amb 358 data=malloc(option_filesort_ramsize);
335 amb 310
336     filename=(char*)malloc(strlen(option_tmpdirname)+24);
337    
338     /* Loop around, fill the buffer, sort the data and write a temporary file */
339    
340 amb 311 if(ReadFile(fd_in,&nextitemsize,FILESORT_VARSIZE)) /* Always have the next item size known in advance */
341 amb 310 goto tidy_and_exit;
342    
343     do
344     {
345     int fd,n=0;
346 amb 311 size_t ramused=FILESORT_VARALIGN-FILESORT_VARSIZE;
347 amb 310
348 amb 358 datap=data+option_filesort_ramsize;
349 amb 310
350     /* Read in the data and create pointers */
351    
352 amb 311 while((ramused+FILESORT_VARSIZE+nextitemsize)<=((void*)datap-sizeof(void*)-data))
353 amb 310 {
354 amb 311 FILESORT_VARINT itemsize=nextitemsize;
355 amb 310
356     if(itemsize>largestitemsize)
357     largestitemsize=itemsize;
358    
359 amb 311 *(FILESORT_VARINT*)(data+ramused)=itemsize;
360 amb 310
361 amb 311 ramused+=FILESORT_VARSIZE;
362 amb 310
363 amb 311 ReadFile(fd_in,data+ramused,itemsize);
364 amb 310
365 amb 311 *--datap=data+ramused; /* points to real data */
366 amb 310
367 amb 311 ramused+=itemsize;
368    
369     ramused =FILESORT_VARALIGN*((ramused+FILESORT_VARSIZE-1)/FILESORT_VARALIGN);
370     ramused+=FILESORT_VARALIGN-FILESORT_VARSIZE;
371    
372 amb 310 total++;
373     n++;
374    
375 amb 311 if(ReadFile(fd_in,&nextitemsize,FILESORT_VARSIZE))
376 amb 310 {
377     more=0;
378     break;
379     }
380     }
381    
382 amb 543 /* No new data read in this time round */
383    
384 amb 310 if(n==0)
385     break;
386    
387     /* Sort the data pointers using a heap sort */
388    
389 amb 503 filesort_heapsort(datap,n,compare);
390 amb 310
391     /* Shortcut if all read in and sorted at once */
392    
393     if(nfiles==0 && !more)
394     {
395     for(i=0;i<n;i++)
396     {
397     if(!buildindex || buildindex(datap[i],count))
398     {
399 amb 311 FILESORT_VARINT itemsize=*(FILESORT_VARINT*)(datap[i]-FILESORT_VARSIZE);
400 amb 310
401 amb 311 WriteFile(fd_out,datap[i]-FILESORT_VARSIZE,itemsize+FILESORT_VARSIZE);
402 amb 310 count++;
403     }
404     }
405    
406     goto tidy_and_exit;
407     }
408    
409     /* Create a temporary file and write the result */
410    
411     sprintf(filename,"%s/filesort.%d.tmp",option_tmpdirname,nfiles);
412    
413 amb 502 fd=OpenFileNew(filename);
414 amb 310
415     for(i=0;i<n;i++)
416     {
417 amb 311 FILESORT_VARINT itemsize=*(FILESORT_VARINT*)(datap[i]-FILESORT_VARSIZE);
418 amb 310
419 amb 311 WriteFile(fd,datap[i]-FILESORT_VARSIZE,itemsize+FILESORT_VARSIZE);
420 amb 310 }
421    
422     CloseFile(fd);
423    
424     nfiles++;
425     }
426     while(more);
427    
428     /* Check that number of files is less than file size */
429    
430 amb 311 largestitemsize=FILESORT_VARALIGN*(1+(largestitemsize+FILESORT_VARALIGN-FILESORT_VARSIZE)/FILESORT_VARALIGN);
431 amb 310
432 amb 358 assert(nfiles<((option_filesort_ramsize-nfiles*sizeof(void*))/largestitemsize));
433 amb 310
434     /* Open all of the temporary files */
435    
436     fds=(int*)malloc(nfiles*sizeof(int));
437    
438     for(i=0;i<nfiles;i++)
439     {
440     sprintf(filename,"%s/filesort.%d.tmp",option_tmpdirname,i);
441    
442     fds[i]=ReOpenFile(filename);
443    
444     DeleteFile(filename);
445     }
446    
447     /* Perform an n-way merge using a binary heap */
448    
449     heap=(int*)malloc(nfiles*sizeof(int));
450    
451 amb 358 datap=data+option_filesort_ramsize-nfiles*sizeof(void*);
452 amb 310
453     /* Fill the heap to start with */
454    
455     for(i=0;i<nfiles;i++)
456     {
457     int index;
458 amb 311 FILESORT_VARINT itemsize;
459 amb 310
460 amb 311 datap[i]=data+FILESORT_VARALIGN-FILESORT_VARSIZE+i*largestitemsize;
461 amb 310
462 amb 311 ReadFile(fds[i],&itemsize,FILESORT_VARSIZE);
463 amb 310
464 amb 311 *(FILESORT_VARINT*)(datap[i]-FILESORT_VARSIZE)=itemsize;
465 amb 310
466     ReadFile(fds[i],datap[i],itemsize);
467    
468     heap[i]=i;
469    
470     index=i;
471    
472     /* Bubble up the new value */
473    
474     while(index>0 &&
475     compare(datap[heap[index]],datap[heap[(index-1)/2]])<0)
476     {
477     int newindex;
478     int temp;
479    
480     newindex=(index-1)/2;
481    
482     temp=heap[index];
483     heap[index]=heap[newindex];
484     heap[newindex]=temp;
485    
486     index=newindex;
487     }
488     }
489    
490     /* Repeatedly pull out the root of the heap and refill from the same file */
491    
492     ndata=nfiles;
493    
494     do
495     {
496     int index=0;
497 amb 311 FILESORT_VARINT itemsize;
498 amb 310
499     if(!buildindex || buildindex(datap[heap[0]],count))
500     {
501 amb 311 itemsize=*(FILESORT_VARINT*)(datap[heap[0]]-FILESORT_VARSIZE);
502 amb 310
503 amb 311 WriteFile(fd_out,datap[heap[0]]-FILESORT_VARSIZE,itemsize+FILESORT_VARSIZE);
504 amb 310 count++;
505     }
506    
507 amb 311 if(ReadFile(fds[heap[0]],&itemsize,FILESORT_VARSIZE))
508 amb 310 {
509     ndata--;
510     heap[0]=heap[ndata];
511     }
512     else
513     {
514 amb 311 *(FILESORT_VARINT*)(datap[heap[0]]-FILESORT_VARSIZE)=itemsize;
515 amb 310
516     ReadFile(fds[heap[0]],datap[heap[0]],itemsize);
517     }
518    
519     /* Bubble down the new value */
520    
521     while((2*index+2)<ndata &&
522     (compare(datap[heap[index]],datap[heap[2*index+1]])>0 ||
523     compare(datap[heap[index]],datap[heap[2*index+2]])>0))
524     {
525     int newindex;
526     int temp;
527    
528     if(compare(datap[heap[2*index+1]],datap[heap[2*index+2]])<0)
529     newindex=2*index+1;
530     else
531     newindex=2*index+2;
532    
533     temp=heap[newindex];
534     heap[newindex]=heap[index];
535     heap[index]=temp;
536    
537     index=newindex;
538     }
539    
540     if((2*index+2)==ndata &&
541     compare(datap[heap[index]],datap[heap[2*index+1]])>0)
542     {
543     int newindex;
544     int temp;
545    
546     newindex=2*index+1;
547    
548     temp=heap[newindex];
549     heap[newindex]=heap[index];
550     heap[index]=temp;
551     }
552     }
553     while(ndata>0);
554    
555     /* Tidy up */
556    
557     tidy_and_exit:
558    
559     if(fds)
560     {
561     for(i=0;i<nfiles;i++)
562     CloseFile(fds[i]);
563     free(fds);
564     }
565    
566     if(heap)
567     free(heap);
568    
569     free(data);
570     free(filename);
571     }
572    
573    
574     /*++++++++++++++++++++++++++++++++++++++
575 amb 269 A function to sort an array of pointers efficiently.
576    
577     The data is sorted using a "Heap sort" http://en.wikipedia.org/wiki/Heapsort,
578     in particular an this good because it can operate in-place and doesn't
579     allocate more memory like using qsort() does.
580    
581     void **datap A pointer to the array of pointers to sort.
582    
583     size_t nitems The number of items of data to sort.
584    
585     int(*compare)(const void *, const void *) The comparison function (identical to qsort if the
586     data to be sorted was an array of things not pointers).
587     ++++++++++++++++++++++++++++++++++++++*/
588    
589 amb 503 void filesort_heapsort(void **datap,size_t nitems,int(*compare)(const void*, const void*))
590 amb 269 {
591     int i;
592    
593     /* Fill the heap by pretending to insert the data that is already there */
594    
595     for(i=1;i<nitems;i++)
596     {
597     int index=i;
598    
599     /* Bubble up the new value (upside-down, put largest at top) */
600    
601     while(index>0 &&
602     compare(datap[index],datap[(index-1)/2])>0) /* reversed compared to filesort() above */
603     {
604     int newindex;
605     void *temp;
606    
607     newindex=(index-1)/2;
608    
609     temp=datap[index];
610     datap[index]=datap[newindex];
611     datap[newindex]=temp;
612    
613     index=newindex;
614     }
615     }
616    
617     /* Repeatedly pull out the root of the heap and swap with the bottom item */
618    
619     for(i=nitems-1;i>0;i--)
620     {
621     int index=0;
622     void *temp;
623    
624     temp=datap[index];
625     datap[index]=datap[i];
626     datap[i]=temp;
627    
628     /* Bubble down the new value (upside-down, put largest at top) */
629    
630     while((2*index+2)<i &&
631     (compare(datap[index],datap[2*index+1])<0 || /* reversed compared to filesort() above */
632     compare(datap[index],datap[2*index+2])<0)) /* reversed compared to filesort() above */
633     {
634     int newindex;
635     void *temp;
636    
637     if(compare(datap[2*index+1],datap[2*index+2])>0) /* reversed compared to filesort() above */
638     newindex=2*index+1;
639     else
640     newindex=2*index+2;
641    
642     temp=datap[newindex];
643     datap[newindex]=datap[index];
644     datap[index]=temp;
645    
646     index=newindex;
647     }
648    
649     if((2*index+2)==i &&
650     compare(datap[index],datap[2*index+1])<0) /* reversed compared to filesort() above */
651     {
652     int newindex;
653     void *temp;
654    
655     newindex=2*index+1;
656    
657     temp=datap[newindex];
658     datap[newindex]=datap[index];
659     datap[index]=temp;
660     }
661     }
662     }

Properties

Name Value
cvs:description Functions to perform sorting.