diff --git a/NEWS.md b/NEWS.md index 2f38cf6..b69312d 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,10 @@ +# monolix2rx 0.0.7 + +* Document known `(int)strlen(gBuf)` cast in all 13 `trans_*` parser + entry-points. Inputs at or above `INT_MAX` bytes cause silent length + truncation in the `dparse()` call. A long-term fix will switch each + call site to `udparse()` once dparser-R ships that symbol to CRAN. + # monolix2rx 0.0.6 * Updated to add types for rstudio completion diff --git a/src/dataSettings.c b/src/dataSettings.c index d53b02a..729b675 100644 --- a/src/dataSettings.c +++ b/src/dataSettings.c @@ -132,6 +132,10 @@ void trans_data_settings(const char* parse){ errP = curP; eBufLast = 0; gBufFree=0; + /* TODO(long-term): switch to udparse() once dparser-R ships that symbol + * to CRAN. udparse() accepts an unsigned int for buf_len, eliminating + * the silent (int)strlen truncation on inputs >= INT_MAX bytes. + * Track at https://github.com/nlmixr2/dparser-R */ _pn= dparse(curP, gBuf, (int)strlen(gBuf)); if (!_pn || curP->syntax_errors) { } else { diff --git a/src/equation.c b/src/equation.c index c4e811c..6b5b6f7 100644 --- a/src/equation.c +++ b/src/equation.c @@ -526,6 +526,10 @@ void trans_equation(const char* parse){ errP = curP; eBufLast = 0; gBufFree=0; + /* TODO(long-term): switch to udparse() once dparser-R ships that symbol + * to CRAN. udparse() accepts an unsigned int for buf_len, eliminating + * the silent (int)strlen truncation on inputs >= INT_MAX bytes. + * Track at https://github.com/nlmixr2/dparser-R */ _pn= dparse(curP, gBuf, (int)strlen(gBuf)); if (!_pn || curP->syntax_errors) { } else { diff --git a/src/longDef.c b/src/longDef.c index d792fa7..bfcf302 100644 --- a/src/longDef.c +++ b/src/longDef.c @@ -549,6 +549,10 @@ void trans_longdef(const char* parse){ errP = curP; eBufLast = 0; gBufFree=0; + /* TODO(long-term): switch to udparse() once dparser-R ships that symbol + * to CRAN. udparse() accepts an unsigned int for buf_len, eliminating + * the silent (int)strlen truncation on inputs >= INT_MAX bytes. + * Track at https://github.com/nlmixr2/dparser-R */ _pn= dparse(curP, gBuf, (int)strlen(gBuf)); if (!_pn || curP->syntax_errors) { } else { diff --git a/src/longOutput.c b/src/longOutput.c index 412fdc0..e65d4b9 100644 --- a/src/longOutput.c +++ b/src/longOutput.c @@ -118,6 +118,10 @@ void trans_longoutput(const char* parse){ errP = curP; eBufLast = 0; gBufFree=0; + /* TODO(long-term): switch to udparse() once dparser-R ships that symbol + * to CRAN. udparse() accepts an unsigned int for buf_len, eliminating + * the silent (int)strlen truncation on inputs >= INT_MAX bytes. + * Track at https://github.com/nlmixr2/dparser-R */ _pn= dparse(curP, gBuf, (int)strlen(gBuf)); if (!_pn || curP->syntax_errors) { } else { diff --git a/src/mlxtranContent.c b/src/mlxtranContent.c index 25a4f41..e3efefd 100644 --- a/src/mlxtranContent.c +++ b/src/mlxtranContent.c @@ -369,6 +369,10 @@ void trans_content(const char* parse){ errP = curP; eBufLast = 0; gBufFree=0; + /* TODO(long-term): switch to udparse() once dparser-R ships that symbol + * to CRAN. udparse() accepts an unsigned int for buf_len, eliminating + * the silent (int)strlen truncation on inputs >= INT_MAX bytes. + * Track at https://github.com/nlmixr2/dparser-R */ _pn= dparse(curP, gBuf, (int)strlen(gBuf)); if (!_pn || curP->syntax_errors) { } else { diff --git a/src/mlxtranFileinfo.c b/src/mlxtranFileinfo.c index d3fe67e..d37d014 100644 --- a/src/mlxtranFileinfo.c +++ b/src/mlxtranFileinfo.c @@ -123,6 +123,10 @@ void trans_fileinfo(const char* parse){ errP = curP; eBufLast = 0; gBufFree=0; + /* TODO(long-term): switch to udparse() once dparser-R ships that symbol + * to CRAN. udparse() accepts an unsigned int for buf_len, eliminating + * the silent (int)strlen truncation on inputs >= INT_MAX bytes. + * Track at https://github.com/nlmixr2/dparser-R */ _pn= dparse(curP, gBuf, (int)strlen(gBuf)); if (!_pn || curP->syntax_errors) { } else { diff --git a/src/mlxtranFit.c b/src/mlxtranFit.c index 4c53be0..c0ef565 100644 --- a/src/mlxtranFit.c +++ b/src/mlxtranFit.c @@ -141,6 +141,10 @@ void trans_fit(const char* parse){ errP = curP; eBufLast = 0; gBufFree=0; + /* TODO(long-term): switch to udparse() once dparser-R ships that symbol + * to CRAN. udparse() accepts an unsigned int for buf_len, eliminating + * the silent (int)strlen truncation on inputs >= INT_MAX bytes. + * Track at https://github.com/nlmixr2/dparser-R */ _pn= dparse(curP, gBuf, (int)strlen(gBuf)); if (!_pn || curP->syntax_errors) { } else { diff --git a/src/mlxtranInd.c b/src/mlxtranInd.c index 5cf9729..7a20246 100644 --- a/src/mlxtranInd.c +++ b/src/mlxtranInd.c @@ -168,6 +168,10 @@ void trans_individual(const char* parse){ errP = curP; eBufLast = 0; gBufFree=0; + /* TODO(long-term): switch to udparse() once dparser-R ships that symbol + * to CRAN. udparse() accepts an unsigned int for buf_len, eliminating + * the silent (int)strlen truncation on inputs >= INT_MAX bytes. + * Track at https://github.com/nlmixr2/dparser-R */ _pn= dparse(curP, gBuf, (int)strlen(gBuf)); if (!_pn || curP->syntax_errors) { } else { diff --git a/src/mlxtranIndDefinition.c b/src/mlxtranIndDefinition.c index 71f86a5..a7072ae 100644 --- a/src/mlxtranIndDefinition.c +++ b/src/mlxtranIndDefinition.c @@ -280,6 +280,10 @@ void trans_indDef(const char* parse){ errP = curP; eBufLast = 0; gBufFree=0; + /* TODO(long-term): switch to udparse() once dparser-R ships that symbol + * to CRAN. udparse() accepts an unsigned int for buf_len, eliminating + * the silent (int)strlen truncation on inputs >= INT_MAX bytes. + * Track at https://github.com/nlmixr2/dparser-R */ _pn= dparse(curP, gBuf, (int)strlen(gBuf)); if (!_pn || curP->syntax_errors) { } else { diff --git a/src/mlxtranOp.c b/src/mlxtranOp.c index 4dfb785..75b741f 100644 --- a/src/mlxtranOp.c +++ b/src/mlxtranOp.c @@ -183,6 +183,10 @@ void trans_mlxtran_op(const char* parse){ errP = curP; eBufLast = 0; gBufFree=0; + /* TODO(long-term): switch to udparse() once dparser-R ships that symbol + * to CRAN. udparse() accepts an unsigned int for buf_len, eliminating + * the silent (int)strlen truncation on inputs >= INT_MAX bytes. + * Track at https://github.com/nlmixr2/dparser-R */ _pn= dparse(curP, gBuf, (int)strlen(gBuf)); if (!_pn || curP->syntax_errors) { } else { diff --git a/src/mlxtranParameter.c b/src/mlxtranParameter.c index be2f09d..28551f8 100644 --- a/src/mlxtranParameter.c +++ b/src/mlxtranParameter.c @@ -140,6 +140,10 @@ void trans_parameter(const char* parse){ errP = curP; eBufLast = 0; gBufFree=0; + /* TODO(long-term): switch to udparse() once dparser-R ships that symbol + * to CRAN. udparse() accepts an unsigned int for buf_len, eliminating + * the silent (int)strlen truncation on inputs >= INT_MAX bytes. + * Track at https://github.com/nlmixr2/dparser-R */ _pn= dparse(curP, gBuf, (int)strlen(gBuf)); if (!_pn || curP->syntax_errors) { } else { diff --git a/src/mlxtranTask.c b/src/mlxtranTask.c index 1cdad77..10ce5af 100644 --- a/src/mlxtranTask.c +++ b/src/mlxtranTask.c @@ -142,6 +142,10 @@ void trans_mlxtrantask(const char* parse){ errP = curP; eBufLast = 0; gBufFree=0; + /* TODO(long-term): switch to udparse() once dparser-R ships that symbol + * to CRAN. udparse() accepts an unsigned int for buf_len, eliminating + * the silent (int)strlen truncation on inputs >= INT_MAX bytes. + * Track at https://github.com/nlmixr2/dparser-R */ _pn= dparse(curP, gBuf, (int)strlen(gBuf)); if (!_pn || curP->syntax_errors) { } else { diff --git a/src/summaryData.c b/src/summaryData.c index 75b563e..0f7ade7 100644 --- a/src/summaryData.c +++ b/src/summaryData.c @@ -131,6 +131,10 @@ void trans_summaryData(const char* parse){ errP = curP; eBufLast = 0; gBufFree=0; + /* TODO(long-term): switch to udparse() once dparser-R ships that symbol + * to CRAN. udparse() accepts an unsigned int for buf_len, eliminating + * the silent (int)strlen truncation on inputs >= INT_MAX bytes. + * Track at https://github.com/nlmixr2/dparser-R */ _pn= dparse(curP, gBuf, (int)strlen(gBuf)); if (!_pn || curP->syntax_errors) { } else { diff --git a/tests/testthat/test-mem-dparse-int-cast.R b/tests/testthat/test-mem-dparse-int-cast.R new file mode 100644 index 0000000..9e8f8b1 --- /dev/null +++ b/tests/testthat/test-mem-dparse-int-cast.R @@ -0,0 +1,30 @@ +test_that("trans_* parsers handle normal-sized inputs without error", { + # Sanity check: regular Mlxtran fragments must parse cleanly. + # The (int)strlen(gBuf) cast in each trans_* entry-point is a known + # long-term issue: inputs >= INT_MAX bytes silently truncate the length + # passed to dparse(). The fix will arrive when dparser-R exports + # udparse() to CRAN; at that point each call site will switch from + # dparse(curP, gBuf, (int)strlen(gBuf)) + # to + # udparse(curP, gBuf, (unsigned int)strlen(gBuf)). + expect_no_error( + tryCatch( + .Call(`_monolix2rx_trans_equation`, + "[LONGITUDINAL] EQUATION:\nf = exp(-k*t)\n", + "[LONGITUDINAL] EQUATION:"), + error = function(e) { + if (grepl("input too large", conditionMessage(e))) stop(e) + # Other parse errors from synthetic input are acceptable. + NULL + } + ) + ) +}) + +test_that("dparse int-cast known issue documented (skipped: requires ~2GB RAM)", { + skip("Requires ~2GB free RAM; fix pending dparser-R udparse() CRAN release") + # When input reaches INT_MAX bytes, (int)strlen silently truncates the + # length, causing dparse() to read from an incorrect position. + big <- strrep("a", 2147483647L) + expect_error(.Call(`_monolix2rx_trans_equation`, big, "")) +})