added five asserts to make sure that the arguments of reclist_insert do exist
[pazpar2-moved-to-github.git] / src / reclists.c
1 /* $Id: reclists.c,v 1.12 2007-04-17 12:15:32 marc Exp $
2    Copyright (c) 2006-2007, Index Data.
3
4 This file is part of Pazpar2.
5
6 Pazpar2 is free software; you can redistribute it and/or modify it under
7 the terms of the GNU General Public License as published by the Free
8 Software Foundation; either version 2, or (at your option) any later
9 version.
10
11 Pazpar2 is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with Pazpar2; see the file LICENSE.  If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
19 02111-1307, USA.
20  */
21
22 #include <assert.h>
23
24 #if HAVE_CONFIG_H
25 #include <cconfig.h>
26 #endif
27
28 #include <yaz/yaz-util.h>
29
30 #include "pazpar2.h"
31 #include "reclists.h"
32
33 extern struct parameters global_parameters;
34
35 // Not threadsafe
36 static struct reclist_sortparms *sortparms = 0;
37
38 struct reclist_bucket
39 {
40     struct record_cluster *record;
41     struct reclist_bucket *next;
42 };
43
44 struct reclist_sortparms *reclist_parse_sortparms(NMEM nmem, const char *parms)
45 {
46     struct reclist_sortparms *res = 0;
47     struct reclist_sortparms **rp = &res;
48     struct conf_service *service = config->servers->service;
49
50     if (strlen(parms) > 256)
51         return 0;
52     while (*parms)
53     {
54         char parm[256];
55         char *pp;
56         const char *cpp;
57         int increasing;
58         int i;
59         int offset;
60         enum conf_sortkey_type type;
61         struct reclist_sortparms *new;
62
63         if (!(cpp = strchr(parms, ',')))
64             cpp = parms + strlen(parms);
65         strncpy(parm, parms, cpp - parms); 
66         parm[cpp-parms] = '\0';
67
68         if ((pp = strchr(parm, ':')))
69         {
70             increasing = pp[1] == '1' ? 1 : 0;
71             *pp = '\0';
72         }
73         else
74             increasing = 0;
75         if (!strcmp(parm, "relevance"))
76         {
77             type = Metadata_sortkey_relevance;
78             offset = -1;
79         }
80         else
81         {
82             for (i = 0; i < service->num_sortkeys; i++)
83             {
84                 struct conf_sortkey *sk = &service->sortkeys[i];
85                 if (!strcmp(sk->name, parm))
86                 {
87                     type = sk->type;
88                     if (type == Metadata_sortkey_skiparticle)
89                         type = Metadata_sortkey_string;
90                     break;
91                 }
92             }
93             if (i >= service->num_sortkeys)
94             {
95                 yaz_log(YLOG_FATAL, "Bad sortkey: %s", parm);
96                 return 0;
97             }
98             else
99                 offset = i;
100         }
101         new = *rp = nmem_malloc(nmem, sizeof(struct reclist_sortparms));
102         new->next = 0;
103         new->offset = offset;
104         new->type = type;
105         new->increasing = increasing;
106         rp = &new->next;
107         if (*(parms = cpp))
108             parms++;
109     }
110     return res;
111 }
112
113 static int reclist_cmp(const void *p1, const void *p2)
114 {
115     struct record_cluster *r1 = (*(struct record_cluster**) p1);
116     struct record_cluster *r2 = (*(struct record_cluster**) p2);
117     struct reclist_sortparms *s;
118
119     for (s = sortparms; s; s = s->next)
120     {
121         int res;
122         switch (s->type)
123         {
124             case Metadata_sortkey_relevance:
125                 res = r2->relevance - r1->relevance;
126                 break;
127             case Metadata_sortkey_string:
128                 res = strcmp(r2->sortkeys[s->offset]->text, r1->sortkeys[s->offset]->text);
129                 break;
130             case Metadata_sortkey_numeric:
131                 res = 0;
132                 break;
133             default:
134                 yaz_log(YLOG_FATAL, "Bad sort type: %d", s->type);
135                 exit(1);
136         }
137         if (res)
138         {
139             if (s->increasing)
140                 res *= -1;
141             return res;
142         }
143     }
144     return 0;
145 }
146
147 void reclist_sort(struct reclist *l, struct reclist_sortparms *parms)
148 {
149     sortparms = parms;
150     qsort(l->flatlist, l->num_records, sizeof(struct record_cluster*), reclist_cmp);
151     reclist_rewind(l);
152 }
153
154 struct record_cluster *reclist_read_record(struct reclist *l)
155 {
156     if (l->pointer < l->num_records)
157         return l->flatlist[l->pointer++];
158     else
159         return 0;
160 }
161
162 void reclist_rewind(struct reclist *l)
163 {
164     l->pointer = 0;
165 }
166
167 // Jenkins one-at-a-time hash (from wikipedia)
168 static unsigned int hash(const unsigned char *key)
169 {
170     unsigned int hash = 0;
171
172     while (*key)
173     {
174         hash += *(key++);
175         hash += (hash << 10);
176         hash ^= (hash >> 6);
177     }
178     hash += (hash << 3);
179     hash ^= (hash >> 11);
180     hash += (hash << 15);
181     return hash;
182 }
183
184 struct reclist *reclist_create(NMEM nmem, int numrecs)
185 {
186     int hashsize = 1;
187     struct reclist *res;
188
189     assert(numrecs);
190     while (hashsize < numrecs)
191         hashsize <<= 1;
192     res = nmem_malloc(nmem, sizeof(struct reclist));
193     res->hashtable = nmem_malloc(nmem, hashsize * sizeof(struct reclist_bucket*));
194     memset(res->hashtable, 0, hashsize * sizeof(struct reclist_bucket*));
195     res->hashtable_size = hashsize;
196     res->nmem = nmem;
197     res->hashmask = hashsize - 1; // Creates a bitmask
198
199     res->num_records = 0;
200     res->flatlist = nmem_malloc(nmem, numrecs * sizeof(struct record_cluster*));
201     res->flatlist_size = numrecs;
202
203     return res;
204 }
205
206 // Insert a record. Return record cluster (newly formed or pre-existing)
207 struct record_cluster *reclist_insert( struct reclist *l,
208                                        struct conf_service *service, 
209                                        struct record  *record,
210                                        char *merge_key, int *total)
211 {
212     unsigned int bucket;
213     struct reclist_bucket **p;
214     struct record_cluster *cluster = 0;
215     
216     assert(service);
217     assert(l);
218     assert(record);
219     assert(merge_key);
220     assert(total);
221
222     bucket = hash((unsigned char*) merge_key) & l->hashmask;
223
224     for (p = &l->hashtable[bucket]; *p; p = &(*p)->next)
225     {
226         // We found a matching record. Merge them
227         if (!strcmp(merge_key, (*p)->record->merge_key))
228         {
229             struct record_cluster *existing = (*p)->record;
230             record->next = existing->records;
231             existing->records = record;
232             cluster = existing;
233             break;
234         }
235     }
236     if (!cluster && l->num_records < l->flatlist_size)
237     {
238         struct reclist_bucket *new =
239             nmem_malloc(l->nmem, sizeof(struct reclist_bucket));
240         struct record_cluster *newc =
241             nmem_malloc(l->nmem, sizeof(struct record_cluster));
242         
243         record->next = 0;
244         new->record = newc;
245         new->next = 0;
246         newc->records = record;
247         newc->merge_key = merge_key;
248         newc->relevance = 0;
249         newc->term_frequency_vec = 0;
250         newc->recid = (*total)++;
251         newc->metadata = nmem_malloc(l->nmem,
252                 sizeof(struct record_metadata*) * service->num_metadata);
253         memset(newc->metadata, 0, sizeof(struct record_metadata*) * service->num_metadata);
254         newc->sortkeys = nmem_malloc(l->nmem,
255                 sizeof(struct record_metadata*) * service->num_sortkeys);
256         memset(newc->sortkeys, 0, sizeof(union data_types*) * service->num_sortkeys);
257
258         *p = new;
259         l->flatlist[l->num_records++] = newc;
260         cluster = newc;
261     }
262     return cluster;
263 }
264
265
266 /*
267  * Local variables:
268  * c-basic-offset: 4
269  * indent-tabs-mode: nil
270  * End:
271  * vim: shiftwidth=4 tabstop=8 expandtab
272  */