mod_musicindex  1.4.1
sort.c
Go to the documentation of this file.
1 /*
2  * inf.c
3  * mod_musicindex
4  *
5  * $Id: sort.c 862 2009-09-01 12:58:42Z varenet $
6  *
7  * Created by Thibaut VARENE on Thu Mar 20 2003.
8  * Copyright (c) 2003-2004 Regis BOUDIN
9  * Copyright (c) 2007-2008 Thibaut VARENE
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 2.1,
13  * as published by the Free Software Foundation.
14  *
15  */
16 
36 /*
37  * Some history on the inf_by_artist() (etc) algorithm (all comments relative to
38  * code built at -O0, ie no compiler optimization):
39  *
40  * The initial algorithm read as follows:
41  * if ((!first->artist) || (!second->artist)) {
42  * if (first->artist)
43  * return -1;
44  * else if (second->artist)
45  * return 1;
46  * else
47  * return 0;
48  * }
49  * return (strcasecmp(first->artist, second->artist));
50  *
51  * This algorithm had 4 checks in the worst case condition, and 4 return points
52  * and 4 conditional branches in the code stream, including one taken branch in the
53  * most frequent path (we assume the most frequent case to be the one where both
54  * fields are !NULL).
55  * Depending on the cases, the number of checks to perform varied between 4 to 2.
56  *
57  * This algorith was later on reduced to:
58  * if (first->artist) {
59  * if (second->artist)
60  * return strcasecmp(first->artist, second->artist);
61  * else
62  * return -1;
63  * } else if (second->artist)
64  * return 1;
65  * else
66  * return 0;
67  *
68  * This particular implementation reduced the number of checks to 2 in ALL cases
69  * but still had 4 return points and 4 conditional branches. In the fast path (the
70  * most frequent case), the code had 2 checks and NO taken branch.
71  *
72  * Then the final algorithm was implemented:
73  * register short ret = -1;
74  * if (first->artist)
75  * ret &= 0x1;
76  * if (second->artist)
77  * ret &= ~0x1;
78  * return (!ret ? strcasecmp(first->artist, second->artist) : ~ret);
79  *
80  * This algorithm has 3 checks in ALL cases, zero taken branch in the fast path,
81  * and 3 taken branch in the worst case. It has only one return point. The generated
82  * machine code is thus much more compact and much less prone to pipeline bubbles.
83  * The return value is slightly different than the first algorithm (-2 instead of -1)
84  * but since we're using a sort algorithm that only deals with the relative
85  * position of 2 elements, it doesn't matter.
86  * Also the use of short values should enable immediate loads and masks on most
87  * architectures (tested on ppc).
88  *
89  * Of course, this algorithm is also very easy to invert if the most frequent case
90  * is the "empty one":
91  * register short ret = 0;
92  * if (!first->artist)
93  * ret |= ~0x1;
94  * if (!second->artist)
95  * ret |= 0x1;
96  * return (!ret ? strcasecmp(first->artist, second->artist) : ~ret);
97  */
98 
99 #include "sort.h"
100 
101 #ifdef HAVE_STRING_H
102 #include <string.h>
103 #endif
104 
105 /* static declarations of local routines */
106 static inline short inf_by_rand(const mu_ent *const restrict first, const mu_ent *const restrict second);
107 static inline short inf_by_track(const mu_ent *const restrict first, const mu_ent *const restrict second);
108 static inline short inf_by_posn(const mu_ent *const restrict first, const mu_ent *const restrict second);
109 static inline short inf_by_date(const mu_ent *const restrict first, const mu_ent *const restrict second);
110 static inline short inf_by_length(const mu_ent *const restrict first, const mu_ent *const restrict second);
111 static inline short inf_by_bitrate(const mu_ent *const restrict first, const mu_ent *const restrict second);
112 static inline short inf_by_freq(const mu_ent *const restrict first, const mu_ent *const restrict second);
113 static inline short inf_by_size(const mu_ent *const restrict first, const mu_ent *const restrict second);
114 static inline short inf_by_mtime(const mu_ent *const restrict first, const mu_ent *const restrict second);
115 static inline short inf_by_artist(const mu_ent *const restrict first, const mu_ent *const restrict second);
116 static inline short inf_by_album(const mu_ent *const restrict first, const mu_ent *const restrict second);
117 static inline short inf_by_title(const mu_ent *const restrict first, const mu_ent *const restrict second);
118 static inline short inf_by_filetype(const mu_ent *const restrict first, const mu_ent *const restrict second);
119 static inline short inf_by_file(const mu_ent *const restrict first, const mu_ent *const restrict second);
120 static inline short inf_by_uri(const mu_ent *const restrict first, const mu_ent *const restrict second);
121 static inline short inf_by_genre(const mu_ent *const restrict first, const mu_ent *const restrict second);
122 static inline short inf_by_dir(const mu_ent *const restrict first, const mu_ent *const restrict second);
123 static inline short inf_global(const mu_ent *const first, const mu_ent *const second, const unsigned char *const order);
124 
126 typedef short (*inf_ptr)(const mu_ent *const restrict, const mu_ent *const restrict);
127 
136 static const inf_ptr const sort_order_functions[] = {
137  NULL,
138  inf_by_album,
139  inf_by_posn,
140  inf_by_track,
142  inf_by_title,
145  inf_by_freq,
147  inf_by_file,
148  inf_by_uri,
149  inf_by_genre,
150  inf_by_date,
151  inf_by_size,
152  inf_by_mtime,
153  inf_by_rand,
154  inf_by_dir,
155  NULL,
156 };
157 
181 void sort_mergesort(mu_pack *const pack, const unsigned char *const order)
182 {
183  const mu_ent *p, /*< left sorted set pointer */
184  *q, /*< right sorted set pointer */
185  *m, /*< merge element */
186  *head = pack->head; /*< head of the merged list */
187  mu_ent *tail; /*< tail of the merged list */
188 
189  unsigned long packsize, /*< total number of elements in the list */
190  ssmsize, /*< maximum size of the sorted sets */
191  count, /*< number of elements left to merge */
192  psize, /*< size of the left sorted set */
193  qsize; /*< size of the right sorted set */
194 
195  packsize = pack->dirnb + pack->filenb;
196 
197  /* we begin with packsize sorted sets of 1 elements, merge them,
198  * then double the size of the sorted sets to merge on each pass,
199  * until we're left with 1 sorted set of packsize elements */
200  for (ssmsize = 1; ssmsize <= packsize; ssmsize *= 2) {
201  /* have both left and right sorted set begin at the last known head of the list */
202  q = p = head;
203 
204  /* forget about previous head and tail as we'll set new ones */
205  head = tail = NULL;
206 
207  /* start counter at packsize */
208  count = packsize;
209 
210  while (count) {
211  /* move q at most ssmsize positions ahead of p */
212  for (psize=0; (psize < ssmsize) && q; psize++)
213  q = q->next;
214 
215  /* q set has at most ssmsize elements */
216  qsize = ssmsize;
217 
218  /* merge both sets */
219  while (psize || (qsize && q)) {
220  if (!psize) {
221  /* p empty -> m = first elt of q */
222  m = q;
223  q = q->next;
224  qsize--;
225  } else if (!qsize || !q) {
226  /* q empty -> m = first elt of p */
227  m = p;
228  p = p->next;
229  psize--;
230  } else if (inf_global(p, q, order) <= 0) {
231  /* 1st elt of p is <= q; -> m = 1st elt of p */
232  /* the "equal" case ensures stability */
233  m = p;
234  p = p->next;
235  psize--;
236  } else {
237  /* 1st elt of q is < p; -> m = 1st elt of q */
238  m = q;
239  q = q->next;
240  qsize--;
241  }
242 
243  /* put the merge element at the tail of the merged list */
244  if (likely(tail))
245  tail->next = m;
246  else
247  head = m;
248 
249  /* update the tail pointer */
250  /* we have to deconstify, but this should be the
251  * /only/ place where we have to */
252  tail = (mu_ent *)m;
253 
254  /* decrease the counter of elements remaining to merge */
255  count--;
256  }
257 
258  /* at the end of this loop, p now points to where q was
259  * at the begining of the loop (that is, the head of the
260  * right sorted set), and q now points to the next left
261  * sorted set, so have p point to it as well, before we
262  * start another iteration */
263  p = q;
264  }
265 
266  /* we've walked packsize elements, mark the end of this pass' list */
267  tail->next = NULL;
268  }
269 
270  /* At this point we have a list of packsize sorted elements */
271  pack->head = head;
272 
273  /* In order to simplify the module's work, we're gonna mark the head of
274  * songs list (if any) here */
275  if (pack->filenb) {
276  for (p = pack->head; p->filetype < 0; p = p->next);
277  pack->fhead = p;
278  }
279 
280  return;
281 }
282 
283 
284 /* All the following function should return an INCREASING order,
285  * unless specified otherwise. */
286 
287 static inline short inf_by_rand(const mu_ent *const restrict first, const mu_ent *const restrict second)
288 {
289  return (rand()-(RAND_MAX/2)); /* if rand returns long, the implicit cast will add entropy ;o) */
290 }
291 
292 static inline short inf_by_track(const mu_ent *const restrict first, const mu_ent *const restrict second)
293 {
294  return (first->track - second->track);
295 }
296 
297 static inline short inf_by_posn(const mu_ent *const restrict first, const mu_ent *const restrict second)
298 {
299  return (first->posn - second->posn);
300 }
301 
302 static inline short inf_by_date(const mu_ent *const restrict first, const mu_ent *const restrict second)
303 {
304  return (first->date - second->date);
305 }
306 
307 static inline short inf_by_length(const mu_ent *const restrict first, const mu_ent *const restrict second)
308 {
309  return (first->length - second->length);
310 }
311 
312 static inline short inf_by_bitrate(const mu_ent *const restrict first, const mu_ent *const restrict second)
313 {
314  /* bitrate is long */
315  return ((first->bitrate <= second->bitrate) ? -1 : 1);
316 }
317 
324 static inline short inf_by_freq(const mu_ent *const restrict first, const mu_ent *const restrict second)
325 {
326  return (first->freq - second->freq);
327 }
328 
329 static inline short inf_by_size(const mu_ent *const restrict first, const mu_ent *const restrict second)
330 {
331  /* size is long */
332  return ((first->size <= second->size) ? -1 : 1);
333 }
334 
343 static inline short inf_by_mtime(const mu_ent *const restrict first, const mu_ent *const restrict second)
344 {
345  /* mtime is long */
346  return ((first->mtime < second->mtime) ? 1 : -1);
347 }
348 
349 static inline short inf_by_artist(const mu_ent *const restrict first, const mu_ent *const restrict second)
350 {
351  register short ret = -1;
352  if (likely(first->artist))
353  ret &= 0x1;
354  if (likely(second->artist))
355  ret &= ~0x1;
356  return (likely(!ret) ? strcasecmp(first->artist, second->artist) : ~ret);
357 }
358 
359 static inline short inf_by_album(const mu_ent *const restrict first, const mu_ent *const restrict second)
360 {
361  register short ret = -1;
362  if (likely(first->album))
363  ret &= 0x1;
364  if (likely(second->album))
365  ret &= ~0x1;
366  return (likely(!ret) ? strcasecmp(first->album, second->album) : ~ret);
367 }
368 
369 /* title always exists now */
370 static inline short inf_by_title(const mu_ent *const restrict first, const mu_ent *const restrict second)
371 {
372  return strcasecmp(first->title, second->title);
373 }
374 
375 static inline short inf_by_filetype(const mu_ent *const restrict first, const mu_ent *const restrict second)
376 {
377  return (first->filetype - second->filetype);
378 }
379 
380 static inline short inf_by_file(const mu_ent *const restrict first, const mu_ent *const restrict second)
381 {
382  return strcasecmp(first->file, second->file);
383 }
384 
385 static inline short inf_by_uri(const mu_ent *const restrict first, const mu_ent *const restrict second)
386 {
387  return strcmp(first->uri, second->uri);
388 }
389 
390 static inline short inf_by_genre(const mu_ent *const restrict first, const mu_ent *const restrict second)
391 {
392  register short ret = -1;
393  if (likely(first->genre))
394  ret &= 0x1;
395  if (likely(second->genre))
396  ret &= ~0x1;
397  return (likely(!ret) ? strcasecmp(first->genre, second->genre) : ~ret);
398 }
399 
400 /* XXX celle la elle meriterait un ptit commentaire... */
401 static inline short inf_by_dir(const mu_ent *const restrict first, const mu_ent *const restrict second)
402 {
403  const char *const restrict one = first->uri;
404  const char *const restrict two = second->uri;
405  unsigned char cone = 'a', ctwo = 'a';
406  register unsigned short i;
407 
408  for (i=0; one[i] == two[i]; i++)
409  continue;
410 
411  for (; ((cone!='\0') && (cone!='/')) || ((ctwo!='\0') && (ctwo!='/')); i++) {
412  if (((one[i]=='\0')||(one[i]=='/')) && (cone!='\0') && (cone!='/'))
413  cone = one[i];
414 
415  if (((two[i]=='\0')||(two[i]=='/')) && (ctwo!='\0') && (ctwo!='/'))
416  ctwo = two[i];
417  }
418 
419  return ((short)cone - (short)ctwo);
420 }
421 
451 static inline short inf_global(const mu_ent *const first, const mu_ent *const second, const unsigned char *const restrict order)
452 {
453  short result;
454  register unsigned short i;
455 
456  if (unlikely(first == second))
457  return 0;
458 
459  if (first->filetype < 0) {
460  if (second->filetype < 0)
461  return inf_by_title(first, second);
462  else
463  return -1;
464  } else if (second->filetype < 0)
465  return 1;
466 
467  /* This loop will return the first element of comparison between two
468  * entries. eg. if the user set "title artist" as MusicSortOrder, then
469  * this function will first compare titles. If they differ it will return
470  * the difference. Otherwise, it will compare artists and return the
471  * difference. */
472  /* XXX NOTE: We assert here that order[i] will never trigger an out of
473  * bound access */
474  for (i = 0; sort_order_functions[order[i]]; i++) {
475  if ((result = (sort_order_functions[order[i]](first, second))))
476  return result;
477  }
478  return 0;
479 }