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