Coverage Report

Created: 2026-10-16 07:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/r-source/src/main/apply.c
Line
Count
Source
1
/*
2
 *  R : A Computer Language for Statistical Data Analysis
3
 *  Copyright (C) 2000-2025  The R Core Team
4
 *
5
 *  This program is free software; you can redistribute it and/or modify
6
 *  it under the terms of the GNU General Public License as published by
7
 *  the Free Software Foundation; either version 2 of the License, or
8
 *  (at your option) any later version.
9
 *
10
 *  This program is distributed in the hope that it will be useful,
11
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 *  GNU General Public License for more details.
14
 *
15
 *  You should have received a copy of the GNU General Public License
16
 *  along with this program; if not, a copy is available at
17
 *  https://www.R-project.org/Licenses/
18
 */
19
20
#ifdef HAVE_CONFIG_H
21
#include <config.h>
22
#endif
23
24
#include <Defn.h>
25
#include <Internal.h>
26
27
318k
static SEXP checkArgIsSymbol(SEXP x) {
28
318k
    if (TYPEOF(x) != SYMSXP)
29
0
  error("argument must be a symbol");
30
318k
    return x;
31
318k
}
32
33
/* .Internal(lapply(X, FUN)) */
34
35
/* This is a special .Internal, so has unevaluated arguments.  It is
36
   called from a closure wrapper, so X and FUN will be symbols that
37
   are bound to promises in rho.
38
39
   FUN must be unevaluated for use in e.g. bquote .
40
*/
41
attribute_hidden SEXP do_lapply(SEXP call, SEXP op, SEXP args, SEXP rho)
42
159k
{
43
159k
    PROTECT_INDEX pidx, cidx;
44
45
159k
    checkArity(op, args);
46
159k
    SEXP X, XX, FUN;
47
159k
    X = checkArgIsSymbol(CAR(args));
48
159k
    XX = PROTECT(eval(CAR(args), rho));
49
159k
    R_xlen_t n = xlength(XX);  // a vector, so will be valid.
50
159k
    FUN = checkArgIsSymbol(CADR(args));
51
159k
    bool realIndx = n > INT_MAX;
52
53
159k
    SEXP ans = PROTECT(allocVector(VECSXP, n));
54
159k
    SEXP names = getAttrib(XX, R_NamesSymbol);
55
159k
    if(!isNull(names)) setAttrib(ans, R_NamesSymbol, names);
56
57
    /* Build call: FUN(X[[<ind>]], ...) */
58
159k
    SEXP isym = install("i");
59
159k
    SEXP tmp = PROTECT(lang3(R_Bracket2Symbol, X, isym));
60
159k
    SEXP R_fcall = PROTECT(lang3(FUN, tmp, R_DotsSymbol));
61
159k
    MARK_NOT_MUTABLE(R_fcall);
62
63
    /* Create the loop index variable and value */
64
159k
    SEXP ind = allocVector(realIndx ? REALSXP : INTSXP, 1);
65
159k
    PROTECT_WITH_INDEX(ind, &pidx);
66
159k
    defineVar(isym, ind, rho);
67
159k
    INCREMENT_NAMED(ind);
68
159k
    R_varloc_t loc = R_findVarLocInFrame(rho, isym);
69
159k
    PROTECT_WITH_INDEX(loc.cell, &cidx);
70
71
72
417k
    for(R_xlen_t i = 0; i < n; i++) {
73
258k
  if (realIndx) REAL(ind)[0] = (double)(i + 1);
74
258k
  else INTEGER(ind)[0] = (int)(i + 1);
75
258k
  tmp = R_forceAndCall(R_fcall, 1, rho);
76
258k
  if (MAYBE_REFERENCED(tmp)) tmp = lazy_duplicate(tmp);
77
258k
  SET_VECTOR_ELT(ans, i, tmp);
78
258k
  if (ind != R_GetVarLocValue(loc) || MAYBE_SHARED(ind)) {
79
      /* ind has been captured or removed by FUN so fix it up */
80
0
      REPROTECT(ind = duplicate(ind), pidx);
81
0
      defineVar(isym, ind, rho);
82
0
      INCREMENT_NAMED(ind);
83
0
      loc = R_findVarLocInFrame(rho, isym);
84
0
      REPROTECT(loc.cell, cidx);
85
0
  }
86
258k
    }
87
88
159k
    UNPROTECT(6);
89
159k
    return ans;
90
159k
}
91
92
/* .Internal(vapply(X, FUN, FUN.VALUE, USE.NAMES)) */
93
94
/* This is a special .Internal */
95
attribute_hidden SEXP do_vapply(SEXP call, SEXP op, SEXP args, SEXP rho)
96
466k
{
97
466k
    SEXP R_fcall, ans, names = R_NilValue, rowNames = R_NilValue,
98
466k
  X, XX, FUN, value, dim_v;
99
466k
    R_xlen_t i, n;
100
466k
    int commonLen;
101
466k
    int useNames, rnk_v = -1; // = array_rank(value) := length(dim(value))
102
466k
    bool array_value;
103
466k
    SEXPTYPE commonType;
104
466k
    PROTECT_INDEX index = 0; /* initialize to avoid a warning */
105
106
466k
    checkArity(op, args);
107
466k
    PROTECT(X = CAR(args));
108
466k
    PROTECT(XX = eval(CAR(args), rho));
109
466k
    FUN = CADR(args);  /* must be unevaluated for use in e.g. bquote */
110
466k
    PROTECT(value = eval(CADDR(args), rho));
111
466k
    if (!isVector(value)) error(_("'FUN.VALUE' must be a vector"));
112
466k
    useNames = asLogical(PROTECT(eval(CADDDR(args), rho)));
113
466k
    UNPROTECT(1);
114
    // FIXME: does not protect against length > 1
115
466k
    if (useNames == NA_LOGICAL) error(_("invalid '%s' value"), "USE.NAMES");
116
117
466k
    n = xlength(XX);
118
466k
    if (n == NA_INTEGER) error(_("invalid length"));
119
466k
    bool realIndx = n > INT_MAX;
120
121
466k
    commonLen = length(value);
122
466k
    if (commonLen > 1 && n > INT_MAX)
123
0
  error(_("long vectors are not supported for matrix/array results"));
124
466k
    commonType = TYPEOF(value);
125
    // check once here
126
466k
    if (commonType != CPLXSXP && commonType != REALSXP &&
127
465k
  commonType != INTSXP  && commonType != LGLSXP &&
128
447k
  commonType != RAWSXP  && commonType != STRSXP &&
129
0
  commonType != VECSXP)
130
0
  error(_("type '%s' is not supported"), R_typeToChar(value));
131
466k
    dim_v = getAttrib(value, R_DimSymbol);
132
466k
    array_value = (TYPEOF(dim_v) == INTSXP && LENGTH(dim_v) >= 1);
133
466k
    PROTECT(ans = allocVector(commonType, n*commonLen));
134
466k
    if (useNames) {
135
465k
  PROTECT(names = getAttrib(XX, R_NamesSymbol));
136
465k
  if (isNull(names) && TYPEOF(XX) == STRSXP) {
137
0
      UNPROTECT(1);
138
0
      PROTECT(names = XX);
139
0
  }
140
465k
  PROTECT_WITH_INDEX(rowNames = getAttrib(value,
141
465k
            array_value ? R_DimNamesSymbol
142
465k
            : R_NamesSymbol),
143
465k
         &index);
144
465k
    }
145
    /* The R level code has ensured that XX is a vector.
146
       If it is atomic we can speed things up slightly by
147
       using the evaluated version.
148
    */
149
466k
    {
150
466k
  SEXP ind, tmp;
151
  /* Build call: FUN(XX[[<ind>]], ...) */
152
153
466k
  SEXP isym = install("i");
154
466k
  PROTECT(ind = allocVector(realIndx ? REALSXP : INTSXP, 1));
155
466k
  defineVar(isym, ind, rho);
156
466k
  INCREMENT_NAMED(ind);
157
158
  /* Notice that it is OK to have one arg to LCONS do memory
159
     allocation and not PROTECT the result (LCONS does memory
160
     protection of its args internally), but not both of them,
161
     since the computation of one may destroy the other */
162
466k
  PROTECT(tmp = LCONS(R_Bracket2Symbol,
163
466k
          LCONS(X, LCONS(isym, R_NilValue))));
164
466k
  PROTECT(R_fcall = LCONS(FUN,
165
466k
        LCONS(tmp, LCONS(R_DotsSymbol, R_NilValue))));
166
167
466k
  int common_len_offset = 0;
168
1.83M
  for(i = 0; i < n; i++) {
169
1.36M
      SEXP val; SEXPTYPE valType;
170
1.36M
      PROTECT_INDEX indx;
171
1.36M
      if (realIndx) REAL(ind)[0] = (double)(i + 1);
172
1.36M
      else INTEGER(ind)[0] = (int)(i + 1);
173
1.36M
      val = R_forceAndCall(R_fcall, 1, rho);
174
1.36M
      if (MAYBE_REFERENCED(val))
175
12.9k
    val = lazy_duplicate(val); // Need to duplicate? Copying again anyway
176
1.36M
      PROTECT_WITH_INDEX(val, &indx);
177
1.36M
      if (length(val) != commonLen)
178
0
    error(_("values must be length %d,\n but FUN(X[[%lld]]) result is length %d"),
179
0
           commonLen, (long long)i+1, length(val));
180
1.36M
      valType = TYPEOF(val);
181
1.36M
      if (valType != commonType) {
182
1.74k
    bool okay = false;
183
1.74k
    switch (commonType) {
184
0
    case CPLXSXP: okay = (valType == REALSXP) || (valType == INTSXP)
185
0
            || (valType == LGLSXP); break;
186
1.74k
    case REALSXP: okay = (valType == INTSXP) || (valType == LGLSXP); break;
187
0
    case INTSXP:  okay = (valType == LGLSXP); break;
188
1.74k
    }
189
1.74k
    if (!okay)
190
0
        error(_("values must be type '%s',\n but FUN(X[[%lld]]) result is type '%s'"),
191
0
        R_typeToChar(value), (long long)i+1, R_typeToChar(val));
192
1.74k
    REPROTECT(val = coerceVector(val, commonType), indx);
193
1.74k
      }
194
      /* Take row names from the first result only */
195
1.36M
      if (i == 0 && useNames && isNull(rowNames))
196
465k
    REPROTECT(rowNames = getAttrib(val,
197
1.36M
                 array_value ? R_DimNamesSymbol : R_NamesSymbol),
198
1.36M
        index);
199
      // two cases - only for efficiency
200
1.36M
      if(commonLen == 1) { // common case
201
1.36M
    switch (commonType) {
202
0
    case CPLXSXP: COMPLEX(ans)[i] = COMPLEX(val)[0]; break;
203
1.74k
    case REALSXP: REAL(ans)   [i] = REAL   (val)[0]; break;
204
0
    case INTSXP:  INTEGER(ans)[i] = INTEGER(val)[0]; break;
205
24.3k
    case LGLSXP:  LOGICAL(ans)[i] = LOGICAL(val)[0]; break;
206
0
    case RAWSXP:  RAW(ans)    [i] = RAW    (val)[0]; break;
207
1.34M
    case STRSXP:  SET_STRING_ELT(ans, i, STRING_ELT(val, 0)); break;
208
0
    case VECSXP:  SET_VECTOR_ELT(ans, i, VECTOR_ELT(val, 0)); break;
209
1.36M
    }
210
1.36M
      } else if (commonLen) { // commonLen > 1
211
308
    switch (commonType) {
212
0
    case REALSXP:
213
0
        memcpy(REAL(ans) + common_len_offset,
214
0
         REAL(val), commonLen * sizeof(double)); break;
215
0
    case INTSXP:
216
0
        memcpy(INTEGER(ans) + common_len_offset,
217
0
         INTEGER(val), commonLen * sizeof(int)); break;
218
308
    case LGLSXP:
219
308
        memcpy(LOGICAL(ans) + common_len_offset,
220
308
         LOGICAL(val), commonLen * sizeof(int)); break;
221
0
    case RAWSXP:
222
0
        memcpy(RAW(ans) + common_len_offset,
223
0
         RAW(val), commonLen * sizeof(Rbyte)); break;
224
0
    case CPLXSXP:
225
0
        memcpy(COMPLEX(ans) + common_len_offset,
226
0
         COMPLEX(val), commonLen * sizeof(Rcomplex)); break;
227
0
    case STRSXP:
228
0
        for (int j = 0; j < commonLen; j++)
229
0
      SET_STRING_ELT(ans, common_len_offset + j, STRING_ELT(val, j));
230
0
        break;
231
0
    case VECSXP:
232
0
        for (int j = 0; j < commonLen; j++)
233
0
      SET_VECTOR_ELT(ans, common_len_offset + j, VECTOR_ELT(val, j));
234
0
        break;
235
308
    }
236
308
    common_len_offset += commonLen;
237
308
      }
238
1.36M
      UNPROTECT(1);
239
1.36M
  }
240
466k
  UNPROTECT(3);
241
466k
    }
242
243
466k
    if (commonLen != 1) {
244
308
  SEXP dim;
245
308
  rnk_v = array_value ? LENGTH(dim_v) : 1;
246
308
  PROTECT(dim = allocVector(INTSXP, rnk_v+1));
247
308
  if(array_value)
248
0
      for(int j = 0; j < rnk_v; j++)
249
0
    INTEGER(dim)[j] = INTEGER(dim_v)[j];
250
308
  else
251
308
      INTEGER(dim)[0] = commonLen;
252
308
  INTEGER(dim)[rnk_v] = (int) n;  // checked above
253
308
  setAttrib(ans, R_DimSymbol, dim);
254
308
  UNPROTECT(1);
255
308
    }
256
257
466k
    if (useNames) {
258
465k
  if (commonLen == 1) {
259
465k
      if(!isNull(names)) setAttrib(ans, R_NamesSymbol, names);
260
465k
  } else {
261
308
      if (!isNull(names) || !isNull(rowNames)) {
262
0
    SEXP dimnames;
263
0
    PROTECT(dimnames = allocVector(VECSXP, rnk_v+1));
264
0
    if(array_value && !isNull(rowNames)) {
265
0
        if(TYPEOF(rowNames) != VECSXP || LENGTH(rowNames) != rnk_v)
266
      // should never happen ..
267
0
      error(_("dimnames(<value>) is neither NULL nor list of length %d"),
268
0
            rnk_v);
269
0
        for(int j = 0; j < rnk_v; j++)
270
0
      SET_VECTOR_ELT(dimnames, j, VECTOR_ELT(rowNames, j));
271
0
    } else
272
0
        SET_VECTOR_ELT(dimnames, 0, rowNames);
273
274
0
    SET_VECTOR_ELT(dimnames, rnk_v, names);
275
0
    setAttrib(ans, R_DimNamesSymbol, dimnames);
276
0
    UNPROTECT(1);
277
0
      }
278
308
  }
279
465k
  UNPROTECT(2); /* names and rowNames */
280
465k
    }
281
466k
    UNPROTECT(4); /* X, XX, value, ans */
282
466k
    return ans;
283
466k
}
284
285
//  Apply FUN() to X recursively;  workhorse of rapply()
286
static SEXP do_one(SEXP X, SEXP FUN, SEXP classes, SEXP deflt,
287
       bool replace, SEXP rho)
