]> mj.ucw.cz Git - libucw.git/blob - lib/buck2obj.c
Added tests for the hash table module.
[libucw.git] / lib / buck2obj.c
1 /*
2  *      Generating Objects from Buckets
3  *
4  *      (c) 2004, Robert Spalek <robert@ucw.cz>
5  *      (c) 2004, Martin Mares <mj@ucw.cz>
6  */
7
8 #undef LOCAL_DEBUG
9
10 #include "lib/lib.h"
11 #include "lib/unaligned.h"
12 #include "lib/mempool.h"
13 #include "lib/fastbuf.h"
14 #include "lib/unicode.h"
15 #include "lib/object.h"
16 #include "lib/bucket.h"
17 #include "lib/lizard.h"
18 #include "lib/bbuf.h"
19 #include "lib/ff-utf8.h"
20
21 #include <stdlib.h>
22 #include <errno.h>
23 #include <unistd.h>
24
25 #define RET_ERR(num)    ({ errno = num; return -1; })
26
27 struct buck2obj_buf
28 {
29   bb_t bb;
30   struct lizard_buffer *lizard;
31 };
32
33 static uns get_attr_type;
34
35 void
36 get_attr_set_type(uns type)
37 {
38   if (type < BUCKET_TYPE_PLAIN || type > BUCKET_TYPE_V33_LIZARD)
39     die("Unknown buckettype %x", type);
40   get_attr_type = type;
41 }
42
43 int
44 get_attr(byte **pos, byte *end, struct parsed_attr *attr)
45 {
46   byte *ptr = *pos;
47   if (ptr >= end)
48     return -1;
49   if (get_attr_type < BUCKET_TYPE_V33)
50   {
51     if (get_attr_type == BUCKET_TYPE_PLAIN)
52     {
53       while (ptr < end && *ptr == '\n')
54         ptr++;
55       *pos = ptr;
56       if (ptr >= end)
57         return -1;
58     }
59     else if (*ptr == '\n')
60     {
61       *pos = ++ptr;
62       attr->attr = 0;
63       return 0;
64     }
65     attr->attr = *ptr++;
66     attr->val = ptr;
67     while (ptr < end && *ptr != '\n')
68       ptr++;
69     attr->len = ptr++ - attr->val;
70   }
71   else
72   {
73     uns len;
74     GET_UTF8_32(ptr, len);
75     if (!len--)
76     {
77       *pos = ptr;
78       attr->attr = 0;
79       return 0;
80     }
81     attr->attr = ptr[len];
82     attr->val = ptr;
83     attr->len = len;
84     ptr += len+1;
85   }
86   if (ptr > end)
87     die("Incomplete attribute %c", attr->attr);
88   *pos = ptr;
89   return attr->attr;
90 }
91
92 int
93 bget_attr(struct fastbuf *b, struct parsed_attr *attr)
94 {
95   static bb_t buf;
96   if (get_attr_type < BUCKET_TYPE_V33)
97   {
98     int c = bgetc(b);
99     if (c < 0)
100       return -1;
101     if (get_attr_type == BUCKET_TYPE_PLAIN)
102     {
103       while (c == '\n')
104         c = bgetc(b);
105       if (c < 0)
106         return -1;
107     }
108     else if (c == '\n')
109     {
110       attr->attr = 0;
111       return 0;
112     }
113     attr->attr = c;
114
115     byte *ptr, *end;
116     uns len = bdirect_read_prepare(b, &ptr);
117     end = ptr + len;
118     attr->val = ptr;
119     while (ptr < end && *ptr != '\n')
120       ptr++;
121     if (ptr < end)
122     {
123       bdirect_read_commit(b, ptr+1);
124       attr->len = ptr - attr->val;
125       return attr->attr;
126     }
127
128     len = 0;
129     c = bgetc(b);
130     while (c >= 0 && c != '\n')
131     {
132       bb_grow(&buf, len+1);
133       buf.ptr[len++] = c;
134       c = bgetc(b);
135     }
136     if (c < 0)
137       die("Incomplete attribute %c", attr->attr);
138     attr->val = buf.ptr;
139     attr->len = len;
140   }
141   else
142   {
143     int len = bget_utf8_32(b);
144     if (len < 0)
145       return -1;
146     if (!len)
147     {
148       attr->attr = 0;
149       return 0;
150     }
151     attr->len = len-1;
152
153     byte *ptr;
154     int avail = bdirect_read_prepare(b, &ptr);
155     if (avail >= len)
156     {
157       attr->val = ptr;
158       attr->attr = ptr[len-1];
159       bdirect_read_commit(b, ptr + len);
160       return attr->attr;
161     }
162     bb_grow(&buf, --len);
163     breadb(b, buf.ptr, len);
164     attr->val = buf.ptr;
165     attr->len = len;
166     attr->attr = bgetc(b);
167     if (attr->attr < 0)
168       die("Incomplete attribute %c", attr->attr);
169   }
170   return attr->attr;
171 }
172
173 struct buck2obj_buf *
174 buck2obj_alloc(void)
175 {
176   struct buck2obj_buf *buf = xmalloc(sizeof(struct buck2obj_buf));
177   bb_init(&buf->bb);
178   buf->lizard = lizard_alloc();
179   return buf;
180 }
181
182 void
183 buck2obj_free(struct buck2obj_buf *buf)
184 {
185   lizard_free(buf->lizard);
186   bb_done(&buf->bb);
187   xfree(buf);
188 }
189
190 static inline byte *
191 decode_attributes(byte *ptr, byte *end, struct odes *o, uns can_overwrite)
192 {
193   if (can_overwrite >= 2)
194     while (ptr < end)
195     {
196       uns len;
197       GET_UTF8_32(ptr, len);
198       if (!len--)
199         break;
200       byte type = ptr[len];
201
202       ptr[len] = 0;
203       obj_add_attr_ref(o, type, ptr);
204
205       ptr += len + 1;
206     }
207   else
208     while (ptr < end)
209     {
210       uns len;
211       GET_UTF8_32(ptr, len);
212       if (!len--)
213         break;
214       byte type = ptr[len];
215
216       byte *dup = mp_alloc_fast_noalign(o->pool, len+1);
217       memcpy(dup, ptr, len);
218       dup[len] = 0;
219       obj_add_attr_ref(o, type, dup);
220
221       ptr += len + 1;
222     }
223   return ptr;
224 }
225
226 int
227 buck2obj_parse(struct buck2obj_buf *buf, uns buck_type, uns buck_len, struct fastbuf *body, struct odes *o_hdr, uns *body_start, struct odes *o_body)
228 {
229   if (buck_type <= BUCKET_TYPE_PLAIN)
230   {
231     if (body_start)                     // there is no header part
232       *body_start = 0;
233     // ignore empty lines and read until the end of the bucket
234     sh_off_t end = btell(body) + buck_len;
235     byte buf[MAX_ATTR_SIZE];
236     while (btell(body) < end && bgets(body, buf, sizeof(buf)))
237       if (buf[0])
238         obj_add_attr(o_hdr, buf[0], buf+1);
239     ASSERT(btell(body) == end);
240   }
241   else if (buck_type == BUCKET_TYPE_V30)
242   {
243     sh_off_t start = btell(body);
244     sh_off_t end = start + buck_len;
245     byte buf[MAX_ATTR_SIZE];
246     while (btell(body) < end && bgets(body, buf, sizeof(buf)) && buf[0])
247       obj_add_attr(o_hdr, buf[0], buf+1);
248     if (body_start)
249       *body_start = btell(body) - start;
250     else
251     {
252       while (btell(body) < end && bgets(body, buf, sizeof(buf)))
253         if (buf[0])
254           obj_add_attr(o_body, buf[0], buf+1);
255       ASSERT(btell(body) == end);
256     }
257   }
258   else if (buck_type == BUCKET_TYPE_V33 || buck_type == BUCKET_TYPE_V33_LIZARD)
259   {
260     /* Avoid reading the whole bucket if only its header is needed.  */
261     if (body_start)
262     {
263       sh_off_t start = btell(body);
264       sh_off_t end = start + buck_len;
265       while (btell(body) < end)
266       {
267         uns len = bget_utf8_32(body);
268         if (!len)
269           break;
270         byte *buf = mp_alloc_fast_noalign(o_hdr->pool, len);
271         bread(body, buf, len);
272         uns type = buf[--len];
273         buf[len] = 0;
274         obj_add_attr_ref(o_hdr, type, buf);
275       }
276       *body_start = btell(body) - start;
277       return 0;
278     }
279
280     /* Read all the bucket into 1 buffer, 0-copy if possible.  */
281     byte *ptr, *end;
282     uns len = bdirect_read_prepare(body, &ptr);
283     uns copied = 0;
284     if (len < buck_len
285     || (body->can_overwrite_buffer < 2 && buck_type == BUCKET_TYPE_V33))
286     {
287       /* Copy if the original buffer is too small.
288        * If it is write-protected, copy it also if it is uncompressed.  */
289       DBG("NO ZC: %d < %d, %d %08x", len, buck_len, body->can_overwrite_buffer, buck_type);
290       bb_grow(&buf->bb, buck_len);
291       len = bread(body, buf->bb.ptr, buck_len);
292       ptr = buf->bb.ptr;
293       copied = 1;
294     }
295     else
296       DBG("ZC (%d >= %d, %d %08x)", len, buck_len, body->can_overwrite_buffer, buck_type);
297     end = ptr + buck_len;
298
299     ptr = decode_attributes(ptr, end, o_hdr, 0);                // header
300     if (buck_type == BUCKET_TYPE_V33_LIZARD)            // decompression
301     {
302       if (ptr + 8 > end)
303         {
304           if (ptr == end)                               // truncated bucket
305             goto commit;
306           RET_ERR(EINVAL);
307         }
308       len = GET_U32(ptr);
309       ptr += 4;
310       uns adler = GET_U32(ptr);
311       ptr += 4;
312       byte *new_ptr = lizard_decompress_safe(ptr, buf->lizard, len);
313       if (!new_ptr)
314         return -1;
315       if (adler32(new_ptr, len) != adler)
316         RET_ERR(EINVAL);
317       if (!copied)
318         bdirect_read_commit(body, end);
319       ptr = new_ptr;
320       end = ptr + len;
321       copied = 1;
322     }
323     ptr = decode_attributes(ptr, end, o_body, 2);       // body
324     if (ptr != end)
325       RET_ERR(EINVAL);
326   commit:
327     if (!copied)
328       bdirect_read_commit_modified(body, ptr);
329   }
330   else
331     {
332       bskip(body, buck_len);
333       RET_ERR(EINVAL);
334     }
335   return 0;
336 }
337
338 struct odes *
339 obj_read_bucket(struct buck2obj_buf *buf, struct mempool *pool, uns buck_type, uns buck_len, struct fastbuf *body, uns *body_start)
340 {
341   struct odes *o = obj_new(pool);
342   if (buck2obj_parse(buf, buck_type, buck_len, body, o, body_start, o) < 0)
343     return NULL;
344   else
345     return o;
346 }
347
348 int
349 obj_read(struct fastbuf *f, struct odes *o)
350 {
351   byte buf[MAX_ATTR_SIZE];
352
353   while (bgets(f, buf, sizeof(buf)))
354     {
355       if (!buf[0])
356         return 1;
357       obj_add_attr(o, buf[0], buf+1);
358     }
359   return 0;
360 }