981d3a5fff6e396aeffe18108d4ea884890409cd
[idzebra-moved-to-github.git] / isams / isams.c
1 /* This file is part of the Zebra server.
2    Copyright (C) 2004-2013 Index Data
3
4 Zebra 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 Zebra 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
21 #if HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 #include <stdlib.h>
25 #include <assert.h>
26 #include <string.h>
27 #include <stdio.h>
28
29 #include <yaz/log.h>
30 #include <yaz/xmalloc.h>
31 #include <idzebra/isams.h>
32
33 typedef struct {
34     int last_offset;
35     int last_block;
36 } ISAMS_head;
37
38 typedef unsigned ISAMS_BLOCK_SIZE;
39
40 struct ISAMS_s {
41     ISAMS_M *method;
42     ISAMS_head head;
43     ISAMS_head head_old;
44     char *merge_buf;
45
46     int block_size;
47     int debug;
48     BFile bf;
49 };
50
51 struct ISAMS_PP_s {
52     ISAMS is;
53     char *buf;
54     int block_offset;
55     int block_no;
56     void *decodeClientData;
57     int numKeys;
58     int numRead;
59 };
60
61 void isams_getmethod (ISAMS_M *m)
62 {
63     m->codec.start = NULL;
64     m->codec.decode = NULL;
65     m->codec.encode = NULL;
66     m->codec.stop = NULL;
67     m->codec.reset = NULL;
68
69     m->compare_item = NULL;
70     m->log_item = NULL;
71
72     m->debug = 1;
73     m->block_size = 128;
74 }
75
76 ISAMS isams_open (BFiles bfs, const char *name, int writeflag,
77                   ISAMS_M *method)
78 {
79     ISAMS is = (ISAMS) xmalloc (sizeof(*is));
80
81     is->method = (ISAMS_M *) xmalloc (sizeof(*is->method));
82     memcpy (is->method, method, sizeof(*method));
83     is->block_size = is->method->block_size;
84     is->debug = is->method->debug;
85
86     is->bf = bf_open (bfs, name, is->block_size, writeflag);
87
88     if (!bf_read (is->bf, 0, 0, sizeof(ISAMS_head), &is->head))
89     {
90         is->head.last_block = 1;
91         is->head.last_offset = 0;
92     }
93     memcpy (&is->head_old, &is->head, sizeof(is->head));
94     is->merge_buf = (char *) xmalloc(2*is->block_size);
95     memset(is->merge_buf, 0, 2*is->block_size);
96     return is;
97 }
98
99 int isams_close (ISAMS is)
100 {
101     if (memcmp(&is->head, &is->head_old, sizeof(is->head)))
102     {
103         if (is->head.last_offset)
104             bf_write(is->bf, is->head.last_block, 0, is->head.last_offset,
105                      is->merge_buf);
106         bf_write (is->bf, 0, 0, sizeof(is->head), &is->head);
107     }
108     bf_close (is->bf);
109     xfree (is->merge_buf);
110     xfree (is->method);
111     xfree (is);
112     return 0;
113 }
114
115 ISAM_P isams_merge (ISAMS is, ISAMS_I data)
116 {
117     char i_item[128];
118     int i_more, i_mode;
119     void *r_clientData;
120     int first_block = is->head.last_block;
121     int first_offset = is->head.last_offset;
122     int count = 0;
123
124     r_clientData = (*is->method->codec.start)();
125
126     is->head.last_offset += sizeof(int);
127     if (is->head.last_offset > is->block_size)
128     {
129         if (is->debug > 2)
130             yaz_log (YLOG_LOG, "first_block=%d", first_block);
131         bf_write(is->bf, is->head.last_block, 0, 0, is->merge_buf);
132         (is->head.last_block)++;
133         is->head.last_offset -= is->block_size;
134         memcpy (is->merge_buf, is->merge_buf + is->block_size,
135                 is->head.last_offset);
136     }
137     while (1)
138     {
139         char *tmp_ptr = i_item;
140         i_more = (*data->read_item)(data->clientData, &tmp_ptr, &i_mode);
141         assert (i_mode);
142
143         if (!i_more)
144             break;
145         else
146         {
147             char *r_out_ptr = is->merge_buf + is->head.last_offset;
148
149             const char *i_item_ptr = i_item;
150             (*is->method->codec.encode)(r_clientData, &r_out_ptr, &i_item_ptr);
151             is->head.last_offset = r_out_ptr - is->merge_buf;
152             if (is->head.last_offset > is->block_size)
153             {
154                 bf_write(is->bf, is->head.last_block, 0, 0, is->merge_buf);
155                 (is->head.last_block)++;
156                 is->head.last_offset -= is->block_size;
157                 memcpy (is->merge_buf, is->merge_buf + is->block_size,
158                         is->head.last_offset);
159             }
160             count++;
161         }
162     }
163     (*is->method->codec.stop)(r_clientData);
164     if (first_block == is->head.last_block)
165         memcpy(is->merge_buf + first_offset, &count, sizeof(int));
166     else if (first_block == is->head.last_block-1)
167     {
168         int gap = first_offset + sizeof(int) - is->block_size;
169         assert (gap <= (int) sizeof(int));
170         if (gap > 0)
171         {
172             if (gap < (int) sizeof(int))
173                 bf_write(is->bf, first_block, first_offset, sizeof(int)-gap,
174                          &count);
175             memcpy (is->merge_buf, ((char*)&count)+(sizeof(int)-gap), gap);
176         }
177         else
178             bf_write(is->bf, first_block, first_offset, sizeof(int), &count);
179     }
180     else
181     {
182         bf_write(is->bf, first_block, first_offset, sizeof(int), &count);
183     }
184     return first_block * is->block_size + first_offset;
185 }
186
187 ISAMS_PP isams_pp_open (ISAMS is, ISAM_P pos)
188 {
189     ISAMS_PP pp = (ISAMS_PP) xmalloc (sizeof(*pp));
190
191     if (is->debug > 1)
192         yaz_log (YLOG_LOG, "isams: isams_pp_open pos=%ld", (long) pos);
193     pp->is = is;
194     pp->decodeClientData = (*is->method->codec.start)();
195     pp->numKeys = 0;
196     pp->numRead = 0;
197     pp->buf = (char *) xmalloc(is->block_size*2);
198     pp->block_no = (int) (pos/is->block_size);
199     pp->block_offset = (int) (pos - pp->block_no * is->block_size);
200     if (is->debug)
201         yaz_log (YLOG_LOG, "isams: isams_pp_open off=%d no=%d",
202               pp->block_offset, pp->block_no);
203     if (pos)
204     {
205         bf_read (is->bf, pp->block_no, 0, 0, pp->buf);
206         bf_read (is->bf, pp->block_no+1, 0, 0, pp->buf + is->block_size);
207         memcpy(&pp->numKeys, pp->buf + pp->block_offset, sizeof(int));
208         if (is->debug)
209             yaz_log (YLOG_LOG, "isams: isams_pp_open numKeys=%d", pp->numKeys);
210         pp->block_offset += sizeof(int);
211     }
212     return pp;
213 }
214
215 void isams_pp_close (ISAMS_PP pp)
216 {
217     (*pp->is->method->codec.stop)(pp->decodeClientData);
218     xfree(pp->buf);
219     xfree(pp);
220 }
221
222 int isams_pp_num (ISAMS_PP pp)
223 {
224     return pp->numKeys;
225 }
226
227 int isams_pp_read (ISAMS_PP pp, void *buf)
228 {
229     char *cp = buf;
230     return isams_read_item (pp, &cp);
231 }
232
233 int isams_read_item (ISAMS_PP pp, char **dst)
234 {
235     const char *src;
236     if (pp->numRead >= pp->numKeys)
237         return 0;
238     (pp->numRead)++;
239     if (pp->block_offset > pp->is->block_size)
240     {
241         pp->block_offset -= pp->is->block_size;
242         (pp->block_no)++;
243         memcpy (pp->buf, pp->buf + pp->is->block_size, pp->is->block_size);
244         bf_read (pp->is->bf, pp->block_no+1, 0, 0,
245                  pp->buf + pp->is->block_size);
246     }
247     src = pp->buf + pp->block_offset;
248     (*pp->is->method->codec.decode)(pp->decodeClientData, dst, &src);
249     pp->block_offset = src - pp->buf;
250     return 1;
251 }
252
253
254 /*
255  * Local variables:
256  * c-basic-offset: 4
257  * c-file-style: "Stroustrup"
258  * indent-tabs-mode: nil
259  * End:
260  * vim: shiftwidth=4 tabstop=8 expandtab
261  */
262