288
0
{
289
0
    SEXP ans, names, klass;
290
0
    bool matched = false;
291
292
    /* if X is a list, recurse.  Otherwise if it matches classes call f */
293
0
    if(X == R_NilValue || isVectorList(X)) {
294
0
  R_xlen_t n = xlength(X);
295
0
  if (replace) {
296
0
      PROTECT(ans = shallow_duplicate(X));
297
0
  } else {
298
0
      PROTECT(ans = allocVector(VECSXP, n));
299
0
      names = getAttrib(X, R_NamesSymbol);
300
0
      if(!isNull(names)) setAttrib(ans, R_NamesSymbol, names);
301
0
  }
302
0
  for(R_xlen_t i = 0; i < n; i++)
303
0
      SET_VECTOR_ELT(ans, i, do_one(VECTOR_ELT(X, i), FUN, classes,
304
0
            deflt, replace, rho));
305
0
  UNPROTECT(1);
306
0
  return ans;
307
0
    }
308
0
    if(strcmp(CHAR(STRING_ELT(classes, 0)), "ANY") == 0) /* ASCII */
309
0
  matched = true;
310
0
    else {
311
0
  PROTECT(klass = R_data_class(X, false));
312
0
  for(int i = 0; i < LENGTH(klass); i++)
313
0
      for(int j = 0; j < length(classes); j++)
314
0
    if(Seql(STRING_ELT(klass, i), STRING_ELT(classes, j)))
315
0
        matched = true;
316
0
  UNPROTECT(1);
317
0
    }
318
0
    if(matched) {
319
  /* This stores value to which the function is to be applied in
320
     a variable X in the environment of the rapply closure call
321
     that calls into the rapply .Internal. */
322
0
  SEXP R_fcall; /* could allocate once and preserve for re-use */
323
0
  SEXP Xsym = install("X");
324
0
  defineVar(Xsym, X, rho);
325
0
  INCREMENT_NAMED(X);
326
  /* PROTECT(R_fcall = lang2(FUN, Xsym)); */
327
0
  PROTECT(R_fcall = lang3(FUN, Xsym, R_DotsSymbol));
328
0
  ans = R_forceAndCall(R_fcall, 1, rho);
329
0
  if (MAYBE_REFERENCED(ans))
330
0
      ans = lazy_duplicate(ans);
331
0
  UNPROTECT(1);
332
0
  return(ans);
333
0
    } else if(replace) return lazy_duplicate(X);
334
0
    else return lazy_duplicate(deflt);
335
0
}
336
337
attribute_hidden SEXP do_rapply(SEXP call, SEXP op, SEXP args, SEXP rho)
338
0
{
339
0
    SEXP X, FUN, classes, deflt, how, ans;
340
341
0
    checkArity(op, args);
342
0
    X = CAR(args); args = CDR(args);
343
0
    if(!isVectorList(X))
344
0
  error(_("'%s' must be a list or expression"), "object");
345
0
    FUN = CAR(args); args = CDR(args);
346
0
    if(!isFunction(FUN)) error(_("invalid '%s' argument"), "f");
347
0
    classes = CAR(args); args = CDR(args);
348
0
    if(!isString(classes)) error(_("invalid '%s' argument"), "classes");
349
0
    deflt = CAR(args); args = CDR(args);
350
0
    how = CAR(args);
351
0
    if(!isString(how)) error(_("invalid '%s' argument"), "how");
352
0
    bool replace = strcmp(CHAR(STRING_ELT(how, 0)), "replace") == 0; /* ASCII */
353
0
    R_xlen_t n = xlength(X);
354
0
    if (replace) {
355
0
      PROTECT(ans = shallow_duplicate(X));
356
0
    } else {
357
0
      PROTECT(ans = allocVector(VECSXP, n));
358
0
      SEXP names = getAttrib(X, R_NamesSymbol);
359
0
      if(!isNull(names)) setAttrib(ans, R_NamesSymbol, names);
360
0
    }
361
0
    for(R_xlen_t i = 0; i < n; i++)
362
0
  SET_VECTOR_ELT(ans, i, do_one(VECTOR_ELT(X, i), FUN, classes, deflt,
363
0
              replace, rho));
364
0
    UNPROTECT(1);
365
0
    return ans;
366
0
}
367
368
/**
369
 * Recursively check if  X  is a tree with only factor leaves;
370
 *   the workhorse for do_islistfactor()
371
 * @param X  list or expression
372
 * @return TRUE(1), FALSE(0) or NA_LOGICAL
373
 */
