From d3289b79c7c16d0e6ecf59ce3c525f028ac681ea Mon Sep 17 00:00:00 2001 From: nathan-russell Date: Sat, 22 Apr 2017 15:53:02 -0400 Subject: [PATCH 1/6] Sugar function 'trimws' with unit tests (closes #679) --- ChangeLog | 8 + inst/NEWS.Rd | 5 + .../Rcpp/sugar/functions/strings/strings.h | 1 + .../Rcpp/sugar/functions/strings/trimws.h | 201 ++++++++++++++++++ inst/unitTests/cpp/sugar.cpp | 18 ++ inst/unitTests/runit.sugar.R | 131 ++++++++++++ 6 files changed, 364 insertions(+) create mode 100644 inst/include/Rcpp/sugar/functions/strings/trimws.h diff --git a/ChangeLog b/ChangeLog index 8b652e93b..3690ef436 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2017-04-22 Nathan Russell + + * inst/include/Rcpp/sugar/functions/strings/trimws.h: Added sugar + function trimws with unit tests + * inst/include/Rcpp/sugar/functions/strings/strings.h: Idem + * inst/unitTests/cpp/sugar.cpp: Idem + * inst/unitTests/runit.sugar.R: Idem + 2017-04-20 Dirk Eddelbuettel * DESCRIPTION (Version, Date): Roll minor version diff --git a/inst/NEWS.Rd b/inst/NEWS.Rd index 4e1be6c0b..c63d50cf6 100644 --- a/inst/NEWS.Rd +++ b/inst/NEWS.Rd @@ -25,6 +25,11 @@ (James Balamuta in \ghpr{661} addressing \ghit{628}, \ghit{563}, \ghit{552}, \ghit{460}, \ghit{419}, and \ghit{251}). } + \item Changes in Rcpp Sugar: + \itemize{ + \item Added sugar function \code{trimws} (Nathan Russell in \ghpr{680} + addressing \ghit{679}). + } } } diff --git a/inst/include/Rcpp/sugar/functions/strings/strings.h b/inst/include/Rcpp/sugar/functions/strings/strings.h index 260b3af16..6031e53b5 100644 --- a/inst/include/Rcpp/sugar/functions/strings/strings.h +++ b/inst/include/Rcpp/sugar/functions/strings/strings.h @@ -23,5 +23,6 @@ #define RCPP_SUGAR_FUNCTIONS_STRINGS_H #include +#include #endif diff --git a/inst/include/Rcpp/sugar/functions/strings/trimws.h b/inst/include/Rcpp/sugar/functions/strings/trimws.h new file mode 100644 index 000000000..b8a2e76dd --- /dev/null +++ b/inst/include/Rcpp/sugar/functions/strings/trimws.h @@ -0,0 +1,201 @@ +// -*- mode: C++; c-indent-level: 4; c-basic-offset: 4; indent-tabs-mode: nil; -*- +// +// trimws.h: Rcpp R/C++ interface class library -- trimws +// +// Copyright (C) 2017 Nathan Russell +// +// This file is part of Rcpp. +// +// Rcpp is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 2 of the License, or +// (at your option) any later version. +// +// Rcpp is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Rcpp. If not, see . + +#ifndef Rcpp__sugar__trimws_h +#define Rcpp__sugar__trimws_h + +#include +#include + +namespace Rcpp { +namespace sugar { +namespace detail { + + +/* NB: std::isspace is not used because it also counts + '\f' and '\v' as whitespace, whereas base::trimws only + checks for ' ', '\t', '\r', and '\n' */ +inline bool isws(const char c) { + return c == ' ' || c == '\t' || c == '\n' || c == '\r'; +} + +inline const char* trim_left(const char* str) { + static std::string buff; + + if (!str) { + return ""; + } + + buff.clear(); + char c = *str; + + while (isws(c)) { + ++str; + c = *str; + } + + buff.append(str); + return buff.c_str(); +} + +inline const char* trim_right(const char* str) { + static std::string buff; + + if (!str) { + return ""; + } + + buff.clear(); + std::size_t sz = std::strlen(str); + + const char* ptr = str + sz - 1; + char c = *ptr; + + for (; ptr > str && isws(c); c = *ptr) { + --sz; --ptr; + } + + buff.append(str, sz - isws(*ptr)); + return buff.c_str(); +} + +inline const char* trim_both(const char* str) { + static std::string buff; + + if (!str) { + return ""; + } + + buff.clear(); + char c = *str; + + while (isws(c)) { + ++str; + c = *str; + } + + std::size_t sz = std::strlen(str); + const char* ptr = str + sz - 1; + c = *ptr; + + for (; ptr >= str; c = *ptr, --sz, --ptr) { + if (!isws(c)) { + break; + } + } + + buff.append(str, sz + 1); + return buff.c_str(); +} + + +} // detail +} // sugar + + +inline Vector trimws(const Vector& x, const char* which = "both") { + typedef const char* (*trim_function)(const char*); + trim_function trim = NULL; + + if (*which == 'b') { + trim = sugar::detail::trim_both; + } else if (*which == 'l') { + trim = sugar::detail::trim_left; + } else if (*which == 'r') { + trim = sugar::detail::trim_right; + } else { + stop("Invalid `which` argument '%s'!", which); + return Vector::create("Unreachable"); + } + + R_xlen_t i = 0, sz = x.size(); + Vector res(sz); + + for (; i < sz; i++) { + if (traits::is_na(x[i])) { + res[i] = x[i]; + } else { + res[i] = (*trim)(x[i]); + } + } + + return res; +} + +inline Matrix trimws(const Matrix& x, const char* which = "both") { + typedef const char* (*trim_function)(const char*); + trim_function trim = NULL; + + if (*which == 'b') { + trim = sugar::detail::trim_both; + } else if (*which == 'l') { + trim = sugar::detail::trim_left; + } else if (*which == 'r') { + trim = sugar::detail::trim_right; + } else { + stop("Invalid `which` argument '%s'!", which); + return Matrix(); + } + + R_xlen_t i = 0, sz = x.size(); + Matrix res(x.nrow(), x.ncol()); + + for (; i < sz; i++) { + if (traits::is_na(x[i])) { + res[i] = x[i]; + } else { + res[i] = (*trim)(x[i]); + } + } + + return res; +} + +inline String trimws(const String& str, const char* which = "both") { + if (*which == 'b') { + if (traits::is_na(str.get_sexp())) { + return String(str.get_sexp()); + } + return sugar::detail::trim_both(str.get_cstring()); + } + + if (*which == 'l') { + if (traits::is_na(str.get_sexp())) { + return String(str.get_sexp()); + } + return sugar::detail::trim_left(str.get_cstring()); + } + + if (*which == 'r') { + if (traits::is_na(str.get_sexp())) { + return String(str.get_sexp()); + } + return sugar::detail::trim_right(str.get_cstring()); + } + + stop("Invalid `which` argument '%s'!", which); + return String("Unreachable"); +} + + +} // Rcpp + +#endif // Rcpp__sugar__trimws_h diff --git a/inst/unitTests/cpp/sugar.cpp b/inst/unitTests/cpp/sugar.cpp index c002bff63..a5e7f26b8 100644 --- a/inst/unitTests/cpp/sugar.cpp +++ b/inst/unitTests/cpp/sugar.cpp @@ -1201,3 +1201,21 @@ LogicalMatrix UpperTri(NumericMatrix x, bool diag = false) { LogicalMatrix LowerTri(NumericMatrix x, bool diag = false) { return lower_tri(x, diag); } + + +// 22 April 2017: trimws + +// [[Rcpp::export]] +CharacterVector vtrimws(CharacterVector x, const char* which = "both") { + return trimws(x, which); +} + +// [[Rcpp::export]] +CharacterMatrix mtrimws(CharacterMatrix x, const char* which = "both") { + return trimws(x, which); +} + +// [[Rcpp::export]] +String strimws(String x, const char* which = "both") { + return trimws(x, which); +} diff --git a/inst/unitTests/runit.sugar.R b/inst/unitTests/runit.sugar.R index 632af26ee..c8cf3ed2f 100644 --- a/inst/unitTests/runit.sugar.R +++ b/inst/unitTests/runit.sugar.R @@ -2048,4 +2048,135 @@ if (.runThisTest) { } + + ## 22 April 2017 + ## trimws -- vector + test.sugar.vtrimws <- function() { + + x <- c( + " a b c", "a b c ", " a b c ", + "\t\ta b c", "a b c\t\t", "\t\ta b c\t\t", + "\r\ra b c", "a b c\r\r", "\r\ra b c\r\r", + "\n\na b c", "a b c\n\n", "\n\na b c\n\n", + NA, "", " ", " \t\r\n ", "\n \t \r " + ) + + checkEquals( + vtrimws(x), trimws(x), + "vtrimws / which = 'both'" + ) + + checkEquals( + vtrimws(x, 'l'), trimws(x, 'l'), + "vtrimws / which = 'left'" + ) + + checkEquals( + vtrimws(x, 'r'), trimws(x, 'r'), + "vtrimws / which = 'right'" + ) + + checkException( + vtrimws(x, "invalid"), + msg = "vtrimws -- bad `which` argument" + ) + + } + + + ## trimws -- matrix + test.sugar.mtrimws <- function() { + + x <- c( + " a b c", "a b c ", " a b c ", + "\t\ta b c", "a b c\t\t", "\t\ta b c\t\t", + "\r\ra b c", "a b c\r\r", "\r\ra b c\r\r", + "\n\na b c", "a b c\n\n", "\n\na b c\n\n", + NA, "", " ", " \t\r\n ", "\n \t \r " + ) + x <- matrix(x, nrow = length(x), ncol = 4) + + checkEquals( + mtrimws(x), trimws(x), + "mtrimws / which = 'both'" + ) + + checkEquals( + mtrimws(x, 'l'), trimws(x, 'l'), + "mtrimws / which = 'left'" + ) + + checkEquals( + mtrimws(x, 'r'), trimws(x, 'r'), + "mtrimws / which = 'right'" + ) + + checkException( + mtrimws(x, "invalid"), + msg = "mtrimws -- bad `which` argument" + ) + + } + + + ## trimws -- String + test.sugar.strimws <- function() { + + x <- c( + " a b c", "a b c ", " a b c ", + "\t\ta b c", "a b c\t\t", "\t\ta b c\t\t", + "\r\ra b c", "a b c\r\r", "\r\ra b c\r\r", + "\n\na b c", "a b c\n\n", "\n\na b c\n\n", + NA, "", " ", " \t\r\n ", "\n \t \r " + ) + + lhs <- vapply( + x, strimws, character(1), + USE.NAMES = FALSE + ) + rhs <- vapply( + x, trimws, character(1), + USE.NAMES = FALSE + ) + + checkEquals( + lhs, rhs, + "strimws / which = 'both'" + ) + + lhs <- vapply( + x, strimws, character(1), + which = 'l', USE.NAMES = FALSE + ) + rhs <- vapply( + x, trimws, character(1), + which = 'l', USE.NAMES = FALSE + ) + + checkEquals( + lhs, rhs, + "strimws / which = 'left'" + ) + + lhs <- vapply( + x, strimws, character(1), + which = 'r', USE.NAMES = FALSE + ) + rhs <- vapply( + x, trimws, character(1), + which = 'r', USE.NAMES = FALSE + ) + + checkEquals( + lhs, rhs, + "strimws / which = 'right'" + ) + + checkException( + strimws(x[1], "invalid"), + msg = "strimws -- bad `which` argument" + ) + + } + } From 5c0ef70cd68d3c8d93d5ede37bf46fe01d8421e2 Mon Sep 17 00:00:00 2001 From: nathan-russell Date: Sat, 22 Apr 2017 19:59:22 -0400 Subject: [PATCH 2/6] Remove extraneous variables --- .../Rcpp/sugar/functions/strings/trimws.h | 29 ++++--------------- 1 file changed, 6 insertions(+), 23 deletions(-) diff --git a/inst/include/Rcpp/sugar/functions/strings/trimws.h b/inst/include/Rcpp/sugar/functions/strings/trimws.h index b8a2e76dd..1883c9af2 100644 --- a/inst/include/Rcpp/sugar/functions/strings/trimws.h +++ b/inst/include/Rcpp/sugar/functions/strings/trimws.h @@ -38,22 +38,15 @@ inline bool isws(const char c) { } inline const char* trim_left(const char* str) { - static std::string buff; - if (!str) { return ""; } - buff.clear(); - char c = *str; - - while (isws(c)) { + while (isws(*str)) { ++str; - c = *str; } - buff.append(str); - return buff.c_str(); + return str; } inline const char* trim_right(const char* str) { @@ -67,11 +60,8 @@ inline const char* trim_right(const char* str) { std::size_t sz = std::strlen(str); const char* ptr = str + sz - 1; - char c = *ptr; - for (; ptr > str && isws(c); c = *ptr) { - --sz; --ptr; - } + for (; ptr > str && isws(*ptr); --sz, --ptr); buff.append(str, sz - isws(*ptr)); return buff.c_str(); @@ -85,24 +75,17 @@ inline const char* trim_both(const char* str) { } buff.clear(); - char c = *str; - while (isws(c)) { + while (isws(*str)) { ++str; - c = *str; } std::size_t sz = std::strlen(str); const char* ptr = str + sz - 1; - c = *ptr; - for (; ptr >= str; c = *ptr, --sz, --ptr) { - if (!isws(c)) { - break; - } - } + for (; ptr > str && isws(*ptr); --sz, --ptr); - buff.append(str, sz + 1); + buff.append(str, sz); return buff.c_str(); } From cb577185718ce83d22ebb2feb4ef84ef51c1acbc Mon Sep 17 00:00:00 2001 From: nathan-russell Date: Sun, 23 Apr 2017 08:50:02 -0400 Subject: [PATCH 3/6] Use no_init; remove function pointers --- .../Rcpp/sugar/functions/strings/trimws.h | 78 +++++++++++-------- 1 file changed, 46 insertions(+), 32 deletions(-) diff --git a/inst/include/Rcpp/sugar/functions/strings/trimws.h b/inst/include/Rcpp/sugar/functions/strings/trimws.h index 1883c9af2..66009ab49 100644 --- a/inst/include/Rcpp/sugar/functions/strings/trimws.h +++ b/inst/include/Rcpp/sugar/functions/strings/trimws.h @@ -95,60 +95,74 @@ inline const char* trim_both(const char* str) { inline Vector trimws(const Vector& x, const char* which = "both") { - typedef const char* (*trim_function)(const char*); - trim_function trim = NULL; + R_xlen_t i = 0, sz = x.size(); + Vector res = no_init(sz); if (*which == 'b') { - trim = sugar::detail::trim_both; + for (; i < sz; i++) { + if (traits::is_na(x[i])) { + res[i] = x[i]; + } else { + res[i] = sugar::detail::trim_both(x[i]); + } + } } else if (*which == 'l') { - trim = sugar::detail::trim_left; + for (; i < sz; i++) { + if (traits::is_na(x[i])) { + res[i] = x[i]; + } else { + res[i] = sugar::detail::trim_left(x[i]); + } + } } else if (*which == 'r') { - trim = sugar::detail::trim_right; + for (; i < sz; i++) { + if (traits::is_na(x[i])) { + res[i] = x[i]; + } else { + res[i] = sugar::detail::trim_right(x[i]); + } + } } else { stop("Invalid `which` argument '%s'!", which); return Vector::create("Unreachable"); } - R_xlen_t i = 0, sz = x.size(); - Vector res(sz); - - for (; i < sz; i++) { - if (traits::is_na(x[i])) { - res[i] = x[i]; - } else { - res[i] = (*trim)(x[i]); - } - } - return res; } inline Matrix trimws(const Matrix& x, const char* which = "both") { - typedef const char* (*trim_function)(const char*); - trim_function trim = NULL; + R_xlen_t i = 0, nr = x.nrow(), nc = x.ncol(), sz = x.size(); + Matrix res = no_init(nr, nc); if (*which == 'b') { - trim = sugar::detail::trim_both; + for (; i < sz; i++) { + if (traits::is_na(x[i])) { + res[i] = x[i]; + } else { + res[i] = sugar::detail::trim_both(x[i]); + } + } } else if (*which == 'l') { - trim = sugar::detail::trim_left; + for (; i < sz; i++) { + if (traits::is_na(x[i])) { + res[i] = x[i]; + } else { + res[i] = sugar::detail::trim_left(x[i]); + } + } } else if (*which == 'r') { - trim = sugar::detail::trim_right; + for (; i < sz; i++) { + if (traits::is_na(x[i])) { + res[i] = x[i]; + } else { + res[i] = sugar::detail::trim_right(x[i]); + } + } } else { stop("Invalid `which` argument '%s'!", which); return Matrix(); } - R_xlen_t i = 0, sz = x.size(); - Matrix res(x.nrow(), x.ncol()); - - for (; i < sz; i++) { - if (traits::is_na(x[i])) { - res[i] = x[i]; - } else { - res[i] = (*trim)(x[i]); - } - } - return res; } From 6819eb5a5a67764345f21758cb350cf1be080712 Mon Sep 17 00:00:00 2001 From: nathan-russell Date: Sun, 23 Apr 2017 10:15:43 -0400 Subject: [PATCH 4/6] Replace static buffer --- .../Rcpp/sugar/functions/strings/trimws.h | 36 ++++++++++--------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/inst/include/Rcpp/sugar/functions/strings/trimws.h b/inst/include/Rcpp/sugar/functions/strings/trimws.h index 66009ab49..2c556fdf6 100644 --- a/inst/include/Rcpp/sugar/functions/strings/trimws.h +++ b/inst/include/Rcpp/sugar/functions/strings/trimws.h @@ -49,32 +49,32 @@ inline const char* trim_left(const char* str) { return str; } -inline const char* trim_right(const char* str) { - static std::string buff; +inline const char* trim_right(const char* str, std::string* buff) { + // static std::string buff; if (!str) { return ""; } - buff.clear(); + buff->clear(); std::size_t sz = std::strlen(str); const char* ptr = str + sz - 1; for (; ptr > str && isws(*ptr); --sz, --ptr); - buff.append(str, sz - isws(*ptr)); - return buff.c_str(); + buff->append(str, sz - isws(*ptr)); + return buff->c_str(); } -inline const char* trim_both(const char* str) { - static std::string buff; +inline const char* trim_both(const char* str, std::string* buff) { + // static std::string buff; if (!str) { return ""; } - buff.clear(); + buff->clear(); while (isws(*str)) { ++str; @@ -85,8 +85,8 @@ inline const char* trim_both(const char* str) { for (; ptr > str && isws(*ptr); --sz, --ptr); - buff.append(str, sz); - return buff.c_str(); + buff->append(str, sz); + return buff->c_str(); } @@ -97,13 +97,14 @@ inline const char* trim_both(const char* str) { inline Vector trimws(const Vector& x, const char* which = "both") { R_xlen_t i = 0, sz = x.size(); Vector res = no_init(sz); + std::string buffer; if (*which == 'b') { for (; i < sz; i++) { if (traits::is_na(x[i])) { res[i] = x[i]; } else { - res[i] = sugar::detail::trim_both(x[i]); + res[i] = sugar::detail::trim_both(x[i], &buffer); } } } else if (*which == 'l') { @@ -119,7 +120,7 @@ inline Vector trimws(const Vector& x, const char* which = "both" if (traits::is_na(x[i])) { res[i] = x[i]; } else { - res[i] = sugar::detail::trim_right(x[i]); + res[i] = sugar::detail::trim_right(x[i], &buffer); } } } else { @@ -133,13 +134,14 @@ inline Vector trimws(const Vector& x, const char* which = "both" inline Matrix trimws(const Matrix& x, const char* which = "both") { R_xlen_t i = 0, nr = x.nrow(), nc = x.ncol(), sz = x.size(); Matrix res = no_init(nr, nc); + std::string buffer; if (*which == 'b') { for (; i < sz; i++) { if (traits::is_na(x[i])) { res[i] = x[i]; } else { - res[i] = sugar::detail::trim_both(x[i]); + res[i] = sugar::detail::trim_both(x[i], &buffer); } } } else if (*which == 'l') { @@ -155,7 +157,7 @@ inline Matrix trimws(const Matrix& x, const char* which = "both" if (traits::is_na(x[i])) { res[i] = x[i]; } else { - res[i] = sugar::detail::trim_right(x[i]); + res[i] = sugar::detail::trim_right(x[i], &buffer); } } } else { @@ -167,11 +169,13 @@ inline Matrix trimws(const Matrix& x, const char* which = "both" } inline String trimws(const String& str, const char* which = "both") { + std::string buffer; + if (*which == 'b') { if (traits::is_na(str.get_sexp())) { return String(str.get_sexp()); } - return sugar::detail::trim_both(str.get_cstring()); + return sugar::detail::trim_both(str.get_cstring(), &buffer); } if (*which == 'l') { @@ -185,7 +189,7 @@ inline String trimws(const String& str, const char* which = "both") { if (traits::is_na(str.get_sexp())) { return String(str.get_sexp()); } - return sugar::detail::trim_right(str.get_cstring()); + return sugar::detail::trim_right(str.get_cstring(), &buffer); } stop("Invalid `which` argument '%s'!", which); From 69e395aaa5de8e3fcd9cac163e9732ecf3b52587 Mon Sep 17 00:00:00 2001 From: nathan-russell Date: Sun, 23 Apr 2017 10:41:43 -0400 Subject: [PATCH 5/6] Replace strlen with LENGTH --- .../Rcpp/sugar/functions/strings/trimws.h | 50 ++++++++++++------- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/inst/include/Rcpp/sugar/functions/strings/trimws.h b/inst/include/Rcpp/sugar/functions/strings/trimws.h index 2c556fdf6..2cf78d2b0 100644 --- a/inst/include/Rcpp/sugar/functions/strings/trimws.h +++ b/inst/include/Rcpp/sugar/functions/strings/trimws.h @@ -49,16 +49,12 @@ inline const char* trim_left(const char* str) { return str; } -inline const char* trim_right(const char* str, std::string* buff) { - // static std::string buff; - +inline const char* trim_right(const char* str, R_len_t sz, std::string* buff) { if (!str) { return ""; } buff->clear(); - std::size_t sz = std::strlen(str); - const char* ptr = str + sz - 1; for (; ptr > str && isws(*ptr); --sz, --ptr); @@ -67,9 +63,7 @@ inline const char* trim_right(const char* str, std::string* buff) { return buff->c_str(); } -inline const char* trim_both(const char* str, std::string* buff) { - // static std::string buff; - +inline const char* trim_both(const char* str, R_len_t sz, std::string* buff) { if (!str) { return ""; } @@ -77,10 +71,8 @@ inline const char* trim_both(const char* str, std::string* buff) { buff->clear(); while (isws(*str)) { - ++str; + ++str; --sz; } - - std::size_t sz = std::strlen(str); const char* ptr = str + sz - 1; for (; ptr > str && isws(*ptr); --sz, --ptr); @@ -104,7 +96,11 @@ inline Vector trimws(const Vector& x, const char* which = "both" if (traits::is_na(x[i])) { res[i] = x[i]; } else { - res[i] = sugar::detail::trim_both(x[i], &buffer); + res[i] = sugar::detail::trim_both( + x[i], + LENGTH(x[i]), + &buffer + ); } } } else if (*which == 'l') { @@ -120,7 +116,11 @@ inline Vector trimws(const Vector& x, const char* which = "both" if (traits::is_na(x[i])) { res[i] = x[i]; } else { - res[i] = sugar::detail::trim_right(x[i], &buffer); + res[i] = sugar::detail::trim_right( + x[i], + LENGTH(x[i]), + &buffer + ); } } } else { @@ -141,7 +141,11 @@ inline Matrix trimws(const Matrix& x, const char* which = "both" if (traits::is_na(x[i])) { res[i] = x[i]; } else { - res[i] = sugar::detail::trim_both(x[i], &buffer); + res[i] = sugar::detail::trim_both( + x[i], + LENGTH(x[i]), + &buffer + ); } } } else if (*which == 'l') { @@ -157,7 +161,11 @@ inline Matrix trimws(const Matrix& x, const char* which = "both" if (traits::is_na(x[i])) { res[i] = x[i]; } else { - res[i] = sugar::detail::trim_right(x[i], &buffer); + res[i] = sugar::detail::trim_right( + x[i], + LENGTH(x[i]), + &buffer + ); } } } else { @@ -175,7 +183,11 @@ inline String trimws(const String& str, const char* which = "both") { if (traits::is_na(str.get_sexp())) { return String(str.get_sexp()); } - return sugar::detail::trim_both(str.get_cstring(), &buffer); + return sugar::detail::trim_both( + str.get_cstring(), + LENGTH(str.get_sexp()), + &buffer + ); } if (*which == 'l') { @@ -189,7 +201,11 @@ inline String trimws(const String& str, const char* which = "both") { if (traits::is_na(str.get_sexp())) { return String(str.get_sexp()); } - return sugar::detail::trim_right(str.get_cstring(), &buffer); + return sugar::detail::trim_right( + str.get_cstring(), + LENGTH(str.get_sexp()), + &buffer + ); } stop("Invalid `which` argument '%s'!", which); From c56e54b130d63302ca0a33f7bfd6c515d2759b3a Mon Sep 17 00:00:00 2001 From: nathan-russell Date: Sun, 23 Apr 2017 10:42:06 -0400 Subject: [PATCH 6/6] Replace strlen with LENGTH --- inst/include/Rcpp/sugar/functions/strings/trimws.h | 1 + 1 file changed, 1 insertion(+) diff --git a/inst/include/Rcpp/sugar/functions/strings/trimws.h b/inst/include/Rcpp/sugar/functions/strings/trimws.h index 2cf78d2b0..92ca7a409 100644 --- a/inst/include/Rcpp/sugar/functions/strings/trimws.h +++ b/inst/include/Rcpp/sugar/functions/strings/trimws.h @@ -73,6 +73,7 @@ inline const char* trim_both(const char* str, R_len_t sz, std::string* buff) { while (isws(*str)) { ++str; --sz; } + const char* ptr = str + sz - 1; for (; ptr > str && isws(*ptr); --sz, --ptr);