| 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 | |