diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 55bd69396..5badc6688 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -124,9 +124,11 @@ if(RMM_NVTX) target_compile_definitions(rmm PUBLIC RMM_NVTX) endif() -set(RMM_CXX_FLAGS -Wall -Werror -Wextra -Wno-unknown-pragmas -Wno-error=deprecated-declarations) -set(RMM_CUDA_FLAGS -Werror=all-warnings - -Xcompiler=-Wall,-Werror,-Wextra,-Wno-error=deprecated-declarations) +set(RMM_CXX_FLAGS -Wall -Werror -Wextra -Wsign-conversion -Wno-unknown-pragmas + -Wno-error=deprecated-declarations) +set(RMM_CUDA_FLAGS + -Werror=all-warnings + -Xcompiler=-Wall,-Werror,-Wextra,-Wsign-conversion,-Wno-error=deprecated-declarations) target_compile_options(rmm PRIVATE "$<$:${RMM_CXX_FLAGS}>" "$<$:${RMM_CUDA_FLAGS}>") diff --git a/cpp/benchmarks/CMakeLists.txt b/cpp/benchmarks/CMakeLists.txt index 8aa50055d..85948ac18 100644 --- a/cpp/benchmarks/CMakeLists.txt +++ b/cpp/benchmarks/CMakeLists.txt @@ -9,8 +9,9 @@ option(DISABLE_DEPRECATION_WARNING "Disable warnings generated from deprecated declarations." OFF) option(PER_THREAD_DEFAULT_STREAM "Build with per-thread default stream" OFF) -set(RMM_BENCHMARKS_CXX_FLAGS -Wall -Werror -Wextra -Wno-unknown-pragmas) -set(RMM_BENCHMARKS_CUDA_FLAGS -Werror=all-warnings -Xcompiler=-Wall,-Werror,-Wextra) +set(RMM_BENCHMARKS_CXX_FLAGS -Wall -Werror -Wextra -Wsign-conversion -Wno-unknown-pragmas) +set(RMM_BENCHMARKS_CUDA_FLAGS -Werror=all-warnings + -Xcompiler=-Wall,-Werror,-Wextra,-Wsign-conversion) if(PER_THREAD_DEFAULT_STREAM) message(STATUS "RMM: Building benchmarks with per-thread default stream") diff --git a/cpp/benchmarks/device_uvector/device_uvector_bench.cu b/cpp/benchmarks/device_uvector/device_uvector_bench.cu index e92c302c8..6a005e709 100644 --- a/cpp/benchmarks/device_uvector/device_uvector_bench.cu +++ b/cpp/benchmarks/device_uvector/device_uvector_bench.cu @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: Copyright (c) 2020-2025, NVIDIA CORPORATION. + * SPDX-FileCopyrightText: Copyright (c) 2020-2026, NVIDIA CORPORATION. * SPDX-License-Identifier: Apache-2.0 */ @@ -32,7 +32,8 @@ void BM_UvectorSizeConstruction(benchmark::State& state) rmm::mr::set_current_device_resource_ref(mr); for (auto _ : state) { // NOLINT(clang-analyzer-deadcode.DeadStores) - rmm::device_uvector vec(state.range(0), rmm::cuda_stream_view{}); + rmm::device_uvector vec(static_cast(state.range(0)), + rmm::cuda_stream_view{}); cudaDeviceSynchronize(); } @@ -54,7 +55,7 @@ void BM_ThrustVectorSizeConstruction(benchmark::State& state) rmm::mr::set_current_device_resource_ref(mr); for (auto _ : state) { // NOLINT(clang-analyzer-deadcode.DeadStores) - rmm::device_vector vec(state.range(0)); + rmm::device_vector vec(static_cast(state.range(0))); cudaDeviceSynchronize(); } @@ -81,7 +82,7 @@ using rmm_vector = rmm::device_vector; using rmm_uvector = rmm::device_uvector; template -Vector make_vector(std::int64_t num_elements, rmm::cuda_stream_view stream, bool zero_init = false) +Vector make_vector(std::size_t num_elements, rmm::cuda_stream_view stream, bool zero_init = false) { static_assert(std::is_same_v or std::is_same_v or std::is_same_v, @@ -134,7 +135,7 @@ void BM_VectorWorkflow(benchmark::State& state) rmm::cuda_stream input_stream; std::vector streams(4); - auto const num_elements = state.range(0); + auto const num_elements = static_cast(state.range(0)); auto constexpr block_size = 256; auto constexpr num_blocks = 16; @@ -145,7 +146,8 @@ void BM_VectorWorkflow(benchmark::State& state) auto constexpr num_accesses = 9; auto const bytes = num_elements * sizeof(std::int32_t) * num_accesses; - state.SetBytesProcessed(static_cast(state.iterations() * bytes)); + state.SetBytesProcessed( + static_cast(static_cast(state.iterations()) * bytes)); rmm::mr::reset_current_device_resource_ref(); } diff --git a/cpp/benchmarks/multi_stream_allocations/multi_stream_allocations_bench.cu b/cpp/benchmarks/multi_stream_allocations/multi_stream_allocations_bench.cu index 4993a6062..3328dd6b7 100644 --- a/cpp/benchmarks/multi_stream_allocations/multi_stream_allocations_bench.cu +++ b/cpp/benchmarks/multi_stream_allocations/multi_stream_allocations_bench.cu @@ -70,12 +70,12 @@ static void BM_MultiStreamAllocations(benchmark::State& state, MRFactoryFunc con auto num_kernels = state.range(1); bool do_prewarm = state.range(2) != 0; - auto stream_pool = rmm::cuda_stream_pool(num_streams); + auto stream_pool = rmm::cuda_stream_pool(static_cast(num_streams)); if (do_prewarm) { run_prewarm(stream_pool, mr.get()); } for (auto _ : state) { // NOLINT(clang-analyzer-deadcode.DeadStores) - run_test(num_kernels, stream_pool, mr.get()); + run_test(static_cast(num_kernels), stream_pool, mr.get()); cudaDeviceSynchronize(); } @@ -170,11 +170,11 @@ void run_profile(std::string const& resource_name, int kernel_count, int stream_ { auto mr_factory = get_mr_factory(resource_name); auto mr = mr_factory(); - auto stream_pool = rmm::cuda_stream_pool(stream_count); + auto stream_pool = rmm::cuda_stream_pool(static_cast(stream_count)); if (prewarm) { run_prewarm(stream_pool, mr.get()); } - run_test(kernel_count, stream_pool, mr.get()); + run_test(static_cast(kernel_count), stream_pool, mr.get()); } int main(int argc, char** argv) diff --git a/cpp/benchmarks/random_allocations/random_allocations.cpp b/cpp/benchmarks/random_allocations/random_allocations.cpp index a409e3bf6..ff19288cf 100644 --- a/cpp/benchmarks/random_allocations/random_allocations.cpp +++ b/cpp/benchmarks/random_allocations/random_allocations.cpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: Copyright (c) 2019-2025, NVIDIA CORPORATION. + * SPDX-FileCopyrightText: Copyright (c) 2019-2026, NVIDIA CORPORATION. * SPDX-License-Identifier: Apache-2.0 */ @@ -62,9 +62,9 @@ void random_allocation_free(rmm::mr::device_memory_resource& mr, constexpr int allocation_probability{73}; // percent constexpr int max_op_chance{99}; std::uniform_int_distribution op_distribution(0, max_op_chance); - std::uniform_int_distribution index_distribution(0, static_cast(num_allocations) - 1); + std::uniform_int_distribution index_distribution(0, num_allocations - 1); - int active_allocations{0}; + std::size_t active_allocations{0}; std::size_t allocation_count{0}; allocation_vector allocations{}; @@ -185,8 +185,8 @@ static void BM_RandomAllocations(benchmark::State& state, MRFactoryFunc const& f { auto mr = factory(); - std::size_t num_allocations = state.range(0); - std::size_t max_size = state.range(1); + std::size_t num_allocations = static_cast(state.range(0)); + std::size_t max_size = static_cast(state.range(1)); try { for (auto _ : state) { // NOLINT(clang-analyzer-deadcode.DeadStores) @@ -312,7 +312,9 @@ int main(int argc, char** argv) std::cout << "Profiling " << resource << " with " << num_allocations << " allocations of max " << max_size << "B\n"; - profile_random_allocations(funcs.at(resource), num_allocations, max_size); + profile_random_allocations(funcs.at(resource), + static_cast(num_allocations), + static_cast(max_size)); std::cout << "Finished\n"; } else { diff --git a/cpp/benchmarks/replay/replay.cpp b/cpp/benchmarks/replay/replay.cpp index 65b680669..f5a374828 100644 --- a/cpp/benchmarks/replay/replay.cpp +++ b/cpp/benchmarks/replay/replay.cpp @@ -209,7 +209,7 @@ struct replay_benchmark { { SetUp(state); - auto const& my_events = events_.at(state.thread_index()); + auto const& my_events = events_.at(static_cast(state.thread_index())); for (auto _ : state) { // NOLINT(clang-analyzer-deadcode.DeadStores) // At start of each iteration event_index must be reset. // Any thread could do this, but this is easy @@ -288,9 +288,9 @@ std::vector> parse_per_thread_events(std::string std::transform(events_per_thread.begin(), events_per_thread.end(), per_thread_events.begin(), - [&all_events, offset = 0](auto num_events) mutable { + [&all_events, offset = std::ptrdiff_t{0}](auto num_events) mutable { auto begin = offset; - offset += num_events; + offset += static_cast(num_events); auto end = offset; std::vector thread_events(all_events.cbegin() + begin, all_events.cbegin() + end); diff --git a/cpp/benchmarks/synchronization/synchronization.cpp b/cpp/benchmarks/synchronization/synchronization.cpp index dd0e650f0..b33f514f1 100644 --- a/cpp/benchmarks/synchronization/synchronization.cpp +++ b/cpp/benchmarks/synchronization/synchronization.cpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: Copyright (c) 2019-2021, NVIDIA CORPORATION. + * SPDX-FileCopyrightText: Copyright (c) 2019-2026, NVIDIA CORPORATION. * SPDX-License-Identifier: Apache-2.0 */ @@ -32,9 +32,11 @@ cuda_event_timer::cuda_event_timer(benchmark::State& state, if (l2_cache_bytes > 0) { const int memset_value = 0; - rmm::device_buffer l2_cache_buffer(l2_cache_bytes, stream); - RMM_CUDA_TRY( - cudaMemsetAsync(l2_cache_buffer.data(), memset_value, l2_cache_bytes, stream.value())); + rmm::device_buffer l2_cache_buffer(static_cast(l2_cache_bytes), stream); + RMM_CUDA_TRY(cudaMemsetAsync(l2_cache_buffer.data(), + memset_value, + static_cast(l2_cache_bytes), + stream.value())); } } diff --git a/cpp/benchmarks/utilities/log_parser.hpp b/cpp/benchmarks/utilities/log_parser.hpp index ffd4f7f67..5114b7ea0 100644 --- a/cpp/benchmarks/utilities/log_parser.hpp +++ b/cpp/benchmarks/utilities/log_parser.hpp @@ -146,7 +146,7 @@ inline std::vector parse_csv(std::string const& filename) auto parse_pointer = [](std::string const& str, uintptr_t& ptr) { auto const base{16}; - ptr = (str == "(nil)") ? 0 : std::stoll(str, nullptr, base); + ptr = (str == "(nil)") ? 0 : static_cast(std::stoull(str, nullptr, base)); }; std::vector pointers = csv.GetColumn("Pointer", parse_pointer); diff --git a/cpp/benchmarks/utilities/rapidcsv.h b/cpp/benchmarks/utilities/rapidcsv.h index c382b7707..fa1b89e50 100644 --- a/cpp/benchmarks/utilities/rapidcsv.h +++ b/cpp/benchmarks/utilities/rapidcsv.h @@ -1,14 +1,14 @@ /* - * SPDX-FileCopyrightText: Copyright (C) 2017-2020 Kristofer Berggren. All rights reserved. + * SPDX-FileCopyrightText: Copyright (C) 2017-2026 Kristofer Berggren. All rights reserved. * SPDX-License-Identifier: BSD-3-Clause */ /* * rapidcsv.h * * URL: https://github.com/d99kris/rapidcsv - * Version: 6.10 + * Version: 8.92 * - * Copyright (C) 2017-2020 Kristofer Berggren + * Copyright (C) 2017-2026 Kristofer Berggren * All rights reserved. * * rapidcsv is distributed under the BSD 3-Clause license, see LICENSE for details. @@ -22,27 +22,26 @@ #include #ifdef HAS_CODECVT #include +#include #endif #include #include +#include #include +#include #include #include #include #include #include -#if defined(_MSC_VER) -#include -typedef SSIZE_T ssize_t; -#endif - namespace rapidcsv { #if defined(_MSC_VER) static const bool sPlatformHasCR = true; #else static const bool sPlatformHasCR = false; #endif +static const std::vector s_Utf8BOM = {'\xef', '\xbb', '\xbf'}; /** * @brief Datastructure holding parameters controlling how invalid numbers (including @@ -56,14 +55,18 @@ struct ConverterParams { * an exception to be thrown (default). * @param pDefaultFloat floating-point default value to represent invalid numbers. * @param pDefaultInteger integer default value to represent invalid numbers. + * @param pNumericLocale specifies whether to honor LC_NUMERIC locale (default + * true). */ explicit ConverterParams( const bool pHasDefaultConverter = false, const long double pDefaultFloat = std::numeric_limits::signaling_NaN(), - const long long pDefaultInteger = 0) + const long long pDefaultInteger = 0, + const bool pNumericLocale = true) : mHasDefaultConverter(pHasDefaultConverter), mDefaultFloat(pDefaultFloat), - mDefaultInteger(pDefaultInteger) + mDefaultInteger(pDefaultInteger), + mNumericLocale(pNumericLocale) { } @@ -82,6 +85,11 @@ struct ConverterParams { * @brief integer default value to represent invalid numbers. */ long long mDefaultInteger; + + /** + * @brief specifies whether to honor LC_NUMERIC locale. + */ + bool mNumericLocale; }; /** @@ -89,11 +97,12 @@ struct ConverterParams { * is not supported by the Converter class. */ class no_converter : public std::exception { + public: /** * @brief Provides details about the exception * @returns an explanatory string */ - virtual const char* what() const throw() { return "unsupported conversion datatype"; } + const char* what() const throw() override { return "unsupported conversion datatype"; } }; /** @@ -118,14 +127,30 @@ class Converter { */ void ToStr(const T& pVal, std::string& pStr) const { - if (typeid(T) == typeid(int) || typeid(T) == typeid(long) || typeid(T) == typeid(long long) || - typeid(T) == typeid(unsigned) || typeid(T) == typeid(unsigned long) || - typeid(T) == typeid(unsigned long long) || typeid(T) == typeid(float) || - typeid(T) == typeid(double) || typeid(T) == typeid(long double) || - typeid(T) == typeid(char)) { + if (typeid(T) == typeid(int) || typeid(T) == typeid(short) || + typeid(T) == typeid(unsigned short) || typeid(T) == typeid(long) || + typeid(T) == typeid(long long) || typeid(T) == typeid(unsigned) || + typeid(T) == typeid(unsigned long) || typeid(T) == typeid(unsigned long long) || + typeid(T) == typeid(long double) || typeid(T) == typeid(char)) { std::ostringstream out; out << pVal; pStr = out.str(); + } else if (typeid(T) == typeid(float)) { + std::ostringstream out; + out << std::setprecision(9) << pVal; + pStr = out.str(); + } else if (typeid(T) == typeid(double)) { + std::ostringstream out; + out << std::setprecision(17) << pVal; + pStr = out.str(); + } else if (typeid(T) == typeid(signed char)) { + std::ostringstream out; + out << static_cast(pVal); + pStr = out.str(); + } else if (typeid(T) == typeid(unsigned char)) { + std::ostringstream out; + out << static_cast(pVal); + pStr = out.str(); } else { throw no_converter(); } @@ -142,6 +167,18 @@ class Converter { if (typeid(T) == typeid(int)) { pVal = static_cast(std::stoi(pStr)); return; + } else if (typeid(T) == typeid(signed char)) { + pVal = static_cast(std::stoi(pStr)); + return; + } else if (typeid(T) == typeid(unsigned char)) { + pVal = static_cast(std::stoi(pStr)); + return; + } else if (typeid(T) == typeid(short)) { + pVal = static_cast(std::stoi(pStr)); + return; + } else if (typeid(T) == typeid(unsigned short)) { + pVal = static_cast(std::stoi(pStr)); + return; } else if (typeid(T) == typeid(long)) { pVal = static_cast(std::stol(pStr)); return; @@ -168,15 +205,28 @@ class Converter { } try { - if (typeid(T) == typeid(float)) { - pVal = static_cast(std::stof(pStr)); - return; - } else if (typeid(T) == typeid(double)) { - pVal = static_cast(std::stod(pStr)); - return; - } else if (typeid(T) == typeid(long double)) { - pVal = static_cast(std::stold(pStr)); - return; + if (mConverterParams.mNumericLocale) { + if (typeid(T) == typeid(float)) { + pVal = static_cast(std::stof(pStr)); + return; + } else if (typeid(T) == typeid(double)) { + pVal = static_cast(std::stod(pStr)); + return; + } else if (typeid(T) == typeid(long double)) { + pVal = static_cast(std::stold(pStr)); + return; + } + } else { + if ((typeid(T) == typeid(float)) || (typeid(T) == typeid(double)) || + (typeid(T) == typeid(long double))) { + std::istringstream iss(pStr); + iss.imbue(std::locale::classic()); + iss >> pVal; + if (iss.fail() || iss.bad() || !iss.eof()) { + throw std::invalid_argument("istringstream: no conversion"); + } + return; + } } } catch (...) { if (!mConverterParams.mHasDefaultConverter) { @@ -233,14 +283,24 @@ struct LabelParams { * @brief Constructor * @param pColumnNameIdx specifies the zero-based row index of the column labels, setting * it to -1 prevents column lookup by label name, and gives access - * to all rows as document data. + * to all rows as document data. Default: 0 * @param pRowNameIdx specifies the zero-based column index of the row labels, setting * it to -1 prevents row lookup by label name, and gives access - * to all columns as document data. + * to all columns as document data. Default: -1 */ - explicit LabelParams(const int pColumnNameIdx = 0, const int pRowNameIdx = 0) + explicit LabelParams(const int pColumnNameIdx = 0, const int pRowNameIdx = -1) : mColumnNameIdx(pColumnNameIdx), mRowNameIdx(pRowNameIdx) { + if (mColumnNameIdx < -1) { + const std::string errStr = + "invalid column name index " + std::to_string(mColumnNameIdx) + " < -1"; + throw std::out_of_range(errStr); + } + + if (mRowNameIdx < -1) { + const std::string errStr = "invalid row name index " + std::to_string(mRowNameIdx) + " < -1"; + throw std::out_of_range(errStr); + } } /** @@ -262,17 +322,28 @@ struct SeparatorParams { * @brief Constructor * @param pSeparator specifies the column separator (default ','). * @param pTrim specifies whether to trim leading and trailing spaces from - * cells read. + * cells read (default false). * @param pHasCR specifies whether a new document (i.e. not an existing document * read) should use CR/LF instead of only LF (default is to use standard behavior of underlying * platforms - CR/LF for Win, and LF for others). - * @param pQuotedLinebreaks specifies whether to allow line breaks in quoted text. + * @param pQuotedLinebreaks specifies whether to allow line breaks in quoted text (default + * false) + * @param pAutoQuote specifies whether to automatically dequote data during read, and + * add quotes during write (default true). + * @param pQuoteChar specifies the quote character (default '\"'). */ explicit SeparatorParams(const char pSeparator = ',', const bool pTrim = false, const bool pHasCR = sPlatformHasCR, - const bool pQuotedLinebreaks = false) - : mSeparator(pSeparator), mTrim(pTrim), mHasCR(pHasCR), mQuotedLinebreaks(pQuotedLinebreaks) + const bool pQuotedLinebreaks = false, + const bool pAutoQuote = true, + const char pQuoteChar = '"') + : mSeparator(pSeparator), + mTrim(pTrim), + mHasCR(pHasCR), + mQuotedLinebreaks(pQuotedLinebreaks), + mAutoQuote(pAutoQuote), + mQuoteChar(pQuoteChar) { } @@ -295,6 +366,54 @@ struct SeparatorParams { * @brief specifies whether to allow line breaks in quoted text. */ bool mQuotedLinebreaks; + + /** + * @brief specifies whether to automatically dequote cell data. + */ + bool mAutoQuote; + + /** + * @brief specifies the quote character. + */ + char mQuoteChar; +}; + +/** + * @brief Datastructure holding parameters controlling how special line formats should be + * treated. + */ +struct LineReaderParams { + /** + * @brief Constructor + * @param pSkipCommentLines specifies whether to skip lines prefixed with + * mCommentPrefix. Default: false + * @param pCommentPrefix specifies which prefix character to indicate a comment + * line. Default: # + * @param pSkipEmptyLines specifies whether to skip empty lines. Default: false + */ + explicit LineReaderParams(const bool pSkipCommentLines = false, + const char pCommentPrefix = '#', + const bool pSkipEmptyLines = false) + : mSkipCommentLines(pSkipCommentLines), + mCommentPrefix(pCommentPrefix), + mSkipEmptyLines(pSkipEmptyLines) + { + } + + /** + * @brief specifies whether to skip lines prefixed with mCommentPrefix. + */ + bool mSkipCommentLines; + + /** + * @brief specifies which prefix character to indicate a comment line. + */ + char mCommentPrefix; + + /** + * @brief specifies whether to skip empty lines. + */ + bool mSkipEmptyLines; }; /** @@ -310,63 +429,96 @@ class Document { * @param pSeparatorParams specifies which field and row separators should be used. * @param pConverterParams specifies how invalid numbers (including empty strings) should * be handled. + * @param pLineReaderParams specifies how special line formats should be treated. */ - explicit Document(const std::string& pPath = std::string(), - const LabelParams& pLabelParams = LabelParams(), - const SeparatorParams& pSeparatorParams = SeparatorParams(), - const ConverterParams& pConverterParams = ConverterParams()) + explicit Document(const std::string& pPath = std::string(), + const LabelParams& pLabelParams = LabelParams(), + const SeparatorParams& pSeparatorParams = SeparatorParams(), + const ConverterParams& pConverterParams = ConverterParams(), + const LineReaderParams& pLineReaderParams = LineReaderParams()) : mPath(pPath), mLabelParams(pLabelParams), mSeparatorParams(pSeparatorParams), - mConverterParams(pConverterParams) + mConverterParams(pConverterParams), + mLineReaderParams(pLineReaderParams), + mData(), + mColumnNames(), + mRowNames() { if (!mPath.empty()) { ReadCsv(); } } /** * @brief Constructor - * @param pStream specifies an input stream to read CSV data from. + * @param pStream specifies a binary input stream to read CSV data from. * @param pLabelParams specifies which row and column should be treated as labels. * @param pSeparatorParams specifies which field and row separators should be used. * @param pConverterParams specifies how invalid numbers (including empty strings) should * be handled. + * @param pLineReaderParams specifies how special line formats should be treated. */ explicit Document(std::istream& pStream, - const LabelParams& pLabelParams = LabelParams(), - const SeparatorParams& pSeparatorParams = SeparatorParams(), - const ConverterParams& pConverterParams = ConverterParams()) + const LabelParams& pLabelParams = LabelParams(), + const SeparatorParams& pSeparatorParams = SeparatorParams(), + const ConverterParams& pConverterParams = ConverterParams(), + const LineReaderParams& pLineReaderParams = LineReaderParams()) : mPath(), mLabelParams(pLabelParams), mSeparatorParams(pSeparatorParams), - mConverterParams(pConverterParams) + mConverterParams(pConverterParams), + mLineReaderParams(pLineReaderParams), + mData(), + mColumnNames(), + mRowNames() { ReadCsv(pStream); } /** - * @brief Copy constructor - * @param pDocument specifies the Document instance to copy. + * @brief Read Document data from file. + * @param pPath specifies the path of an existing CSV-file to populate the + * Document data with. + * @param pLabelParams specifies which row and column should be treated as labels. + * @param pSeparatorParams specifies which field and row separators should be used. + * @param pConverterParams specifies how invalid numbers (including empty strings) should + * be handled. + * @param pLineReaderParams specifies how special line formats should be treated. */ - explicit Document(const Document& pDocument) - : mPath(pDocument.mPath), - mLabelParams(pDocument.mLabelParams), - mSeparatorParams(pDocument.mSeparatorParams), - mConverterParams(pDocument.mConverterParams), - mData(pDocument.mData), - mColumnNames(pDocument.mColumnNames), - mRowNames(pDocument.mRowNames) + void Load(const std::string& pPath, + const LabelParams& pLabelParams = LabelParams(), + const SeparatorParams& pSeparatorParams = SeparatorParams(), + const ConverterParams& pConverterParams = ConverterParams(), + const LineReaderParams& pLineReaderParams = LineReaderParams()) { + mPath = pPath; + mLabelParams = pLabelParams; + mSeparatorParams = pSeparatorParams; + mConverterParams = pConverterParams; + mLineReaderParams = pLineReaderParams; + ReadCsv(); } /** - * @brief Read Document data from file. - * @param pPath specifies the path of an existing CSV-file to populate the - * Document data with. + * @brief Read Document data from stream. + * @param pStream specifies a binary input stream to read CSV data from. + * @param pLabelParams specifies which row and column should be treated as labels. + * @param pSeparatorParams specifies which field and row separators should be used. + * @param pConverterParams specifies how invalid numbers (including empty strings) should + * be handled. + * @param pLineReaderParams specifies how special line formats should be treated. */ - void Load(const std::string& pPath) + void Load(std::istream& pStream, + const LabelParams& pLabelParams = LabelParams(), + const SeparatorParams& pSeparatorParams = SeparatorParams(), + const ConverterParams& pConverterParams = ConverterParams(), + const LineReaderParams& pLineReaderParams = LineReaderParams()) { - mPath = pPath; - ReadCsv(); + mPath = ""; + mLabelParams = pLabelParams; + mSeparatorParams = pSeparatorParams; + mConverterParams = pConverterParams; + mLineReaderParams = pLineReaderParams; + ReadCsv(pStream); } /** @@ -383,9 +535,40 @@ class Document { /** * @brief Write Document data to stream. - * @param pStream specifies an output stream to write the data to. + * @param pStream specifies a binary output stream to write the data to. + */ + void Save(std::ostream& pStream) const { WriteCsv(pStream); } + + /** + * @brief Clears loaded Document data. + * + */ + void Clear() + { + mData.clear(); + mColumnNames.clear(); + mRowNames.clear(); +#ifdef HAS_CODECVT + mIsUtf16 = false; + mIsLE = false; +#endif + mHasUtf8BOM = false; + } + + /** + * @brief Get column index by name. + * @param pColumnName column label name. + * @returns zero-based column index. */ - void Save(std::ostream& pStream) { WriteCsv(pStream); } + int GetColumnIdx(const std::string& pColumnName) const + { + if (mLabelParams.mColumnNameIdx >= 0) { + if (mColumnNames.find(pColumnName) != mColumnNames.end()) { + return static_cast(mColumnNames.at(pColumnName)) - (mLabelParams.mRowNameIdx + 1); + } + } + return -1; + } /** * @brief Get column by index. @@ -395,14 +578,25 @@ class Document { template std::vector GetColumn(const size_t pColumnIdx) const { - const ssize_t columnIdx = pColumnIdx + (mLabelParams.mRowNameIdx + 1); + const size_t dataColumnIdx = GetDataColumnIndex(pColumnIdx); std::vector column; Converter converter(mConverterParams); for (auto itRow = mData.begin(); itRow != mData.end(); ++itRow) { if (std::distance(mData.begin(), itRow) > mLabelParams.mColumnNameIdx) { - T val; - converter.ToVal(itRow->at(columnIdx), val); - column.push_back(val); + if (dataColumnIdx < itRow->size()) { + T val; + converter.ToVal(itRow->at(dataColumnIdx), val); + column.push_back(val); + } else { + const std::string errStr = + "requested column index " + std::to_string(pColumnIdx) + + " >= " + std::to_string(itRow->size() - GetDataColumnIndex(0)) + + " (number of columns on row index " + + std::to_string(std::distance(mData.begin(), itRow) - + (mLabelParams.mColumnNameIdx + 1)) + + ")"; + throw std::out_of_range(errStr); + } } } return column; @@ -417,12 +611,12 @@ class Document { template std::vector GetColumn(const size_t pColumnIdx, ConvFunc pToVal) const { - const ssize_t columnIdx = pColumnIdx + (mLabelParams.mRowNameIdx + 1); + const size_t dataColumnIdx = GetDataColumnIndex(pColumnIdx); std::vector column; for (auto itRow = mData.begin(); itRow != mData.end(); ++itRow) { if (std::distance(mData.begin(), itRow) > mLabelParams.mColumnNameIdx) { T val; - pToVal(itRow->at(columnIdx), val); + pToVal(itRow->at(dataColumnIdx), val); column.push_back(val); } } @@ -437,9 +631,9 @@ class Document { template std::vector GetColumn(const std::string& pColumnName) const { - const ssize_t columnIdx = GetColumnIdx(pColumnName); + const int columnIdx = GetColumnIdx(pColumnName); if (columnIdx < 0) { throw std::out_of_range("column not found: " + pColumnName); } - return GetColumn(columnIdx); + return GetColumn(static_cast(columnIdx)); } /** @@ -451,9 +645,9 @@ class Document { template std::vector GetColumn(const std::string& pColumnName, ConvFunc pToVal) const { - const ssize_t columnIdx = GetColumnIdx(pColumnName); + const int columnIdx = GetColumnIdx(pColumnName); if (columnIdx < 0) { throw std::out_of_range("column not found: " + pColumnName); } - return GetColumn(columnIdx, pToVal); + return GetColumn(static_cast(columnIdx), pToVal); } /** @@ -464,17 +658,19 @@ class Document { template void SetColumn(const size_t pColumnIdx, const std::vector& pColumn) { - const size_t columnIdx = pColumnIdx + (mLabelParams.mRowNameIdx + 1); + const size_t dataColumnIdx = GetDataColumnIndex(pColumnIdx); - while (pColumn.size() + (mLabelParams.mColumnNameIdx + 1) > GetDataRowCount()) { + while (GetDataRowIndex(pColumn.size()) > GetDataRowCount()) { std::vector row; row.resize(GetDataColumnCount()); mData.push_back(row); } - if ((columnIdx + 1) > GetDataColumnCount()) { + if ((dataColumnIdx + 1) > GetDataColumnCount()) { for (auto itRow = mData.begin(); itRow != mData.end(); ++itRow) { - itRow->resize(columnIdx + 1 + (mLabelParams.mRowNameIdx + 1)); + if (std::distance(mData.begin(), itRow) >= mLabelParams.mColumnNameIdx) { + itRow->resize(GetDataColumnIndex(dataColumnIdx + 1)); + } } } @@ -482,8 +678,10 @@ class Document { for (auto itRow = pColumn.begin(); itRow != pColumn.end(); ++itRow) { std::string str; converter.ToStr(*itRow, str); - mData.at(std::distance(pColumn.begin(), itRow) + (mLabelParams.mColumnNameIdx + 1)) - .at(columnIdx) = str; + mData + .at(static_cast(std::distance(pColumn.begin(), itRow) + + mLabelParams.mColumnNameIdx + 1)) + .at(dataColumnIdx) = str; } } @@ -495,9 +693,9 @@ class Document { template void SetColumn(const std::string& pColumnName, const std::vector& pColumn) { - const ssize_t columnIdx = GetColumnIdx(pColumnName); + const int columnIdx = GetColumnIdx(pColumnName); if (columnIdx < 0) { throw std::out_of_range("column not found: " + pColumnName); } - SetColumn(columnIdx, pColumn); + SetColumn(static_cast(columnIdx), pColumn); } /** @@ -506,10 +704,21 @@ class Document { */ void RemoveColumn(const size_t pColumnIdx) { - const ssize_t columnIdx = pColumnIdx + (mLabelParams.mRowNameIdx + 1); + const size_t dataColumnIdx = GetDataColumnIndex(pColumnIdx); for (auto itRow = mData.begin(); itRow != mData.end(); ++itRow) { - itRow->erase(itRow->begin() + columnIdx); + if (std::distance(mData.begin(), itRow) >= mLabelParams.mColumnNameIdx) { + if (dataColumnIdx < itRow->size()) { + itRow->erase(itRow->begin() + static_cast(dataColumnIdx)); + } else { + const std::string errStr = "column out of range: " + std::to_string(pColumnIdx) + + " (on row " + + std::to_string(std::distance(mData.begin(), itRow)) + ")"; + throw std::out_of_range(errStr); + } + } } + + UpdateColumnNames(); } /** @@ -518,19 +727,93 @@ class Document { */ void RemoveColumn(const std::string& pColumnName) { - ssize_t columnIdx = GetColumnIdx(pColumnName); + int columnIdx = GetColumnIdx(pColumnName); if (columnIdx < 0) { throw std::out_of_range("column not found: " + pColumnName); } - RemoveColumn(columnIdx); + RemoveColumn(static_cast(columnIdx)); } /** - * @brief Get number of data columns. + * @brief Insert column at specified index. + * @param pColumnIdx zero-based column index. + * @param pColumn vector of column data (optional argument). + * @param pColumnName column label name (optional argument). + */ + template + void InsertColumn(const size_t pColumnIdx, + const std::vector& pColumn = std::vector(), + const std::string& pColumnName = std::string()) + { + const size_t dataColumnIdx = GetDataColumnIndex(pColumnIdx); + + std::vector column; + if (pColumn.empty()) { + column.resize(GetDataRowCount()); + } else { + column.resize(GetDataRowIndex(pColumn.size())); + Converter converter(mConverterParams); + for (auto itRow = pColumn.begin(); itRow != pColumn.end(); ++itRow) { + std::string str; + converter.ToStr(*itRow, str); + const size_t rowIdx = static_cast(std::distance(pColumn.begin(), itRow) + + (mLabelParams.mColumnNameIdx + 1)); + column.at(rowIdx) = str; + } + } + + while (column.size() > GetDataRowCount()) { + std::vector row; + const size_t columnCount = std::max( + static_cast(mLabelParams.mColumnNameIdx + 1), GetDataColumnCount()); + row.resize(columnCount); + mData.push_back(row); + } + + for (auto itRow = mData.begin(); itRow != mData.end(); ++itRow) { + if (std::distance(mData.begin(), itRow) >= mLabelParams.mColumnNameIdx) { + const size_t rowIdx = static_cast(std::distance(mData.begin(), itRow)); + if (dataColumnIdx <= itRow->size()) { + itRow->insert(itRow->begin() + static_cast(dataColumnIdx), column.at(rowIdx)); + } else { + const std::string errStr = "column out of range: " + std::to_string(pColumnIdx) + + " (on row " + + std::to_string(std::distance(mData.begin(), itRow)) + ")"; + throw std::out_of_range(errStr); + } + } + } + + if (!pColumnName.empty()) { SetColumnName(pColumnIdx, pColumnName); } + + UpdateColumnNames(); + } + + /** + * @brief Get number of data columns (excluding label columns). * @returns column count. */ size_t GetColumnCount() const { - return (mData.size() > 0) ? (mData.at(0).size() - (mLabelParams.mRowNameIdx + 1)) : 0; + const size_t firstRow = + static_cast((mLabelParams.mColumnNameIdx >= 0) ? mLabelParams.mColumnNameIdx : 0); + const int count = static_cast((mData.size() > firstRow) ? mData.at(firstRow).size() : 0) - + (mLabelParams.mRowNameIdx + 1); + return (count >= 0) ? static_cast(count) : 0; + } + + /** + * @brief Get row index by name. + * @param pRowName row label name. + * @returns zero-based row index. + */ + int GetRowIdx(const std::string& pRowName) const + { + if (mLabelParams.mRowNameIdx >= 0) { + if (mRowNames.find(pRowName) != mRowNames.end()) { + return static_cast(mRowNames.at(pRowName)) - (mLabelParams.mColumnNameIdx + 1); + } + } + return -1; } /** @@ -541,11 +824,11 @@ class Document { template std::vector GetRow(const size_t pRowIdx) const { - const ssize_t rowIdx = pRowIdx + (mLabelParams.mColumnNameIdx + 1); + const size_t dataRowIdx = GetDataRowIndex(pRowIdx); std::vector row; Converter converter(mConverterParams); - for (auto itCol = mData.at(rowIdx).begin(); itCol != mData.at(rowIdx).end(); ++itCol) { - if (std::distance(mData.at(rowIdx).begin(), itCol) > mLabelParams.mRowNameIdx) { + for (auto itCol = mData.at(dataRowIdx).begin(); itCol != mData.at(dataRowIdx).end(); ++itCol) { + if (std::distance(mData.at(dataRowIdx).begin(), itCol) > mLabelParams.mRowNameIdx) { T val; converter.ToVal(*itCol, val); row.push_back(val); @@ -563,11 +846,11 @@ class Document { template std::vector GetRow(const size_t pRowIdx, ConvFunc pToVal) const { - const ssize_t rowIdx = pRowIdx + (mLabelParams.mColumnNameIdx + 1); + const size_t dataRowIdx = GetDataRowIndex(pRowIdx); std::vector row; Converter converter(mConverterParams); - for (auto itCol = mData.at(rowIdx).begin(); itCol != mData.at(rowIdx).end(); ++itCol) { - if (std::distance(mData.at(rowIdx).begin(), itCol) > mLabelParams.mRowNameIdx) { + for (auto itCol = mData.at(dataRowIdx).begin(); itCol != mData.at(dataRowIdx).end(); ++itCol) { + if (std::distance(mData.at(dataRowIdx).begin(), itCol) > mLabelParams.mRowNameIdx) { T val; pToVal(*itCol, val); row.push_back(val); @@ -584,9 +867,9 @@ class Document { template std::vector GetRow(const std::string& pRowName) const { - ssize_t rowIdx = GetRowIdx(pRowName); + int rowIdx = GetRowIdx(pRowName); if (rowIdx < 0) { throw std::out_of_range("row not found: " + pRowName); } - return GetRow(rowIdx); + return GetRow(static_cast(rowIdx)); } /** @@ -598,9 +881,9 @@ class Document { template std::vector GetRow(const std::string& pRowName, ConvFunc pToVal) const { - ssize_t rowIdx = GetRowIdx(pRowName); + int rowIdx = GetRowIdx(pRowName); if (rowIdx < 0) { throw std::out_of_range("row not found: " + pRowName); } - return GetRow(rowIdx, pToVal); + return GetRow(static_cast(rowIdx), pToVal); } /** @@ -611,9 +894,9 @@ class Document { template void SetRow(const size_t pRowIdx, const std::vector& pRow) { - const size_t rowIdx = pRowIdx + (mLabelParams.mColumnNameIdx + 1); + const size_t dataRowIdx = GetDataRowIndex(pRowIdx); - while ((rowIdx + 1) > GetDataRowCount()) { + while ((dataRowIdx + 1) > GetDataRowCount()) { std::vector row; row.resize(GetDataColumnCount()); mData.push_back(row); @@ -621,7 +904,9 @@ class Document { if (pRow.size() > GetDataColumnCount()) { for (auto itRow = mData.begin(); itRow != mData.end(); ++itRow) { - itRow->resize(pRow.size() + (mLabelParams.mRowNameIdx + 1)); + if (std::distance(mData.begin(), itRow) >= mLabelParams.mColumnNameIdx) { + itRow->resize(GetDataColumnIndex(pRow.size())); + } } } @@ -629,8 +914,9 @@ class Document { for (auto itCol = pRow.begin(); itCol != pRow.end(); ++itCol) { std::string str; converter.ToStr(*itCol, str); - mData.at(rowIdx).at(std::distance(pRow.begin(), itCol) + (mLabelParams.mRowNameIdx + 1)) = - str; + mData.at(dataRowIdx) + .at(static_cast(std::distance(pRow.begin(), itCol) + mLabelParams.mRowNameIdx + + 1)) = str; } } @@ -642,9 +928,9 @@ class Document { template void SetRow(const std::string& pRowName, const std::vector& pRow) { - ssize_t rowIdx = GetRowIdx(pRowName); + int rowIdx = GetRowIdx(pRowName); if (rowIdx < 0) { throw std::out_of_range("row not found: " + pRowName); } - return SetRow(rowIdx, pRow); + return SetRow(static_cast(rowIdx), pRow); } /** @@ -653,8 +939,15 @@ class Document { */ void RemoveRow(const size_t pRowIdx) { - const ssize_t rowIdx = pRowIdx + (mLabelParams.mColumnNameIdx + 1); - mData.erase(mData.begin() + rowIdx); + const size_t dataRowIdx = GetDataRowIndex(pRowIdx); + if (dataRowIdx < mData.size()) { + mData.erase(mData.begin() + static_cast(dataRowIdx)); + } else { + const std::string errStr = "row out of range: " + std::to_string(pRowIdx); + throw std::out_of_range(errStr); + } + + UpdateRowNames(); } /** @@ -663,17 +956,61 @@ class Document { */ void RemoveRow(const std::string& pRowName) { - ssize_t rowIdx = GetRowIdx(pRowName); + int rowIdx = GetRowIdx(pRowName); if (rowIdx < 0) { throw std::out_of_range("row not found: " + pRowName); } - RemoveRow(rowIdx); + RemoveRow(static_cast(rowIdx)); + } + + /** + * @brief Insert row at specified index. + * @param pRowIdx zero-based row index. + * @param pRow vector of row data (optional argument). + * @param pRowName row label name (optional argument). + */ + template + void InsertRow(const size_t pRowIdx, + const std::vector& pRow = std::vector(), + const std::string& pRowName = std::string()) + { + const size_t rowIdx = GetDataRowIndex(pRowIdx); + + std::vector row; + if (pRow.empty()) { + row.resize(GetDataColumnCount()); + } else { + row.resize(GetDataColumnIndex(pRow.size())); + Converter converter(mConverterParams); + for (auto itCol = pRow.begin(); itCol != pRow.end(); ++itCol) { + std::string str; + converter.ToStr(*itCol, str); + row.at(static_cast(std::distance(pRow.begin(), itCol) + mLabelParams.mRowNameIdx + + 1)) = str; + } + } + + while (rowIdx > GetDataRowCount()) { + std::vector tempRow; + tempRow.resize(GetDataColumnCount()); + mData.push_back(tempRow); + } + + mData.insert(mData.begin() + static_cast(rowIdx), row); + + if (!pRowName.empty()) { SetRowName(pRowIdx, pRowName); } + + UpdateRowNames(); } /** - * @brief Get number of data rows. + * @brief Get number of data rows (excluding label rows). * @returns row count. */ - size_t GetRowCount() const { return mData.size() - (mLabelParams.mColumnNameIdx + 1); } + size_t GetRowCount() const + { + const int count = static_cast(mData.size()) - (mLabelParams.mColumnNameIdx + 1); + return (count >= 0) ? static_cast(count) : 0; + } /** * @brief Get cell by index. @@ -684,12 +1021,12 @@ class Document { template T GetCell(const size_t pColumnIdx, const size_t pRowIdx) const { - const ssize_t columnIdx = pColumnIdx + (mLabelParams.mRowNameIdx + 1); - const ssize_t rowIdx = pRowIdx + (mLabelParams.mColumnNameIdx + 1); + const size_t dataColumnIdx = GetDataColumnIndex(pColumnIdx); + const size_t dataRowIdx = GetDataRowIndex(pRowIdx); T val; Converter converter(mConverterParams); - converter.ToVal(mData.at(rowIdx).at(columnIdx), val); + converter.ToVal(mData.at(dataRowIdx).at(dataColumnIdx), val); return val; } @@ -703,11 +1040,11 @@ class Document { template T GetCell(const size_t pColumnIdx, const size_t pRowIdx, ConvFunc pToVal) const { - const ssize_t columnIdx = pColumnIdx + (mLabelParams.mRowNameIdx + 1); - const ssize_t rowIdx = pRowIdx + (mLabelParams.mColumnNameIdx + 1); + const size_t dataColumnIdx = GetDataColumnIndex(pColumnIdx); + const size_t dataRowIdx = GetDataRowIndex(pRowIdx); T val; - pToVal(mData.at(rowIdx).at(columnIdx), val); + pToVal(mData.at(dataRowIdx).at(dataColumnIdx), val); return val; } @@ -720,13 +1057,13 @@ class Document { template T GetCell(const std::string& pColumnName, const std::string& pRowName) const { - const ssize_t columnIdx = GetColumnIdx(pColumnName); + const int columnIdx = GetColumnIdx(pColumnName); if (columnIdx < 0) { throw std::out_of_range("column not found: " + pColumnName); } - const ssize_t rowIdx = GetRowIdx(pRowName); + const int rowIdx = GetRowIdx(pRowName); if (rowIdx < 0) { throw std::out_of_range("row not found: " + pRowName); } - return GetCell(columnIdx, rowIdx); + return GetCell(static_cast(columnIdx), static_cast(rowIdx)); } /** @@ -739,13 +1076,13 @@ class Document { template T GetCell(const std::string& pColumnName, const std::string& pRowName, ConvFunc pToVal) const { - const ssize_t columnIdx = GetColumnIdx(pColumnName); + const int columnIdx = GetColumnIdx(pColumnName); if (columnIdx < 0) { throw std::out_of_range("column not found: " + pColumnName); } - const ssize_t rowIdx = GetRowIdx(pRowName); + const int rowIdx = GetRowIdx(pRowName); if (rowIdx < 0) { throw std::out_of_range("row not found: " + pRowName); } - return GetCell(columnIdx, rowIdx, pToVal); + return GetCell(static_cast(columnIdx), static_cast(rowIdx), pToVal); } /** @@ -757,10 +1094,10 @@ class Document { template T GetCell(const std::string& pColumnName, const size_t pRowIdx) const { - const ssize_t columnIdx = GetColumnIdx(pColumnName); + const int columnIdx = GetColumnIdx(pColumnName); if (columnIdx < 0) { throw std::out_of_range("column not found: " + pColumnName); } - return GetCell(columnIdx, pRowIdx); + return GetCell(static_cast(columnIdx), pRowIdx); } /** @@ -773,10 +1110,10 @@ class Document { template T GetCell(const std::string& pColumnName, const size_t pRowIdx, ConvFunc pToVal) const { - const ssize_t columnIdx = GetColumnIdx(pColumnName); + const int columnIdx = GetColumnIdx(pColumnName); if (columnIdx < 0) { throw std::out_of_range("column not found: " + pColumnName); } - return GetCell(columnIdx, pRowIdx, pToVal); + return GetCell(static_cast(columnIdx), pRowIdx, pToVal); } /** @@ -788,10 +1125,10 @@ class Document { template T GetCell(const size_t pColumnIdx, const std::string& pRowName) const { - const ssize_t rowIdx = GetRowIdx(pRowName); + const int rowIdx = GetRowIdx(pRowName); if (rowIdx < 0) { throw std::out_of_range("row not found: " + pRowName); } - return GetCell(pColumnIdx, rowIdx); + return GetCell(pColumnIdx, static_cast(rowIdx)); } /** @@ -804,10 +1141,10 @@ class Document { template T GetCell(const size_t pColumnIdx, const std::string& pRowName, ConvFunc pToVal) const { - const ssize_t rowIdx = GetRowIdx(pRowName); + const int rowIdx = GetRowIdx(pRowName); if (rowIdx < 0) { throw std::out_of_range("row not found: " + pRowName); } - return GetCell(pColumnIdx, rowIdx, pToVal); + return GetCell(pColumnIdx, static_cast(rowIdx), pToVal); } /** @@ -819,25 +1156,27 @@ class Document { template void SetCell(const size_t pColumnIdx, const size_t pRowIdx, const T& pCell) { - const size_t columnIdx = pColumnIdx + (mLabelParams.mRowNameIdx + 1); - const size_t rowIdx = pRowIdx + (mLabelParams.mColumnNameIdx + 1); + const size_t dataColumnIdx = GetDataColumnIndex(pColumnIdx); + const size_t dataRowIdx = GetDataRowIndex(pRowIdx); - while ((rowIdx + 1) > GetDataRowCount()) { + while ((dataRowIdx + 1) > GetDataRowCount()) { std::vector row; row.resize(GetDataColumnCount()); mData.push_back(row); } - if ((columnIdx + 1) > GetDataColumnCount()) { + if ((dataColumnIdx + 1) > GetDataColumnCount()) { for (auto itRow = mData.begin(); itRow != mData.end(); ++itRow) { - itRow->resize(columnIdx + 1); + if (std::distance(mData.begin(), itRow) >= mLabelParams.mColumnNameIdx) { + itRow->resize(dataColumnIdx + 1); + } } } std::string str; Converter converter(mConverterParams); converter.ToStr(pCell, str); - mData.at(rowIdx).at(columnIdx) = str; + mData.at(dataRowIdx).at(dataColumnIdx) = str; } /** @@ -849,13 +1188,43 @@ class Document { template void SetCell(const std::string& pColumnName, const std::string& pRowName, const T& pCell) { - const ssize_t columnIdx = GetColumnIdx(pColumnName); + const int columnIdx = GetColumnIdx(pColumnName); if (columnIdx < 0) { throw std::out_of_range("column not found: " + pColumnName); } - const ssize_t rowIdx = GetRowIdx(pRowName); + const int rowIdx = GetRowIdx(pRowName); if (rowIdx < 0) { throw std::out_of_range("row not found: " + pRowName); } - SetCell(columnIdx, rowIdx, pCell); + SetCell(static_cast(columnIdx), static_cast(rowIdx), pCell); + } + + /** + * @brief Set cell by column index and row name. + * @param pColumnIdx zero-based column index. + * @param pRowName row label name. + * @param pCell cell data. + */ + template + void SetCell(const size_t pColumnIdx, const std::string& pRowName, const T& pCell) + { + const int rowIdx = GetRowIdx(pRowName); + if (rowIdx < 0) { throw std::out_of_range("row not found: " + pRowName); } + + SetCell(pColumnIdx, static_cast(rowIdx), pCell); + } + + /** + * @brief Set cell by column name and row index. + * @param pColumnName column label name. + * @param pRowIdx zero-based row index. + * @param pCell cell data. + */ + template + void SetCell(const std::string& pColumnName, const size_t pRowIdx, const T& pCell) + { + const int columnIdx = GetColumnIdx(pColumnName); + if (columnIdx < 0) { throw std::out_of_range("column not found: " + pColumnName); } + + SetCell(static_cast(columnIdx), pRowIdx, pCell); } /** @@ -863,15 +1232,15 @@ class Document { * @param pColumnIdx zero-based column index. * @returns column name. */ - std::string GetColumnName(const ssize_t pColumnIdx) + std::string GetColumnName(const size_t pColumnIdx) const { - const ssize_t columnIdx = pColumnIdx + (mLabelParams.mRowNameIdx + 1); + const size_t dataColumnIdx = GetDataColumnIndex(pColumnIdx); if (mLabelParams.mColumnNameIdx < 0) { throw std::out_of_range("column name row index < 0: " + std::to_string(mLabelParams.mColumnNameIdx)); } - return mData.at(mLabelParams.mColumnNameIdx).at(columnIdx); + return mData.at(static_cast(mLabelParams.mColumnNameIdx)).at(dataColumnIdx); } /** @@ -881,26 +1250,34 @@ class Document { */ void SetColumnName(size_t pColumnIdx, const std::string& pColumnName) { - const ssize_t columnIdx = pColumnIdx + (mLabelParams.mRowNameIdx + 1); - mColumnNames[pColumnName] = columnIdx; if (mLabelParams.mColumnNameIdx < 0) { throw std::out_of_range("column name row index < 0: " + std::to_string(mLabelParams.mColumnNameIdx)); } - mData.at(mLabelParams.mColumnNameIdx).at(columnIdx) = pColumnName; + const size_t dataColumnIdx = GetDataColumnIndex(pColumnIdx); + mColumnNames[pColumnName] = dataColumnIdx; + + // increase table size if necessary: + const size_t rowIdx = static_cast(mLabelParams.mColumnNameIdx); + if (rowIdx >= mData.size()) { mData.resize(rowIdx + 1); } + auto& row = mData[rowIdx]; + if (dataColumnIdx >= row.size()) { row.resize(dataColumnIdx + 1); } + + mData.at(static_cast(mLabelParams.mColumnNameIdx)).at(dataColumnIdx) = pColumnName; } /** * @brief Get column names * @returns vector of column names. */ - std::vector GetColumnNames() + std::vector GetColumnNames() const { if (mLabelParams.mColumnNameIdx >= 0) { return std::vector( - mData.at(mLabelParams.mColumnNameIdx).begin() + (mLabelParams.mRowNameIdx + 1), - mData.at(mLabelParams.mColumnNameIdx).end()); + mData.at(static_cast(mLabelParams.mColumnNameIdx)).begin() + + (mLabelParams.mRowNameIdx + 1), + mData.at(static_cast(mLabelParams.mColumnNameIdx)).end()); } return std::vector(); @@ -911,15 +1288,15 @@ class Document { * @param pRowIdx zero-based column index. * @returns row name. */ - std::string GetRowName(const ssize_t pRowIdx) + std::string GetRowName(const size_t pRowIdx) const { - const ssize_t rowIdx = pRowIdx + (mLabelParams.mColumnNameIdx + 1); + const size_t dataRowIdx = GetDataRowIndex(pRowIdx); if (mLabelParams.mRowNameIdx < 0) { throw std::out_of_range("row name column index < 0: " + std::to_string(mLabelParams.mRowNameIdx)); } - return mData.at(rowIdx).at(mLabelParams.mRowNameIdx); + return mData.at(dataRowIdx).at(static_cast(mLabelParams.mRowNameIdx)); } /** @@ -929,27 +1306,34 @@ class Document { */ void SetRowName(size_t pRowIdx, const std::string& pRowName) { - const ssize_t rowIdx = pRowIdx + (mLabelParams.mColumnNameIdx + 1); - mRowNames[pRowName] = rowIdx; + const size_t dataRowIdx = GetDataRowIndex(pRowIdx); + mRowNames[pRowName] = dataRowIdx; if (mLabelParams.mRowNameIdx < 0) { throw std::out_of_range("row name column index < 0: " + std::to_string(mLabelParams.mRowNameIdx)); } - mData.at(rowIdx).at(mLabelParams.mRowNameIdx) = pRowName; + // increase table size if necessary: + if (dataRowIdx >= mData.size()) { mData.resize(dataRowIdx + 1); } + auto& row = mData[dataRowIdx]; + if (mLabelParams.mRowNameIdx >= static_cast(row.size())) { + row.resize(static_cast(mLabelParams.mRowNameIdx) + 1); + } + + mData.at(dataRowIdx).at(static_cast(mLabelParams.mRowNameIdx)) = pRowName; } /** * @brief Get row names * @returns vector of row names. */ - std::vector GetRowNames() + std::vector GetRowNames() const { std::vector rownames; if (mLabelParams.mRowNameIdx >= 0) { for (auto itRow = mData.begin(); itRow != mData.end(); ++itRow) { if (std::distance(mData.begin(), itRow) > mLabelParams.mColumnNameIdx) { - rownames.push_back(itRow->at(mLabelParams.mRowNameIdx)); + rownames.push_back(itRow->at(static_cast(mLabelParams.mRowNameIdx))); } } } @@ -962,53 +1346,73 @@ class Document { std::ifstream stream; stream.exceptions(std::ifstream::failbit | std::ifstream::badbit); stream.open(mPath, std::ios::binary); + ReadCsv(stream); + } -#ifdef HAS_CODECVT - stream.seekg(0, std::ios::end); - std::streamsize length = stream.tellg(); - stream.seekg(0, std::ios::beg); + void ReadCsv(std::istream& pStream) + { + Clear(); + pStream.seekg(0, std::ios::end); + std::streamsize length = pStream.tellg(); + pStream.seekg(0, std::ios::beg); - std::vector bom(2, '\0'); - if (length >= 2) { stream.read(bom.data(), 2); } +#ifdef HAS_CODECVT + std::vector bom2b(2, '\0'); + if (length >= 2) { + pStream.read(bom2b.data(), 2); + pStream.seekg(0, std::ios::beg); + } static const std::vector bomU16le = {'\xff', '\xfe'}; static const std::vector bomU16be = {'\xfe', '\xff'}; - if ((bom == bomU16le) || (bom == bomU16be)) { + if ((bom2b == bomU16le) || (bom2b == bomU16be)) { mIsUtf16 = true; - mIsLE = (bom == bomU16le); + mIsLE = (bom2b == bomU16le); - std::wifstream wstream; - wstream.exceptions(std::wifstream::failbit | std::wifstream::badbit); - wstream.open(mPath, std::ios::binary); - if (mIsLE) { - wstream.imbue( - std::locale(wstream.getloc(), - new std::codecvt_utf16(std::consume_header | - std::little_endian)>)); - } else { - wstream.imbue(std::locale(wstream.getloc(), - new std::codecvt_utf16)); - } - std::wstringstream wss; - wss << wstream.rdbuf(); + std::vector buffer(static_cast(length)); + pStream.read(buffer.data(), length); + + const std::wstring& utf16 = [&]() { + if (mIsLE) { + const std::codecvt_mode mode = + static_cast(std::consume_header | std::little_endian); + std::wstring_convert> utf16conv; + return utf16conv.from_bytes(buffer.data(), buffer.data() + length); + } else { + const std::codecvt_mode mode = static_cast(std::consume_header); + std::wstring_convert> utf16conv; + return utf16conv.from_bytes(buffer.data(), buffer.data() + length); + } + }(); + + std::wstringstream wss(utf16); std::string utf8 = ToString(wss.str()); std::stringstream ss(utf8); - ReadCsv(ss); + ParseCsv(ss, static_cast(utf8.size())); } else #endif { - stream.seekg(0, std::ios::beg); - ReadCsv(stream); + // check for UTF-8 Byte order mark and skip it when found + if (length >= 3) { + std::vector bom3b(3, '\0'); + pStream.read(bom3b.data(), 3); + + if (bom3b != s_Utf8BOM) { + // file does not start with a UTF-8 Byte order mark + pStream.seekg(0, std::ios::beg); + } else { + // file did start with a UTF-8 Byte order mark, simply skip it + length -= 3; + mHasUtf8BOM = true; + } + } + + ParseCsv(pStream, length); } } - void ReadCsv(std::istream& pStream) + void ParseCsv(std::istream& pStream, std::streamsize p_FileLength) { - pStream.seekg(0, std::ios::end); - std::streamsize fileLength = pStream.tellg(); - pStream.seekg(0, std::ios::beg); const std::streamsize bufLength = 64 * 1024; std::vector buffer(bufLength); std::vector row; @@ -1017,16 +1421,32 @@ class Document { int cr = 0; int lf = 0; - while (fileLength > 0) { - std::streamsize readLength = std::min(fileLength, bufLength); - pStream.read(buffer.data(), readLength); - for (int i = 0; i < readLength; ++i) { - if (buffer[i] == '"') { - if (cell.empty() || cell[0] == '"') { quoted = !quoted; } + while (p_FileLength > 0) { + const std::streamsize toReadLength = std::min(p_FileLength, bufLength); + pStream.read(buffer.data(), toReadLength); + + // With user-specified istream opened in non-binary mode on windows, we may have a + // data length mismatch, so ensure we don't parse outside actual data length read. + const std::streamsize readLength = pStream.gcount(); + if (readLength <= 0) { break; } + + for (size_t i = 0; i < static_cast(readLength); ++i) { + if (buffer[i] == mSeparatorParams.mQuoteChar) { + if (cell.empty() || (cell[0] == mSeparatorParams.mQuoteChar)) { + quoted = !quoted; + } else if (mSeparatorParams.mTrim) { + // allow whitespace before first mQuoteChar + const auto firstQuote = + std::find(cell.begin(), cell.end(), mSeparatorParams.mQuoteChar); + if (std::all_of( + cell.begin(), firstQuote, [](unsigned char ch) { return isspace(ch); })) { + quoted = !quoted; + } + } cell += buffer[i]; } else if (buffer[i] == mSeparatorParams.mSeparator) { if (!quoted) { - row.push_back(mSeparatorParams.mTrim ? Trim(cell) : cell); + row.push_back(Unquote(Trim(cell))); cell.clear(); } else { cell += buffer[i]; @@ -1042,46 +1462,56 @@ class Document { cell += buffer[i]; } else { ++lf; - row.push_back(mSeparatorParams.mTrim ? Trim(cell) : cell); - cell.clear(); - mData.push_back(row); - row.clear(); - quoted = false; + if (mLineReaderParams.mSkipEmptyLines && row.empty() && cell.empty()) { + // skip empty line + } else { + row.push_back(Unquote(Trim(cell))); + + if (mLineReaderParams.mSkipCommentLines && !row.at(0).empty() && + (row.at(0)[0] == mLineReaderParams.mCommentPrefix)) { + // skip comment line + } else { + mData.push_back(row); + } + + cell.clear(); + row.clear(); + quoted = false; + } } } else { cell += buffer[i]; } } - fileLength -= readLength; + p_FileLength -= readLength; } - // Handle last line without linebreak - if (!cell.empty() || !row.empty()) { - row.push_back(mSeparatorParams.mTrim ? Trim(cell) : cell); + // Handle last row / cell without linebreak + if (row.empty() && cell.empty()) { + // skip empty trailing line + } else { + row.push_back(Unquote(Trim(cell))); + + if (mLineReaderParams.mSkipCommentLines && !row.at(0).empty() && + (row.at(0)[0] == mLineReaderParams.mCommentPrefix)) { + // skip comment line + } else { + mData.push_back(row); + } + cell.clear(); - mData.push_back(row); row.clear(); + quoted = false; } // Assume CR/LF if at least half the linebreaks have CR mSeparatorParams.mHasCR = (cr > (lf / 2)); // Set up column labels - if ((mLabelParams.mColumnNameIdx >= 0) && (mData.size() > 0)) { - int i = 0; - for (auto& columnName : mData[mLabelParams.mColumnNameIdx]) { - mColumnNames[columnName] = i++; - } - } + UpdateColumnNames(); // Set up row labels - if ((mLabelParams.mRowNameIdx >= 0) && - (static_cast(mData.size()) > (mLabelParams.mColumnNameIdx + 1))) { - int i = 0; - for (auto& dataRow : mData) { - mRowNames[dataRow[mLabelParams.mRowNameIdx]] = i++; - } - } + UpdateRowNames(); } void WriteCsv() const @@ -1106,7 +1536,7 @@ class Document { wstream.imbue(std::locale(wstream.getloc(), new std::codecvt_utf16)); } - wstream << (wchar_t)0xfeff; + wstream << static_cast(0xfeff); wstream << wstr; } else #endif @@ -1114,6 +1544,8 @@ class Document { std::ofstream stream; stream.exceptions(std::ofstream::failbit | std::ofstream::badbit); stream.open(mPath, std::ios::binary | std::ios::trunc); + if (mHasUtf8BOM) { stream.write(s_Utf8BOM.data(), 3); } + WriteCsv(stream); } } @@ -1122,11 +1554,17 @@ class Document { { for (auto itr = mData.begin(); itr != mData.end(); ++itr) { for (auto itc = itr->begin(); itc != itr->end(); ++itc) { - if ((std::string::npos == itc->find(mSeparatorParams.mSeparator)) || - ((itc->length() >= 2) && ((*itc)[0] == '\"') && ((*itc)[itc->length() - 1] == '\"'))) { - pStream << *itc; + if (mSeparatorParams.mAutoQuote && + ((itc->find(mSeparatorParams.mSeparator) != std::string::npos) || + (itc->find(' ') != std::string::npos) || (itc->find('\n') != std::string::npos))) { + // escape quotes in string + std::string str = *itc; + const std::string quoteCharStr = std::string(1, mSeparatorParams.mQuoteChar); + ReplaceString(str, quoteCharStr, quoteCharStr + quoteCharStr); + + pStream << quoteCharStr << str << quoteCharStr; } else { - pStream << '"' << *itc << '"'; + pStream << *itc; } if (std::distance(itc, itr->end()) > 1) { pStream << mSeparatorParams.mSeparator; } @@ -1135,71 +1573,124 @@ class Document { } } - ssize_t GetColumnIdx(const std::string& pColumnName) const + size_t GetDataRowCount() const { return mData.size(); } + + size_t GetDataColumnCount() const { - if (mLabelParams.mColumnNameIdx >= 0) { - if (mColumnNames.find(pColumnName) != mColumnNames.end()) { - return mColumnNames.at(pColumnName) - (mLabelParams.mRowNameIdx + 1); - } + const size_t firstDataRow = + static_cast((mLabelParams.mColumnNameIdx >= 0) ? mLabelParams.mColumnNameIdx : 0); + return (mData.size() > firstDataRow) ? mData.at(firstDataRow).size() : 0; + } + + inline size_t GetDataRowIndex(const size_t pRowIdx) const + { + const size_t firstDataRow = static_cast( + (mLabelParams.mColumnNameIdx + 1 >= 0) ? mLabelParams.mColumnNameIdx + 1 : 0); + return pRowIdx + firstDataRow; + } + + inline size_t GetDataColumnIndex(const size_t pColumnIdx) const + { + const size_t firstDataColumn = + static_cast((mLabelParams.mRowNameIdx + 1 >= 0) ? mLabelParams.mRowNameIdx + 1 : 0); + return pColumnIdx + firstDataColumn; + } + + std::string Trim(const std::string& pStr) const + { + if (mSeparatorParams.mTrim) { + std::string str = pStr; + + // ltrim + str.erase(str.begin(), std::find_if(str.begin(), str.end(), [](unsigned char ch) { + return !isspace(ch); + })); + + // rtrim + str.erase( + std::find_if(str.rbegin(), str.rend(), [](unsigned char ch) { return !isspace(ch); }) + .base(), + str.end()); + + return str; + } else { + return pStr; } - return -1; } - ssize_t GetRowIdx(const std::string& pRowName) const + std::string Unquote(const std::string& pStr) const { - if (mLabelParams.mRowNameIdx >= 0) { - if (mRowNames.find(pRowName) != mRowNames.end()) { - return mRowNames.at(pRowName) - (mLabelParams.mColumnNameIdx + 1); - } + if (mSeparatorParams.mAutoQuote && (pStr.size() >= 2) && + (pStr.front() == mSeparatorParams.mQuoteChar) && + (pStr.back() == mSeparatorParams.mQuoteChar)) { + // remove start/end quotes + std::string str = pStr.substr(1, pStr.size() - 2); + + // unescape quotes in string + const std::string quoteCharStr = std::string(1, mSeparatorParams.mQuoteChar); + ReplaceString(str, quoteCharStr + quoteCharStr, quoteCharStr); + + return str; + } else { + return pStr; } - return -1; } - size_t GetDataRowCount() const { return mData.size(); } + void UpdateColumnNames() + { + mColumnNames.clear(); + if ((mLabelParams.mColumnNameIdx >= 0) && + (static_cast(mData.size()) > mLabelParams.mColumnNameIdx)) { + size_t i = 0; + for (auto& columnName : mData[static_cast(mLabelParams.mColumnNameIdx)]) { + mColumnNames[columnName] = i++; + } + } + } - size_t GetDataColumnCount() const { return (mData.size() > 0) ? mData.at(0).size() : 0; } + void UpdateRowNames() + { + mRowNames.clear(); + if ((mLabelParams.mRowNameIdx >= 0) && + (static_cast(mData.size()) > (mLabelParams.mColumnNameIdx + 1))) { + size_t i = 0; + for (auto& dataRow : mData) { + if (static_cast(dataRow.size()) > mLabelParams.mRowNameIdx) { + mRowNames[dataRow[static_cast(mLabelParams.mRowNameIdx)]] = i++; + } + } + } + } #ifdef HAS_CODECVT #if defined(_MSC_VER) +#pragma warning(push) #pragma warning(disable : 4996) #endif static std::string ToString(const std::wstring& pWStr) { - size_t len = std::wcstombs(nullptr, pWStr.c_str(), 0) + 1; - char* cstr = new char[len]; - std::wcstombs(cstr, pWStr.c_str(), len); - std::string str(cstr); - delete[] cstr; - return str; + return std::wstring_convert, wchar_t>{}.to_bytes(pWStr); } static std::wstring ToWString(const std::string& pStr) { - size_t len = 1 + mbstowcs(nullptr, pStr.c_str(), 0); - wchar_t* wcstr = new wchar_t[len]; - std::mbstowcs(wcstr, pStr.c_str(), len); - std::wstring wstr(wcstr); - delete[] wcstr; - return wstr; + return std::wstring_convert, wchar_t>{}.from_bytes(pStr); } #if defined(_MSC_VER) -#pragma warning(default : 4996) +#pragma warning(pop) #endif #endif - static std::string Trim(const std::string& pStr) + static void ReplaceString(std::string& pStr, + const std::string& pSearch, + const std::string& pReplace) { - std::string str = pStr; - - // ltrim - str.erase(str.begin(), - std::find_if(str.begin(), str.end(), [](int ch) { return !isspace(ch); })); + size_t pos = 0; - // rtrim - str.erase(std::find_if(str.rbegin(), str.rend(), [](int ch) { return !isspace(ch); }).base(), - str.end()); - - return str; + while ((pos = pStr.find(pSearch, pos)) != std::string::npos) { + pStr.replace(pos, pSearch.size(), pReplace); + pos += pReplace.size(); + } } private: @@ -1207,6 +1698,7 @@ class Document { LabelParams mLabelParams; SeparatorParams mSeparatorParams; ConverterParams mConverterParams; + LineReaderParams mLineReaderParams; std::vector> mData; std::map mColumnNames; std::map mRowNames; @@ -1214,5 +1706,6 @@ class Document { bool mIsUtf16 = false; bool mIsLE = false; #endif + bool mHasUtf8BOM = false; }; } // namespace rapidcsv diff --git a/cpp/include/rmm/detail/format.hpp b/cpp/include/rmm/detail/format.hpp index c2c5e7245..24d8bed70 100644 --- a/cpp/include/rmm/detail/format.hpp +++ b/cpp/include/rmm/detail/format.hpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: Copyright (c) 2024, NVIDIA CORPORATION. + * SPDX-FileCopyrightText: Copyright (c) 2024-2026, NVIDIA CORPORATION. * SPDX-License-Identifier: Apache-2.0 */ @@ -20,8 +20,8 @@ inline std::string format_bytes(std::size_t value) { static std::array units{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"}; - int index = 0; - auto size = static_cast(value); + std::size_t index = 0; + auto size = static_cast(value); while (size > 1024) { size /= 1024; index++; diff --git a/cpp/include/rmm/mr/detail/stream_ordered_memory_resource.hpp b/cpp/include/rmm/mr/detail/stream_ordered_memory_resource.hpp index d2f1c895d..59a84943b 100644 --- a/cpp/include/rmm/mr/detail/stream_ordered_memory_resource.hpp +++ b/cpp/include/rmm/mr/detail/stream_ordered_memory_resource.hpp @@ -267,9 +267,10 @@ class stream_ordered_memory_resource : public crtp, public device_ // the CUDA runtime and thread_local destructors (can) run below // main: it is undefined behaviour to call into the CUDA // runtime below main. - thread_local std::vector events_tls(rmm::get_num_cuda_devices()); + thread_local std::vector events_tls( + static_cast(rmm::get_num_cuda_devices())); auto event = [device_id = this->device_id_]() { - auto& e = events_tls[device_id.value()]; + auto& e = events_tls[static_cast(device_id.value())]; if (!e) { // These events are deliberately not destructed and therefore live until // program exit. diff --git a/cpp/include/rmm/mr/fixed_size_memory_resource.hpp b/cpp/include/rmm/mr/fixed_size_memory_resource.hpp index 4bac00f8f..46d952e56 100644 --- a/cpp/include/rmm/mr/fixed_size_memory_resource.hpp +++ b/cpp/include/rmm/mr/fixed_size_memory_resource.hpp @@ -169,7 +169,7 @@ class fixed_size_memory_resource auto num_blocks = upstream_chunk_size_ / block_size_; - auto block_gen = [ptr, this](int index) { + auto block_gen = [ptr, this](std::size_t index) { // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) return block_type{static_cast(ptr) + index * block_size_}; }; diff --git a/cpp/include/rmm/mr/statistics_resource_adaptor.hpp b/cpp/include/rmm/mr/statistics_resource_adaptor.hpp index e775efac1..88d625a97 100644 --- a/cpp/include/rmm/mr/statistics_resource_adaptor.hpp +++ b/cpp/include/rmm/mr/statistics_resource_adaptor.hpp @@ -227,7 +227,7 @@ class statistics_resource_adaptor final : public device_memory_resource { write_lock_t lock(mtx_); // Increment the allocation_count_ while we have the lock - counter_stack_.top().first += bytes; + counter_stack_.top().first += static_cast(bytes); counter_stack_.top().second += 1; } @@ -247,7 +247,7 @@ class statistics_resource_adaptor final : public device_memory_resource { write_lock_t lock(mtx_); // Decrement the current allocated counts. - counter_stack_.top().first -= bytes; + counter_stack_.top().first -= static_cast(bytes); counter_stack_.top().second -= 1; } get_upstream_resource().deallocate(stream, ptr, bytes); diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index 0d0f5eaa1..f513dd0ae 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -9,8 +9,8 @@ option(DISABLE_DEPRECATION_WARNING "Disable warnings generated from deprecated declarations." OFF) option(CODE_COVERAGE "Enable generating code coverage with gcov." OFF) -set(RMM_TESTS_CXX_FLAGS -Wall -Werror -Wextra -Wno-unknown-pragmas) -set(RMM_TESTS_CUDA_FLAGS -Werror=all-warnings -Xcompiler=-Wall,-Werror,-Wextra) +set(RMM_TESTS_CXX_FLAGS -Wall -Werror -Wextra -Wsign-conversion -Wno-unknown-pragmas) +set(RMM_TESTS_CUDA_FLAGS -Werror=all-warnings -Xcompiler=-Wall,-Werror,-Wextra,-Wsign-conversion) include(rapids-test) rapids_test_init() diff --git a/cpp/tests/mr/mr_ref_test.hpp b/cpp/tests/mr/mr_ref_test.hpp index 3d25434e6..4b4f091bf 100644 --- a/cpp/tests/mr/mr_ref_test.hpp +++ b/cpp/tests/mr/mr_ref_test.hpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: Copyright (c) 2023-2025, NVIDIA CORPORATION. + * SPDX-FileCopyrightText: Copyright (c) 2023-2026, NVIDIA CORPORATION. * SPDX-License-Identifier: Apache-2.0 */ @@ -309,10 +309,11 @@ inline void test_mixed_random_allocation_free(resource_ref ref, EXPECT_NE(nullptr, new_allocation.ptr); EXPECT_TRUE(is_properly_aligned(new_allocation.ptr)); } else { - auto const index = static_cast(index_distribution(generator) % active_allocations); + auto const index = + static_cast(index_distribution(generator)) % active_allocations; active_allocations--; allocation to_free = allocations[index]; - allocations.erase(std::next(allocations.begin(), index)); + allocations.erase(std::next(allocations.begin(), static_cast(index))); EXPECT_NO_THROW(ref.deallocate_sync(to_free.ptr, to_free.size)); } } @@ -356,10 +357,11 @@ inline void test_mixed_random_async_allocation_free(rmm::device_async_resource_r EXPECT_NE(nullptr, new_allocation.ptr); EXPECT_TRUE(is_properly_aligned(new_allocation.ptr)); } else { - auto const index = static_cast(index_distribution(generator) % active_allocations); + auto const index = + static_cast(index_distribution(generator)) % active_allocations; active_allocations--; allocation to_free = allocations[index]; - allocations.erase(std::next(allocations.begin(), index)); + allocations.erase(std::next(allocations.begin(), static_cast(index))); EXPECT_NO_THROW(ref.deallocate(stream, to_free.ptr, to_free.size)); } } diff --git a/cpp/tests/mr/mr_ref_test_mt.hpp b/cpp/tests/mr/mr_ref_test_mt.hpp index 3833d9c4c..674d3a78d 100644 --- a/cpp/tests/mr/mr_ref_test_mt.hpp +++ b/cpp/tests/mr/mr_ref_test_mt.hpp @@ -155,7 +155,7 @@ TEST_P(mr_ref_test_mt, SetCurrentDeviceResourceRefPerThread_mt) RMM_CUDA_TRY(cudaGetDeviceCount(&num_devices)); std::vector threads; - threads.reserve(num_devices); + threads.reserve(static_cast(num_devices)); auto mr = this->ref; diff --git a/cpp/tests/mr/tracking_mr_tests.cpp b/cpp/tests/mr/tracking_mr_tests.cpp index 9d1878758..f6ed789c3 100644 --- a/cpp/tests/mr/tracking_mr_tests.cpp +++ b/cpp/tests/mr/tracking_mr_tests.cpp @@ -116,7 +116,7 @@ TEST(TrackingTest, AllocationsLeftWithStacks) allocations.push_back(mr.allocate_sync(ten_MiB)); } for (int i = 0; i < num_allocations; i += 2) { - mr.deallocate_sync(allocations[i], ten_MiB); + mr.deallocate_sync(allocations[static_cast(i)], ten_MiB); } EXPECT_EQ(mr.get_outstanding_allocations().size(), num_allocations / 2); EXPECT_EQ(mr.get_allocated_bytes(), ten_MiB * (num_allocations / 2)); @@ -135,7 +135,7 @@ TEST(TrackingTest, AllocationsLeftWithoutStacks) } for (int i = 0; i < num_allocations; i += 2) { - mr.deallocate_sync(allocations[i], ten_MiB); + mr.deallocate_sync(allocations[static_cast(i)], ten_MiB); } EXPECT_EQ(mr.get_outstanding_allocations().size(), num_allocations / 2); EXPECT_EQ(mr.get_allocated_bytes(), ten_MiB * (num_allocations / 2)); diff --git a/cpp/tests/prefetch_tests.cpp b/cpp/tests/prefetch_tests.cpp index 0062e7f7a..30fe0a647 100644 --- a/cpp/tests/prefetch_tests.cpp +++ b/cpp/tests/prefetch_tests.cpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: Copyright (c) 2019-2025, NVIDIA CORPORATION. + * SPDX-FileCopyrightText: Copyright (c) 2019-2026, NVIDIA CORPORATION. * SPDX-License-Identifier: Apache-2.0 */ @@ -89,9 +89,10 @@ TYPED_TEST(PrefetchTest, DeviceUVector) // test iterator range of part of the vector (implicitly constructs a span) { rmm::device_uvector uvec(this->size, this->stream, &this->mr); - rmm::prefetch({uvec.begin(), std::next(uvec.begin(), this->size / 2)}, // span - rmm::get_current_cuda_device(), - this->stream); + rmm::prefetch( + {uvec.begin(), std::next(uvec.begin(), static_cast(this->size / 2))}, // span + rmm::get_current_cuda_device(), + this->stream); this->expect_prefetched( uvec.data(), this->size / 2 * sizeof(int), rmm::get_current_cuda_device()); }