Skip to content

Commit

Permalink
Refactor CHECK_RANGE to be i18n friendly (#6530)
Browse files Browse the repository at this point in the history
* Refactor CHECK_RANGE to be i18n friendly

* Move column_desc buffer inside columnDesc routine

* make nominative case clearer
  • Loading branch information
MichaelChirico authored Sep 26, 2024
1 parent e22f774 commit 34ec8e9
Showing 1 changed file with 24 additions and 19 deletions.
43 changes: 24 additions & 19 deletions src/assign.c
Original file line number Diff line number Diff line change
Expand Up @@ -744,6 +744,12 @@ SEXP assign(SEXP dt, SEXP rows, SEXP cols, SEXP newcolnames, SEXP values)
#define MSGSIZE 1000
static char memrecycle_message[MSGSIZE+1]; // returned to rbindlist so it can prefix with which one of the list of data.table-like objects

const char *columnDesc(int colnum, const char *colname) {
static char column_desc[MSGSIZE+1]; // can contain column names, hence relatively large allocation.
snprintf(column_desc, MSGSIZE, _("(column %d named '%s')"), colnum, colname);
return column_desc;
}

const char *memrecycle(const SEXP target, const SEXP where, const int start, const int len, SEXP source, const int sourceStart, const int sourceLen, const int colnum, const char *colname)
// like memcpy but recycles single-item source
// 'where' a 1-based INTEGER vector subset of target to assign to, or NULL or integer()
Expand Down Expand Up @@ -932,18 +938,17 @@ const char *memrecycle(const SEXP target, const SEXP where, const int start, con
// inside BODY that cater for 'where' or not. Maybe there's a way to merge the two macros in future.
// The idea is to do these range checks without calling coerceVector() (which allocates)

#define CHECK_RANGE(STYPE, RFUN, COND, FMT, TO, FMTVAL) {{ \
#define CHECK_RANGE(STYPE, RFUN, COND, FMTVAL, FMT) {{ \
const STYPE *sd = (const STYPE *)RFUN(source); \
for (int i=0; i<slen; ++i) { \
const STYPE val = sd[i+soff]; \
if (COND) { \
const char *sType = sourceIsI64 ? "integer64" : type2char(TYPEOF(source)); \
const char *tType = targetIsI64 ? "integer64" : type2char(TYPEOF(target)); \
snprintf(memrecycle_message, MSGSIZE, \
colnum == 0 \
? _("%"FMT" (type '%s') at RHS position %d "TO" when assigning to type '%s' (target vector)") \
: _("%"FMT" (type '%s') at RHS position %d "TO" when assigning to type '%s' (column %d named '%s')"), \
FMTVAL, sType, i+1, tType, colnum, colname); \
snprintf(memrecycle_message, MSGSIZE, FMT, \
FMTVAL, sType, i+1, tType, \
/* NB: important for () to be part of the translated string as a signal of nominative case to translators */ \
colnum == 0 ? _("(target vector)") : columnDesc(colnum, colname)); \
/* string returned so that rbindlist/dogroups can prefix it with which item of its list this refers to */ \
break; \
} \
Expand All @@ -953,36 +958,36 @@ const char *memrecycle(const SEXP target, const SEXP where, const int start, con
switch(TYPEOF(target)) {
case LGLSXP:
switch (TYPEOF(source)) {
case RAWSXP: CHECK_RANGE(Rbyte, RAW, val!=0 && val!=1, "d", "taken as TRUE", val)
case INTSXP: CHECK_RANGE(int, INTEGER, val!=0 && val!=1 && val!=NA_INTEGER, "d", "taken as TRUE", val)
case RAWSXP: CHECK_RANGE(Rbyte, RAW, val!=0 && val!=1, val, _("%d (type '%s') at RHS position %d taken as TRUE when assigning to type '%s' %s"))
case INTSXP: CHECK_RANGE(int, INTEGER, val!=0 && val!=1 && val!=NA_INTEGER, val, _("%d (type '%s') at RHS position %d taken as TRUE when assigning to type '%s' %s"))
case REALSXP: if (sourceIsI64)
CHECK_RANGE(int64_t, REAL, val!=0 && val!=1 && val!=NA_INTEGER64, PRId64, "taken as TRUE", val)
else CHECK_RANGE(double, REAL, !ISNAN(val) && val!=0.0 && val!=1.0, "f", "taken as TRUE", val)
CHECK_RANGE(int64_t, REAL, val!=0 && val!=1 && val!=NA_INTEGER64, val, _("%"PRId64" (type '%s') at RHS position %d taken as TRUE when assigning to type '%s' %s"))
else CHECK_RANGE(double, REAL, !ISNAN(val) && val!=0.0 && val!=1.0, val, _("%f (type '%s') at RHS position %d taken as TRUE when assigning to type '%s' %s"))
} break;
case RAWSXP:
switch (TYPEOF(source)) {
case INTSXP: CHECK_RANGE(int, INTEGER, val<0 || val>255, "d", "taken as 0", val)
case INTSXP: CHECK_RANGE(int, INTEGER, val<0 || val>255, val, _("%d (type '%s' at RHS position %d taken as 0 when assigning to type '%s' %s"))
case REALSXP: if (sourceIsI64)
CHECK_RANGE(int64_t, REAL, val<0 || val>255, PRId64, "taken as 0", val)
else CHECK_RANGE(double, REAL, !R_FINITE(val) || val<0.0 || val>256.0 || (int)val!=val, "f", "either truncated (precision lost) or taken as 0", val)
CHECK_RANGE(int64_t, REAL, val<0 || val>255, val, _("%"PRId64" (type '%s') at RHS position %d taken as 0 when assigning to type '%s' %s"))
else CHECK_RANGE(double, REAL, !R_FINITE(val) || val<0.0 || val>256.0 || (int)val!=val, val, _("%f (type '%s') at RHS position %d either truncated (precision lost) or taken as 0 when assigning to type '%s' %s"))
} break;
case INTSXP:
switch (TYPEOF(source)) {
case REALSXP: if (sourceIsI64)
CHECK_RANGE(int64_t, REAL, val!=NA_INTEGER64 && (val<=NA_INTEGER || val>INT_MAX), PRId64, "out-of-range (NA)", val)
else CHECK_RANGE(double, REAL, !ISNAN(val) && (!within_int32_repres(val) || (int)val!=val), "f", "out-of-range(NA) or truncated (precision lost)", val)
CHECK_RANGE(int64_t, REAL, val!=NA_INTEGER64 && (val<=NA_INTEGER || val>INT_MAX), val, _("%"PRId64" (type '%s') at RHS position %d out-of-range (NA) when assigning to type '%s' %s"))
else CHECK_RANGE(double, REAL, !ISNAN(val) && (!within_int32_repres(val) || (int)val!=val), val, _("%f (type '%s') at RHS position %d out-of-range(NA) or truncated (precision lost) when assigning to type '%s' %s"))
case CPLXSXP: CHECK_RANGE(Rcomplex, COMPLEX, !((ISNAN(val.i) || (R_FINITE(val.i) && val.i==0.0)) &&
(ISNAN(val.r) || (within_int32_repres(val.r) && (int)val.r==val.r))), "f", "either imaginary part discarded or real part truncated (precision lost)", val.r)
(ISNAN(val.r) || (within_int32_repres(val.r) && (int)val.r==val.r))), val.r, _("%f (type '%s') at RHS position %d either imaginary part discarded or real part truncated (precision lost) when assigning to type '%s' %s"))
} break;
case REALSXP:
switch (TYPEOF(source)) {
case REALSXP: if (targetIsI64 && !sourceIsI64)
CHECK_RANGE(double, REAL, !ISNAN(val) && (!within_int64_repres(val) || (int64_t)val!=val), "f", "out-of-range(NA) or truncated (precision lost)", val)
CHECK_RANGE(double, REAL, !ISNAN(val) && (!within_int64_repres(val) || (int64_t)val!=val), val, _("%f (type '%s') at RHS position %d out-of-range(NA) or truncated (precision lost) when assigning to type '%s' %s"))
break;
case CPLXSXP: if (targetIsI64)
CHECK_RANGE(Rcomplex, COMPLEX, !((ISNAN(val.i) || (R_FINITE(val.i) && val.i==0.0)) &&
(ISNAN(val.r) || (R_FINITE(val.r) && (int64_t)val.r==val.r))), "f", "either imaginary part discarded or real part truncated (precision lost)", val.r)
else CHECK_RANGE(Rcomplex, COMPLEX, !(ISNAN(val.i) || (R_FINITE(val.i) && val.i==0.0)), "f", "imaginary part discarded", val.i)
(ISNAN(val.r) || (R_FINITE(val.r) && (int64_t)val.r==val.r))), val.r, _("%f (type '%s') at RHS position %d either imaginary part discarded or real part truncated (precision lost) when assigning to type '%s' %s"))
else CHECK_RANGE(Rcomplex, COMPLEX, !(ISNAN(val.i) || (R_FINITE(val.i) && val.i==0.0)), val.r, _("%f (type '%s') at RHS position %d imaginary part discarded when assigning to type '%s' %s"))
}
}
}
Expand Down

0 comments on commit 34ec8e9

Please sign in to comment.