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
35int TYPE_blob;
36
37mal_export str BLOBprelude(void *ret);
38
39mal_export int BLOBcmp(const blob *l, const blob *r);
40mal_export BUN BLOBhash(const blob *b);
41mal_export const blob *BLOBnull(void);
42mal_export var_t BLOBput(Heap *h, var_t *bun, const blob *val);
43mal_export void BLOBdel(Heap *h, var_t *index);
44mal_export size_t BLOBlength(const blob *p);
45mal_export void BLOBheap(Heap *heap, size_t capacity);
46mal_export str BLOBtoblob(blob **retval, str *s);
47mal_export str BLOBnitems(int *ret, blob **b);
48mal_export int BLOBget(Heap *h, int *bun, int *l, blob **val);
49mal_export blob * BLOBread(blob *a, stream *s, size_t cnt);
50mal_export gdk_return BLOBwrite(const blob *a, stream *s, size_t cnt);
51
52mal_export str BLOBblob_blob(blob **d, blob **s);
53mal_export str BLOBblob_fromstr(blob **b, const char **d);
54
55str
56BLOBprelude(void *ret)
57{
58 (void) ret;
59 TYPE_blob = ATOMindex("blob");
60 return MAL_SUCCEED;
61}
62
63var_t
64blobsize(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
72static blob nullval = {
73 ~(size_t) 0
74};
75
76static 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 */
84int
85BLOBcmp(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
104void
105BLOBdel(Heap *h, var_t *idx)
106{
107 HEAP_free(h, *idx);
108}
109
110BUN
111BLOBhash(const blob *b)
112{
113 return (BUN) b->nitems;
114}
115
116const blob *
117BLOBnull(void)
118{
119 return &nullval;
120}
121
122blob *
123BLOBread(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
140gdk_return
141BLOBwrite(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
153size_t
154BLOBlength(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
161void
162BLOBheap(Heap *heap, size_t capacity)
163{
164 HEAP_initialize(heap, capacity, 0, (int) sizeof(var_t));
165}
166
167var_t
168BLOBput(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
181str
182BLOBnitems(int *ret, blob **b)
183{
184 assert((*b)->nitems <INT_MAX);
185 *ret = (int) (*b)->nitems;
186 return MAL_SUCCEED;
187}
188
189str
190BLOBtoblob(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
203ssize_t
204BLOBtostr(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
243ssize_t
244BLOBfromstr(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
334str
335BLOBblob_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
349str
350BLOBblob_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