LCOV - code coverage report
Current view: top level - contrib/ltree - ltxtquery_io.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 72.2 % 291 210
Test Date: 2026-02-28 15:14:49 Functions: 86.7 % 15 13
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*
       2              :  * txtquery io
       3              :  * Teodor Sigaev <teodor@stack.net>
       4              :  * contrib/ltree/ltxtquery_io.c
       5              :  */
       6              : #include "postgres.h"
       7              : 
       8              : #include <ctype.h>
       9              : 
      10              : #include "crc32.h"
      11              : #include "libpq/pqformat.h"
      12              : #include "ltree.h"
      13              : #include "miscadmin.h"
      14              : #include "nodes/miscnodes.h"
      15              : #include "varatt.h"
      16              : 
      17              : 
      18              : /* parser's states */
      19              : #define WAITOPERAND 1
      20              : #define INOPERAND 2
      21              : #define WAITOPERATOR    3
      22              : 
      23              : /*
      24              :  * node of query tree, also used
      25              :  * for storing polish notation in parser
      26              :  */
      27              : typedef struct NODE
      28              : {
      29              :     int32       type;
      30              :     int32       val;
      31              :     int16       distance;
      32              :     int16       length;
      33              :     uint16      flag;
      34              :     struct NODE *next;
      35              : } NODE;
      36              : 
      37              : typedef struct
      38              : {
      39              :     char       *buf;
      40              :     int32       state;
      41              :     int32       count;
      42              :     struct Node *escontext;
      43              :     /* reverse polish notation in list (for temporary usage) */
      44              :     NODE       *str;
      45              :     /* number in str */
      46              :     int32       num;
      47              : 
      48              :     /* user-friendly operand */
      49              :     int32       lenop;
      50              :     int32       sumlen;
      51              :     char       *op;
      52              :     char       *curop;
      53              : } QPRS_STATE;
      54              : 
      55              : /*
      56              :  * get token from query string
      57              :  *
      58              :  * caller needs to check if a soft-error was set if the result is ERR.
      59              :  */
      60              : static int32
      61          108 : gettoken_query(QPRS_STATE *state, int32 *val, int32 *lenval, char **strval, uint16 *flag)
      62              : {
      63              :     int         charlen;
      64              : 
      65              :     for (;;)
      66              :     {
      67          365 :         charlen = pg_mblen_cstr(state->buf);
      68              : 
      69          365 :         switch (state->state)
      70              :         {
      71           83 :             case WAITOPERAND:
      72           83 :                 if (t_iseq(state->buf, '!'))
      73              :                 {
      74            6 :                     (state->buf)++;
      75            6 :                     *val = (int32) '!';
      76            6 :                     return OPR;
      77              :                 }
      78           77 :                 else if (t_iseq(state->buf, '('))
      79              :                 {
      80            0 :                     state->count++;
      81            0 :                     (state->buf)++;
      82            0 :                     return OPEN;
      83              :                 }
      84           77 :                 else if (ISLABEL(state->buf))
      85              :                 {
      86           50 :                     state->state = INOPERAND;
      87           50 :                     *strval = state->buf;
      88           50 :                     *lenval = charlen;
      89           50 :                     *flag = 0;
      90              :                 }
      91           27 :                 else if (!isspace((unsigned char) *state->buf))
      92            2 :                     ereturn(state->escontext, ERR,
      93              :                             (errcode(ERRCODE_SYNTAX_ERROR),
      94              :                              errmsg("operand syntax error")));
      95           75 :                 break;
      96          207 :             case INOPERAND:
      97          207 :                 if (ISLABEL(state->buf))
      98              :                 {
      99          127 :                     if (*flag)
     100            0 :                         ereturn(state->escontext, ERR,
     101              :                                 (errcode(ERRCODE_SYNTAX_ERROR),
     102              :                                  errmsg("modifiers syntax error")));
     103          127 :                     *lenval += charlen;
     104              :                 }
     105           80 :                 else if (t_iseq(state->buf, '%'))
     106            4 :                     *flag |= LVAR_SUBLEXEME;
     107           76 :                 else if (t_iseq(state->buf, '@'))
     108           12 :                     *flag |= LVAR_INCASE;
     109           64 :                 else if (t_iseq(state->buf, '*'))
     110           14 :                     *flag |= LVAR_ANYEND;
     111              :                 else
     112              :                 {
     113           50 :                     state->state = WAITOPERATOR;
     114           50 :                     return VAL;
     115              :                 }
     116          157 :                 break;
     117           75 :             case WAITOPERATOR:
     118           75 :                 if (t_iseq(state->buf, '&') || t_iseq(state->buf, '|'))
     119              :                 {
     120           25 :                     state->state = WAITOPERAND;
     121           25 :                     *val = (int32) *(state->buf);
     122           25 :                     (state->buf)++;
     123           25 :                     return OPR;
     124              :                 }
     125           50 :                 else if (t_iseq(state->buf, ')'))
     126              :                 {
     127            0 :                     (state->buf)++;
     128            0 :                     state->count--;
     129            0 :                     return (state->count < 0) ? ERR : CLOSE;
     130              :                 }
     131           50 :                 else if (*(state->buf) == '\0')
     132              :                 {
     133           25 :                     return (state->count) ? ERR : END;
     134              :                 }
     135           25 :                 else if (!t_iseq(state->buf, ' '))
     136              :                 {
     137            0 :                     return ERR;
     138              :                 }
     139           25 :                 break;
     140            0 :             default:
     141            0 :                 return ERR;
     142              :                 break;
     143              :         }
     144              : 
     145          257 :         state->buf += charlen;
     146              :     }
     147              : 
     148              :     /* should not get here */
     149              : }
     150              : 
     151              : /*
     152              :  * push new one in polish notation reverse view
     153              :  */
     154              : static bool
     155           81 : pushquery(QPRS_STATE *state, int32 type, int32 val, int32 distance, int32 lenval, uint16 flag)
     156              : {
     157           81 :     NODE       *tmp = palloc_object(NODE);
     158              : 
     159           81 :     tmp->type = type;
     160           81 :     tmp->val = val;
     161           81 :     tmp->flag = flag;
     162           81 :     if (distance > 0xffff)
     163            0 :         ereturn(state->escontext, false,
     164              :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     165              :                  errmsg("value is too big")));
     166           81 :     if (lenval > 0xff)
     167            0 :         ereturn(state->escontext, false,
     168              :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     169              :                  errmsg("operand is too long")));
     170           81 :     tmp->distance = distance;
     171           81 :     tmp->length = lenval;
     172           81 :     tmp->next = state->str;
     173           81 :     state->str = tmp;
     174           81 :     state->num++;
     175           81 :     return true;
     176              : }
     177              : 
     178              : /*
     179              :  * This function is used for query text parsing
     180              :  */
     181              : static bool
     182           50 : pushval_asis(QPRS_STATE *state, int type, char *strval, int lenval, uint16 flag)
     183              : {
     184           50 :     if (lenval > 0xffff)
     185            0 :         ereturn(state->escontext, false,
     186              :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     187              :                  errmsg("word is too long")));
     188              : 
     189           50 :     if (!pushquery(state, type, ltree_crc32_sz(strval, lenval),
     190           50 :                    state->curop - state->op, lenval, flag))
     191            0 :         return false;
     192              : 
     193           50 :     while (state->curop - state->op + lenval + 1 >= state->lenop)
     194              :     {
     195            0 :         int32       tmp = state->curop - state->op;
     196              : 
     197            0 :         state->lenop *= 2;
     198            0 :         state->op = (char *) repalloc(state->op, state->lenop);
     199            0 :         state->curop = state->op + tmp;
     200              :     }
     201           50 :     memcpy(state->curop, strval, lenval);
     202           50 :     state->curop += lenval;
     203           50 :     *(state->curop) = '\0';
     204           50 :     state->curop++;
     205           50 :     state->sumlen += lenval + 1;
     206           50 :     return true;
     207              : }
     208              : 
     209              : #define STACKDEPTH      32
     210              : /*
     211              :  * make polish notation of query
     212              :  */
     213              : static int32
     214           27 : makepol(QPRS_STATE *state)
     215              : {
     216           27 :     int32       val = 0,
     217              :                 type;
     218           27 :     int32       lenval = 0;
     219           27 :     char       *strval = NULL;
     220              :     int32       stack[STACKDEPTH];
     221           27 :     int32       lenstack = 0;
     222           27 :     uint16      flag = 0;
     223              : 
     224              :     /* since this function recurses, it could be driven to stack overflow */
     225           27 :     check_stack_depth();
     226              : 
     227          108 :     while ((type = gettoken_query(state, &val, &lenval, &strval, &flag)) != END)
     228              :     {
     229           83 :         switch (type)
     230              :         {
     231           50 :             case VAL:
     232           50 :                 if (!pushval_asis(state, VAL, strval, lenval, flag))
     233            0 :                     return ERR;
     234           79 :                 while (lenstack && (stack[lenstack - 1] == (int32) '&' ||
     235            8 :                                     stack[lenstack - 1] == (int32) '!'))
     236              :                 {
     237           29 :                     lenstack--;
     238           29 :                     if (!pushquery(state, OPR, stack[lenstack], 0, 0, 0))
     239            0 :                         return ERR;
     240              :                 }
     241           50 :                 break;
     242           31 :             case OPR:
     243           31 :                 if (lenstack && val == (int32) '|')
     244              :                 {
     245            0 :                     if (!pushquery(state, OPR, val, 0, 0, 0))
     246            0 :                         return ERR;
     247              :                 }
     248              :                 else
     249              :                 {
     250           31 :                     if (lenstack == STACKDEPTH)
     251              :                         /* internal error */
     252            0 :                         elog(ERROR, "stack too short");
     253           31 :                     stack[lenstack] = val;
     254           31 :                     lenstack++;
     255              :                 }
     256           31 :                 break;
     257            0 :             case OPEN:
     258            0 :                 if (makepol(state) == ERR)
     259            0 :                     return ERR;
     260            0 :                 while (lenstack && (stack[lenstack - 1] == (int32) '&' ||
     261            0 :                                     stack[lenstack - 1] == (int32) '!'))
     262              :                 {
     263            0 :                     lenstack--;
     264            0 :                     if (!pushquery(state, OPR, stack[lenstack], 0, 0, 0))
     265            0 :                         return ERR;
     266              :                 }
     267            0 :                 break;
     268            0 :             case CLOSE:
     269            0 :                 while (lenstack)
     270              :                 {
     271            0 :                     lenstack--;
     272            0 :                     if (!pushquery(state, OPR, stack[lenstack], 0, 0, 0))
     273            0 :                         return ERR;
     274              :                 };
     275            0 :                 return END;
     276              :                 break;
     277            2 :             case ERR:
     278            2 :                 if (SOFT_ERROR_OCCURRED(state->escontext))
     279            2 :                     return ERR;
     280              :                 pg_fallthrough;
     281              :             default:
     282            0 :                 ereturn(state->escontext, ERR,
     283              :                         (errcode(ERRCODE_SYNTAX_ERROR),
     284              :                          errmsg("syntax error")));
     285              : 
     286              :         }
     287              :     }
     288           27 :     while (lenstack)
     289              :     {
     290            2 :         lenstack--;
     291            2 :         if (!pushquery(state, OPR, stack[lenstack], 0, 0, 0))
     292            0 :             return ERR;
     293              :     };
     294           25 :     return END;
     295              : }
     296              : 
     297              : static void
     298           81 : findoprnd(ITEM *ptr, int32 *pos)
     299              : {
     300              :     /* since this function recurses, it could be driven to stack overflow. */
     301           81 :     check_stack_depth();
     302              : 
     303           81 :     if (ptr[*pos].type == VAL || ptr[*pos].type == VALTRUE)
     304              :     {
     305           50 :         ptr[*pos].left = 0;
     306           50 :         (*pos)++;
     307              :     }
     308           31 :     else if (ptr[*pos].val == (int32) '!')
     309              :     {
     310            6 :         ptr[*pos].left = 1;
     311            6 :         (*pos)++;
     312            6 :         findoprnd(ptr, pos);
     313              :     }
     314              :     else
     315              :     {
     316           25 :         ITEM       *curitem = &ptr[*pos];
     317           25 :         int32       tmp = *pos;
     318              : 
     319           25 :         (*pos)++;
     320           25 :         findoprnd(ptr, pos);
     321           25 :         curitem->left = *pos - tmp;
     322           25 :         findoprnd(ptr, pos);
     323              :     }
     324           81 : }
     325              : 
     326              : 
     327              : /*
     328              :  * input
     329              :  */
     330              : static ltxtquery *
     331           27 : queryin(char *buf, struct Node *escontext)
     332              : {
     333              :     QPRS_STATE  state;
     334              :     int32       i;
     335              :     ltxtquery  *query;
     336              :     int32       commonlen;
     337              :     ITEM       *ptr;
     338              :     NODE       *tmp;
     339           27 :     int32       pos = 0;
     340              : 
     341              :     /* init state */
     342           27 :     state.buf = buf;
     343           27 :     state.state = WAITOPERAND;
     344           27 :     state.count = 0;
     345           27 :     state.num = 0;
     346           27 :     state.str = NULL;
     347           27 :     state.escontext = escontext;
     348              : 
     349              :     /* init list of operand */
     350           27 :     state.sumlen = 0;
     351           27 :     state.lenop = 64;
     352           27 :     state.curop = state.op = (char *) palloc(state.lenop);
     353           27 :     *(state.curop) = '\0';
     354              : 
     355              :     /* parse query & make polish notation (postfix, but in reverse order) */
     356           27 :     if (makepol(&state) == ERR)
     357            2 :         return NULL;
     358           25 :     if (!state.num)
     359            0 :         ereturn(escontext, NULL,
     360              :                 (errcode(ERRCODE_SYNTAX_ERROR),
     361              :                  errmsg("syntax error"),
     362              :                  errdetail("Empty query.")));
     363              : 
     364           25 :     if (LTXTQUERY_TOO_BIG(state.num, state.sumlen))
     365            0 :         ereturn(escontext, NULL,
     366              :                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
     367              :                  errmsg("ltxtquery is too large")));
     368           25 :     commonlen = COMPUTESIZE(state.num, state.sumlen);
     369              : 
     370           25 :     query = (ltxtquery *) palloc0(commonlen);
     371           25 :     SET_VARSIZE(query, commonlen);
     372           25 :     query->size = state.num;
     373           25 :     ptr = GETQUERY(query);
     374              : 
     375              :     /* set item in polish notation */
     376          106 :     for (i = 0; i < state.num; i++)
     377              :     {
     378           81 :         ptr[i].type = state.str->type;
     379           81 :         ptr[i].val = state.str->val;
     380           81 :         ptr[i].distance = state.str->distance;
     381           81 :         ptr[i].length = state.str->length;
     382           81 :         ptr[i].flag = state.str->flag;
     383           81 :         tmp = state.str->next;
     384           81 :         pfree(state.str);
     385           81 :         state.str = tmp;
     386              :     }
     387              : 
     388              :     /* set user-friendly operand view */
     389           25 :     memcpy(GETOPERAND(query), state.op, state.sumlen);
     390           25 :     pfree(state.op);
     391              : 
     392              :     /* set left operand's position for every operator */
     393           25 :     pos = 0;
     394           25 :     findoprnd(ptr, &pos);
     395              : 
     396           25 :     return query;
     397              : }
     398              : 
     399              : /*
     400              :  * in without morphology
     401              :  */
     402            3 : PG_FUNCTION_INFO_V1(ltxtq_in);
     403              : Datum
     404           27 : ltxtq_in(PG_FUNCTION_ARGS)
     405              : {
     406              :     ltxtquery  *res;
     407              : 
     408           27 :     if ((res = queryin(PG_GETARG_POINTER(0), fcinfo->context)) == NULL)
     409            2 :         PG_RETURN_NULL();
     410           25 :     PG_RETURN_POINTER(res);
     411              : }
     412              : 
     413              : /*
     414              :  * ltxtquery type recv function
     415              :  *
     416              :  * The type is sent as text in binary mode, so this is almost the same
     417              :  * as the input function, but it's prefixed with a version number so we
     418              :  * can change the binary format sent in future if necessary. For now,
     419              :  * only version 1 is supported.
     420              :  */
     421            2 : PG_FUNCTION_INFO_V1(ltxtq_recv);
     422              : Datum
     423            0 : ltxtq_recv(PG_FUNCTION_ARGS)
     424              : {
     425            0 :     StringInfo  buf = (StringInfo) PG_GETARG_POINTER(0);
     426            0 :     int         version = pq_getmsgint(buf, 1);
     427              :     char       *str;
     428              :     int         nbytes;
     429              :     ltxtquery  *res;
     430              : 
     431            0 :     if (version != 1)
     432            0 :         elog(ERROR, "unsupported ltxtquery version number %d", version);
     433              : 
     434            0 :     str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
     435            0 :     res = queryin(str, NULL);
     436            0 :     pfree(str);
     437              : 
     438            0 :     PG_RETURN_POINTER(res);
     439              : }
     440              : 
     441              : /*
     442              :  * out function
     443              :  */
     444              : typedef struct
     445              : {
     446              :     ITEM       *curpol;
     447              :     char       *buf;
     448              :     char       *cur;
     449              :     char       *op;
     450              :     int32       buflen;
     451              : } INFIX;
     452              : 
     453              : #define RESIZEBUF(inf,addsize) \
     454              : while( ( (inf)->cur - (inf)->buf ) + (addsize) + 1 >= (inf)->buflen ) \
     455              : { \
     456              :     int32 len = (inf)->cur - (inf)->buf; \
     457              :     (inf)->buflen *= 2; \
     458              :     (inf)->buf = (char*) repalloc( (void*)(inf)->buf, (inf)->buflen ); \
     459              :     (inf)->cur = (inf)->buf + len; \
     460              : }
     461              : 
     462              : /*
     463              :  * recursive walk on tree and print it in
     464              :  * infix (human-readable) view
     465              :  */
     466              : static void
     467           10 : infix(INFIX *in, bool first)
     468              : {
     469              :     /* since this function recurses, it could be driven to stack overflow. */
     470           10 :     check_stack_depth();
     471              : 
     472           10 :     if (in->curpol->type == VAL)
     473              :     {
     474            6 :         char       *op = in->op + in->curpol->distance;
     475              : 
     476            8 :         RESIZEBUF(in, in->curpol->length * 2 + 5);
     477           32 :         while (*op)
     478              :         {
     479           26 :             *(in->cur) = *op;
     480           26 :             op++;
     481           26 :             in->cur++;
     482              :         }
     483            6 :         if (in->curpol->flag & LVAR_SUBLEXEME)
     484              :         {
     485            2 :             *(in->cur) = '%';
     486            2 :             in->cur++;
     487              :         }
     488            6 :         if (in->curpol->flag & LVAR_INCASE)
     489              :         {
     490            1 :             *(in->cur) = '@';
     491            1 :             in->cur++;
     492              :         }
     493            6 :         if (in->curpol->flag & LVAR_ANYEND)
     494              :         {
     495            3 :             *(in->cur) = '*';
     496            3 :             in->cur++;
     497              :         }
     498            6 :         *(in->cur) = '\0';
     499            6 :         in->curpol++;
     500              :     }
     501            4 :     else if (in->curpol->val == (int32) '!')
     502              :     {
     503            1 :         bool        isopr = false;
     504              : 
     505            1 :         RESIZEBUF(in, 1);
     506            1 :         *(in->cur) = '!';
     507            1 :         in->cur++;
     508            1 :         *(in->cur) = '\0';
     509            1 :         in->curpol++;
     510            1 :         if (in->curpol->type == OPR)
     511              :         {
     512            0 :             isopr = true;
     513            0 :             RESIZEBUF(in, 2);
     514            0 :             sprintf(in->cur, "( ");
     515            0 :             in->cur = strchr(in->cur, '\0');
     516              :         }
     517            1 :         infix(in, isopr);
     518            1 :         if (isopr)
     519              :         {
     520            0 :             RESIZEBUF(in, 2);
     521            0 :             sprintf(in->cur, " )");
     522            0 :             in->cur = strchr(in->cur, '\0');
     523              :         }
     524              :     }
     525              :     else
     526              :     {
     527            3 :         int32       op = in->curpol->val;
     528              :         INFIX       nrm;
     529              : 
     530            3 :         in->curpol++;
     531            3 :         if (op == (int32) '|' && !first)
     532              :         {
     533            0 :             RESIZEBUF(in, 2);
     534            0 :             sprintf(in->cur, "( ");
     535            0 :             in->cur = strchr(in->cur, '\0');
     536              :         }
     537              : 
     538            3 :         nrm.curpol = in->curpol;
     539            3 :         nrm.op = in->op;
     540            3 :         nrm.buflen = 16;
     541            3 :         nrm.cur = nrm.buf = palloc_array(char, nrm.buflen);
     542              : 
     543              :         /* get right operand */
     544            3 :         infix(&nrm, false);
     545              : 
     546              :         /* get & print left operand */
     547            3 :         in->curpol = nrm.curpol;
     548            3 :         infix(in, false);
     549              : 
     550              :         /* print operator & right operand */
     551            3 :         RESIZEBUF(in, 3 + (nrm.cur - nrm.buf));
     552            3 :         sprintf(in->cur, " %c %s", op, nrm.buf);
     553            3 :         in->cur = strchr(in->cur, '\0');
     554            3 :         pfree(nrm.buf);
     555              : 
     556            3 :         if (op == (int32) '|' && !first)
     557              :         {
     558            0 :             RESIZEBUF(in, 2);
     559            0 :             sprintf(in->cur, " )");
     560            0 :             in->cur = strchr(in->cur, '\0');
     561              :         }
     562              :     }
     563           10 : }
     564              : 
     565            3 : PG_FUNCTION_INFO_V1(ltxtq_out);
     566              : Datum
     567            3 : ltxtq_out(PG_FUNCTION_ARGS)
     568              : {
     569            3 :     ltxtquery  *query = PG_GETARG_LTXTQUERY_P(0);
     570              :     INFIX       nrm;
     571              : 
     572            3 :     if (query->size == 0)
     573            0 :         ereport(ERROR,
     574              :                 (errcode(ERRCODE_SYNTAX_ERROR),
     575              :                  errmsg("syntax error"),
     576              :                  errdetail("Empty query.")));
     577              : 
     578            3 :     nrm.curpol = GETQUERY(query);
     579            3 :     nrm.buflen = 32;
     580            3 :     nrm.cur = nrm.buf = palloc_array(char, nrm.buflen);
     581            3 :     *(nrm.cur) = '\0';
     582            3 :     nrm.op = GETOPERAND(query);
     583            3 :     infix(&nrm, true);
     584              : 
     585            3 :     PG_RETURN_POINTER(nrm.buf);
     586              : }
     587              : 
     588              : /*
     589              :  * ltxtquery type send function
     590              :  *
     591              :  * The type is sent as text in binary mode, so this is almost the same
     592              :  * as the output function, but it's prefixed with a version number so we
     593              :  * can change the binary format sent in future if necessary. For now,
     594              :  * only version 1 is supported.
     595              :  */
     596            2 : PG_FUNCTION_INFO_V1(ltxtq_send);
     597              : Datum
     598            0 : ltxtq_send(PG_FUNCTION_ARGS)
     599              : {
     600            0 :     ltxtquery  *query = PG_GETARG_LTXTQUERY_P(0);
     601              :     StringInfoData buf;
     602            0 :     int         version = 1;
     603              :     INFIX       nrm;
     604              : 
     605            0 :     if (query->size == 0)
     606            0 :         ereport(ERROR,
     607              :                 (errcode(ERRCODE_SYNTAX_ERROR),
     608              :                  errmsg("syntax error"),
     609              :                  errdetail("Empty query.")));
     610              : 
     611            0 :     nrm.curpol = GETQUERY(query);
     612            0 :     nrm.buflen = 32;
     613            0 :     nrm.cur = nrm.buf = palloc_array(char, nrm.buflen);
     614            0 :     *(nrm.cur) = '\0';
     615            0 :     nrm.op = GETOPERAND(query);
     616            0 :     infix(&nrm, true);
     617              : 
     618            0 :     pq_begintypsend(&buf);
     619            0 :     pq_sendint8(&buf, version);
     620            0 :     pq_sendtext(&buf, nrm.buf, strlen(nrm.buf));
     621            0 :     pfree(nrm.buf);
     622              : 
     623            0 :     PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
     624              : }
        

Generated by: LCOV version 2.0-1