374
static int islistfactor(SEXP X)
375
52.9k
{
376
52.9k
    switch(TYPEOF(X)) {
377
26.4k
    case VECSXP:
378
26.4k
    case EXPRSXP: {
379
26.4k
  int n = LENGTH(X), ans = NA_LOGICAL;
380
26.4k
  for(int i = 0; i < n; i++) {
381
26.4k
      int isLF = islistfactor(VECTOR_ELT(X, i));
382
26.4k
      if(!isLF)
383
26.4k
    return false;
384
0
      else if(isLF == true)
385
0
    ans = true;
386
      // else isLF is NA
387
26.4k
  }
388
0
  return ans;
389
26.4k
    }
390
26.4k
    default:
391
26.4k
  return isFactor(X);
392
52.9k
    }
393
52.9k
}
394
395
396
/* is this a tree with only factor leaves? */
397
// currently only called from unlist()
398
attribute_hidden SEXP do_islistfactor(SEXP call, SEXP op, SEXP args, SEXP rho)
399
32.9k
{
400
32.9k
    checkArity(op, args);
401
32.9k
    SEXP X = CAR(args);
402
32.9k
    bool recursive = asBool2(CADR(args), call);
403
32.9k
    int n = length(X);
404
32.9k
    if(n == 0 || !isVectorList(X))
405
473
  return ScalarLogical(false);
406
407
32.5k
    if(!recursive) {
408
6.05k
  for(int i = 0; i < n; i++)
409
6.05k
      if(!isFactor(VECTOR_ELT(X, i)))
410
6.05k
    return ScalarLogical(false);
411
412
0
  return ScalarLogical(true);
413
6.05k
    }
414
26.4k
    else { // recursive:  isVectorList(X) <==> X is VECSXP or EXPRSXP
415
26.4k
  return ScalarLogical((islistfactor(X) == true) ? true : false);
416
26.4k
    }
417
32.5k
}