16aaf31e1c5bcc5d20806fcb7d1ceab9b54f711d
[idzebra-moved-to-github.git] / rset / rset.c
1 /* $Id: rset.c,v 1.46 2005-05-24 11:35:43 adam Exp $
2    Copyright (C) 1995-2005
3    Index Data ApS
4
5 This file is part of the Zebra server.
6
7 Zebra is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
10 version.
11
12 Zebra is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Zebra; see the file LICENSE.zebra.  If not, write to the
19 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
20 02111-1307, USA.
21 */
22
23 #include <stdio.h>
24 #include <string.h>
25 #include <idzebra/util.h>
26 #include <assert.h>
27 #include <yaz/nmem.h>
28 #include <rset.h>
29
30 static int log_level = 0;
31 static int log_level_initialized = 0;
32
33 /** \fn rfd_create_base(RSET rs)
34  *
35  * creates an rfd. Either allocates a new one, in which case the priv 
36  * pointer is null, and will have to be filled in, or picks up one 
37  * from the freelist, in which case the priv is already allocated,
38  * and presumably everything that hangs from it as well 
39  */
40 RSFD rfd_create_base(RSET rs)
41 {
42     RSFD rnew = rs->free_list;
43
44     if (rnew) 
45     {
46         rs->free_list = rnew->next;
47         assert(rnew->rset==rs);
48         yaz_log(log_level, "rfd_create_base (fl): rfd=%p rs=%p fl=%p priv=%p", 
49                 rnew, rs, rs->free_list, rnew->priv); 
50     } 
51     else
52     {
53         rnew = nmem_malloc(rs->nmem, sizeof(*rnew));
54         rnew->counted_buf = nmem_malloc(rs->nmem, rs->keycontrol->key_size);
55         rnew->priv = 0;
56         rnew->rset = rs;
57         yaz_log(log_level, "rfd_create_base (new): rfd=%p rs=%p fl=%p priv=%p", 
58                 rnew, rs, rs->free_list, rnew->priv); 
59     }
60     rnew->next = rs->use_list;
61     rs->use_list = rnew;
62     rnew->counted_items = 0;
63     return rnew;
64 }
65
66 /** \fn rfd_delete_base
67  *
68  * puts an rfd into the freelist of the rset. Only when the rset gets
69  * deleted, will all the nmem disappear */
70 void rset_close(RSFD rfd)
71 {
72     RSFD *pfd;
73     RSET rs = rfd->rset;
74
75     if (rs->hits_count == 0)
76     {
77         TERMID termid;
78         char buf[100];
79         while(rfd->counted_items < rs->hits_limit
80               && rset_default_read(rfd, buf, &termid))
81             ;
82         
83         rs->hits_count = rfd->counted_items;
84         rs->hits_approx = 0;
85         if (rs->hits_count >= rs->hits_limit)
86         {
87             double cur, tot;
88             zint est;
89             rset_pos(rfd, &cur, &tot); 
90             if (tot > 0) {
91                 int i;
92                 double ratio = cur/tot;
93                 est = (zint)(0.5 + rs->hits_count / ratio);
94                 yaz_log(log_level, "Estimating hits (%s) "
95                         "%0.1f->" ZINT_FORMAT
96                         "; %0.1f->" ZINT_FORMAT,
97                         rs->control->desc,
98                         cur, rs->hits_count,
99                         tot, est);
100                 i = 0; /* round to significant digits */
101                 while (est > rs->hits_round) {
102                     est /= 10;
103                     i++;
104                 }
105                 while (i--)
106                     est *= 10;
107                 rs->hits_count = est;
108                 rs->hits_approx = 1;
109             }
110         }
111         yaz_log(log_level, "rset_close p=%p count=" ZINT_FORMAT, rs,
112                 rs->hits_count);
113     }
114     (*rs->control->f_close)(rfd);
115     
116     yaz_log(log_level, "rfd_delete_base: rfd=%p rs=%p priv=%p fl=%p",
117             rfd, rs, rfd->priv, rs->free_list); 
118     for (pfd = &rs->use_list; *pfd; pfd = &(*pfd)->next)
119         if (*pfd == rfd)
120         {
121             *pfd = (*pfd)->next;
122             rfd->next = rs->free_list;
123             rs->free_list = rfd;
124             return;
125         }
126     yaz_log(YLOG_WARN, "rset_close handle not found. type=%s",
127             rs->control->desc);
128 }
129
130 RSET rset_create_base(const struct rset_control *sel, 
131                       NMEM nmem, struct rset_key_control *kcontrol,
132                       int scope, TERMID term,
133                       int no_children, RSET *children)
134 {
135     RSET rset;
136     NMEM M;
137     assert(nmem);  /* can not yet be used, api/t4 fails */
138     if (!log_level_initialized) 
139     {
140         log_level = yaz_log_module_level("rset");
141         log_level_initialized = 1;
142     }
143
144     if (nmem) 
145         M = nmem;
146     else
147         M = nmem_create();
148     rset = (RSET) nmem_malloc(M, sizeof(*rset));
149     yaz_log(log_level, "rs_create(%s) rs=%p (nm=%p)", sel->desc, rset, nmem); 
150     rset->nmem = M;
151     if (nmem)
152         rset->my_nmem = 0;
153     else 
154         rset->my_nmem = 1;
155     rset->control = sel;
156     rset->refcount = 1;
157     rset->priv = 0;
158     rset->free_list = NULL;
159     rset->use_list = NULL;
160     rset->hits_count = 0;
161     rset->hits_limit = 1000;
162     rset->hits_round = 1000;
163     rset->keycontrol = kcontrol;
164     (*kcontrol->inc)(kcontrol);
165     rset->scope = scope;
166     rset->term = term;
167     if (term)
168         term->rset = rset;
169
170     rset->no_children = no_children;
171     rset->children = 0;
172     if (no_children)
173     {
174         rset->children = (RSET*)
175             nmem_malloc(rset->nmem, no_children*sizeof(RSET *));
176         memcpy(rset->children, children, no_children*sizeof(RSET *));
177     }
178     return rset;
179 }
180
181 void rset_delete(RSET rs)
182 {
183     (rs->refcount)--;
184     yaz_log(log_level, "rs_delete(%s), rs=%p, refcount=%d",
185             rs->control->desc, rs, rs->refcount); 
186     if (!rs->refcount)
187     {
188         int i;
189         if (rs->use_list)
190             yaz_log(YLOG_WARN, "rs_delete(%s) still has RFDs in use",
191                     rs->control->desc);
192         for (i = 0; i<rs->no_children; i++)
193             rset_delete(rs->children[i]);
194         (*rs->control->f_delete)(rs);
195         (*rs->keycontrol->dec)(rs->keycontrol);
196         if (rs->my_nmem)
197             nmem_destroy(rs->nmem);
198     }
199 }
200
201 int rfd_is_last(RSFD rfd)
202 {
203     if (rfd->rset->use_list == rfd && rfd->next == 0)
204         return 1;
205     return 0;
206 }
207
208 RSET rset_dup (RSET rs)
209 {
210     (rs->refcount)++;
211     yaz_log(log_level, "rs_dup(%s), rs=%p, refcount=%d",
212             rs->control->desc, rs, rs->refcount); 
213     (*rs->keycontrol->inc)(rs->keycontrol);
214     return rs;
215 }
216
217 /** 
218  * rset_count uses rset_pos to get the total and returns that.
219  * This is ok for rsisamb/c/s, and for some other rsets, but in case of
220  * booleans etc it will give bad estimate, as nothing has been read
221  * from that rset
222  */
223 zint rset_count(RSET rs)
224 {
225     double cur, tot;
226     RSFD rfd = rset_open(rs, 0);
227     rset_pos(rfd, &cur, &tot);
228     rset_close(rfd);
229     return (zint) tot;
230 }
231
232 /** rset_get_no_terms is a getterms function for those that don't have any */
233 void rset_get_no_terms(RSET ct, TERMID *terms, int maxterms, int *curterm)
234 {
235     return;
236 }
237
238 /* rset_get_one_term gets that one term from an rset. Used by rsisamX */
239 void rset_get_one_term(RSET ct, TERMID *terms, int maxterms, int *curterm)
240 {
241     if (ct->term)
242     {
243         if (*curterm < maxterms)
244             terms[*curterm] = ct->term;
245         (*curterm)++;
246     }
247 }
248
249
250 TERMID rset_term_create(const char *name, int length, const char *flags,
251                         int type, NMEM nmem)
252
253 {
254     TERMID t;
255     yaz_log (log_level, "term_create '%s' %d f=%s type=%d nmem=%p",
256             name, length, flags, type, nmem);
257     t= (TERMID) nmem_malloc(nmem, sizeof(*t));
258     if (!name)
259         t->name = NULL;
260     else if (length == -1)
261         t->name = nmem_strdup(nmem, name);
262     else
263     {
264         t->name = (char*) nmem_malloc(nmem, length+1);
265         memcpy (t->name, name, length);
266         t->name[length] = '\0';
267     }
268     if (!flags)
269         t->flags = NULL;
270     else
271         t->flags = nmem_strdup(nmem, flags);
272     t->type = type;
273     t->rankpriv = 0;
274     t->rset = 0;
275     return t;
276 }
277
278 int rset_default_read(RSFD rfd, void *buf, TERMID *term)
279 {
280     RSET rset = rfd->rset;
281     int rc = (*rset->control->f_read)(rfd, buf, term);
282     if (rc > 0)
283     {
284         if (rfd->counted_items == 0 ||
285             (rset->keycontrol->cmp)(buf, rfd->counted_buf) >= rset->scope)
286         {
287             memcpy(rfd->counted_buf, buf, rset->keycontrol->key_size);
288             rfd->counted_items++;
289         }
290     }
291     return rc;
292 }
293
294 int rset_default_forward(RSFD rfd, void *buf, TERMID *term,
295                          const void *untilbuf)
296 {
297     RSET rset = rfd->rset;
298     int more;
299
300     if (rset->control->f_forward &&
301         rfd->counted_items >= rset->hits_limit)
302     {
303         assert (rset->control->f_forward != rset_default_forward);
304         return rset->control->f_forward(rfd, buf, term, untilbuf);
305     }
306     
307     while ((more = rset_read(rfd, buf, term)) > 0)
308     {
309         if ((rfd->rset->keycontrol->cmp)(untilbuf, buf) <= 1)
310             break;
311     }
312     if (log_level)
313         yaz_log (log_level, "rset_default_forward exiting m=%d c=%d",
314                  more, rset->scope);
315     
316     return more;
317 }
318
319 void rset_visit(RSET rset, int level)
320 {
321     int i;
322     yaz_log(YLOG_LOG, "%*s%c " ZINT_FORMAT, level, "",
323             rset->hits_approx ? '~' : '=',
324             rset->hits_count);
325     for (i = 0; i<rset->no_children; i++)
326         rset_visit(rset->children[i], level+1);
327 }
328