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 532 - (hide annotations) (download) (as text)
Sat Nov 27 14:56:37 2010 UTC (14 years, 4 months ago) by amb
File MIME type: text/x-csrc
File size: 15858 byte(s)
Split functions.h into fakes.h, sorting.h and the remainder in functions.h.

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

Properties

Name Value
cvs:description Functions to perform sorting.