]> mj.ucw.cz Git - libucw.git/blob - lib/buck2obj.c
buck2obj_convert() renamed to obj_read_bucket()
[libucw.git] / lib / buck2obj.c
1 /*
2  *      Bucket -> Object converter
3  *
4  *      (c) 2004, Robert Spalek <robert@ucw.cz>
5  */
6
7 #include "lib/lib.h"
8 #include "lib/unaligned.h"
9 #include "lib/pools.h"
10 #include "lib/fastbuf.h"
11 #include "charset/unicode.h"
12 #include "lib/object.h"
13 #include "lib/bucket.h"
14 #include "lib/lizard.h"
15 #include "lib/buck2obj.h"
16
17 #include <stdlib.h>
18 #include <errno.h>
19 #include <unistd.h>
20
21 #define MAX_HEADER_SIZE 1024            // extra space for the header not counted in MaxObjSize
22 #define RET_ERR(num)    ({ errno = num; return NULL; })
23
24 struct buck2obj_buf
25 {
26   uns max_len, raw_len;
27   byte *raw;
28   struct lizard_buffer *lizard;
29   struct mempool *mp;
30 };
31
32 static void
33 buck2obj_alloc_internal(struct buck2obj_buf *buf, uns max_len)
34 {
35   buf->max_len = max_len;
36   buf->raw_len = max_len * LIZARD_MAX_MULTIPLY + LIZARD_MAX_ADD + MAX_HEADER_SIZE;
37   buf->raw = xmalloc(buf->raw_len);
38 }
39
40 struct buck2obj_buf *
41 buck2obj_alloc(uns max_len, struct mempool *mp)
42 {
43   struct buck2obj_buf *buf = xmalloc(sizeof(struct buck2obj_buf));
44   buck2obj_alloc_internal(buf, max_len);
45   buf->lizard = lizard_alloc(max_len);
46   buf->mp = mp;
47   return buf;
48 }
49
50 void
51 buck2obj_free(struct buck2obj_buf *buf)
52 {
53   lizard_free(buf->lizard);
54   xfree(buf->raw);
55   xfree(buf);
56 }
57
58 void
59 buck2obj_realloc(struct buck2obj_buf *buf, uns max_len)
60 {
61   if (max_len <= buf->max_len)
62     return;
63   if (max_len < 2*buf->max_len + 1)             // to ensure amortized logarithmic complexity
64     max_len = 2*buf->max_len + 1;
65   xfree(buf->raw);
66   buck2obj_alloc_internal(buf, max_len);
67   lizard_realloc(buf->lizard, max_len);
68 }
69
70 static inline byte *
71 decode_attributes(byte *ptr, byte *end, struct odes *o, uns can_overwrite)
72 {
73   if (can_overwrite >= 2)
74     while (ptr < end)
75     {
76       uns len;
77       GET_UTF8(ptr, len);
78       if (!len--)
79         break;
80       byte type = ptr[len];
81
82       ptr[len] = 0;
83       obj_add_attr_ref(o, type, ptr);
84
85       ptr += len + 1;
86     }
87   else if (can_overwrite == 1)
88     while (ptr < end)
89     {
90       uns len;
91       GET_UTF8(ptr, len);
92       if (!len--)
93         break;
94       byte type = ptr[len];
95
96       ptr[len] = 0;
97       obj_add_attr(o, type, ptr);
98       ptr[len] = type;
99
100       ptr += len + 1;
101     }
102   else
103     while (ptr < end)
104     {
105       uns len;
106       GET_UTF8(ptr, len);
107       if (!len--)
108         break;
109       byte type = ptr[len];
110
111       byte *dup = mp_alloc_fast_noalign(o->pool, len+1);
112       memcpy(dup, ptr, len);
113       dup[len] = 0;
114       obj_add_attr_ref(o, type, dup);
115
116       ptr += len + 1;
117     }
118   return ptr;
119 }
120
121 struct odes *
122 obj_read_bucket(struct buck2obj_buf *buf, uns buck_type, struct fastbuf *body, uns want_body)
123 {
124   mp_flush(buf->mp);
125   struct odes *o = obj_new(buf->mp);
126
127   if (buck_type < BUCKET_TYPE_V33)
128   {
129     if (want_body)                      // ignore empty lines, read until EOF
130       obj_read_multi(body, o);
131     else                                // end on EOF or the first empty line
132       obj_read(body, o);
133   }
134   else
135   {
136     /* Compute the length of the bucket.  We cannot fetch this attribute
137      * directly due to remote indexing.  */
138     bseek(body, 0, SEEK_END);
139     sh_off_t buck_len = btell(body);
140     bsetpos(body, 0);
141
142     /* Read all the bucket into 1 buffer, 0-copy if possible.  */
143     int can_overwrite = bconfig(body, BCONFIG_CAN_OVERWRITE, 0);
144     if (can_overwrite < 0)
145       can_overwrite = 0;
146     uns overwritten;
147     byte *ptr, *end;
148     uns len = bdirect_read_prepare(body, &ptr);
149     if (len < buck_len
150     || (can_overwrite < 2 && buck_type == BUCKET_TYPE_V33))
151     {
152       /* Copy if the original buffer is too small.
153        * If it is write-protected, copy it also if it is uncompressed.  */
154       if (buck_len > buf->raw_len)
155         buck2obj_realloc(buf, buck_len);
156       len = bread(body, buf->raw, buck_len);
157       ptr = buf->raw;
158       can_overwrite = 2;
159       overwritten = 0;
160     }
161     else
162       overwritten = can_overwrite > 1;
163     end = ptr + len;
164
165     ptr = decode_attributes(ptr, end, o, can_overwrite);// header
166     if (!want_body)
167       return o;
168     if (buck_type == BUCKET_TYPE_V33)
169       ;
170     else if (buck_type == BUCKET_TYPE_V33_LIZARD)       // decompression
171     {
172       len = GET_U32(ptr);
173       ptr += 4;
174       int res = lizard_decompress_safe(ptr, buf->lizard, len);
175       if (res != (int) len)
176       {
177         if (res >= 0)
178           errno = EINVAL;
179         return NULL;
180       }
181       ptr = buf->lizard->ptr;
182       end = ptr + len;
183       can_overwrite = 2;
184     }
185     else                                                // unknown bucket type
186       RET_ERR(EINVAL);
187     ASSERT(can_overwrite == 2);                         // because of the policy and decompression
188     ptr = decode_attributes(ptr, end, o, 2);            // body
189
190     if (ptr != end)
191       RET_ERR(EINVAL);
192     /* If (overwritten), bflush(body) might be needed.  */
193   }
194   return o;
195 }