Annocate static vars left
[pazpar2-moved-to-github.git] / src / reclists.c
1 /* This file is part of Pazpar2.
2    Copyright (C) 2006-2009 Index Data
3
4 Pazpar2 is free software; you can redistribute it and/or modify it under
5 the terms of the GNU General Public License as published by the Free
6 Software Foundation; either version 2, or (at your option) any later
7 version.
8
9 Pazpar2 is distributed in the hope that it will be useful, but WITHOUT ANY
10 WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17
18 */
19
20 #include <assert.h>
21
22 #if HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <yaz/yaz-util.h>
27
28 #include "pazpar2.h"
29 #include "reclists.h"
30
31 extern struct parameters global_parameters;
32
33 static struct reclist_sortparms *qsort_sortparms = 0; /* thread pr */
34
35 struct reclist_bucket
36 {
37     struct record_cluster *record;
38     struct reclist_bucket *next;
39 };
40
41 #if 0
42 struct reclist_sortparms * 
43 reclist_sortparms_insert_field_id(NMEM nmem,
44                          struct reclist_sortparms **sortparms,
45                          int field_id,
46                          enum conf_sortkey_type type,
47                          int increasing)
48 {
49     struct reclist_sortparms * tmp_rlsp = 0;
50     // assert(nmem);
51
52     if(!sortparms || field_id < 0)
53         return 0;
54
55     // construct new reclist_sortparms
56     tmp_rlsp  = nmem_malloc(nmem, sizeof(struct reclist_sortparms));
57     tmp_rlsp->offset = field_id;
58     tmp_rlsp->type = type;
59     tmp_rlsp->increasing = increasing;
60
61
62     // insert in *sortparms place, moving *sortparms one down the list
63     tmp_rlsp->next = *sortparms;
64     *sortparms = tmp_rlsp;
65
66     return *sortparms;
67 };
68 #endif
69
70 #if 0
71 struct reclist_sortparms * 
72 reclist_sortparms_insert(NMEM nmem, 
73                          struct reclist_sortparms **sortparms,
74                          struct conf_service * service,
75                          const char * name,
76                          int increasing)
77 {
78     int field_id = 0;
79
80     if (!sortparms || !service || !name)  
81         return 0;
82     
83     field_id = conf_service_sortkey_field_id(service, name);
84
85     if (-1 == field_id)
86         return 0;
87
88     return reclist_sortparms_insert_field_id(nmem, sortparms, field_id,
89                                              service->sortkeys[field_id].type,
90                                              increasing);
91 };
92 #endif
93
94
95 struct reclist_sortparms *reclist_parse_sortparms(NMEM nmem, const char *parms,
96     struct conf_service *service)
97 {
98     struct reclist_sortparms *res = 0;
99     struct reclist_sortparms **rp = &res;
100
101     if (strlen(parms) > 256)
102         return 0;
103     while (*parms)
104     {
105         char parm[256];
106         char *pp;
107         const char *cpp;
108         int increasing;
109         int i;
110         int offset = 0;
111         enum conf_sortkey_type type;
112         struct reclist_sortparms *new;
113
114         if (!(cpp = strchr(parms, ',')))
115             cpp = parms + strlen(parms);
116         strncpy(parm, parms, cpp - parms); 
117         parm[cpp-parms] = '\0';
118
119         if ((pp = strchr(parm, ':')))
120         {
121             increasing = pp[1] == '1' ? 1 : 0;
122             *pp = '\0';
123         }
124         else
125             increasing = 0;
126         if (!strcmp(parm, "relevance"))
127         {
128             type = Metadata_sortkey_relevance;
129         }
130         else
131         {
132             for (i = 0; i < service->num_sortkeys; i++)
133             {
134                 struct conf_sortkey *sk = &service->sortkeys[i];
135                 if (!strcmp(sk->name, parm))
136                 {
137                     type = sk->type;
138                     if (type == Metadata_sortkey_skiparticle)
139                         type = Metadata_sortkey_string;
140                     break;
141                 }
142             }
143             if (i >= service->num_sortkeys)
144             {
145                 yaz_log(YLOG_FATAL, "Bad sortkey: %s", parm);
146                 return 0;
147             }
148             else
149                 offset = i;
150         }
151         new = *rp = nmem_malloc(nmem, sizeof(struct reclist_sortparms));
152         new->next = 0;
153         new->offset = offset;
154         new->type = type;
155         new->increasing = increasing;
156         rp = &new->next;
157         if (*(parms = cpp))
158             parms++;
159     }
160     return res;
161 }
162
163 static int reclist_cmp(const void *p1, const void *p2)
164 {
165     struct record_cluster *r1 = (*(struct record_cluster**) p1);
166     struct record_cluster *r2 = (*(struct record_cluster**) p2);
167     struct reclist_sortparms *s;
168     int res = 0;
169
170     for (s = qsort_sortparms; s && res == 0; s = s->next)
171     {
172         union data_types *ut1 = r1->sortkeys[s->offset];
173         union data_types *ut2 = r2->sortkeys[s->offset];
174         switch (s->type)
175         {
176             const char *s1, *s2;
177             
178             case Metadata_sortkey_relevance:
179                 res = r2->relevance - r1->relevance;
180                 break;
181             case Metadata_sortkey_string:
182                 s1 = ut1 ? ut1->text.sort : "";
183                 s2 = ut2 ? ut2->text.sort : "";
184                 res = strcmp(s2, s1);
185                 if (res)
186                 {
187                     if (s->increasing)
188                         res *= -1;
189                 }
190                 break;
191             case Metadata_sortkey_numeric:
192                 if (ut1 && ut2)
193                 {
194                     if (s->increasing)
195                         res = ut1->number.min  - ut2->number.min;
196                     else
197                         res = ut2->number.max  - ut1->number.max;
198                 }
199                 else if (ut1 && !ut2)
200                     res = -1;
201                 else if (!ut1 && ut2)
202                     res = 1;
203                 else
204                     res = 0;
205                 break;
206             default:
207                 yaz_log(YLOG_WARN, "Bad sort type: %d", s->type);
208                 res = 0;
209         }
210     }
211     return res;
212 }
213
214 void reclist_sort(struct reclist *l, struct reclist_sortparms *parms)
215 {
216     qsort_sortparms = parms;
217     qsort(l->flatlist, l->num_records, 
218           sizeof(struct record_cluster*), reclist_cmp);
219     reclist_rewind(l);
220 }
221
222 struct record_cluster *reclist_read_record(struct reclist *l)
223 {
224     if (l && l->pointer < l->num_records)
225         return l->flatlist[l->pointer++];
226     else
227         return 0;
228 }
229
230 void reclist_rewind(struct reclist *l)
231 {
232     if (l)
233         l->pointer = 0;
234 }
235
236 // Jenkins one-at-a-time hash (from wikipedia)
237 static unsigned int hash(const unsigned char *key)
238 {
239     unsigned int hash = 0;
240
241     while (*key)
242     {
243         hash += *(key++);
244         hash += (hash << 10);
245         hash ^= (hash >> 6);
246     }
247     hash += (hash << 3);
248     hash ^= (hash >> 11);
249     hash += (hash << 15);
250     return hash;
251 }
252
253 struct reclist *reclist_create(NMEM nmem, int numrecs)
254 {
255     int hashsize = 1;
256     struct reclist *res;
257
258     assert(numrecs);
259     while (hashsize < numrecs)
260         hashsize <<= 1;
261     res = nmem_malloc(nmem, sizeof(struct reclist));
262     res->hashtable 
263         = nmem_malloc(nmem, hashsize * sizeof(struct reclist_bucket*));
264     memset(res->hashtable, 0, hashsize * sizeof(struct reclist_bucket*));
265     res->hashtable_size = hashsize;
266     res->nmem = nmem;
267     res->hashmask = hashsize - 1; // Creates a bitmask
268
269     res->num_records = 0;
270     res->flatlist = nmem_malloc(nmem, numrecs * sizeof(struct record_cluster*));
271     res->flatlist_size = numrecs;
272
273     return res;
274 }
275
276 // Insert a record. Return record cluster (newly formed or pre-existing)
277 struct record_cluster *reclist_insert( struct reclist *l,
278                                        struct conf_service *service, 
279                                        struct record  *record,
280                                        char *merge_key, int *total)
281 {
282     unsigned int bucket;
283     struct reclist_bucket **p;
284     struct record_cluster *cluster = 0;
285     
286     assert(service);
287     assert(l);
288     assert(record);
289     assert(merge_key);
290     assert(total);
291
292     bucket = hash((unsigned char*) merge_key) & l->hashmask;
293
294     for (p = &l->hashtable[bucket]; *p; p = &(*p)->next)
295     {
296         // We found a matching record. Merge them
297         if (!strcmp(merge_key, (*p)->record->merge_key))
298         {
299             struct record_cluster *existing = (*p)->record;
300             record->next = existing->records;
301             existing->records = record;
302             cluster = existing;
303             break;
304         }
305     }
306     if (!cluster && l->num_records < l->flatlist_size)
307     {
308         struct reclist_bucket *new =
309             nmem_malloc(l->nmem, sizeof(struct reclist_bucket));
310         struct record_cluster *newc =
311             nmem_malloc(l->nmem, sizeof(struct record_cluster));
312         
313         record->next = 0;
314         new->record = newc;
315         new->next = 0;
316         newc->records = record;
317         newc->merge_key = merge_key;
318         newc->relevance = 0;
319         newc->term_frequency_vec = 0;
320         newc->recid = merge_key;
321         (*total)++;
322         newc->metadata = nmem_malloc(l->nmem,
323                 sizeof(struct record_metadata*) * service->num_metadata);
324         memset(newc->metadata, 0, 
325                sizeof(struct record_metadata*) * service->num_metadata);
326         newc->sortkeys = nmem_malloc(l->nmem,
327                 sizeof(struct record_metadata*) * service->num_sortkeys);
328         memset(newc->sortkeys, 0,
329                sizeof(union data_types*) * service->num_sortkeys);
330
331         *p = new;
332         l->flatlist[l->num_records++] = newc;
333         cluster = newc;
334     }
335     return cluster;
336 }
337
338
339 /*
340  * Local variables:
341  * c-basic-offset: 4
342  * c-file-style: "Stroustrup"
343  * indent-tabs-mode: nil
344  * End:
345  * vim: shiftwidth=4 tabstop=8 expandtab
346  */
347