]> mj.ucw.cz Git - libucw.git/blob - lib/sorter.h
6ce1a6c8a42bb0671df7f4ec641793f83db236db
[libucw.git] / lib / sorter.h
1 /*
2  *      Sherlock Library -- Universal Sorter
3  *
4  *      (c) 2001--2002 Martin Mares <mj@ucw.cz>
5  */
6
7 /*
8  *  This is not a normal header file, it's a generator of sorting
9  *  routines.  Each time you include it with parameters set in the
10  *  corresponding preprocessor macros, it generates a file sorter
11  *  with the parameters given.
12  *
13  *  Recognized parameter macros: (those marked with [*] are mandatory)
14  *
15  *  SORT_KEY        [*] data type capable of storing a single key
16  *  SORT_PREFIX(x)  [*] add a name prefix (used on all global names
17  *                      defined by the sorter)
18  *  SORT_PRESORT        include an in-core presorting pass
19  *  SORT_UNIFY          merge items with identical keys
20  *  SORT_DELETE_INPUT   a C expression, if true, the input files are
21  *                      deleted as soon as possible
22  *  SORT_INPUT_FILE     input is a file with this name
23  *  SORT_INPUT_FB       input is a fastbuf stream
24  *                      (can be safely NULL if you want to treat original
25  *                      input in a different way by file read functions)
26  *  SORT_INPUT_FBPAIR   input is a pair of fastbuf streams
27  *                      (not supported by the presorter)
28  *  SORT_OUTPUT_FILE    output is a file with this name
29  *  SORT_OUTPUT_FB      output is a temporary fastbuf stream
30  *
31  *  You also need to define some (usually inline) functions which
32  *  are called by the sorter to process your data:
33  *
34  *  int PREFIX_compare(SORT_KEY *a, *b)
35  *                      compare two keys, result like strcmp
36  *  int PREFIX_fetch_key(struct fastbuf *f, SORT_KEY *k)
37  *                      fetch next key, returns 1=ok, 0=eof
38  *  void PREFIX_copy_data(struct fastbuf *src, *dest, SORT_KEY *k)
39  *                      write just fetched key k to dest and copy all data
40  *                      belonging to this key from src to dest.
41  *  void PREFIX_merge_data(struct fastbuf *src1, *src2, *dest, SORT_KEY *k1, *k2)
42  *                      [used only in case SORT_UNIFY is defined]
43  *                      write just fetched key k to dest and merge data from
44  *                      two records with the same key (k1 and k2 are key occurences
45  *                      in the corresponding streams).
46  *  byte * PREFIX_fetch_item(struct fastbuf *f, SORT_KEY *k, byte *limit)
47  *                      [used only with SORT_PRESORT]
48  *                      fetch data belonging to a just fetched key and store
49  *                      them to memory following the key, but not over limit.
50  *                      Returns a pointer to first byte after the data
51  *                      or NULL if the data don't fit. For variable-length
52  *                      keys, it can use the rest of SORT_KEY and even return
53  *                      pointer before end of the key.
54  *                      Important: before PREFIX_fetch_item() succeeds, the key
55  *                      must be position independent, the sorter can copy it.
56  *  void PREFIX_store_item(struct fastbuf *f, SORT_KEY *k)
57  *                      [used only with SORT_PRESORT]
58  *                      write key and all its data read with PREFIX_fetch_data
59  *                      to the stream given.
60  *  SORT_KEY * PREFIX_merge_items(SORT_KEY *a, SORT_KEY *b)
61  *                      [used only with SORT_PRESORT && SORT_UNIFY]
62  *                      merge two items with the same key, returns pointer
63  *                      to at most one of the items, the rest will be removed
64  *                      from the list of items, but not deallocated, so
65  *                      the remaining item can freely reference data of the
66  *                      other one.
67  */
68
69 /* Declarations of externals from sorter.c */
70
71 #ifndef SORT_DECLS_READ
72 #define SORT_DECLS_READ
73
74 extern uns sorter_trace;
75 extern uns sorter_presort_bufsize;
76 extern uns sorter_stream_bufsize;
77
78 extern uns sorter_pass_counter;
79
80 #endif          /* !SORT_DECLS_READ */
81
82 /* The sorter proper */
83
84 #ifndef SORT_DECLARE_ONLY
85
86 #include "lib/fastbuf.h"
87 #include <unistd.h>
88 #include <fcntl.h>
89 #include <string.h>
90
91 #if !defined(SORT_KEY) || !defined(SORT_PREFIX)
92 #error Some of the mandatory configuration macros are missing.
93 #endif
94
95 #define P(x) SORT_PREFIX(x)
96 #define SWAP(x,y,z) do { z=x; x=y; y=z; } while(0)
97
98 #if defined(SORT_UNIFY) || defined(SORT_UNIQUE)
99 #define LESS <
100 #else
101 #define LESS <=
102 #endif
103
104 static struct fastbuf *
105 P(flush_out)(struct fastbuf *out)
106 {
107   if (out)
108     {
109       bflush(out);
110       bsetpos(out, 0);
111     }
112   return out;
113 }
114
115 static void
116 P(pass)(struct fastbuf **fb1, struct fastbuf **fb2)
117 {
118   struct fastbuf *in1 = *fb1;
119   struct fastbuf *in2 = *fb2;
120   struct fastbuf *out1 = NULL;
121   struct fastbuf *out2 = NULL;
122   SORT_KEY kbuf1, kbuf2, kbuf3, kbuf4;
123   SORT_KEY *kin1 = &kbuf1;
124   SORT_KEY *kprev1 = &kbuf2;
125   SORT_KEY *kin2 = &kbuf3;
126   SORT_KEY *kprev2 = &kbuf4;
127   SORT_KEY *kout = NULL;
128   SORT_KEY *ktmp;
129   int next1, next2, comp;
130   int run1, run2;
131   uns run_count = 0;
132
133   run1 = next1 = in1 ? P(fetch_key)(in1, kin1) : 0;
134   run2 = next2 = in2 ? P(fetch_key)(in2, kin2) : 0;
135   while (next1 || next2)
136     {
137       if (!run1)
138         comp = 1;
139       else if (!run2)
140         comp = -1;
141       else
142         comp = P(compare)(kin1, kin2);
143       ktmp = (comp <= 0) ? kin1 : kin2;
144       if (!kout || !(P(compare)(kout, ktmp) LESS 0))
145         {
146           struct fastbuf *t;
147           SWAP(out1, out2, t);
148           if (!out1)
149             out1 = bopen_tmp(sorter_stream_bufsize);
150           run_count++;
151         }
152       if (comp LESS 0)
153         {
154           P(copy_data)(in1, out1, kin1);
155           SWAP(kin1, kprev1, ktmp);
156           next1 = P(fetch_key)(in1, kin1);
157           run1 = next1 && (P(compare)(kprev1, kin1) LESS 0);
158           kout = kprev1;
159         }
160 #ifdef SORT_UNIFY
161       else if (comp == 0)
162         {
163           P(merge_data)(in1, in2, out1, kin1, kin2);
164           SWAP(kin1, kprev1, ktmp);
165           next1 = P(fetch_key)(in1, kin1);
166           run1 = next1 && (P(compare)(kprev1, kin1) LESS 0);
167           SWAP(kin2, kprev2, ktmp);
168           next2 = P(fetch_key)(in2, kin2);
169           run2 = next2 && (P(compare)(kprev2, kin2) LESS 0);
170           kout = kprev2;
171         }
172 #endif
173       else
174         {
175           P(copy_data)(in2, out1, kin2);
176           SWAP(kin2, kprev2, ktmp);
177           next2 = P(fetch_key)(in2, kin2);
178           run2 = next2 && (P(compare)(kprev2, kin2) LESS 0);
179           kout = kprev2;
180         }
181       if (!run1 && !run2)
182         {
183           run1 = next1;
184           run2 = next2;
185         }
186     }
187   bclose(in1);
188   bclose(in2);
189   if (sorter_trace)
190     log(L_INFO, "Pass %d: %d runs, %d+%d KB", sorter_pass_counter, run_count,
191         (out1 ? (int)((btell(out1) + 1023) / 1024) : 0),
192         (out2 ? (int)((btell(out2) + 1023) / 1024) : 0));
193   *fb1 = P(flush_out)(out1);
194   *fb2 = P(flush_out)(out2);
195   sorter_pass_counter++;
196 }
197
198 #ifdef SORT_PRESORT
199
200 #define SORT_NODE struct P(presort_node)
201
202 SORT_NODE {
203   SORT_NODE *next;
204   SORT_KEY key;
205 };
206
207 static SORT_NODE *
208 P(mergesort)(SORT_NODE *x)
209 {
210   SORT_NODE *f1, **l1, *f2, **l2, **l;
211
212   l1 = &f1;
213   l2 = &f2;
214   while (x)
215     {
216       *l1 = x;
217       l1 = &x->next;
218       x = x->next;
219       if (!x)
220         break;
221       *l2 = x;
222       l2 = &x->next;
223       x = x->next;
224     }
225   *l1 = *l2 = NULL;
226
227   if (f1 && f1->next)
228     f1 = P(mergesort)(f1);
229   if (f2 && f2->next)
230     f2 = P(mergesort)(f2);
231   l = &x;
232   while (f1 && f2)
233     {
234       if (P(compare)(&f1->key, &f2->key) <= 0)
235         {
236           *l = f1;
237           l = &f1->next;
238           f1 = f1->next;
239         }
240       else
241         {
242           *l = f2;
243           l = &f2->next;
244           f2 = f2->next;
245         }
246     }
247   *l = f1 ? : f2;
248   return x;
249 }
250
251 static void
252 P(presort)(struct fastbuf **fb1, struct fastbuf **fb2)
253 {
254   struct fastbuf *in = *fb1;
255   struct fastbuf *out1 = NULL;
256   struct fastbuf *out2 = NULL;
257   struct fastbuf *tbuf;
258   byte *buffer, *bufend, *current;
259   SORT_NODE *first, **last, *this, *leftover;
260   int cont = 1;
261   uns run_count = 0;
262   uns giant_count = 0;
263   uns split_count = 0;
264
265   ASSERT(!*fb2);
266   if (sorter_presort_bufsize < 2*sizeof(SORT_NODE))
267     die("PresortBuffer set too low");
268   buffer = xmalloc(sorter_presort_bufsize);
269   bufend = buffer + sorter_presort_bufsize;
270   leftover = NULL;
271   while (cont)
272     {
273       SWAP(out1, out2, tbuf);
274       if (!out1)
275         out1 = bopen_tmp(sorter_stream_bufsize);
276       current = buffer;
277       last = &first;
278       if (leftover)
279         {
280           memmove(buffer, leftover, sizeof(SORT_NODE));
281           this = leftover = (SORT_NODE *) buffer;
282           split_count++;
283           goto get_data;
284         }
285       for(;;)
286         {
287           current = (byte *) ALIGN((addr_int_t) current, CPU_STRUCT_ALIGN);
288           if (current + sizeof(*this) > bufend)
289             break;
290           this = (SORT_NODE *) current;
291           cont = P(fetch_key)(in, &this->key);
292           if (!cont)
293             break;
294         get_data:
295           current = P(fetch_item)(in, &this->key, bufend);
296           if (!current)
297             {
298               if (leftover)             /* Single node too large */
299                 {
300                   P(copy_data)(in, out1, &leftover->key);
301                   leftover = NULL;
302                   run_count++;
303                   giant_count++;
304                 }
305               else                      /* Node will be left over to the next phase */
306                 leftover = this;
307               break;
308             }
309           *last = this;
310           last = &this->next;
311           leftover = NULL;
312         }
313       *last = NULL;
314       if (!first)
315         continue;
316
317       first = P(mergesort)(first);
318       run_count++;
319       while (first)
320         {
321 #ifdef SORT_UNIFY
322           SORT_NODE *second = first->next;
323           if (second && !P(compare)(&first->key, &second->key))
324             {
325               SORT_KEY *n = P(merge_items)(&first->key, &second->key);
326               if (n == &first->key)
327                 first->next = second->next;
328               else if (n)
329                 first = first->next;
330               else
331                 first = second->next;
332               continue;
333             }
334 #endif
335           P(store_item)(out1, &first->key);
336           first = first->next;
337         }
338     }
339
340   bclose(in);
341   if (sorter_trace)
342     log(L_INFO, "Pass 0: %d runs (%d giants, %d splits), %d+%d KB",
343         run_count, giant_count, split_count,
344         (out1 ? (int)((btell(out1) + 1023) / 1024) : 0),
345         (out2 ? (int)((btell(out2) + 1023) / 1024) : 0));
346   *fb1 = P(flush_out)(out1);
347   *fb2 = P(flush_out)(out2);
348   xfree(buffer);
349 }
350
351 #endif          /* SORT_PRESORT */
352
353 static
354 #ifdef SORT_OUTPUT_FB
355 struct fastbuf *
356 #elif defined(SORT_OUTPUT_FILE)
357 void
358 #else
359 #error No output defined.
360 #endif
361 P(sort)(
362 #ifdef SORT_INPUT_FILE
363 byte *inname
364 #elif defined(SORT_INPUT_FB)
365 struct fastbuf *fb1
366 #elif defined(SORT_INPUT_FBPAIR)
367 struct fastbuf *fb1, struct fastbuf *fb2
368 #else
369 #error No input defined.
370 #endif
371 #ifdef SORT_OUTPUT_FILE
372 ,byte *outname
373 #endif
374 )
375 {
376 #ifdef SORT_INPUT_FILE
377   struct fastbuf *fb1, *fb2;
378   fb1 = bopen(inname, O_RDONLY, sorter_stream_bufsize);
379   fb2 = NULL;
380 #elif defined(SORT_INPUT_FB)
381   struct fastbuf *fb2 = NULL;
382 #endif
383
384 #ifdef SORT_DELETE_INPUT
385   fb1->is_temp_file = SORT_DELETE_INPUT;
386 #endif
387   sorter_pass_counter = 1;
388 #ifdef SORT_PRESORT
389   P(presort)(&fb1, &fb2);
390   if (fb2)
391 #endif
392     do P(pass)(&fb1, &fb2); while (fb1 && fb2);
393   if (!fb1)
394     fb1 = bopen_tmp(sorter_stream_bufsize);
395
396 #ifdef SORT_OUTPUT_FB
397   return fb1;
398 #else
399   fb1->is_temp_file = 0;
400   if (rename(fb1->name, outname) < 0)
401     die("rename(%s,%s): %m", fb1->name, outname);
402   bclose(fb1);
403 #endif
404 }
405
406 #undef P
407 #undef LESS
408 #undef SWAP
409 #undef SORT_NODE
410
411 #endif          /* !SORT_DECLARE_ONLY */