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