1 | /* |
2 | * This Source Code Form is subject to the terms of the Mozilla Public |
3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
5 | * |
6 | * Copyright 1997 - July 2008 CWI, August 2008 - 2019 MonetDB B.V. |
7 | */ |
8 | |
9 | /* |
10 | * @f blob |
11 | * @v 1.0 |
12 | * @a Wilko Quak, Peter Boncz, M. Kersten, N. Nes |
13 | * @+ The blob data type |
14 | * The datatype 'blob' introduced here illustrates the power |
15 | * in the hands of a programmer to extend the functionality of the |
16 | * Monet GDK library. It consists of an interface specification for |
17 | * the necessary operators, a startup routine to register the |
18 | * type in thekernel, and some additional operators used outside |
19 | * the kernel itself. |
20 | * |
21 | * The 'blob' data type is used in many database engines to |
22 | * store a variable sized atomary value. |
23 | * Its definition forms a generic base to store arbitrary structures |
24 | * in the database, without knowing its internal coding, layout, |
25 | * or interpretation. |
26 | * |
27 | * The blob memory layout consists of first 4 bytes containing |
28 | * the bytes-size of the blob (excluding the integer), and then just binary data. |
29 | * |
30 | * @+ Module Definition |
31 | */ |
32 | #include "monetdb_config.h" |
33 | #include "blob.h" |
34 | |
35 | int TYPE_blob; |
36 | |
37 | mal_export str BLOBprelude(void *ret); |
38 | |
39 | mal_export int BLOBcmp(const blob *l, const blob *r); |
40 | mal_export BUN BLOBhash(const blob *b); |
41 | mal_export const blob *BLOBnull(void); |
42 | mal_export var_t BLOBput(Heap *h, var_t *bun, const blob *val); |
43 | mal_export void BLOBdel(Heap *h, var_t *index); |
44 | mal_export size_t BLOBlength(const blob *p); |
45 | mal_export void BLOBheap(Heap *heap, size_t capacity); |
46 | mal_export str BLOBtoblob(blob **retval, str *s); |
47 | mal_export str BLOBnitems(int *ret, blob **b); |
48 | mal_export int BLOBget(Heap *h, int *bun, int *l, blob **val); |
49 | mal_export blob * BLOBread(blob *a, stream *s, size_t cnt); |
50 | mal_export gdk_return BLOBwrite(const blob *a, stream *s, size_t cnt); |
51 | |
52 | mal_export str BLOBblob_blob(blob **d, blob **s); |
53 | mal_export str BLOBblob_fromstr(blob **b, const char **d); |
54 | |
55 | str |
56 | BLOBprelude(void *ret) |
57 | { |
58 | (void) ret; |
59 | TYPE_blob = ATOMindex("blob" ); |
60 | return MAL_SUCCEED; |
61 | } |
62 | |
63 | var_t |
64 | blobsize(size_t nitems) |
65 | { |
66 | if (nitems == ~(size_t) 0) |
67 | nitems = 0; |
68 | assert(offsetof(blob, data) + nitems <= VAR_MAX); |
69 | return (var_t) (offsetof(blob, data) + nitems); |
70 | } |
71 | |
72 | static blob nullval = { |
73 | ~(size_t) 0 |
74 | }; |
75 | |
76 | static char hexit[] = "0123456789ABCDEF" ; |
77 | |
78 | /* |
79 | * @- Wrapping section |
80 | * This section contains the wrappers to re-use the implementation |
81 | * section of the blob modules from MonetDB 4.3 |
82 | * @- |
83 | */ |
84 | int |
85 | BLOBcmp(const blob *l, const blob *r) |
86 | { |
87 | int c; |
88 | if (r->nitems == ~(size_t)0) |
89 | return l->nitems != ~(size_t)0; |
90 | if (l->nitems == ~(size_t)0) |
91 | return -1; |
92 | if (l->nitems < r->nitems) { |
93 | c = memcmp(l->data, r->data, l->nitems); |
94 | if (c == 0) |
95 | return -1; |
96 | } else { |
97 | c = memcmp(l->data, r->data, r->nitems); |
98 | if (c == 0) |
99 | return l->nitems > r->nitems; |
100 | } |
101 | return c; |
102 | } |
103 | |
104 | void |
105 | BLOBdel(Heap *h, var_t *idx) |
106 | { |
107 | HEAP_free(h, *idx); |
108 | } |
109 | |
110 | BUN |
111 | BLOBhash(const blob *b) |
112 | { |
113 | return (BUN) b->nitems; |
114 | } |
115 | |
116 | const blob * |
117 | BLOBnull(void) |
118 | { |
119 | return &nullval; |
120 | } |
121 | |
122 | blob * |
123 | BLOBread(blob *a, stream *s, size_t cnt) |
124 | { |
125 | int len; |
126 | |
127 | (void) cnt; |
128 | assert(cnt == 1); |
129 | if (mnstr_readInt(s, &len) != 1) |
130 | return NULL; |
131 | if ((a = GDKmalloc(len)) == NULL) |
132 | return NULL; |
133 | if (mnstr_read(s, (char *) a, len, 1) != 1) { |
134 | GDKfree(a); |
135 | return NULL; |
136 | } |
137 | return a; |
138 | } |
139 | |
140 | gdk_return |
141 | BLOBwrite(const blob *a, stream *s, size_t cnt) |
142 | { |
143 | var_t len = blobsize(a->nitems); |
144 | |
145 | (void) cnt; |
146 | assert(cnt == 1); |
147 | if (!mnstr_writeInt(s, (int) len) /* 64bit: check for overflow */ || |
148 | mnstr_write(s, a, len, 1) < 0) |
149 | return GDK_FAIL; |
150 | return GDK_SUCCEED; |
151 | } |
152 | |
153 | size_t |
154 | BLOBlength(const blob *p) |
155 | { |
156 | var_t l = blobsize(p->nitems); /* 64bit: check for overflow */ |
157 | assert(l <= GDK_int_max); |
158 | return (size_t) l; |
159 | } |
160 | |
161 | void |
162 | BLOBheap(Heap *heap, size_t capacity) |
163 | { |
164 | HEAP_initialize(heap, capacity, 0, (int) sizeof(var_t)); |
165 | } |
166 | |
167 | var_t |
168 | BLOBput(Heap *h, var_t *bun, const blob *val) |
169 | { |
170 | char *base = NULL; |
171 | |
172 | *bun = HEAP_malloc(h, blobsize(val->nitems)); |
173 | base = h->base; |
174 | if (*bun) { |
175 | memcpy(&base[*bun], val, blobsize(val->nitems)); |
176 | h->dirty = true; |
177 | } |
178 | return *bun; |
179 | } |
180 | |
181 | str |
182 | BLOBnitems(int *ret, blob **b) |
183 | { |
184 | assert((*b)->nitems <INT_MAX); |
185 | *ret = (int) (*b)->nitems; |
186 | return MAL_SUCCEED; |
187 | } |
188 | |
189 | str |
190 | BLOBtoblob(blob **retval, str *s) |
191 | { |
192 | size_t len = strLen(*s); |
193 | blob *b = (blob *) GDKmalloc(blobsize(len)); |
194 | |
195 | if( b == NULL) |
196 | throw(MAL, "blob.toblob" , SQLSTATE(HY001) MAL_MALLOC_FAIL); |
197 | b->nitems = len; |
198 | memcpy(b->data, *s, len); |
199 | *retval = b; |
200 | return MAL_SUCCEED; |
201 | } |
202 | |
203 | ssize_t |
204 | BLOBtostr(str *tostr, size_t *l, const blob *p, bool external) |
205 | { |
206 | char *s; |
207 | size_t i; |
208 | size_t expectedlen; |
209 | |
210 | if (p->nitems == ~(size_t) 0) |
211 | expectedlen = external ? 4 : 2; |
212 | else |
213 | expectedlen = 24 + (p->nitems * 3); |
214 | if (*l < expectedlen || *tostr == NULL) { |
215 | GDKfree(*tostr); |
216 | *tostr = GDKmalloc(expectedlen); |
217 | if (*tostr == NULL) |
218 | return -1; |
219 | *l = expectedlen; |
220 | } |
221 | if (p->nitems == ~(size_t) 0) { |
222 | if (external) { |
223 | strcpy(*tostr, "nil" ); |
224 | return 3; |
225 | } |
226 | strcpy(*tostr, str_nil); |
227 | return 1; |
228 | } |
229 | |
230 | s = *tostr; |
231 | |
232 | for (i = 0; i < p->nitems; i++) { |
233 | int val = (p->data[i] >> 4) & 15; |
234 | |
235 | *s++ = hexit[val]; |
236 | val = p->data[i] & 15; |
237 | *s++ = hexit[val]; |
238 | } |
239 | *s = '\0'; |
240 | return (ssize_t) (s - *tostr); |
241 | } |
242 | |
243 | ssize_t |
244 | BLOBfromstr(const char *instr, size_t *l, blob **val, bool external) |
245 | { |
246 | size_t i; |
247 | size_t nitems; |
248 | var_t nbytes; |
249 | blob *result; |
250 | const char *s = instr; |
251 | |
252 | if (GDK_STRNIL(instr) || (external && strncmp(instr, "nil" , 3) == 0)) { |
253 | nbytes = blobsize(0); |
254 | if (*l < nbytes || *val == NULL) { |
255 | GDKfree(*val); |
256 | if ((*val = GDKmalloc(nbytes)) == NULL) |
257 | return -1; |
258 | } |
259 | **val = nullval; |
260 | return GDK_STRNIL(instr) ? 1 : 3; |
261 | } |
262 | |
263 | /* count hexits and check for hexits/space |
264 | */ |
265 | for (i = nitems = 0; instr[i]; i++) { |
266 | if (isxdigit((unsigned char) instr[i])) |
267 | nitems++; |
268 | else if (!isspace((unsigned char) instr[i])) { |
269 | GDKerror("blob_fromstr: Illegal char in blob\n" ); |
270 | return -1; |
271 | } |
272 | } |
273 | if (nitems % 2 != 0) { |
274 | GDKerror("blob_fromstr: Illegal blob length '%zu' (should be even)\n" , nitems); |
275 | return -1; |
276 | } |
277 | nitems /= 2; |
278 | nbytes = blobsize(nitems); |
279 | |
280 | if (*l < nbytes || *val == NULL) { |
281 | GDKfree(*val); |
282 | *val = GDKmalloc(nbytes); |
283 | if( *val == NULL) |
284 | return -1; |
285 | *l = (size_t) nbytes; |
286 | } |
287 | result = *val; |
288 | result->nitems = nitems; |
289 | |
290 | /* |
291 | // Read the values of the blob. |
292 | */ |
293 | for (i = 0; i < nitems; ++i) { |
294 | char res = 0; |
295 | |
296 | for (;;) { |
297 | if (isdigit((unsigned char) *s)) { |
298 | res = *s - '0'; |
299 | } else if (*s >= 'A' && *s <= 'F') { |
300 | res = 10 + *s - 'A'; |
301 | } else if (*s >= 'a' && *s <= 'f') { |
302 | res = 10 + *s - 'a'; |
303 | } else { |
304 | assert(isspace((unsigned char) *s)); |
305 | s++; |
306 | continue; |
307 | } |
308 | break; |
309 | } |
310 | s++; |
311 | res <<= 4; |
312 | for (;;) { |
313 | if (isdigit((unsigned char) *s)) { |
314 | res += *s - '0'; |
315 | } else if (*s >= 'A' && *s <= 'F') { |
316 | res += 10 + *s - 'A'; |
317 | } else if (*s >= 'a' && *s <= 'f') { |
318 | res += 10 + *s - 'a'; |
319 | } else { |
320 | assert(isspace((unsigned char) *s)); |
321 | s++; |
322 | continue; |
323 | } |
324 | break; |
325 | } |
326 | s++; |
327 | |
328 | result->data[i] = res; |
329 | } |
330 | |
331 | return (ssize_t) (s - instr); |
332 | } |
333 | |
334 | str |
335 | BLOBblob_blob(blob **d, blob **s) |
336 | { |
337 | size_t len = blobsize((*s)->nitems); |
338 | blob *b; |
339 | |
340 | *d = b = GDKmalloc(len); |
341 | if (b == NULL) |
342 | throw(MAL,"blob" , SQLSTATE(HY001) MAL_MALLOC_FAIL); |
343 | b->nitems = (*s)->nitems; |
344 | if (b->nitems != ~(size_t) 0 && b->nitems != 0) |
345 | memcpy(b->data, (*s)->data, b->nitems); |
346 | return MAL_SUCCEED; |
347 | } |
348 | |
349 | str |
350 | BLOBblob_fromstr(blob **b, const char **s) |
351 | { |
352 | size_t len = 0; |
353 | |
354 | if (BLOBfromstr(*s, &len, b, false) < 0) |
355 | throw(MAL, "blob" , GDK_EXCEPTION); |
356 | return MAL_SUCCEED; |
357 | } |
358 | |