ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/oocsort.c
Revision: 2.1
Committed: Tue Feb 24 19:39:26 2015 UTC (9 years, 3 months ago) by greg
Content type: text/plain
Branch: MAIN
Log Message:
Initial check-in of photon map addition by Roland Schregle

File Contents

# Content
1 /*
2 ==================================================================
3 N-way hybrid out-of-core merge sort
4 Sorts N blocks internally using quicksort, followed by N-way
5 merge using priority queue.
6
7 Roland Schregle (roland.schregle@{hslu.ch, gmail.com})
8 (c) Fraunhofer Institute for Solar Energy Systems,
9 Lucerne University of Applied Sciences & Arts
10 ==================================================================
11
12 $Id $
13 */
14
15
16 #include "oocsort.h"
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20
21
22 /* Priority queue node */
23 typedef struct {
24 OOC_Sort_Key pri; /* Record's priority (sort key) */
25 unsigned blk; /* Block containing record */
26 } OOC_Sort_PQNode;
27
28 typedef struct {
29 OOC_Sort_PQNode *node;
30 unsigned len, tail;
31 } OOC_Sort_PQueue;
32
33 /* Block descriptor */
34 typedef struct {
35 FILE *file; /* Temporary file */
36 char *buf, *head, *tail; /* Associated I/O buffa,
37 head/tail pointers */
38 } OOC_Sort_Block;
39
40
41 /* Record priority evaluation function */
42 static OOC_Sort_Key (*pri)(const void *);
43
44
45 static int OOC_Sort_Compare (const void *rec1, const void *rec2)
46 /* Comparison function for internal sorting */
47 {
48 const OOC_Sort_Key pri1 = pri(rec1), pri2 = pri(rec2);
49
50 if (!rec1 || !rec2)
51 return 0;
52
53 if (pri1 < pri2)
54 return -1;
55 else if (pri1 > pri2)
56 return 1;
57 else
58 return 0;
59 }
60
61
62 static char *OOC_Sort_Read (OOC_Sort_Block *blk, unsigned recSize)
63 /* Read next record from specified block; return pointer to buffa entry
64 or NULL on error */
65 {
66 char *rec = NULL;
67
68 if (blk -> head >= blk -> tail) {
69 /* Input buffa empty; read next block (potentially truncated if last),
70 * where tail marks end of buffa */
71 const unsigned long bufSize = blk -> tail - blk -> buf;
72
73 if (feof(blk -> file))
74 return NULL;
75
76 blk -> head = blk -> buf;
77 blk -> tail = blk -> buf + fread(blk -> buf, 1, bufSize, blk -> file);
78
79 if (blk -> tail == blk -> head)
80 return NULL;
81 }
82
83 rec = blk -> head;
84 blk -> head += recSize;
85
86 return rec;
87 }
88
89
90 static char *OOC_Sort_Peek (OOC_Sort_Block *blk, unsigned recSize)
91 /* Return next record from specified block WITHOUT removing from buffa */
92 {
93 char *rec = NULL;
94
95 if (rec = OOC_Sort_Read(blk, recSize)) {
96 /* Restore buffa head */
97 blk -> head -= recSize;
98 }
99
100 return rec;
101 }
102
103
104 static char *OOC_Sort_Write (OOC_Sort_Block *blk, unsigned recSize,
105 const char *rec)
106 /* Output record to specified block and return pointer to buffa entry
107 or NULL on error */
108 {
109 char *res = NULL;
110
111 if (blk -> head >= blk -> tail) {
112 /* Flush output buffa (tail marks end of buffa) */
113 const unsigned long bufSize = blk -> tail - blk -> buf;
114
115 blk -> head = blk -> buf;
116 if (fwrite(blk -> buf, 1, bufSize, blk -> file) != bufSize)
117 return NULL;
118 }
119
120 if (!rec)
121 return NULL;
122
123 memcpy(blk -> head, rec, recSize);
124 res = blk -> head;
125 blk -> head += recSize;
126
127 return res;
128 }
129
130
131 #ifdef DEBUG
132 static int OOC_Sort_PQCheck (OOC_Sort_PQueue *pq, unsigned root)
133 /* Priority queue sanity check */
134 {
135 unsigned kid;
136
137 if (root < pq -> tail) {
138 if ((kid = (root << 1) + 1) < pq -> tail)
139 if (pq -> node [kid].pri < pq -> node [root].pri)
140 return -1;
141 else return OOC_Sort_PQCheck(pq, kid);
142
143 if ((kid = kid + 1) < pq -> tail)
144 if (pq -> node [kid].pri < pq -> node [root].pri)
145 return -1;
146 else return OOC_Sort_PQCheck(pq, kid);
147
148 }
149
150 return 0;
151 }
152 #endif
153
154
155 static int OOC_Sort_Push (OOC_Sort_PQueue *pq, OOC_Sort_Key pri,
156 unsigned blk)
157 /* Insert specified block index into priority queue; return block index
158 * or -1 if queue is full */
159 {
160 OOC_Sort_PQNode *pqn = pq -> node;
161 unsigned kid, root;
162
163 if (pq -> tail >= pq -> len)
164 /* Queue full */
165 return -1;
166
167 /* Append node at tail and re-sort */
168 kid = pq -> tail++;
169
170 while (kid) {
171 root = kid - 1 >> 1;
172
173 /* Compare with parent node and swap if necessary, else terminate */
174 if (pri < pqn [root].pri) {
175 pqn [kid].pri = pqn [root].pri;
176 pqn [kid].blk = pqn [root].blk;
177 kid = root;
178 }
179 else break;
180 }
181
182 pqn [kid].pri = pri;
183 pqn [kid].blk = blk;
184
185 #ifdef DEBUG
186 if (OOC_Sort_PQCheck(pq, 0) < 0) {
187 fprintf(stderr, "OOC_Sort: priority queue inconsistency\n");
188 return -1;
189 }
190 #endif
191
192 return blk;
193 }
194
195
196 static int OOC_Sort_Pop (OOC_Sort_PQueue *pq)
197 /* Remove head of priority queue and return its block index
198 * or -1 if queue is empty */
199 {
200 OOC_Sort_PQNode *pqn = pq -> node;
201 OOC_Sort_Key pri;
202 unsigned kid, kid2, root = 0, blk, res;
203
204 if (!pq -> tail)
205 /* Queue empty */
206 return -1;
207
208 res = pqn -> blk;
209 pri = pqn [--pq -> tail].pri;
210 blk = pqn [pq -> tail].blk;
211
212 /* Replace head with tail node and re-sort */
213 while ((kid = (root << 1) + 1) < pq -> tail) {
214 /* Compare with with smaller kid and swap if necessary, else
215 * terminate */
216 if ((kid2 = (kid + 1)) < pq -> tail && pqn [kid2].pri < pqn [kid].pri)
217 kid = kid2;
218
219 if (pri > pqn [kid].pri) {
220 pqn [root].pri = pqn [kid].pri;
221 pqn [root].blk = pqn [kid].blk;
222 }
223 else break;
224
225 root = kid;
226 }
227
228 pqn [root].pri = pri;
229 pqn [root].blk = blk;
230
231 #ifdef DEBUG
232 if (OOC_Sort_PQCheck(pq, 0) < 0) {
233 fprintf(stderr, "OOC_Sort: priority queue inconsistency\n");
234 return -1;
235 }
236 #endif
237
238 return res;
239 }
240
241 #if 0
242 int OOC_Sort (const char *inFile, const char *outFile,
243 unsigned long blkSize, unsigned recSize,
244 OOC_Sort_Key (*priority)(const void *))
245 /* Sort records in inFile and output to outFile by (a) internally
246 * quicksorting block of blkSize bytes at a time, then (b) merging them
247 * via a priority queue. RecSize specifies the size in bytes of each data
248 * record. The priority() callback evaluates a record's priority and must be
249 * supplied by the caller. */
250 {
251 FILE *in = NULL;
252 OOC_Sort_PQueue pqueue; /* Priority queue */
253 OOC_Sort_Block *iBlk = NULL, oBlk; /* Block descriptors */
254 char *rec, *sortBuf = NULL; /* Internal sort buffa */
255 unsigned bufSize, numBlk;
256 int b;
257
258 /* Set record priority evaluation callback */
259 pri = priority;
260
261 /* Round block and buffa size down to nearest multiple of record size */
262 blkSize = (blkSize / recSize) * recSize;
263 bufSize = (OOC_SORT_BUFSIZE / recSize) * recSize;
264
265 if (!(in = fopen(inFile, "rb"))) {
266 fprintf(stderr, "OOC_Sort: failure opening input file %s\n", inFile);
267 return -1;
268 }
269
270 /* Get input file size and number of blocks (rounded up to nearest
271 * multiple of block size) */
272 fseek(in, 0, SEEK_END);
273 numBlk = (ftell(in) + blkSize - 1) / blkSize;
274 rewind(in);
275 #else
276
277 int OOC_Sort (const char *inFile, const char *outFile,
278 unsigned numBlk, unsigned recSize,
279 OOC_Sort_Key (*priority)(const void *))
280 /* Sort records in inFile and output to outFile by (a) internally
281 * quicksorting numBlk blocks at a time, then (b) merging them via a priority
282 * queue. RecSize specifies the size in bytes of each data record. The
283 * priority() callback evaluates a record's priority (ordinal index) and
284 * must be supplied by the caller. */
285 {
286 FILE *in = NULL;
287 OOC_Sort_PQueue pqueue; /* Priority queue */
288 OOC_Sort_Block *iBlk = NULL, oBlk; /* Block descriptors */
289 char *rec, *sortBuf = NULL; /* Internal sort buffa */
290 unsigned long blkSize;
291 unsigned bufSize;
292 int b;
293
294 /* Set record priority evaluation callback */
295 pri = priority;
296
297 /* Round buffa size down to nearest multiple of record size */
298 bufSize = (OOC_SORT_BUFSIZE / recSize) * recSize;
299
300 if (!(in = fopen(inFile, "rb"))) {
301 fprintf(stderr, "OOC_Sort: failure opening input file %s\n", inFile);
302 return -1;
303 }
304
305 /* Get input file size and block size (in number of records) rounded up
306 * to nearest multiple of number of blocks */
307 fseek(in, 0, SEEK_END);
308 blkSize = (ftell(in) / recSize + numBlk - 1) / numBlk;
309 rewind(in);
310 #endif
311
312 /* Allocate buffa for internal sorting */
313 if (!(sortBuf = malloc(blkSize))) {
314 fprintf(stderr, "OOC_Sort: failure allocating internal sort buffer\n");
315 return -1;
316 }
317
318 /* Allocate input blocks */
319 if (!(iBlk = calloc(numBlk, sizeof(OOC_Sort_Block)))) {
320 fprintf(stderr, "OOC_Sort: failure allocating input blocks\n");
321 return -1;
322 }
323
324 /* ===================================================================
325 * Pass 1: Internal quicksort of each input block in sortBuf
326 * =================================================================== */
327
328 for (b = 0; b < numBlk; b++) {
329 unsigned long numRec;
330
331 /* Open temporary file associated with block */
332 if (!(iBlk [b].file = tmpfile())) {
333 /* fprintf(stderr, "OOC_Sort: failure opening block file\n"); */
334 perror("OOC_Sort: failure opening block file");
335 return -1;
336 }
337
338 if (feof(in)) {
339 fprintf(stderr, "OOC_Sort: unexpected end of input file %s\n",
340 inFile);
341 return -1;
342 }
343
344 /* Read next block (potentially truncated if last) */
345 if (!(numRec = fread(sortBuf, 1, blkSize, in) / recSize)) {
346 fprintf(stderr, "OOC_Sort: error reading from input file %s\n",
347 inFile);
348 return -1;
349 }
350
351 /* Quicksort block internally and write out to temp file */
352 qsort(sortBuf, numRec, recSize, OOC_Sort_Compare);
353
354 if (fwrite(sortBuf, recSize, numRec, iBlk [b].file) != numRec) {
355 fprintf(stderr, "OOC_Sort: error writing to block file\n");
356 return -1;
357 }
358
359 rewind(iBlk [b].file);
360 }
361
362 /* Close input file and free sort buffa */
363 fclose(in);
364 free(sortBuf);
365
366 /* Allocate priority queue with numBlk nodes */
367 pqueue.tail = 0;
368 pqueue.len = numBlk;
369 if (!(pqueue.node = calloc(numBlk, sizeof(OOC_Sort_PQNode)))) {
370 fprintf(stderr, "OOC_Sort: failure allocating priority queue\n");
371 return -1;
372 }
373
374 /* Prepare for pass 2: allocate buffa for each input block and initialise
375 * fill priority queue with single record from each block */
376 for (b = 0; b < numBlk; b++) {
377 if (!(iBlk [b].buf = malloc(bufSize))) {
378 fprintf(stderr, "OOC_Sort: failure allocating input buffer\n");
379 return -1;
380 }
381
382 /* Peek first record in block file without modifying buffa */
383 iBlk [b].head = iBlk [b].tail = iBlk [b].buf + bufSize;
384 if (!(rec = OOC_Sort_Peek(iBlk + b, recSize))) {
385 fprintf(stderr, "OOC_Sort: error reading from block file\n");
386 return -1;
387 }
388
389 /* Insert record into priority queue */
390 if (OOC_Sort_Push(&pqueue, priority(rec), b) < 0) {
391 fprintf(stderr, "OOC_Sort: failed priority queue insertion\n");
392 return -1;
393 }
394 }
395
396 /* Allocate output buffa and open output file */
397 if (!(oBlk.file = fopen(outFile, "wb"))) {
398 fprintf(stderr, "OOC_Sort: failure opening output file %s\n", outFile);
399 return -1;
400 }
401
402 if (!(oBlk.head = oBlk.buf = malloc(bufSize))) {
403 fprintf(stderr, "OOC_Sort: failure allocating output buffer\n");
404 return -1;
405 }
406
407 /* tail marks end of output buffa */
408 oBlk.tail = oBlk.buf + bufSize;
409
410 /* ===================================================================
411 * Pass 2: External merge of all blocks using priority queue
412 * =================================================================== */
413
414 do {
415 /* Get next node in priority queue, read next record in corresponding
416 * block, and send to output */
417 b = OOC_Sort_Pop(&pqueue);
418
419 if (b >= 0) {
420 /* Priority queue non-empty */
421 if (!(rec = OOC_Sort_Read(iBlk + b, recSize))) {
422 /* Corresponding record should still be in the buffa, so EOF
423 * should not happen */
424 fprintf(stderr, "OOC_Sort: unexpected EOF in block file\n");
425 return -1;
426 }
427
428 if (!OOC_Sort_Write(&oBlk, recSize, rec)) {
429 fprintf(stderr, "OOC_Sort; error writing to output file %s\n",
430 outFile);
431 return -1;
432 }
433
434 /* Peek next record from same block and insert in priority queue */
435 if (rec = OOC_Sort_Peek(iBlk + b, recSize))
436 /* Buffa not exhausted */
437 if (OOC_Sort_Push(&pqueue, priority(rec), b) < 0) {
438 fprintf(stderr, "OOC_Sort: failed priority queue insertion\n");
439 return -1;
440 }
441 }
442 } while (b >= 0);
443
444 /* Priority queue now empty; flush output buffa & close file */
445 oBlk.tail = oBlk.head;
446 OOC_Sort_Write(&oBlk, 0, NULL);
447 fclose(oBlk.file);
448
449 for (b = 0; b < numBlk; b++) {
450 fclose(iBlk [b].file);
451 free(iBlk [b].buf);
452 }
453
454 free(iBlk);
455 free(pqueue.node);
456
457 return 0;
458 }