]> mj.ucw.cz Git - libucw.git/blob - lib/buck2obj.c
ef999f1228ee0c06d4d20c8628e5cc8d719252c4
[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 buck2obj_convert(struct buck2obj_buf *buf, uns buck_type, struct fastbuf *body)
123 {
124   mp_flush(buf->mp);
125   struct odes *o = obj_new(buf->mp);
126
127   if (buck_type < BUCKET_TYPE_V33)
128     obj_read_multi(body, o);
129   else
130   {
131     /* Compute the length of the bucket.  We cannot fetch this attribute
132      * directly due to remote indexing.  */
133     bseek(body, 0, SEEK_END);
134     sh_off_t buck_len = btell(body);
135     bsetpos(body, 0);
136
137     /* Read all the bucket into 1 buffer, 0-copy if possible.  */
138     int can_overwrite = bconfig(body, BCONFIG_CAN_OVERWRITE, 0);
139     if (can_overwrite < 0)
140       can_overwrite = 0;
141     uns overwritten;
142     byte *ptr, *end;
143     uns len = bdirect_read_prepare(body, &ptr);
144     if (len < buck_len
145     || (can_overwrite < 2 && buck_type == BUCKET_TYPE_V33))
146     {
147       /* Copy if the original buffer is too small.
148        * If it is write-protected, copy it also if it is uncompressed.  */
149       if (buck_len > buf->raw_len)
150         buck2obj_realloc(buf, buck_len);
151       len = bread(body, buf->raw, buck_len);
152       ptr = buf->raw;
153       can_overwrite = 2;
154       overwritten = 0;
155     }
156     else
157       overwritten = can_overwrite > 1;
158     end = ptr + len;
159
160     ptr = decode_attributes(ptr, end, o, can_overwrite);// header
161     if (buck_type == BUCKET_TYPE_V33)
162       ;
163     else if (buck_type == BUCKET_TYPE_V33_LIZARD)       // decompression
164     {
165       len = GET_U32(ptr);
166       ptr += 4;
167       int res = lizard_decompress_safe(ptr, buf->lizard, len);
168       if (res != (int) len)
169       {
170         if (res >= 0)
171           errno = EINVAL;
172         return NULL;
173       }
174       ptr = buf->lizard->ptr;
175       end = ptr + len;
176       can_overwrite = 2;
177     }
178     else                                                // unknown bucket type
179       RET_ERR(EINVAL);
180     ASSERT(can_overwrite == 2);                         // because of the policy and decompression
181     ptr = decode_attributes(ptr, end, o, 2);            // body
182
183     if (ptr != end)
184       RET_ERR(EINVAL);
185     /* If (overwritten), bflush(body) might be needed.  */
186   }
187   return o;
188 }