LCOV - code coverage report
Current view: top level - monetdb5/modules/atoms - blob.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 159 253 62.8 %
Date: 2021-10-13 02:24:04 Functions: 19 20 95.0 %

          Line data    Source code
       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 - 2021 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             : #include "mal_client.h"
      35             : #include "mal_interpreter.h"
      36             : 
      37             : int TYPE_blob;
      38             : 
      39             : static blob nullval = {
      40             :         ~(size_t) 0
      41             : };
      42             : 
      43             : #define is_blob_nil(x)  ((x)->nitems == nullval.nitems)
      44             : 
      45             : static str
      46         266 : BLOBprelude(void *ret)
      47             : {
      48             :         (void) ret;
      49         266 :         TYPE_blob = ATOMindex("blob");
      50         266 :         return MAL_SUCCEED;
      51             : }
      52             : 
      53             : var_t
      54        4500 : blobsize(size_t nitems)
      55             : {
      56        4500 :         if (nitems == nullval.nitems)
      57             :                 nitems = 0;
      58        4500 :         assert(offsetof(blob, data) + nitems <= VAR_MAX);
      59        4500 :         return (var_t) (offsetof(blob, data) + nitems);
      60             : }
      61             : 
      62             : static char hexit[] = "0123456789ABCDEF";
      63             : 
      64             : /*
      65             :  * @- Wrapping section
      66             :  * This section contains the wrappers to re-use the implementation
      67             :  * section of the blob modules from MonetDB 4.3
      68             :  * @-
      69             :  */
      70             : static int
      71        8193 : BLOBcmp(const void *L, const void *R)
      72             : {
      73             :         const blob *l = L, *r = R;
      74             :         int c;
      75        8193 :         if (is_blob_nil(r))
      76        4311 :                 return !is_blob_nil(l);
      77        3882 :         if (is_blob_nil(l))
      78             :                 return -1;
      79        3503 :         if (l->nitems < r->nitems) {
      80         603 :                 c = memcmp(l->data, r->data, l->nitems);
      81         603 :                 if (c == 0)
      82         258 :                         return -1;
      83             :         } else {
      84        2900 :                 c = memcmp(l->data, r->data, r->nitems);
      85        2900 :                 if (c == 0)
      86        2211 :                         return l->nitems > r->nitems;
      87             :         }
      88             :         return c;
      89             : }
      90             : 
      91             : static void
      92          19 : BLOBdel(Heap *h, var_t *idx)
      93             : {
      94          19 :         HEAP_free(h, *idx);
      95          19 : }
      96             : 
      97             : static BUN
      98         273 : BLOBhash(const void *B)
      99             : {
     100             :         const blob *b = B;
     101         273 :         return (BUN) b->nitems;
     102             : }
     103             : 
     104             : static const void *
     105         266 : BLOBnull(void)
     106             : {
     107         266 :         return &nullval;
     108             : }
     109             : 
     110             : static void *
     111          77 : BLOBread(void *A, size_t *dstlen, stream *s, size_t cnt)
     112             : {
     113             :         blob *a = A;
     114             :         int len;
     115             : 
     116             :         (void) cnt;
     117          77 :         assert(cnt == 1);
     118          77 :         if (mnstr_readInt(s, &len) != 1 || len < 0)
     119             :                 return NULL;
     120          77 :         if (a == NULL || *dstlen < (size_t) len) {
     121           0 :                 if ((a = GDKrealloc(a, (size_t) len)) == NULL)
     122             :                         return NULL;
     123           0 :                 *dstlen = (size_t) len;
     124             :         }
     125          77 :         if (mnstr_read(s, (char *) a, (size_t) len, 1) != 1) {
     126           0 :                 GDKfree(a);
     127           0 :                 return NULL;
     128             :         }
     129             :         return a;
     130             : }
     131             : 
     132             : static gdk_return
     133          83 : BLOBwrite(const void *A, stream *s, size_t cnt)
     134             : {
     135             :         const blob *a = A;
     136          83 :         var_t len = blobsize(a->nitems);
     137             : 
     138             :         (void) cnt;
     139          83 :         assert(cnt == 1);
     140         166 :         if (!mnstr_writeInt(s, (int) len) /* 64bit: check for overflow */ ||
     141          83 :                 mnstr_write(s, a, len, 1) < 0)
     142           0 :                 return GDK_FAIL;
     143             :         return GDK_SUCCEED;
     144             : }
     145             : 
     146             : static size_t
     147        1803 : BLOBlength(const void *P)
     148             : {
     149             :         const blob *p = P;
     150        1803 :         var_t l = blobsize(p->nitems); /* 64bit: check for overflow */
     151        1803 :         assert(l <= GDK_int_max);
     152        1803 :         return (size_t) l;
     153             : }
     154             : 
     155             : static gdk_return
     156         586 : BLOBheap(Heap *heap, size_t capacity)
     157             : {
     158         586 :         return HEAP_initialize(heap, capacity, 0, (int) sizeof(var_t));
     159             : }
     160             : 
     161             : static var_t
     162        1130 : BLOBput(BAT *b, var_t *bun, const void *VAL)
     163             : {
     164             :         const blob *val = VAL;
     165             :         char *base = NULL;
     166             : 
     167        1130 :         *bun = HEAP_malloc(b, blobsize(val->nitems));
     168        1129 :         base = b->tvheap->base;
     169        1129 :         if (*bun != (var_t) -1) {
     170        1129 :                 memcpy(&base[*bun], val, blobsize(val->nitems));
     171        1128 :                 b->tvheap->dirty = true;
     172             :         }
     173        1128 :         return *bun;
     174             : }
     175             : 
     176             : static str
     177           1 : BLOBnitems(int *ret, blob **b)
     178             : {
     179           1 :         if (is_blob_nil(*b)) {
     180           1 :                 *ret = int_nil;
     181             :         } else {
     182           0 :                 assert((*b)->nitems < INT_MAX);
     183           0 :                 *ret = (int) (*b)->nitems;
     184             :         }
     185           1 :         return MAL_SUCCEED;
     186             : }
     187             : 
     188             : static str
     189           2 : BLOBnitems_bulk(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
     190             : {
     191             :         BATiter bi;
     192             :         BAT *bn = NULL, *b = NULL, *bs = NULL;
     193             :         BUN q = 0;
     194             :         int *restrict vals;
     195             :         str msg = MAL_SUCCEED;
     196             :         bool nils = false;
     197           2 :         struct canditer ci1 = {0};
     198             :         oid off1;
     199           2 :         bat *res = getArgReference_bat(stk, pci, 0), *bid = getArgReference_bat(stk, pci, 1),
     200           2 :                 *sid1 = pci->argc == 3 ? getArgReference_bat(stk, pci, 2) : NULL;
     201             : 
     202             :         (void) cntxt;
     203             :         (void) mb;
     204           2 :         if (!(b = BATdescriptor(*bid))) {
     205           0 :                 msg = createException(MAL, "blob.nitems_bulk", SQLSTATE(HY002) RUNTIME_OBJECT_MISSING);
     206           0 :                 goto bailout;
     207             :         }
     208           2 :         if (sid1 && !is_bat_nil(*sid1) && !(bs = BATdescriptor(*sid1))) {
     209           0 :                 msg = createException(MAL, "blob.nitems_bulk", SQLSTATE(HY002) RUNTIME_OBJECT_MISSING);
     210           0 :                 goto bailout;
     211             :         }
     212           2 :         q = canditer_init(&ci1, b, bs);
     213           2 :         if (!(bn = COLnew(ci1.hseq, TYPE_int, q, TRANSIENT))) {
     214           0 :                 msg = createException(MAL, "blob.nitems_bulk", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     215           0 :                 goto bailout;
     216             :         }
     217             : 
     218           2 :         off1 = b->hseqbase;
     219           2 :         bi = bat_iterator(b);
     220           2 :         vals = Tloc(bn, 0);
     221           2 :         if (ci1.tpe == cand_dense) {
     222           8 :                 for (BUN i = 0; i < q; i++) {
     223           6 :                         oid p1 = (canditer_next_dense(&ci1) - off1);
     224           6 :                         blob *b = (blob*) BUNtvar(bi, p1);
     225             : 
     226           6 :                         if (is_blob_nil(b)) {
     227           2 :                                 vals[i] = int_nil;
     228             :                                 nils = true;
     229             :                         } else {
     230           4 :                                 assert((int) b->nitems < INT_MAX);
     231           4 :                                 vals[i] = (int) b->nitems;
     232             :                         }
     233             :                 }
     234             :         } else {
     235           0 :                 for (BUN i = 0; i < q; i++) {
     236           0 :                         oid p1 = (canditer_next(&ci1) - off1);
     237           0 :                         blob *b = (blob*) BUNtvar(bi, p1);
     238             : 
     239           0 :                         if (is_blob_nil(b)) {
     240           0 :                                 vals[i] = int_nil;
     241             :                                 nils = true;
     242             :                         } else {
     243           0 :                                 assert((int) b->nitems < INT_MAX);
     244           0 :                                 vals[i] = (int) b->nitems;
     245             :                         }
     246             :                 }
     247             :         }
     248           2 :         bat_iterator_end(&bi);
     249             : 
     250           2 : bailout:
     251           2 :         if (bn && !msg) {
     252           2 :                 BATsetcount(bn, q);
     253           2 :                 bn->tnil = nils;
     254           2 :                 bn->tnonil = !nils;
     255           2 :                 bn->tkey = BATcount(bn) <= 1;
     256           2 :                 bn->tsorted = BATcount(bn) <= 1;
     257           2 :                 bn->trevsorted = BATcount(bn) <= 1;
     258           2 :                 BBPkeepref(*res = bn->batCacheid);
     259           0 :         } else if (bn)
     260           0 :                 BBPreclaim(bn);
     261           2 :         if (b)
     262           2 :                 BBPunfix(b->batCacheid);
     263           2 :         if (bs)
     264           0 :                 BBPunfix(bs->batCacheid);
     265           2 :         return msg;
     266             : }
     267             : 
     268             : static str
     269           1 : BLOBtoblob(blob **retval, str *s)
     270             : {
     271           1 :         size_t len = strLen(*s);
     272           1 :         blob *b = (blob *) GDKmalloc(blobsize(len));
     273             : 
     274           1 :         if( b == NULL)
     275           0 :                 throw(MAL, "blob.toblob", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     276           1 :         b->nitems = len;
     277           1 :         memcpy(b->data, *s, len);
     278           1 :         *retval = b;
     279           1 :         return MAL_SUCCEED;
     280             : }
     281             : 
     282             : ssize_t
     283         348 : BLOBtostr(str *tostr, size_t *l, const void *P, bool external)
     284             : {
     285             :         const blob *p = P;
     286             :         char *s;
     287             :         size_t i;
     288             :         size_t expectedlen;
     289             : 
     290         348 :         if (is_blob_nil(p))
     291           3 :                 expectedlen = external ? 4 : 2;
     292             :         else
     293         345 :                 expectedlen = p->nitems * 2 + 1;
     294         348 :         if (*l < expectedlen || *tostr == NULL) {
     295          44 :                 GDKfree(*tostr);
     296          44 :                 *tostr = GDKmalloc(expectedlen);
     297          44 :                 if (*tostr == NULL)
     298             :                         return -1;
     299          44 :                 *l = expectedlen;
     300             :         }
     301         348 :         if (is_blob_nil(p)) {
     302           3 :                 if (external) {
     303           3 :                         strcpy(*tostr, "nil");
     304           3 :                         return 3;
     305             :                 }
     306           0 :                 strcpy(*tostr, str_nil);
     307           0 :                 return 1;
     308             :         }
     309             : 
     310         345 :         s = *tostr;
     311             : 
     312    21941017 :         for (i = 0; i < p->nitems; i++) {
     313    21940672 :                 int val = (p->data[i] >> 4) & 15;
     314             : 
     315    21940672 :                 *s++ = hexit[val];
     316    21940672 :                 val = p->data[i] & 15;
     317    21940672 :                 *s++ = hexit[val];
     318             :         }
     319         345 :         *s = '\0';
     320         345 :         return (ssize_t) (s - *tostr);
     321             : }
     322             : 
     323             : static ssize_t
     324         349 : BLOBfromstr(const char *instr, size_t *l, void **VAL, bool external)
     325             : {
     326             :         blob **val = (blob **) VAL;
     327             :         size_t i;
     328             :         size_t nitems;
     329             :         var_t nbytes;
     330             :         blob *result;
     331             :         const char *s = instr;
     332             : 
     333         349 :         if (strNil(instr) || (external && strncmp(instr, "nil", 3) == 0)) {
     334           0 :                 nbytes = blobsize(0);
     335           0 :                 if (*l < nbytes || *val == NULL) {
     336           0 :                         GDKfree(*val);
     337           0 :                         if ((*val = GDKmalloc(nbytes)) == NULL)
     338             :                                 return -1;
     339             :                 }
     340           0 :                 **val = nullval;
     341           0 :                 return strNil(instr) ? 1 : 3;
     342             :         }
     343             : 
     344             :         /* count hexits and check for hexits/space
     345             :          */
     346    12124827 :         for (i = nitems = 0; instr[i]; i++) {
     347    12124478 :                 if (isxdigit((unsigned char) instr[i]))
     348    12124478 :                         nitems++;
     349           0 :                 else if (!isspace((unsigned char) instr[i])) {
     350           0 :                         GDKerror("Illegal char in blob\n");
     351           0 :                         return -1;
     352             :                 }
     353             :         }
     354         349 :         if (nitems % 2 != 0) {
     355           0 :                 GDKerror("Illegal blob length '%zu' (should be even)\n", nitems);
     356           0 :                 return -1;
     357             :         }
     358         349 :         nitems /= 2;
     359         349 :         nbytes = blobsize(nitems);
     360             : 
     361         349 :         if (*l < nbytes || *val == NULL) {
     362         254 :                 GDKfree(*val);
     363         254 :                 *val = GDKmalloc(nbytes);
     364         254 :                 if( *val == NULL)
     365             :                         return -1;
     366         254 :                 *l = (size_t) nbytes;
     367             :         }
     368         349 :         result = *val;
     369         349 :         result->nitems = nitems;
     370             : 
     371             :         /*
     372             :            // Read the values of the blob.
     373             :          */
     374     6062588 :         for (i = 0; i < nitems; ++i) {
     375             :                 char res = 0;
     376             : 
     377             :                 for (;;) {
     378     6062239 :                         if (isdigit((unsigned char) *s)) {
     379     3600895 :                                 res = *s - '0';
     380     2461344 :                         } else if (*s >= 'A' && *s <= 'F') {
     381      844512 :                                 res = 10 + *s - 'A';
     382     1616832 :                         } else if (*s >= 'a' && *s <= 'f') {
     383     1616832 :                                 res = 10 + *s - 'a';
     384             :                         } else {
     385           0 :                                 assert(isspace((unsigned char) *s));
     386           0 :                                 s++;
     387           0 :                                 continue;
     388             :                         }
     389             :                         break;
     390             :                 }
     391     6062239 :                 s++;
     392     6062239 :                 res <<= 4;
     393             :                 for (;;) {
     394     6062239 :                         if (isdigit((unsigned char) *s)) {
     395     3598971 :                                 res += *s - '0';
     396     2463268 :                         } else if (*s >= 'A' && *s <= 'F') {
     397      847271 :                                 res += 10 + *s - 'A';
     398     1615997 :                         } else if (*s >= 'a' && *s <= 'f') {
     399     1615997 :                                 res += 10 + *s - 'a';
     400             :                         } else {
     401           0 :                                 assert(isspace((unsigned char) *s));
     402           0 :                                 s++;
     403           0 :                                 continue;
     404             :                         }
     405             :                         break;
     406             :                 }
     407     6062239 :                 s++;
     408             : 
     409     6062239 :                 result->data[i] = res;
     410             :         }
     411             : 
     412         349 :         return (ssize_t) (s - instr);
     413             : }
     414             : 
     415             : static str
     416           1 : BLOBblob_blob(blob **d, blob **s)
     417             : {
     418           1 :         size_t len = blobsize((*s)->nitems);
     419             :         blob *b;
     420             : 
     421           1 :         *d = b = GDKmalloc(len);
     422           1 :         if (b == NULL)
     423           0 :                 throw(MAL,"blob", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     424           1 :         b->nitems = (*s)->nitems;
     425           1 :         if (!is_blob_nil(b) && b->nitems != 0)
     426           0 :                 memcpy(b->data, (*s)->data, b->nitems);
     427             :         return MAL_SUCCEED;
     428             : }
     429             : 
     430             : static str
     431           1 : BLOBblob_blob_bulk(bat *res, const bat *bid, const bat *sid)
     432             : {
     433             :         BAT *b = NULL, *s = NULL, *dst = NULL;
     434             :         BATiter bi;
     435             :         str msg = NULL;
     436             :         struct canditer ci;
     437             :         BUN q;
     438             :         oid off;
     439             :         bool nils = false;
     440             : 
     441           1 :         if (sid && !is_bat_nil(*sid)) {
     442           0 :                 if ((s = BATdescriptor(*sid)) == NULL) {
     443           0 :                         msg = createException(SQL, "batcalc.blob_blob_bulk", SQLSTATE(HY002) RUNTIME_OBJECT_MISSING);
     444           0 :                         goto bailout;
     445             :                 }
     446             :         } else {
     447           1 :                 BBPretain(*res = *bid); /* nothing to convert, return */
     448           1 :                 return MAL_SUCCEED;
     449             :         }
     450           0 :         if ((b = BATdescriptor(*bid)) == NULL) {
     451           0 :                 msg = createException(SQL, "batcalc.blob_blob_bulk", SQLSTATE(HY002) RUNTIME_OBJECT_MISSING);
     452           0 :                 goto bailout;
     453             :         }
     454           0 :         off = b->hseqbase;
     455           0 :         q = canditer_init(&ci, b, s);
     456           0 :         if (!(dst = COLnew(ci.hseq, TYPE_blob, q, TRANSIENT))) {
     457           0 :                 msg = createException(SQL, "batcalc.blob_blob_bulk", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     458           0 :                 goto bailout;
     459             :         }
     460             : 
     461           0 :         bi = bat_iterator(b);
     462           0 :         if (ci.tpe == cand_dense) {
     463           0 :                 for (BUN i = 0; i < q; i++) {
     464           0 :                         oid p = (canditer_next_dense(&ci) - off);
     465           0 :                         blob *v = (blob*) BUNtvar(bi, p);
     466             : 
     467           0 :                         if (tfastins_nocheckVAR(dst, i, v) != GDK_SUCCEED) {
     468           0 :                                 msg = createException(SQL, "batcalc.blob_blob_bulk", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     469           0 :                                 bat_iterator_end(&bi);
     470           0 :                                 goto bailout;
     471             :                         }
     472           0 :                         nils |= is_blob_nil(v);
     473             :                 }
     474             :         } else {
     475           0 :                 for (BUN i = 0; i < q; i++) {
     476           0 :                         oid p = (canditer_next(&ci) - off);
     477           0 :                         blob *v = (blob*) BUNtvar(bi, p);
     478             : 
     479           0 :                         if (tfastins_nocheckVAR(dst, i, v) != GDK_SUCCEED) {
     480           0 :                                 msg = createException(SQL, "batcalc.blob_blob_bulk", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     481           0 :                                 bat_iterator_end(&bi);
     482           0 :                                 goto bailout;
     483             :                         }
     484           0 :                         nils |= is_blob_nil(v);
     485             :                 }
     486             :         }
     487           0 :         bat_iterator_end(&bi);
     488             : 
     489           0 : bailout:
     490           0 :         if (b)
     491           0 :                 BBPunfix(b->batCacheid);
     492           0 :         if (s)
     493           0 :                 BBPunfix(s->batCacheid);
     494           0 :         if (dst && !msg) {
     495           0 :                 BATsetcount(dst, q);
     496           0 :                 dst->tnil = nils;
     497           0 :                 dst->tnonil = !nils;
     498           0 :                 dst->tkey = BATcount(dst) <= 1;
     499           0 :                 dst->tsorted = BATcount(dst) <= 1;
     500           0 :                 dst->trevsorted = BATcount(dst) <= 1;
     501           0 :                 BBPkeepref(*res = dst->batCacheid);
     502           0 :         } else if (dst)
     503           0 :                 BBPreclaim(dst);
     504             :         return msg;
     505             : }
     506             : 
     507             : static str
     508           0 : BLOBblob_fromstr(blob **b, const char **s)
     509             : {
     510           0 :         size_t len = 0;
     511             : 
     512           0 :         if (BLOBfromstr(*s, &len, (void **) b, false) < 0)
     513           0 :                 throw(MAL, "blob", GDK_EXCEPTION);
     514             :         return MAL_SUCCEED;
     515             : }
     516             : 
     517             : #include "mel.h"
     518             : static mel_atom blob_init_atoms[] = {
     519             :  { .name="blob", .tostr=BLOBtostr, .fromstr=BLOBfromstr, .cmp=BLOBcmp, .hash=BLOBhash, .null=BLOBnull, .read=BLOBread, .write=BLOBwrite, .put=BLOBput, .del=BLOBdel, .length=BLOBlength, .heap=BLOBheap, },  { .cmp=NULL }
     520             : };
     521             : static mel_func blob_init_funcs[] = {
     522             :  command("blob", "blob", BLOBblob_blob, false, "Noop routine.", args(1,2, arg("",blob),arg("s",blob))),
     523             :  command("blob", "blob", BLOBblob_fromstr, false, "", args(1,2, arg("",blob),arg("s",str))),
     524             :  command("blob", "toblob", BLOBtoblob, false, "store a string as a blob.", args(1,2, arg("",blob),arg("v",str))),
     525             :  command("blob", "nitems", BLOBnitems, false, "get the number of bytes in this blob.", args(1,2, arg("",int),arg("b",blob))),
     526             :  pattern("batblob", "nitems", BLOBnitems_bulk, false, "", args(1,2, batarg("",int),batarg("b",blob))),
     527             :  pattern("batblob", "nitems", BLOBnitems_bulk, false, "", args(1,3, batarg("",int),batarg("b",blob),batarg("s",oid))),
     528             :  command("blob", "prelude", BLOBprelude, false, "", args(1,1, arg("",void))),
     529             :  command("calc", "blob", BLOBblob_blob, false, "", args(1,2, arg("",blob),arg("b",blob))),
     530             :  command("batcalc", "blob", BLOBblob_blob_bulk, false, "", args(1,3, batarg("",blob),batarg("b",blob),batarg("s",oid))),
     531             :  command("calc", "blob", BLOBblob_fromstr, false, "", args(1,2, arg("",blob),arg("s",str))),
     532             :  { .imp=NULL }
     533             : };
     534             : #include "mal_import.h"
     535             : #ifdef _MSC_VER
     536             : #undef read
     537             : #pragma section(".CRT$XCU",read)
     538             : #endif
     539         259 : LIB_STARTUP_FUNC(init_blob_mal)
     540         259 : { mal_module("blob", blob_init_atoms, blob_init_funcs); }

Generated by: LCOV version 1.14