pax_global_header00006660000000000000000000000064151471744160014524gustar00rootroot0000000000000052 comment=381829c2c1696ffec9277b339952a9588e6e67cf imageworks-pystring-a055353/000077500000000000000000000000001514717441600160205ustar00rootroot00000000000000imageworks-pystring-a055353/.github/000077500000000000000000000000001514717441600173605ustar00rootroot00000000000000imageworks-pystring-a055353/.github/workflows/000077500000000000000000000000001514717441600214155ustar00rootroot00000000000000imageworks-pystring-a055353/.github/workflows/ci.yml000066400000000000000000000210051514717441600225310ustar00rootroot00000000000000# simple ci config the idea is to use the latest ubuntu, macos, and windows that github runners give us # test a few different compilers (gcc/clang) in debug and release # and run our tests via ctest name: CI on: push: # Jobs are skipped when ONLY Markdown (*.md) files are changed paths-ignore: - '**.md' pull_request: paths-ignore: - '**.md' jobs: linux: name: '${{ matrix.os }} / ${{ matrix.build-type }} / ${{ matrix.compiler-desc }} ' runs-on: ubuntu-latest strategy: matrix: build: [1, 2, 3, 4, 5, 6] include: # ------------------------------------------------------------------- # CLANG, Release # ------------------------------------------------------------------- - build: 1 build-type: Release build-shared: 'ON' cxx-standard: 17 cxx-compiler: clang++ cxx-flags: '' cc-compiler: clang compiler-desc: clang os: ubuntu-latest # ------------------------------------------------------------------- # CLANG, Debug # ------------------------------------------------------------------- - build: 2 build-type: Debug build-shared: 'ON' cxx-standard: 17 cxx-compiler: clang++ cxx-flags: '' cc-compiler: clang compiler-desc: clang os: ubuntu-latest # ------------------------------------------------------------------- # gcc, Release # ------------------------------------------------------------------- - build: 3 build-type: Release build-shared: 'ON' cxx-standard: 17 cxx-compiler: g++ cxx-flags: '' cc-compiler: gcc compiler-desc: gcc os: ubuntu-latest # ------------------------------------------------------------------- # gcc, Debug # ------------------------------------------------------------------- - build: 4 build-type: Debug build-shared: 'ON' cxx-standard: 17 cxx-compiler: g++ cxx-flags: '' cc-compiler: gcc compiler-desc: gcc os: ubuntu-latest # ------------------------------------------------------------------- # CLANG, Release header only # ------------------------------------------------------------------- - build: 5 build-type: Release build-shared: 'ON' header-only: 'ON' cxx-standard: 17 cxx-compiler: clang++ cxx-flags: '' cc-compiler: clang compiler-desc: clang os: ubuntu-latest # ------------------------------------------------------------------- # gcc, Release header only # ------------------------------------------------------------------- - build: 6 build-type: Release build-shared: 'ON' header-only: 'ON' cxx-standard: 17 cxx-compiler: g++ cxx-flags: '' cc-compiler: gcc compiler-desc: gcc os: ubuntu-latest env: CXX: ${{ matrix.cxx-compiler }} CC: ${{ matrix.cc-compiler }} steps: - name: Checkout uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Create build directories run: | mkdir _install mkdir _build - name: Configure run: | cmake .. \ -DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/_install \ -DCMAKE_BUILD_TYPE=${{ matrix.build-type }} \ -DCMAKE_CXX_STANDARD=${{ matrix.cxx-standard }} \ -DCMAKE_CXX_FLAGS=${{ matrix.cxx-flags }} \ -DCMAKE_VERBOSE_MAKEFILE:BOOL='OFF' \ -DBUILD_SHARED_LIBS=${{ matrix.build-shared }} \ -DPYSTRING_HEADER_ONLY=${{ matrix.header-only }} working-directory: _build - name: Build run: | cmake --build . \ --config ${{ matrix.build-type }} working-directory: _build - name: Test run: | ctest --build-config ${{ matrix.build-type }} --verbose working-directory: _build # --------------------------------------------------------------------------- # macOS # --------------------------------------------------------------------------- macos: name: '${{ matrix.os }} / ${{ matrix.build-type }}' runs-on: macos-latest strategy: matrix: build: [1, 2, 3] include: # Release - build: 1 build-type: Release build-shared: 'ON' cxx-standard: 17 cxx-flags: '' os: macos-latest # Debug - build: 2 build-type: Debug build-shared: 'ON' build-docs: 'OFF' cxx-standard: 17 cxx-flags: '' os: macos-latest # Release header only - build: 3 build-type: Release build-shared: 'ON' header-only: 'ON' cxx-standard: 17 cxx-flags: '' os: macos-latest steps: - name: Checkout uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Create build directories run: | mkdir _install mkdir _build - name: Configure run: | cmake ../. \ -DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/_install \ -DCMAKE_BUILD_TYPE=${{ matrix.build-type }} \ -DCMAKE_CXX_STANDARD=${{ matrix.cxx-standard }} \ -DCMAKE_CXX_FLAGS=${{ matrix.cxx-flags }} \ -DCMAKE_VERBOSE_MAKEFILE:BOOL='OFF' \ -DBUILD_SHARED_LIBS=${{ matrix.build-shared }} \ -DPYSTRING_HEADER_ONLY=${{ matrix.header-only }} working-directory: _build - name: Build run: | cmake --build . \ --config ${{ matrix.build-type }} \ working-directory: _build - name: Test run: | ctest --build-config ${{ matrix.build-type }} --verbose working-directory: _build # --------------------------------------------------------------------------- # Windows # --------------------------------------------------------------------------- windows: name: '${{ matrix.os }} / ${{ matrix.build-type }}' runs-on: windows-latest strategy: matrix: build: [1, 2] include: # Release - build: 1 build-type: Release build-shared: 'ON' cxx-standard: 17 cxx-flags: '' os: windows-latest # Debug - build: 2 build-type: Debug build-shared: 'ON' cxx-standard: 17 cxx-flags: '' os: windows-latest # Release header only - build: 3 build-type: Release build-shared: 'ON' header-only: 'ON' cxx-standard: 17 cxx-flags: '' os: windows-latest steps: - name: Checkout uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Create build directories run: | mkdir _install mkdir _build shell: bash - name: Configure # the windows build needs the -DCMAKE_WINDOWS_EXPORT_ALL_SYMBOLS to work run: | cmake ../. \ -DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/_install \ -DCMAKE_WINDOWS_EXPORT_ALL_SYMBOLS='ON'\ -DCMAKE_BUILD_TYPE=${{ matrix.build-type }} \ -DCMAKE_CXX_STANDARD=${{ matrix.cxx-standard }} \ -DCMAKE_CXX_FLAGS=${{ matrix.cxx-flags }} \ -DCMAKE_VERBOSE_MAKEFILE:BOOL='OFF' \ -DBUILD_SHARED_LIBS=${{ matrix.build-shared }} \ -DPYSTRING_HEADER_ONLY=${{ matrix.header-only }} shell: bash working-directory: _build - name: Build run: | cmake --build . \ --config ${{ matrix.build-type }} shell: bash working-directory: _build - name: Test run: | ctest -C ${{ matrix.build-type }} shell: bash working-directory: _build imageworks-pystring-a055353/.github/workflows/meson.yml000066400000000000000000000127131514717441600232650ustar00rootroot00000000000000name: Meson on: pull_request: push: jobs: meson-build-and-tests: runs-on: ${{ matrix.platform }} name: ${{ matrix.platform }}, ${{ matrix.mode.name }} ${{ matrix.flavor }} ${{ matrix.library_mode }} strategy: fail-fast: false matrix: flavor: - debug - release library_mode: - compiled - header-only mode: - name: default extra_envs: {} # Alternative compiler setups - name: gcc extra_envs: CC: gcc CXX: g++ - name: clang extra_envs: CC: clang CXX: clang++ - name: sanitize args: >- "-Db_sanitize=address,undefined" extra_envs: {} # This is for MSVC, which only supports AddressSanitizer. # https://learn.microsoft.com/en-us/cpp/sanitizers/ - name: sanitize+asanonly args: -Db_sanitize=address extra_envs: ASAN_OPTIONS: report_globals=0:halt_on_error=1:abort_on_error=1:print_summary=1 - name: clang+sanitize args: >- -Db_lundef=false "-Db_sanitize=address,undefined" extra_envs: CC: clang CXX: clang++ # default clang on GitHub hosted runners is from MSYS2. # Use Visual Studio supplied clang-cl instead. - name: clang-cl+sanitize args: >- "-Db_sanitize=address,undefined" extra_envs: CC: clang-cl CXX: clang-cl platform: - ubuntu-22.04 - windows-2022 - macos-latest exclude: # Only test header-only with a subset of configurations to reduce CI time # Test header-only only with default compiler in release mode - library_mode: header-only flavor: debug - library_mode: header-only mode: name: gcc - library_mode: header-only mode: name: clang - library_mode: header-only mode: name: sanitize - library_mode: header-only mode: name: sanitize+asanonly - library_mode: header-only mode: name: clang+sanitize - library_mode: header-only mode: name: clang-cl+sanitize # clang-cl only makes sense on windows. - platform: ubuntu-22.04 mode: name: clang-cl+sanitize - platform: macos-latest mode: name: clang-cl+sanitize # Use clang-cl instead of MSYS2 clang. # # we already tested clang+sanitize on linux, # if this doesn't work, it should be an issue for MSYS2 team to consider. - platform: windows-2022 mode: name: clang - platform: windows-2022 mode: name: clang+sanitize # MSVC-only sanitizers - platform: ubuntu-22.04 mode: name: sanitize+asanonly - platform: macos-latest mode: name: sanitize+asanonly - platform: windows-2022 mode: name: sanitize # clang is the default on macos # also gcc is an alias to clang - platform: macos-latest mode: name: clang - platform: macos-latest mode: name: gcc # gcc is the default on linux - platform: ubuntu-22.04 mode: name: gcc # only run sanitizer tests on linux # # gcc/clang's codegen shouldn't massively change across platforms, # and linux supports most of the sanitizers. - platform: macos-latest mode: name: clang+sanitize - platform: macos-latest mode: name: sanitize steps: - name: Setup meson run: | pipx install meson ninja - name: Checkout uses: actions/checkout@v4 - name: Activate MSVC and Configure if: ${{ matrix.platform == 'windows-2022' }} env: ${{ matrix.mode.extra_envs }} run: | meson setup build-${{ matrix.flavor }}-${{ matrix.library_mode }} --buildtype=${{ matrix.flavor }} -Ddefault_library=static -Dheader_only=${{ matrix.library_mode == 'header-only' && 'true' || 'false' }} ${{ matrix.mode.args }} --vsenv - name: Configuring if: ${{ matrix.platform != 'windows-2022' }} env: ${{ matrix.mode.extra_envs }} run: | meson setup build-${{ matrix.flavor }}-${{ matrix.library_mode }} --buildtype=${{ matrix.flavor }} -Dheader_only=${{ matrix.library_mode == 'header-only' && 'true' || 'false' }} ${{ matrix.mode.args }} - name: Building run: | meson compile -C build-${{ matrix.flavor }}-${{ matrix.library_mode }} - name: Running tests env: ${{ matrix.mode.extra_envs }} run: | meson test -C build-${{ matrix.flavor }}-${{ matrix.library_mode }} --timeout-multiplier 0 - uses: actions/upload-artifact@v4 if: failure() with: name: ${{ matrix.platform }}-${{ matrix.mode.name }}-${{ matrix.flavor }}-${{ matrix.library_mode }}-logs path: build-${{ matrix.flavor }}-${{ matrix.library_mode }}/meson-logsimageworks-pystring-a055353/.gitignore000066400000000000000000000000611514717441600200050ustar00rootroot00000000000000test .libs libpystring.la pystring.lo pystring.o imageworks-pystring-a055353/CMakeLists.txt000066400000000000000000000040541514717441600205630ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.10) project(pystring LANGUAGES CXX VERSION 1.1.4) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) option (BUILD_SHARED_LIBS "Build shared libraries (set to OFF to build static libs)" ON) option(PYSTRING_HEADER_ONLY "Build as header-only library" OFF) # If the user hasn't configured cmake with an explicit # -DCMAKE_INSTALL_PREFIX=..., then set it to safely install into ./dist, to # help prevent the user from accidentally writing over /usr/local or whatever. if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT AND PROJECT_IS_TOP_LEVEL) set (CMAKE_INSTALL_PREFIX "${PROJECT_SOURCE_DIR}/dist" CACHE PATH "Installation location" FORCE) endif() message (STATUS "Installation path will be ${CMAKE_INSTALL_PREFIX}") include(GNUInstallDirs) if(PYSTRING_HEADER_ONLY) message(STATUS "Building pystring as header-only library") add_library(pystring INTERFACE) target_compile_definitions(pystring INTERFACE PYSTRING_HEADER_ONLY) target_include_directories(pystring INTERFACE $ $ ) # Install both headers for header-only mode install(FILES pystring.h pystring_impl.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME} ) else() message(STATUS "Building pystring as compiled library") add_library(pystring pystring.cpp pystring.h ) set_target_properties(pystring PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION ${PROJECT_VERSION_MAJOR} ) install(TARGETS pystring LIBRARY DESTINATION lib RUNTIME DESTINATION bin ARCHIVE DESTINATION lib ) install (FILES pystring.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME} COMPONENT developer ) endif() # Test executable add_executable (pystring_test test.cpp) TARGET_LINK_LIBRARIES (pystring_test pystring) enable_testing() add_test(NAME PyStringTest COMMAND pystring_test) imageworks-pystring-a055353/LICENSE000066400000000000000000000027721514717441600170350ustar00rootroot00000000000000Copyright (c) 2008-present Contributors to the Pystring project. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. imageworks-pystring-a055353/Makefile000066400000000000000000000014071514717441600174620ustar00rootroot00000000000000LIBTOOL ?= libtool PREFIX ?= /usr INCLUDEDIR ?= ${PREFIX}/include/pystring LIBDIR ?= ${PREFIX}/lib CXX ?= g++ CXXFLAGS ?= -g -O3 -Wall -Wextra -Wshadow -Wconversion -Wcast-qual -Wformat=2 all: libpystring.la pystring.lo: pystring.h pystring.cpp $(LIBTOOL) --mode=compile --tag=CXX $(CXX) $(CXXFLAGS) -c pystring.cpp libpystring.la: pystring.lo $(LIBTOOL) --mode=link --tag=CXX $(CXX) -o $@ $< -rpath $(LIBDIR) install: libpystring.la $(LIBTOOL) --mode=install install -Dm755 $< $(DESTDIR)$(LIBDIR)/$< $(LIBTOOL) --mode=install install -Dm644 pystring.h $(DESTDIR)$(INCLUDEDIR)/pystring.h clean: $(RM) -fr pystring.lo pystring.o libpystring.la .libs .PHONY: test test: $(RM) -fr test $(CXX) pystring.cpp test.cpp $(CXXFLAGS) -DPYSTRING_UNITTEST=1 -o test ./test imageworks-pystring-a055353/README.md000066400000000000000000000014761514717441600173070ustar00rootroot00000000000000[![Repology](https://img.shields.io/repology/repositories/pystring)](https://repology.org/project/pystring/versions) Pystring is a collection of C++ functions which match the interface and behavior of python's string class methods using std::string. Implemented in C++, it does not require or make use of a python interpreter. It provides convenience and familiarity for common string operations not included in the standard C++ library. It's also useful in environments where both C++ and python are used. Overlapping functionality (such as index and slice/substr) of std::string is included to match python interfaces. Originally developed at Sony Pictures Imageworks. http://opensource.imageworks.com/ Note: Despite the infrequent updates, this repo is not dead/abandoned - just stable! We use it every day at Imageworks. imageworks-pystring-a055353/meson.build000066400000000000000000000033411514717441600201630ustar00rootroot00000000000000project( 'pystring', 'cpp', version: '1.1.4', license: 'BSD-3-Clause', license_files: 'LICENSE', meson_version: '>=1.3', default_options: ['cpp_std=c++17,c++11', 'warning_level=3'], ) # Option to build as header-only library header_only = get_option('header_only') inc = include_directories('.') hdrs = files('pystring.h') if header_only # Header-only mode: create a header-only dependency message('Building pystring as header-only library') pystring_dep = declare_dependency( include_directories: inc, compile_args: ['-DPYSTRING_HEADER_ONLY'], ) # Install headers for header-only mode install_headers(hdrs, files('pystring_impl.h'), subdir: 'pystring') else # Compiled mode: build as normal library message('Building pystring as compiled library') srcs = files('pystring.cpp') pystring_lib = library( 'pystring', srcs, implicit_include_directories: false, include_directories: inc, version: meson.project_version(), install: true, ) pystring_dep = declare_dependency( link_with: pystring_lib, include_directories: inc, ) # Install headers for compiled mode install_headers(hdrs, subdir: 'pystring') # Generate pkg-config file pkgconfig = import('pkgconfig') pkgconfig.generate( pystring_lib, description: 'C++ functions matching the interface and behavior of python string methods with std::string', ) endif meson.override_dependency('pystring', pystring_dep) # Build and run tests test( 'PyStringTest', executable( 'pystring_test', 'test.cpp', dependencies: pystring_dep, build_by_default: false, ), ) imageworks-pystring-a055353/meson_options.txt000066400000000000000000000001421514717441600214520ustar00rootroot00000000000000option('header_only', type: 'boolean', value: false, description: 'Build as header-only library') imageworks-pystring-a055353/pystring.cpp000066400000000000000000000005101514717441600203770ustar00rootroot00000000000000// Copyright Contributors to the Pystring project. // SPDX-License-Identifier: BSD-3-Clause // https://github.com/imageworks/pystring/blob/master/LICENSE #include "pystring.h" // when not in header only mode include the implementations as non inline // functions #ifndef PYSTRING_HEADER_ONLY #include "pystring_impl.h" #endif imageworks-pystring-a055353/pystring.h000066400000000000000000000605141514717441600200560ustar00rootroot00000000000000// Copyright Contributors to the Pystring project. // SPDX-License-Identifier: BSD-3-Clause // https://github.com/imageworks/pystring/blob/master/LICENSE #ifndef INCLUDED_PYSTRING_H #define INCLUDED_PYSTRING_H #ifdef PYSTRING_HEADER_ONLY #define PYSTRING_INLINE inline #else #define PYSTRING_INLINE #endif #include #include namespace pystring { ////////////////////////////////////////////////////////////////////////////////////////////// /// @mainpage pystring /// /// This is a set of functions matching the interface and behaviors of python string methods /// (as of python 2.3) using std::string. /// /// Overlapping functionality ( such as index and slice/substr ) of std::string is included /// to match python interfaces. /// ////////////////////////////////////////////////////////////////////////////////////////////// /// @defgroup functions pystring /// @{ #define MAX_32BIT_INT 2147483647 ////////////////////////////////////////////////////////////////////////////////////////////// /// @brief Return a copy of the string with only its first character capitalized. /// PYSTRING_INLINE std::string capitalize( const std::string & str ); ////////////////////////////////////////////////////////////////////////////////////////////// /// @brief Return centered in a string of length width. Padding is done using spaces. /// PYSTRING_INLINE std::string center( const std::string & str, int width ); ////////////////////////////////////////////////////////////////////////////////////////////// /// @brief Return the number of occurrences of substring sub in string S[start:end]. Optional /// arguments start and end are interpreted as in slice notation. /// PYSTRING_INLINE int count( const std::string & str, const std::string & substr, int start = 0, int end = MAX_32BIT_INT); ////////////////////////////////////////////////////////////////////////////////////////////// /// @brief Return True if the string ends with the specified suffix, otherwise return False. With /// optional start, test beginning at that position. With optional end, stop comparing at that position. /// PYSTRING_INLINE bool endswith( const std::string & str, const std::string & suffix, int start = 0, int end = MAX_32BIT_INT ); ////////////////////////////////////////////////////////////////////////////////////////////// /// @brief Return a copy of the string where all tab characters are expanded using spaces. If tabsize /// is not given, a tab size of 8 characters is assumed. /// PYSTRING_INLINE std::string expandtabs( const std::string & str, int tabsize = 8); ////////////////////////////////////////////////////////////////////////////////////////////// /// @brief Return the lowest index in the string where substring sub is found, such that sub is /// contained in the range [start, end). Optional arguments start and end are interpreted as /// in slice notation. Return -1 if sub is not found. /// PYSTRING_INLINE int find( const std::string & str, const std::string & sub, int start = 0, int end = MAX_32BIT_INT ); ////////////////////////////////////////////////////////////////////////////////////////////// /// @brief Synonym of find right now. Python version throws exceptions. This one currently doesn't /// PYSTRING_INLINE int index( const std::string & str, const std::string & sub, int start = 0, int end = MAX_32BIT_INT ); ////////////////////////////////////////////////////////////////////////////////////////////// /// @brief Return true if all characters in the string are alphanumeric and there is at least one /// character, false otherwise. /// PYSTRING_INLINE bool isalnum( const std::string & str ); ////////////////////////////////////////////////////////////////////////////////////////////// /// @brief Return true if all characters in the string are alphabetic and there is at least one /// character, false otherwise /// PYSTRING_INLINE bool isalpha( const std::string & str ); ////////////////////////////////////////////////////////////////////////////////////////////// /// @brief Return true if all characters in the string are digits and there is at least one /// character, false otherwise. /// PYSTRING_INLINE bool isdigit( const std::string & str ); ////////////////////////////////////////////////////////////////////////////////////////////// /// @brief Return true if all cased characters in the string are lowercase and there is at least one /// cased character, false otherwise. /// PYSTRING_INLINE bool islower( const std::string & str ); ////////////////////////////////////////////////////////////////////////////////////////////// /// @brief Return true if there are only whitespace characters in the string and there is at least /// one character, false otherwise. /// PYSTRING_INLINE bool isspace( const std::string & str ); ////////////////////////////////////////////////////////////////////////////////////////////// /// @brief Return true if the string is a titlecased string and there is at least one character, /// i.e. uppercase characters may only follow uncased characters and lowercase characters only /// cased ones. Return false otherwise. /// PYSTRING_INLINE bool istitle( const std::string & str ); ////////////////////////////////////////////////////////////////////////////////////////////// /// @brief Return true if all cased characters in the string are uppercase and there is at least one /// cased character, false otherwise. /// PYSTRING_INLINE bool isupper( const std::string & str ); ////////////////////////////////////////////////////////////////////////////////////////////// /// @brief Return a string which is the concatenation of the strings in the sequence seq. /// The separator between elements is the str argument /// PYSTRING_INLINE std::string join( const std::string & str, const std::vector< std::string > & seq ); ////////////////////////////////////////////////////////////////////////////////////////////// /// @brief Return the string left justified in a string of length width. Padding is done using /// spaces. The original string is returned if width is less than str.size(). /// PYSTRING_INLINE std::string ljust( const std::string & str, int width ); ////////////////////////////////////////////////////////////////////////////////////////////// /// @brief Return a copy of the string converted to lowercase. /// PYSTRING_INLINE std::string lower( const std::string & str ); ////////////////////////////////////////////////////////////////////////////////////////////// /// @brief Return a copy of the string with leading characters removed. If chars is omitted or None, /// whitespace characters are removed. If given and not "", chars must be a string; the /// characters in the string will be stripped from the beginning of the string this method /// is called on (argument "str" ). /// PYSTRING_INLINE std::string lstrip( const std::string & str, const std::string & chars = "" ); ////////////////////////////////////////////////////////////////////////////////////////////// /// @brief Return a copy of the string, concatenated N times, together. /// Corresponds to the __mul__ operator. /// PYSTRING_INLINE std::string mul( const std::string & str, int n); ////////////////////////////////////////////////////////////////////////////////////////////// /// @brief Split the string around first occurance of sep. /// Three strings will always placed into result. If sep is found, the strings will /// be the text before sep, sep itself, and the remaining text. If sep is /// not found, the original string will be returned with two empty strings. /// PYSTRING_INLINE void partition( const std::string & str, const std::string & sep, std::vector< std::string > & result ); inline std::vector< std::string > partition( const std::string & str, const std::string & sep ) { std::vector< std::string > result; partition( str, sep, result ); return result; } ////////////////////////////////////////////////////////////////////////////////////////////// /// @brief If str starts with prefix return a copy of the string with prefix at the start /// removed otherwise return an unmodified copy of the string. /// PYSTRING_INLINE std::string removeprefix( const std::string & str, const std::string & prefix ); ////////////////////////////////////////////////////////////////////////////////////////////// /// @brief If str ends with suffix return a copy of the string with suffix at the end removed /// otherwise return an unmodified copy of the string. /// PYSTRING_INLINE std::string removesuffix( const std::string & str, const std::string & suffix ); ////////////////////////////////////////////////////////////////////////////////////////////// /// @brief Return a copy of the string with all occurrences of substring old replaced by new. If /// the optional argument count is given, only the first count occurrences are replaced. /// PYSTRING_INLINE std::string replace( const std::string & str, const std::string & oldstr, const std::string & newstr, int count = -1); ////////////////////////////////////////////////////////////////////////////////////////////// /// @brief Return the highest index in the string where substring sub is found, such that sub is /// contained within s[start,end]. Optional arguments start and end are interpreted as in /// slice notation. Return -1 on failure. /// PYSTRING_INLINE int rfind( const std::string & str, const std::string & sub, int start = 0, int end = MAX_32BIT_INT ); ////////////////////////////////////////////////////////////////////////////////////////////// /// @brief Currently a synonym of rfind. The python version raises exceptions. This one currently /// does not /// PYSTRING_INLINE int rindex( const std::string & str, const std::string & sub, int start = 0, int end = MAX_32BIT_INT ); ////////////////////////////////////////////////////////////////////////////////////////////// /// @brief Return the string right justified in a string of length width. Padding is done using /// spaces. The original string is returned if width is less than str.size(). /// PYSTRING_INLINE std::string rjust( const std::string & str, int width); ////////////////////////////////////////////////////////////////////////////////////////////// /// @brief Split the string around last occurance of sep. /// Three strings will always placed into result. If sep is found, the strings will /// be the text before sep, sep itself, and the remaining text. If sep is /// not found, the original string will be returned with two empty strings. /// PYSTRING_INLINE void rpartition( const std::string & str, const std::string & sep, std::vector< std::string > & result ); inline std::vector< std::string > rpartition ( const std::string & str, const std::string & sep ) { std::vector< std::string > result; rpartition( str, sep, result ); return result; } ////////////////////////////////////////////////////////////////////////////////////////////// /// @brief Return a copy of the string with trailing characters removed. If chars is "", whitespace /// characters are removed. If not "", the characters in the string will be stripped from the /// end of the string this method is called on. /// PYSTRING_INLINE std::string rstrip( const std::string & str, const std::string & chars = "" ); ////////////////////////////////////////////////////////////////////////////////////////////// /// @brief Fills the "result" list with the words in the string, using sep as the delimiter string. /// If maxsplit is > -1, at most maxsplit splits are done. If sep is "", /// any whitespace string is a separator. /// PYSTRING_INLINE void split( const std::string & str, std::vector< std::string > & result, const std::string & sep = "", int maxsplit = -1); inline std::vector< std::string > split( const std::string & str, const std::string & sep = "", int maxsplit = -1) { std::vector< std::string > result; split( str, result, sep, maxsplit ); return result; } ////////////////////////////////////////////////////////////////////////////////////////////// /// @brief Fills the "result" list with the words in the string, using sep as the delimiter string. /// Does a number of splits starting at the end of the string, the result still has the /// split strings in their original order. /// If maxsplit is > -1, at most maxsplit splits are done. If sep is "", /// any whitespace string is a separator. /// PYSTRING_INLINE void rsplit( const std::string & str, std::vector< std::string > & result, const std::string & sep = "", int maxsplit = -1); inline std::vector< std::string > rsplit( const std::string & str, const std::string & sep = "", int maxsplit = -1) { std::vector< std::string > result; rsplit( str, result, sep, maxsplit); return result; } ////////////////////////////////////////////////////////////////////////////////////////////// /// @brief Return a list of the lines in the string, breaking at line boundaries. Line breaks /// are not included in the resulting list unless keepends is given and true. /// PYSTRING_INLINE void splitlines( const std::string & str, std::vector< std::string > & result, bool keepends = false ); inline std::vector< std::string > splitlines( const std::string & str, bool keepends = false ) { std::vector< std::string > result; splitlines( str, result, keepends); return result; } ////////////////////////////////////////////////////////////////////////////////////////////// /// @brief Return True if string starts with the prefix, otherwise return False. With optional start, /// test string beginning at that position. With optional end, stop comparing string at that /// position /// PYSTRING_INLINE bool startswith( const std::string & str, const std::string & prefix, int start = 0, int end = MAX_32BIT_INT ); ////////////////////////////////////////////////////////////////////////////////////////////// /// @brief Return a copy of the string with leading and trailing characters removed. If chars is "", /// whitespace characters are removed. If given not "", the characters in the string will be /// stripped from the both ends of the string this method is called on. /// PYSTRING_INLINE std::string strip( const std::string & str, const std::string & chars = "" ); ////////////////////////////////////////////////////////////////////////////////////////////// /// @brief Return a copy of the string with uppercase characters converted to lowercase and vice versa. /// PYSTRING_INLINE std::string swapcase( const std::string & str ); ////////////////////////////////////////////////////////////////////////////////////////////// /// @brief Return a titlecased version of the string: words start with uppercase characters, /// all remaining cased characters are lowercase. /// PYSTRING_INLINE std::string title( const std::string & str ); ////////////////////////////////////////////////////////////////////////////////////////////// /// @brief Return a copy of the string where all characters occurring in the optional argument /// deletechars are removed, and the remaining characters have been mapped through the given /// translation table, which must be a string of length 256. /// PYSTRING_INLINE std::string translate( const std::string & str, const std::string & table, const std::string & deletechars = ""); ////////////////////////////////////////////////////////////////////////////////////////////// /// @brief Return a copy of the string converted to uppercase. /// PYSTRING_INLINE std::string upper( const std::string & str ); ////////////////////////////////////////////////////////////////////////////////////////////// /// @brief Return the numeric string left filled with zeros in a string of length width. The original /// string is returned if width is less than str.size(). /// PYSTRING_INLINE std::string zfill( const std::string & str, int width ); ////////////////////////////////////////////////////////////////////////////////////////////// /// @brief function matching python's slice functionality. /// PYSTRING_INLINE std::string slice( const std::string & str, int start = 0, int end = MAX_32BIT_INT); /// /// @ } /// namespace os { namespace path { // All of the function below have three versions. // Example: // join(...) // join_nt(...) // join_posix(...) // // The regular function dispatches to the other versions - based on the OS // at compile time - to match the result you'd get from the python // interepreter on the same operating system // // Should you want to 'lock off' to a particular version of the string // manipulation across *all* operating systems, use the version with the // _OS you are interested in. I.e., you can use posix style path joining, // even on Windows, with join_posix. // // The naming, (nt, posix) matches the cpython source implementation. ////////////////////////////////////////////////////////////////////////////////////////////// /// @defgroup functions pystring::os::path /// @{ ////////////////////////////////////////////////////////////////////////////////////////////// /// @brief Return the base name of pathname path. This is the second half of the pair returned /// by split(path). Note that the result of this function is different from the Unix basename /// program; where basename for '/foo/bar/' returns 'bar', the basename() function returns an /// empty string (''). PYSTRING_INLINE std::string basename(const std::string & path); PYSTRING_INLINE std::string basename_nt(const std::string & path); PYSTRING_INLINE std::string basename_posix(const std::string & path); ////////////////////////////////////////////////////////////////////////////////////////////// /// @brief Return the directory name of pathname path. This is the first half of the pair /// returned by split(path). PYSTRING_INLINE std::string dirname(const std::string & path); PYSTRING_INLINE std::string dirname_nt(const std::string & path); PYSTRING_INLINE std::string dirname_posix(const std::string & path); ////////////////////////////////////////////////////////////////////////////////////////////// /// @brief Return True if path is an absolute pathname. On Unix, that means it begins with a /// slash, on Windows that it begins with a (back)slash after chopping off a potential drive /// letter. PYSTRING_INLINE bool isabs(const std::string & path); PYSTRING_INLINE bool isabs_nt(const std::string & path); PYSTRING_INLINE bool isabs_posix(const std::string & s); ////////////////////////////////////////////////////////////////////////////////////////////// /// @brief Return a normalized absolutized version of the pathname path. /// /// NOTE: This differs from the interface of the python equivalent in that it requires you /// to pass in the current working directory as an argument. PYSTRING_INLINE std::string abspath(const std::string & path, const std::string & cwd); PYSTRING_INLINE std::string abspath_nt(const std::string & path, const std::string & cwd); PYSTRING_INLINE std::string abspath_posix(const std::string & path, const std::string & cwd); ////////////////////////////////////////////////////////////////////////////////////////////// /// @brief Join one or more path components intelligently. If any component is an absolute /// path, all previous components (on Windows, including the previous drive letter, if there /// was one) are thrown away, and joining continues. The return value is the concatenation of /// path1, and optionally path2, etc., with exactly one directory separator (os.sep) inserted /// between components, unless path2 is empty. Note that on Windows, since there is a current /// directory for each drive, os.path.join("c:", "foo") represents a path relative to the /// current directory on drive C: (c:foo), not c:\foo. /// This dispatches based on the compilation OS PYSTRING_INLINE std::string join(const std::string & path1, const std::string & path2); PYSTRING_INLINE std::string join_nt(const std::string & path1, const std::string & path2); PYSTRING_INLINE std::string join_posix(const std::string & path1, const std::string & path2); PYSTRING_INLINE std::string join(const std::vector< std::string > & paths); PYSTRING_INLINE std::string join_nt(const std::vector< std::string > & paths); PYSTRING_INLINE std::string join_posix(const std::vector< std::string > & paths); ////////////////////////////////////////////////////////////////////////////////////////////// /// @brief Normalize a pathname. This collapses redundant separators and up-level references /// so that A//B, A/B/, A/./B and A/foo/../B all become A/B. It does not normalize the case /// (use normcase() for that). On Windows, it converts forward slashes to backward slashes. /// It should be understood that this may change the meaning of the path if it contains /// symbolic links! PYSTRING_INLINE std::string normpath(const std::string & path); PYSTRING_INLINE std::string normpath_nt(const std::string & path); PYSTRING_INLINE std::string normpath_posix(const std::string & path); ////////////////////////////////////////////////////////////////////////////////////////////// /// @brief Split the pathname path into a pair, (head, tail) where tail is the last pathname /// component and head is everything leading up to that. The tail part will never contain a /// slash; if path ends in a slash, tail will be empty. If there is no slash in path, head /// will be empty. If path is empty, both head and tail are empty. Trailing slashes are /// stripped from head unless it is the root (one or more slashes only). In all cases, /// join(head, tail) returns a path to the same location as path (but the strings may /// differ). PYSTRING_INLINE void split(std::string & head, std::string & tail, const std::string & path); PYSTRING_INLINE void split_nt(std::string & head, std::string & tail, const std::string & path); PYSTRING_INLINE void split_posix(std::string & head, std::string & tail, const std::string & path); ////////////////////////////////////////////////////////////////////////////////////////////// /// @brief Split the pathname path into a pair (drive, tail) where drive is either a drive /// specification or the empty string. On systems which do not use drive specifications, /// drive will always be the empty string. In all cases, drive + tail will be the same as /// path. PYSTRING_INLINE void splitdrive(std::string & drivespec, std::string & pathspec, const std::string & path); PYSTRING_INLINE void splitdrive_nt(std::string & drivespec, std::string & pathspec, const std::string & p); PYSTRING_INLINE void splitdrive_posix(std::string & drivespec, std::string & pathspec, const std::string & path); ////////////////////////////////////////////////////////////////////////////////////////////// /// @brief Split the pathname path into a pair (root, ext) such that root + ext == path, and /// ext is empty or begins with a period and contains at most one period. Leading periods on /// the basename are ignored; splitext('.cshrc') returns ('.cshrc', ''). PYSTRING_INLINE void splitext(std::string & root, std::string & ext, const std::string & path); PYSTRING_INLINE void splitext_nt(std::string & root, std::string & ext, const std::string & path); PYSTRING_INLINE void splitext_posix(std::string & root, std::string & ext, const std::string & path); /// /// @ } /// } // namespace path } // namespace os } // namespace pystring #if PYSTRING_HEADER_ONLY #include "pystring_impl.h" #endif #endif imageworks-pystring-a055353/pystring_impl.h000066400000000000000000001342251514717441600211000ustar00rootroot00000000000000// Copyright Contributors to the Pystring project. // SPDX-License-Identifier: BSD-3-Clause // https://github.com/imageworks/pystring/blob/master/LICENSE #include #include #include #include #include namespace pystring { #if defined(_WIN32) || defined(_WIN64) || defined(_WINDOWS) || defined(_MSC_VER) #ifndef WINDOWS #define WINDOWS #endif #endif // This definition codes from configure.in in the python src. // Strictly speaking this limits us to str sizes of 2**31. // Should we wish to handle this limit, we could use an architecture // specific #defines and read from ssize_t (unistd.h) if the header exists. // But in the meantime, the use of int assures maximum arch compatibility. // This must also equal the size used in the end = MAX_32BIT_INT default arg. typedef int Py_ssize_t; const std::string forward_slash = "/"; const std::string double_forward_slash = "//"; const std::string triple_forward_slash = "///"; const std::string double_back_slash = "\\"; const std::string empty_string = ""; const std::string dot = "."; const std::string double_dot = ".."; const std::string colon = ":"; /* helper macro to fixup start/end slice values */ #define PYSTRING_ADJUST_INDICES(start, end, len) \ if (end > len) \ end = len; \ else if (end < 0) { \ end += len; \ if (end < 0) \ end = 0; \ } \ if (start < 0) { \ start += len; \ if (start < 0) \ start = 0; \ } namespace { ////////////////////////////////////////////////////////////////////////////////////////////// /// why doesn't the std::reverse work? /// void reverse_strings( std::vector< std::string > & result) { for (std::vector< std::string >::size_type i = 0; i < result.size() / 2; i++ ) { std::swap(result[i], result[result.size() - 1 - i]); } } ////////////////////////////////////////////////////////////////////////////////////////////// /// /// void split_whitespace( const std::string & str, std::vector< std::string > & result, int maxsplit ) { std::string::size_type i, j, len = str.size(); for (i = j = 0; i < len; ) { while ( i < len && ::isspace( str[i] ) ) i++; j = i; while ( i < len && ! ::isspace( str[i]) ) i++; if (j < i) { if ( maxsplit-- <= 0 ) break; result.push_back( str.substr( j, i - j )); while ( i < len && ::isspace( str[i])) i++; j = i; } } if (j < len) { result.push_back( str.substr( j, len - j )); } } ////////////////////////////////////////////////////////////////////////////////////////////// /// /// void rsplit_whitespace( const std::string & str, std::vector< std::string > & result, int maxsplit ) { std::string::size_type len = str.size(); std::string::size_type i, j; for (i = j = len; i > 0; ) { while ( i > 0 && ::isspace( str[i - 1] ) ) i--; j = i; while ( i > 0 && ! ::isspace( str[i - 1]) ) i--; if (j > i) { if ( maxsplit-- <= 0 ) break; result.push_back( str.substr( i, j - i )); while ( i > 0 && ::isspace( str[i - 1])) i--; j = i; } } if (j > 0) { result.push_back( str.substr( 0, j )); } //std::reverse( result, result.begin(), result.end() ); reverse_strings( result ); } } //anonymous namespace ////////////////////////////////////////////////////////////////////////////////////////////// /// /// void split( const std::string & str, std::vector< std::string > & result, const std::string & sep, int maxsplit ) { result.clear(); if ( maxsplit < 0 ) maxsplit = MAX_32BIT_INT;//result.max_size(); if ( sep.size() == 0 ) { split_whitespace( str, result, maxsplit ); return; } std::string::size_type i,j, len = str.size(), n = sep.size(); i = j = 0; while ( i+n <= len ) { if ( str[i] == sep[0] && str.substr( i, n ) == sep ) { if ( maxsplit-- <= 0 ) break; result.push_back( str.substr( j, i - j ) ); i = j = i + n; } else { i++; } } result.push_back( str.substr( j, len-j ) ); } ////////////////////////////////////////////////////////////////////////////////////////////// /// /// void rsplit( const std::string & str, std::vector< std::string > & result, const std::string & sep, int maxsplit ) { if ( maxsplit < 0 ) { split( str, result, sep, maxsplit ); return; } result.clear(); if ( sep.size() == 0 ) { rsplit_whitespace( str, result, maxsplit ); return; } Py_ssize_t i,j, len = (Py_ssize_t) str.size(), n = (Py_ssize_t) sep.size(); i = j = len; while ( i >= n ) { if ( str[i - 1] == sep[n - 1] && str.substr( i - n, n ) == sep ) { if ( maxsplit-- <= 0 ) break; result.push_back( str.substr( i, j - i ) ); i = j = i - n; } else { i--; } } result.push_back( str.substr( 0, j ) ); reverse_strings( result ); } ////////////////////////////////////////////////////////////////////////////////////////////// /// /// #define PYSTRING_LEFTSTRIP 0 #define PYSTRING_RIGHTSTRIP 1 #define PYSTRING_BOTHSTRIP 2 ////////////////////////////////////////////////////////////////////////////////////////////// /// /// std::string do_strip( const std::string & str, int striptype, const std::string & chars ) { Py_ssize_t len = (Py_ssize_t) str.size(), i, j, charslen = (Py_ssize_t) chars.size(); if ( charslen == 0 ) { i = 0; if ( striptype != PYSTRING_RIGHTSTRIP ) { while ( i < len && ::isspace( str[i] ) ) { i++; } } j = len; if ( striptype != PYSTRING_LEFTSTRIP ) { do { j--; } while (j >= i && ::isspace(str[j])); j++; } } else { const char * sep = chars.c_str(); i = 0; if ( striptype != PYSTRING_RIGHTSTRIP ) { while ( i < len && memchr(sep, str[i], charslen) ) { i++; } } j = len; if (striptype != PYSTRING_LEFTSTRIP) { do { j--; } while (j >= i && memchr(sep, str[j], charslen) ); j++; } } if ( i == 0 && j == len ) { return str; } else { return str.substr( i, j - i ); } } ////////////////////////////////////////////////////////////////////////////////////////////// /// /// void partition( const std::string & str, const std::string & sep, std::vector< std::string > & result ) { result.resize(3); int index = find( str, sep ); if ( index < 0 ) { result[0] = str; result[1] = empty_string; result[2] = empty_string; } else { result[0] = str.substr( 0, index ); result[1] = sep; result[2] = str.substr( index + sep.size(), str.size() ); } } ////////////////////////////////////////////////////////////////////////////////////////////// /// /// void rpartition( const std::string & str, const std::string & sep, std::vector< std::string > & result ) { result.resize(3); int index = rfind( str, sep ); if ( index < 0 ) { result[0] = empty_string; result[1] = empty_string; result[2] = str; } else { result[0] = str.substr( 0, index ); result[1] = sep; result[2] = str.substr( index + sep.size(), str.size() ); } } ////////////////////////////////////////////////////////////////////////////////////////////// /// /// std::string strip( const std::string & str, const std::string & chars ) { return do_strip( str, PYSTRING_BOTHSTRIP, chars ); } ////////////////////////////////////////////////////////////////////////////////////////////// /// /// std::string lstrip( const std::string & str, const std::string & chars ) { return do_strip( str, PYSTRING_LEFTSTRIP, chars ); } ////////////////////////////////////////////////////////////////////////////////////////////// /// /// std::string rstrip( const std::string & str, const std::string & chars ) { return do_strip( str, PYSTRING_RIGHTSTRIP, chars ); } ////////////////////////////////////////////////////////////////////////////////////////////// /// /// std::string join( const std::string & str, const std::vector< std::string > & seq ) { std::vector< std::string >::size_type seqlen = seq.size(), i; if ( seqlen == 0 ) return empty_string; if ( seqlen == 1 ) return seq[0]; std::string result( seq[0] ); for ( i = 1; i < seqlen; ++i ) { result += str + seq[i]; } return result; } ////////////////////////////////////////////////////////////////////////////////////////////// /// /// namespace { /* Matches the end (direction >= 0) or start (direction < 0) of self * against substr, using the start and end arguments. Returns * -1 on error, 0 if not found and 1 if found. */ int _string_tailmatch(const std::string & self, const std::string & substr, Py_ssize_t start, Py_ssize_t end, int direction) { Py_ssize_t len = (Py_ssize_t) self.size(); Py_ssize_t slen = (Py_ssize_t) substr.size(); const char* sub = substr.c_str(); const char* str = self.c_str(); PYSTRING_ADJUST_INDICES(start, end, len); if (direction < 0) { // startswith if (start+slen > len) return 0; } else { // endswith if (end-start < slen || start > len) return 0; if (end-slen > start) start = end - slen; } if (end-start >= slen) return (!std::memcmp(str+start, sub, slen)); return 0; } } bool endswith( const std::string & str, const std::string & suffix, int start, int end ) { int result = _string_tailmatch(str, suffix, (Py_ssize_t) start, (Py_ssize_t) end, +1); //if (result == -1) // TODO: Error condition return static_cast(result); } bool startswith( const std::string & str, const std::string & prefix, int start, int end ) { int result = _string_tailmatch(str, prefix, (Py_ssize_t) start, (Py_ssize_t) end, -1); //if (result == -1) // TODO: Error condition return static_cast(result); } ////////////////////////////////////////////////////////////////////////////////////////////// /// /// bool isalnum( const std::string & str ) { std::string::size_type len = str.size(), i; if ( len == 0 ) return false; if( len == 1 ) { return ::isalnum( str[0] ); } for ( i = 0; i < len; ++i ) { if ( !::isalnum( str[i] ) ) return false; } return true; } ////////////////////////////////////////////////////////////////////////////////////////////// /// /// bool isalpha( const std::string & str ) { std::string::size_type len = str.size(), i; if ( len == 0 ) return false; if( len == 1 ) return ::isalpha( (int) str[0] ); for ( i = 0; i < len; ++i ) { if ( !::isalpha( (int) str[i] ) ) return false; } return true; } ////////////////////////////////////////////////////////////////////////////////////////////// /// /// bool isdigit( const std::string & str ) { std::string::size_type len = str.size(), i; if ( len == 0 ) return false; if( len == 1 ) return ::isdigit( str[0] ); for ( i = 0; i < len; ++i ) { if ( ! ::isdigit( str[i] ) ) return false; } return true; } ////////////////////////////////////////////////////////////////////////////////////////////// /// /// bool islower( const std::string & str ) { std::string::size_type len = str.size(), i; if ( len == 0 ) return false; if( len == 1 ) return ::islower( str[0] ); // python's islower is a lot more leniant than c++ // this will match the python behavior so that something like // islower("hello123") is true bool has_cased = false; for (i = 0; i < len; ++i) { if (::islower(str[i])) has_cased = true; else if (::isupper(str[i])) return false; } return has_cased; } ////////////////////////////////////////////////////////////////////////////////////////////// /// /// bool isspace( const std::string & str ) { std::string::size_type len = str.size(), i; if ( len == 0 ) return false; if( len == 1 ) return ::isspace( str[0] ); for ( i = 0; i < len; ++i ) { if ( !::isspace( str[i] ) ) return false; } return true; } ////////////////////////////////////////////////////////////////////////////////////////////// /// /// bool istitle( const std::string & str ) { std::string::size_type len = str.size(), i; if ( len == 0 ) return false; if ( len == 1 ) return ::isupper( str[0] ); bool cased = false, previous_is_cased = false; for ( i = 0; i < len; ++i ) { if ( ::isupper( str[i] ) ) { if ( previous_is_cased ) { return false; } previous_is_cased = true; cased = true; } else if ( ::islower( str[i] ) ) { if (!previous_is_cased) { return false; } previous_is_cased = true; cased = true; } else { previous_is_cased = false; } } return cased; } ////////////////////////////////////////////////////////////////////////////////////////////// /// /// bool isupper( const std::string & str ) { std::string::size_type len = str.size(), i; if ( len == 0 ) return false; if( len == 1 ) return ::isupper( str[0] ); // python's isupper is a lot more leniant than c++ // this will match the python behavior so that something like // isupper("HELLO123") is true bool has_cased = false; for (std::string::size_type i = 0; i < str.size(); ++i) { if (::isupper(str[i])) has_cased = true; else if (::islower(str[i])) return false; } return has_cased; } ////////////////////////////////////////////////////////////////////////////////////////////// /// /// std::string capitalize( const std::string & str ) { std::string s( str ); std::string::size_type len = s.size(), i; if ( len > 0) { if (::islower(s[0])) s[0] = (char) ::toupper( s[0] ); } for ( i = 1; i < len; ++i ) { if (::isupper(s[i])) s[i] = (char) ::tolower( s[i] ); } return s; } ////////////////////////////////////////////////////////////////////////////////////////////// /// /// std::string lower( const std::string & str ) { std::string s( str ); std::string::size_type len = s.size(), i; for ( i = 0; i < len; ++i ) { if ( ::isupper( s[i] ) ) s[i] = (char) ::tolower( s[i] ); } return s; } ////////////////////////////////////////////////////////////////////////////////////////////// /// /// std::string upper( const std::string & str ) { std::string s( str ) ; std::string::size_type len = s.size(), i; for ( i = 0; i < len; ++i ) { if ( ::islower( s[i] ) ) s[i] = (char) ::toupper( s[i] ); } return s; } ////////////////////////////////////////////////////////////////////////////////////////////// /// /// std::string swapcase( const std::string & str ) { std::string s( str ); std::string::size_type len = s.size(), i; for ( i = 0; i < len; ++i ) { if ( ::islower( s[i] ) ) s[i] = (char) ::toupper( s[i] ); else if (::isupper( s[i] ) ) s[i] = (char) ::tolower( s[i] ); } return s; } ////////////////////////////////////////////////////////////////////////////////////////////// /// /// std::string title( const std::string & str ) { std::string s( str ); std::string::size_type len = s.size(), i; bool previous_is_cased = false; for ( i = 0; i < len; ++i ) { int c = s[i]; if ( ::islower(c) ) { if ( !previous_is_cased ) { s[i] = (char) ::toupper(c); } previous_is_cased = true; } else if ( ::isupper(c) ) { if ( previous_is_cased ) { s[i] = (char) ::tolower(c); } previous_is_cased = true; } else { previous_is_cased = false; } } return s; } ////////////////////////////////////////////////////////////////////////////////////////////// /// /// std::string translate( const std::string & str, const std::string & table, const std::string & deletechars ) { std::string s; std::string::size_type len = str.size(), dellen = deletechars.size(); if ( table.size() != 256 ) { // TODO : raise exception instead return str; } //if nothing is deleted, use faster code if ( dellen == 0 ) { s = str; for ( std::string::size_type i = 0; i < len; ++i ) { s[i] = table[ s[i] ]; } return s; } int trans_table[256]; for ( int i = 0; i < 256; i++) { trans_table[i] = table[i]; } for ( std::string::size_type i = 0; i < dellen; i++) { trans_table[(int) deletechars[i] ] = -1; } for ( std::string::size_type i = 0; i < len; ++i ) { if ( trans_table[ (int) str[i] ] != -1 ) { s += table[ str[i] ]; } } return s; } ////////////////////////////////////////////////////////////////////////////////////////////// /// /// std::string zfill( const std::string & str, int width ) { int len = (int)str.size(); if ( len >= width ) { return str; } std::string s( str ); int fill = width - len; s = std::string( fill, '0' ) + s; if ( s[fill] == '+' || s[fill] == '-' ) { s[0] = s[fill]; s[fill] = '0'; } return s; } ////////////////////////////////////////////////////////////////////////////////////////////// /// /// std::string ljust( const std::string & str, int width ) { std::string::size_type len = str.size(); if ( (( int ) len ) >= width ) return str; return str + std::string( width - len, ' ' ); } ////////////////////////////////////////////////////////////////////////////////////////////// /// /// std::string rjust( const std::string & str, int width ) { std::string::size_type len = str.size(); if ( (( int ) len ) >= width ) return str; return std::string( width - len, ' ' ) + str; } ////////////////////////////////////////////////////////////////////////////////////////////// /// /// std::string center( const std::string & str, int width ) { int len = (int) str.size(); int marg, left; if ( len >= width ) return str; marg = width - len; left = marg / 2 + (marg & width & 1); return std::string( left, ' ' ) + str + std::string( marg - left, ' ' ); } ////////////////////////////////////////////////////////////////////////////////////////////// /// /// std::string slice( const std::string & str, int start, int end ) { PYSTRING_ADJUST_INDICES(start, end, (int) str.size()); if ( start >= end ) return empty_string; return str.substr( start, end - start ); } ////////////////////////////////////////////////////////////////////////////////////////////// /// /// int find( const std::string & str, const std::string & sub, int start, int end ) { PYSTRING_ADJUST_INDICES(start, end, (int) str.size()); std::string::size_type result = str.find( sub, start ); // If we cannot find the string, or if the end-point of our found substring is past // the allowed end limit, return that it can't be found. if( result == std::string::npos || (result + sub.size() > (std::string::size_type)end) ) { return -1; } return (int) result; } ////////////////////////////////////////////////////////////////////////////////////////////// /// /// int index( const std::string & str, const std::string & sub, int start, int end ) { return find( str, sub, start, end ); } ////////////////////////////////////////////////////////////////////////////////////////////// /// /// int rfind( const std::string & str, const std::string & sub, int start, int end ) { PYSTRING_ADJUST_INDICES(start, end, (int) str.size()); std::string::size_type result = str.rfind( sub, end ); if( result == std::string::npos || result < (std::string::size_type)start || (result + sub.size() > (std::string::size_type)end)) return -1; return (int)result; } ////////////////////////////////////////////////////////////////////////////////////////////// /// /// int rindex( const std::string & str, const std::string & sub, int start, int end ) { return rfind( str, sub, start, end ); } ////////////////////////////////////////////////////////////////////////////////////////////// /// /// std::string expandtabs( const std::string & str, int tabsize ) { std::string s( str ); std::string::size_type len = str.size(), i = 0; int offset = 0; int j = 0; for ( i = 0; i < len; ++i ) { if ( str[i] == '\t' ) { if ( tabsize > 0 ) { int fillsize = tabsize - (j % tabsize); j += fillsize; s.replace( i + offset, 1, std::string( fillsize, ' ' )); offset += fillsize - 1; } else { s.replace( i + offset, 1, empty_string ); offset -= 1; } } else { j++; if (str[i] == '\n' || str[i] == '\r') { j = 0; } } } return s; } ////////////////////////////////////////////////////////////////////////////////////////////// /// /// int count( const std::string & str, const std::string & substr, int start, int end ) { int nummatches = 0; int cursor = start; // special handling for an empty substring // this will match python's behavior of // "bob".count("") == 4 // "".count("") == 1 if ( substr.empty() ) { PYSTRING_ADJUST_INDICES(start, end, (int)str.size()); return end - start + 1; } while ( 1 ) { cursor = find( str, substr, cursor, end ); if ( cursor < 0 ) break; cursor += (int) substr.size(); nummatches += 1; } return nummatches; } ////////////////////////////////////////////////////////////////////////////////////////////// /// /// std::string replace( const std::string & str, const std::string & oldstr, const std::string & newstr, int count ) { int sofar = 0; int cursor = 0; std::string s( str ); std::string::size_type oldlen = oldstr.size(), newlen = newstr.size(); cursor = find( s, oldstr, cursor ); while ( cursor != -1 && cursor <= (int)s.size() ) { if ( count > -1 && sofar >= count ) { break; } s.replace( cursor, oldlen, newstr ); cursor += (int) newlen; if ( oldlen != 0) { cursor = find( s, oldstr, cursor ); } else { ++cursor; } ++sofar; } return s; } ////////////////////////////////////////////////////////////////////////////////////////////// /// /// void splitlines( const std::string & str, std::vector< std::string > & result, bool keepends ) { result.clear(); std::string::size_type len = str.size(), i, j, eol; for (i = j = 0; i < len; ) { while (i < len && str[i] != '\n' && str[i] != '\r') i++; eol = i; if (i < len) { if (str[i] == '\r' && i + 1 < len && str[i+1] == '\n') { i += 2; } else { i++; } if (keepends) eol = i; } result.push_back( str.substr( j, eol - j ) ); j = i; } if (j < len) { result.push_back( str.substr( j, len - j ) ); } } ////////////////////////////////////////////////////////////////////////////////////////////// /// /// std::string mul( const std::string & str, int n ) { // Early exits if (n <= 0) return empty_string; if (n == 1) return str; std::ostringstream os; for(int i=0; i= 2 && p[1] == ':') { std::string path = p; // In case drivespec == p drivespec = pystring::slice(path, 0, 2); pathspec = pystring::slice(path, 2); } else { drivespec = empty_string; pathspec = p; } } // On Posix, drive is always empty void splitdrive_posix(std::string & drivespec, std::string & pathspec, const std::string & path) { drivespec = empty_string; pathspec = path; } void splitdrive(std::string & drivespec, std::string & pathspec, const std::string & path) { #ifdef WINDOWS return splitdrive_nt(drivespec, pathspec, path); #else return splitdrive_posix(drivespec, pathspec, path); #endif } ////////////////////////////////////////////////////////////////////////////////////////////// /// /// // Test whether a path is absolute // In windows, if the character to the right of the colon // is a forward or backslash it's absolute. bool isabs_nt(const std::string & path) { std::string drivespec, pathspec; splitdrive_nt(drivespec, pathspec, path); if(pathspec.empty()) return false; return ((pathspec[0] == '/') || (pathspec[0] == '\\')); } bool isabs_posix(const std::string & s) { return pystring::startswith(s, forward_slash); } bool isabs(const std::string & path) { #ifdef WINDOWS return isabs_nt(path); #else return isabs_posix(path); #endif } ////////////////////////////////////////////////////////////////////////////////////////////// /// /// std::string abspath_nt(const std::string & path, const std::string & cwd) { std::string p = path; if(!isabs_nt(p)) p = join_nt(cwd, p); return normpath_nt(p); } std::string abspath_posix(const std::string & path, const std::string & cwd) { std::string p = path; if(!isabs_posix(p)) p = join_posix(cwd, p); return normpath_posix(p); } std::string abspath(const std::string & path, const std::string & cwd) { #ifdef WINDOWS return abspath_nt(path, cwd); #else return abspath_posix(path, cwd); #endif } ////////////////////////////////////////////////////////////////////////////////////////////// /// /// std::string join_nt(const std::vector< std::string > & paths) { if(paths.empty()) return empty_string; if(paths.size() == 1) return paths[0]; std::string path = paths[0]; for(unsigned int i=1; i= 2 && path[1] != ':') || (b.size() >= 2 && b[1] == ':')) { // Path doesnt start with a drive letter b_nts = true; } // Else path has a drive letter, and b doesn't but is absolute. else if((path.size()>3) || ((path.size()==3) && !pystring::endswith(path, forward_slash) && !pystring::endswith(path, double_back_slash))) { b_nts = true; } } if(b_nts) { path = b; } else { // Join, and ensure there's a separator. // assert len(path) > 0 if( pystring::endswith(path, forward_slash) || pystring::endswith(path, double_back_slash)) { if(pystring::startswith(b,forward_slash) || pystring::startswith(b,double_back_slash)) { path += pystring::slice(b, 1); } else { path += b; } } else if(pystring::endswith(path, colon)) { path += b; } else if(!b.empty()) { if(pystring::startswith(b, forward_slash) || pystring::startswith(b,double_back_slash)) { path += b; } else { path += double_back_slash + b; } } else { // path is not empty and does not end with a backslash, // but b is empty; since, e.g., split('a/') produces // ('a', ''), it's best if join() adds a backslash in // this case. path += double_back_slash; } } } return path; } // Join two or more pathname components, inserting double_back_slash as needed. std::string join_nt(const std::string & a, const std::string & b) { std::vector< std::string > paths(2); paths[0] = a; paths[1] = b; return join_nt(paths); } // Join pathnames. // If any component is an absolute path, all previous path components // will be discarded. // Ignore the previous parts if a part is absolute. // Insert a '/' unless the first part is empty or already ends in '/'. std::string join_posix(const std::vector< std::string > & paths) { if(paths.empty()) return empty_string; if(paths.size() == 1) return paths[0]; std::string path = paths[0]; for(unsigned int i=1; i paths(2); paths[0] = a; paths[1] = b; return join_posix(paths); } std::string join(const std::string & path1, const std::string & path2) { #ifdef WINDOWS return join_nt(path1, path2); #else return join_posix(path1, path2); #endif } std::string join(const std::vector< std::string > & paths) { #ifdef WINDOWS return join_nt(paths); #else return join_posix(paths); #endif } ////////////////////////////////////////////////////////////////////////////////////////////// /// /// // Split a pathname. // Return (head, tail) where tail is everything after the final slash. // Either part may be empty void split_nt(std::string & head, std::string & tail, const std::string & path) { std::string d, p; splitdrive_nt(d, p, path); // set i to index beyond p's last slash int i = (int)p.size(); // walk back to find the index of the first slash from the end while(i>0 && (p[i-1] != '\\') && (p[i-1] != '/')) { i = i - 1; } head = pystring::slice(p,0,i); tail = pystring::slice(p,i); // now tail has no slashes // remove trailing slashes from head, unless it's all slashes std::string head2 = head; while(!head2.empty() && ((pystring::slice(head2,-1) == forward_slash) || (pystring::slice(head2,-1) == double_back_slash))) { head2 = pystring::slice(head2,0,-1); } if(!head2.empty()) head = head2; head = d + head; } // Split a path in head (everything up to the last '/') and tail (the // rest). If the path ends in '/', tail will be empty. If there is no // '/' in the path, head will be empty. // Trailing '/'es are stripped from head unless it is the root. void split_posix(std::string & head, std::string & tail, const std::string & p) { int i = pystring::rfind(p, forward_slash) + 1; head = pystring::slice(p,0,i); tail = pystring::slice(p,i); if(!head.empty() && (head != pystring::mul(forward_slash, (int) head.size()))) { head = pystring::rstrip(head, forward_slash); } } void split(std::string & head, std::string & tail, const std::string & path) { #ifdef WINDOWS return split_nt(head, tail, path); #else return split_posix(head, tail, path); #endif } ////////////////////////////////////////////////////////////////////////////////////////////// /// /// std::string basename_nt(const std::string & path) { std::string head, tail; split_nt(head, tail, path); return tail; } std::string basename_posix(const std::string & path) { std::string head, tail; split_posix(head, tail, path); return tail; } std::string basename(const std::string & path) { #ifdef WINDOWS return basename_nt(path); #else return basename_posix(path); #endif } std::string dirname_nt(const std::string & path) { std::string head, tail; split_nt(head, tail, path); return head; } std::string dirname_posix(const std::string & path) { std::string head, tail; split_posix(head, tail, path); return head; } std::string dirname(const std::string & path) { #ifdef WINDOWS return dirname_nt(path); #else return dirname_posix(path); #endif } ////////////////////////////////////////////////////////////////////////////////////////////// /// /// // Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A\B. std::string normpath_nt(const std::string & p) { std::string path = p; path = pystring::replace(path, forward_slash,double_back_slash); std::string prefix; splitdrive_nt(prefix, path, path); // We need to be careful here. If the prefix is empty, and the path starts // with a backslash, it could either be an absolute path on the current // drive (\dir1\dir2\file) or a UNC filename (\\server\mount\dir1\file). It // is therefore imperative NOT to collapse multiple backslashes blindly in // that case. // The code below preserves multiple backslashes when there is no drive // letter. This means that the invalid filename \\\a\b is preserved // unchanged, where a\\\b is normalised to a\b. It's not clear that there // is any better behaviour for such edge cases. if(prefix.empty()) { // No drive letter - preserve initial backslashes while(pystring::slice(path,0,1) == double_back_slash) { prefix = prefix + double_back_slash; path = pystring::slice(path,1); } } else { // We have a drive letter - collapse initial backslashes if(pystring::startswith(path, double_back_slash)) { prefix = prefix + double_back_slash; path = pystring::lstrip(path, double_back_slash); } } std::vector comps; pystring::split(path, comps, double_back_slash); int i = 0; while(i<(int)comps.size()) { if(comps[i].empty() || comps[i] == dot) { comps.erase(comps.begin()+i); } else if(comps[i] == double_dot) { if(i>0 && comps[i-1] != double_dot) { comps.erase(comps.begin()+i-1, comps.begin()+i+1); i -= 1; } else if(i == 0 && pystring::endswith(prefix, double_back_slash)) { comps.erase(comps.begin()+i); } else { i += 1; } } else { i += 1; } } // If the path is now empty, substitute '.' if(prefix.empty() && comps.empty()) { comps.push_back(dot); } return prefix + pystring::join(double_back_slash, comps); } // Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A/B. // It should be understood that this may change the meaning of the path // if it contains symbolic links! // Normalize path, eliminating double slashes, etc. std::string normpath_posix(const std::string & p) { if(p.empty()) return dot; std::string path = p; int initial_slashes = pystring::startswith(path, forward_slash) ? 1 : 0; // POSIX allows one or two initial slashes, but treats three or more // as single slash. if (initial_slashes && pystring::startswith(path, double_forward_slash) && !pystring::startswith(path, triple_forward_slash)) initial_slashes = 2; std::vector comps, new_comps; pystring::split(path, comps, forward_slash); for(unsigned int i=0; i 0) path = pystring::mul(forward_slash, initial_slashes) + path; if(path.empty()) return dot; return path; } std::string normpath(const std::string & path) { #ifdef WINDOWS return normpath_nt(path); #else return normpath_posix(path); #endif } ////////////////////////////////////////////////////////////////////////////////////////////// /// /// // Split the extension from a pathname. // Extension is everything from the last dot to the end, ignoring // leading dots. Returns "(root, ext)"; ext may be empty. // It is always true that root + ext == p void splitext_generic(std::string & root, std::string & ext, const std::string & p, const std::string & sep, const std::string & altsep, const std::string & extsep) { int sepIndex = pystring::rfind(p, sep); if(!altsep.empty()) { int altsepIndex = pystring::rfind(p, altsep); sepIndex = std::max(sepIndex, altsepIndex); } int dotIndex = pystring::rfind(p, extsep); if(dotIndex > sepIndex) { // Skip all leading dots int filenameIndex = sepIndex + 1; while(filenameIndex < dotIndex) { if(pystring::slice(p,filenameIndex) != extsep) { root = pystring::slice(p, 0, dotIndex); ext = pystring::slice(p, dotIndex); return; } filenameIndex += 1; } } root = p; ext = empty_string; } void splitext_nt(std::string & root, std::string & ext, const std::string & path) { return splitext_generic(root, ext, path, double_back_slash, forward_slash, dot); } void splitext_posix(std::string & root, std::string & ext, const std::string & path) { return splitext_generic(root, ext, path, forward_slash, empty_string, dot); } void splitext(std::string & root, std::string & ext, const std::string & path) { #ifdef WINDOWS return splitext_nt(root, ext, path); #else return splitext_posix(root, ext, path); #endif } } // namespace path } // namespace os }//namespace pystring imageworks-pystring-a055353/test.cpp000066400000000000000000001467041514717441600175170ustar00rootroot00000000000000// Copyright Contributors to the Pystring project. // SPDX-License-Identifier: BSD-3-Clause // https://github.com/imageworks/pystring/blob/master/LICENSE #include #include "pystring.h" #include "unittest.h" // Helper wrappers (for pystring functions that don't have direct return values) namespace { static std::vector pystring_split(const std::string& s, int maxsplit) { std::vector r; pystring::split(s, r, "", maxsplit); return r; } static std::vector pystring_split(const std::string& s, const std::string& sep, int maxsplit) { std::vector r; pystring::split(s, r, sep, maxsplit); return r; } static std::vector pystring_rsplit(const std::string& s, int maxsplit) { std::vector r; pystring::rsplit(s, r, "", maxsplit); return r; } static std::vector pystring_rsplit(const std::string& s, const std::string& sep, int maxsplit) { std::vector r; pystring::rsplit(s, r, sep, maxsplit); return r; } static std::vector pystring_partition(const std::string& s, const std::string& sep) { std::vector r; pystring::partition(s, sep, r); return r; } static std::vector pystring_rpartition(const std::string& s, const std::string& sep) { std::vector r; pystring::rpartition(s, sep, r); return r; } static std::vector pystring_splitlines(const std::string& s, bool keepends) { std::vector r; pystring::splitlines(s, r, keepends); return r; } } // namespace PYSTRING_TEST_APP(PyStringUnitTests) PYSTRING_ADD_TEST(pystring, endswith) { PYSTRING_CHECK_EQUAL(pystring::endswith("", ""), true); PYSTRING_CHECK_EQUAL(pystring::endswith("", "a"), false); PYSTRING_CHECK_EQUAL(pystring::endswith("a", ""), true); PYSTRING_CHECK_EQUAL(pystring::endswith("", ".mesh"), false); PYSTRING_CHECK_EQUAL(pystring::endswith("help", ".mesh"), false); PYSTRING_CHECK_EQUAL(pystring::endswith("help", ".mesh", 0), false); PYSTRING_CHECK_EQUAL(pystring::endswith("help", ".mesh", 1), false); PYSTRING_CHECK_EQUAL(pystring::endswith("help", ".mesh", 1, 2), false); PYSTRING_CHECK_EQUAL(pystring::endswith("help", ".mesh", 1, 1), false); PYSTRING_CHECK_EQUAL(pystring::endswith("help", ".mesh", 1, -1), false); PYSTRING_CHECK_EQUAL(pystring::endswith("help", ".mesh", -1), false); PYSTRING_CHECK_EQUAL(pystring::endswith(".mesh", ".mesh"), true); PYSTRING_CHECK_EQUAL(pystring::endswith("a.mesh", ".mesh"), true); PYSTRING_CHECK_EQUAL(pystring::endswith("a.", "."), true); PYSTRING_CHECK_EQUAL(pystring::endswith("abcdef", "ef"), true); PYSTRING_CHECK_EQUAL(pystring::endswith("abcdef", "cdef"), true); PYSTRING_CHECK_EQUAL(pystring::endswith("abcdef", "cdef", 2), true); PYSTRING_CHECK_EQUAL(pystring::endswith("abcdef", "cdef", 3), false); PYSTRING_CHECK_EQUAL(pystring::endswith("abcdef", "cdef", 2, 3), false); PYSTRING_CHECK_EQUAL(pystring::endswith("abcdef", "cdef", -10), true); } PYSTRING_ADD_TEST(pystring, isupper) { PYSTRING_CHECK_EQUAL(pystring::isupper("ABC"), true); PYSTRING_CHECK_EQUAL(pystring::isupper("abc"), false); PYSTRING_CHECK_EQUAL(pystring::isupper("ABc"), false); PYSTRING_CHECK_EQUAL(pystring::isupper("AB-C"), true); PYSTRING_CHECK_EQUAL(pystring::isupper("HELLO 123"), true); PYSTRING_CHECK_EQUAL(pystring::isupper("123"), false); PYSTRING_CHECK_EQUAL(pystring::isupper("HELLO!"), true); } PYSTRING_ADD_TEST(pystring, find) { PYSTRING_CHECK_EQUAL(pystring::find("", ""), 0); PYSTRING_CHECK_EQUAL(pystring::find("", "a"), -1); PYSTRING_CHECK_EQUAL(pystring::find("a", ""), 0); PYSTRING_CHECK_EQUAL(pystring::find("a", "a"), 0); PYSTRING_CHECK_EQUAL(pystring::find("abcdef", ""), 0); PYSTRING_CHECK_EQUAL(pystring::find("abcdef", "", -1), 5); PYSTRING_CHECK_EQUAL(pystring::find("abcdef", "", -2), 4); PYSTRING_CHECK_EQUAL(pystring::find("abcdef", "", -5), 1); PYSTRING_CHECK_EQUAL(pystring::find("abcdef", "", -6), 0); PYSTRING_CHECK_EQUAL(pystring::find("abcdef", "", -7), 0); PYSTRING_CHECK_EQUAL(pystring::find("abcdef", "def"), 3); PYSTRING_CHECK_EQUAL(pystring::find("abcdef", "def", 3), 3); PYSTRING_CHECK_EQUAL(pystring::find("abcdef", "def", 4), -1); PYSTRING_CHECK_EQUAL(pystring::find("abcdef", "def", -5), 3); PYSTRING_CHECK_EQUAL(pystring::find("abcdef", "def", -1), -1); PYSTRING_CHECK_EQUAL(pystring::find("abcabcabc", "bc", -2), 7); PYSTRING_CHECK_EQUAL(pystring::find("abcabcabc", "bc", -1), -1); PYSTRING_CHECK_EQUAL(pystring::find("abcabcabc", "bc", 0), 1); PYSTRING_CHECK_EQUAL(pystring::find("abcabcabc", "bc", 1), 1); PYSTRING_CHECK_EQUAL(pystring::find("abcabcabc", "bc", 2), 4); PYSTRING_CHECK_EQUAL(pystring::find("abcabcabc", "bc", 4), 4); PYSTRING_CHECK_EQUAL(pystring::find("abcabcabc", "bc", 7), 7); PYSTRING_CHECK_EQUAL(pystring::find("abcabcabc", "bc", 4, 3), -1); PYSTRING_CHECK_EQUAL(pystring::find("abcabcabc", "bc", 4, 4), -1); PYSTRING_CHECK_EQUAL(pystring::find("abcabcabc", "bc", 4, 5), -1); PYSTRING_CHECK_EQUAL(pystring::find("abcabcabc", "bc", 4, -1), 4); PYSTRING_CHECK_EQUAL(pystring::find("abcabcabc", "bc", 4, 6), 4); } PYSTRING_ADD_TEST(pystring, rfind) { PYSTRING_CHECK_EQUAL(pystring::rfind("", ""), 0); PYSTRING_CHECK_EQUAL(pystring::rfind("", "a"), -1); PYSTRING_CHECK_EQUAL(pystring::rfind("a", ""), 1); PYSTRING_CHECK_EQUAL(pystring::rfind("a", "a"), 0); PYSTRING_CHECK_EQUAL(pystring::rfind("abcdef", ""), 6); PYSTRING_CHECK_EQUAL(pystring::rfind("abcdef", "", 0, 1), 1); PYSTRING_CHECK_EQUAL(pystring::rfind("abcdef", "", 0, 5), 5); PYSTRING_CHECK_EQUAL(pystring::rfind("abcdef", "", 0,-1), 5); PYSTRING_CHECK_EQUAL(pystring::rfind("abcdef", "", 0,-3), 3); PYSTRING_CHECK_EQUAL(pystring::rfind("abcdef", "def"), 3); PYSTRING_CHECK_EQUAL(pystring::rfind("abcdef", "def", 3), 3); PYSTRING_CHECK_EQUAL(pystring::rfind("abcdef", "def", 4), -1); PYSTRING_CHECK_EQUAL(pystring::rfind("abcdef", "def", -5), 3); PYSTRING_CHECK_EQUAL(pystring::rfind("abcdef", "def", -1), -1); PYSTRING_CHECK_EQUAL(pystring::rfind("abcabcabc", "bc", -2), 7); PYSTRING_CHECK_EQUAL(pystring::rfind("abcabcabc", "bc", -1), -1); PYSTRING_CHECK_EQUAL(pystring::rfind("abcabcabc", "bc", 0), 7); PYSTRING_CHECK_EQUAL(pystring::rfind("abcabcabc", "bc", 1), 7); PYSTRING_CHECK_EQUAL(pystring::rfind("abcabcabc", "bc", 4), 7); PYSTRING_CHECK_EQUAL(pystring::rfind("abcabcabc", "bc", 7), 7); PYSTRING_CHECK_EQUAL(pystring::rfind("abcabcabc", "bc", 4, -5), -1); PYSTRING_CHECK_EQUAL(pystring::rfind("abcabcabc", "bc", 4, -10), -1); PYSTRING_CHECK_EQUAL(pystring::rfind("abcabcabc", "bc", 4, 20), 7); PYSTRING_CHECK_EQUAL(pystring::rfind("abcabcabc", "abc", 6, 8), -1); } PYSTRING_ADD_TEST(pystring, removeprefix) { PYSTRING_CHECK_EQUAL(pystring::removeprefix("abcdef", "abc"), "def"); PYSTRING_CHECK_EQUAL(pystring::removeprefix("abcdef", "bcd"), "abcdef"); } PYSTRING_ADD_TEST(pystring, removesuffix) { PYSTRING_CHECK_EQUAL(pystring::removesuffix("abcdef", "def"), "abc"); PYSTRING_CHECK_EQUAL(pystring::removesuffix("abcdef", "cde"), "abcdef"); } PYSTRING_ADD_TEST(pystring, replace) { PYSTRING_CHECK_EQUAL(pystring::replace("abcdef", "foo", "bar"), "abcdef"); PYSTRING_CHECK_EQUAL(pystring::replace("abcdef", "ab", "cd"), "cdcdef"); PYSTRING_CHECK_EQUAL(pystring::replace("abcdef", "ab", ""), "cdef"); PYSTRING_CHECK_EQUAL(pystring::replace("abcabc", "ab", ""), "cc"); PYSTRING_CHECK_EQUAL(pystring::replace("abcdef", "", ""), "abcdef"); PYSTRING_CHECK_EQUAL(pystring::replace("abcdef", "", "."), ".a.b.c.d.e.f."); } PYSTRING_ADD_TEST(pystring, slice) { PYSTRING_CHECK_EQUAL(pystring::slice(""), ""); PYSTRING_CHECK_EQUAL(pystring::slice("", 1), ""); PYSTRING_CHECK_EQUAL(pystring::slice("", -1), ""); PYSTRING_CHECK_EQUAL(pystring::slice("", -1,2), ""); PYSTRING_CHECK_EQUAL(pystring::slice("abcdef"), "abcdef"); PYSTRING_CHECK_EQUAL(pystring::slice("abcdef",0), "abcdef"); PYSTRING_CHECK_EQUAL(pystring::slice("abcdef",1), "bcdef"); PYSTRING_CHECK_EQUAL(pystring::slice("abcdef",2), "cdef"); PYSTRING_CHECK_EQUAL(pystring::slice("abcdef",2,2), ""); PYSTRING_CHECK_EQUAL(pystring::slice("abcdef",2,3), "c"); PYSTRING_CHECK_EQUAL(pystring::slice("abcdef",2,1), ""); PYSTRING_CHECK_EQUAL(pystring::slice("abcdef",2,-1), "cde"); PYSTRING_CHECK_EQUAL(pystring::slice("abcdef",2,-2), "cd"); PYSTRING_CHECK_EQUAL(pystring::slice("abcdef",2,-3), "c"); PYSTRING_CHECK_EQUAL(pystring::slice("abcdef",2,-4), ""); PYSTRING_CHECK_EQUAL(pystring::slice("abcdef",2,-5), ""); PYSTRING_CHECK_EQUAL(pystring::slice("abcdef",-1), "f"); PYSTRING_CHECK_EQUAL(pystring::slice("abcdef",-2), "ef"); PYSTRING_CHECK_EQUAL(pystring::slice("abcdef",-99), "abcdef"); PYSTRING_CHECK_EQUAL(pystring::slice("abcdef",-99,-98), ""); PYSTRING_CHECK_EQUAL(pystring::slice("abcdef",-2, 3), ""); PYSTRING_CHECK_EQUAL(pystring::slice("abcdef",-2, 10), "ef"); PYSTRING_CHECK_EQUAL(pystring::slice("abcdef",-1), "f"); PYSTRING_CHECK_EQUAL(pystring::slice("abcdef",0,-1), "abcde"); } PYSTRING_ADD_TEST(pystring, split) { std::vector< std::string > result; { result.clear(); pystring::split("", result, "/", 1); PYSTRING_CHECK_EQUAL(result.size(), 1); if(result.size()==1) { PYSTRING_CHECK_EQUAL(result[0], ""); } } { result.clear(); pystring::split("/", result, "/", 1); PYSTRING_CHECK_EQUAL(result.size(), 2); if(result.size()==2) { PYSTRING_CHECK_EQUAL(result[0], ""); PYSTRING_CHECK_EQUAL(result[1], ""); } } { result.clear(); pystring::split(" ", result, " ", 1); PYSTRING_CHECK_EQUAL(result.size(), 2); if(result.size()==2) { PYSTRING_CHECK_EQUAL(result[0], ""); PYSTRING_CHECK_EQUAL(result[1], ""); } } { result.clear(); pystring::split(" /", result, "/", 1); PYSTRING_CHECK_EQUAL(result.size(), 2); if(result.size()==2) { PYSTRING_CHECK_EQUAL(result[0], " "); PYSTRING_CHECK_EQUAL(result[1], ""); } } { result.clear(); pystring::split(" //", result, "/", 1); PYSTRING_CHECK_EQUAL(result.size(), 2); if(result.size()==2) { PYSTRING_CHECK_EQUAL(result[0], " "); PYSTRING_CHECK_EQUAL(result[1], "/"); } } { result.clear(); pystring::split("a ", result, " ", 1); PYSTRING_CHECK_EQUAL(result.size(), 2); if(result.size()==2) { PYSTRING_CHECK_EQUAL(result[0], "a"); PYSTRING_CHECK_EQUAL(result[1], " "); } } { result.clear(); pystring::split("//as//rew//gdf", result, "//"); PYSTRING_CHECK_EQUAL(result.size(), 4); if(result.size()==4) { PYSTRING_CHECK_EQUAL(result[0], ""); PYSTRING_CHECK_EQUAL(result[1], "as"); PYSTRING_CHECK_EQUAL(result[2], "rew"); PYSTRING_CHECK_EQUAL(result[3], "gdf"); } } { result.clear(); pystring::split("/root", result, "/", 1); PYSTRING_CHECK_EQUAL(result.size(), 2); if(result.size()==2) { PYSTRING_CHECK_EQUAL(result[0], ""); PYSTRING_CHECK_EQUAL(result[1], "root"); } } { result.clear(); pystring::split("/root/world", result, "/", 0); PYSTRING_CHECK_EQUAL(result.size(), 1); if(result.size()==1) { PYSTRING_CHECK_EQUAL(result[0], "/root/world"); } } { result.clear(); pystring::split("/root/world", result, "/", 1); PYSTRING_CHECK_EQUAL(result.size(), 2); if(result.size()==2) { PYSTRING_CHECK_EQUAL(result[0], ""); PYSTRING_CHECK_EQUAL(result[1], "root/world"); } } { result.clear(); pystring::split("/root/world", result, "/", 2); PYSTRING_CHECK_EQUAL(result.size(), 3); if(result.size()==3) { PYSTRING_CHECK_EQUAL(result[0], ""); PYSTRING_CHECK_EQUAL(result[1], "root"); PYSTRING_CHECK_EQUAL(result[2], "world"); } } { result.clear(); pystring::split("/root/world", result, "/", -1); PYSTRING_CHECK_EQUAL(result.size(), 3); if(result.size()==3) { PYSTRING_CHECK_EQUAL(result[0], ""); PYSTRING_CHECK_EQUAL(result[1], "root"); PYSTRING_CHECK_EQUAL(result[2], "world"); } } } PYSTRING_ADD_TEST(pystring, rsplit) { std::vector< std::string > result; { result.clear(); pystring::rsplit("", result, "/", 1); PYSTRING_CHECK_EQUAL(result.size(), 1); if(result.size()==1) { PYSTRING_CHECK_EQUAL(result[0], ""); } } { // "".rsplit(None, 1) result.clear(); pystring::rsplit("", result, "", 1); PYSTRING_CHECK_EQUAL(result.size(), 0); } { // " ".rsplit(None, 1) result.clear(); pystring::rsplit(" ", result, "", 1); PYSTRING_CHECK_EQUAL(result.size(), 0); } { result.clear(); pystring::rsplit(" ", result, "", 1); PYSTRING_CHECK_EQUAL(result.size(), 0); } { result.clear(); pystring::rsplit("/", result, "/", 1); PYSTRING_CHECK_EQUAL(result.size(), 2); if(result.size()==2) { PYSTRING_CHECK_EQUAL(result[0], ""); PYSTRING_CHECK_EQUAL(result[1], ""); } } { result.clear(); pystring::rsplit(" /", result, "/", 1); PYSTRING_CHECK_EQUAL(result.size(), 2); if(result.size()==2) { PYSTRING_CHECK_EQUAL(result[0], " "); PYSTRING_CHECK_EQUAL(result[1], ""); } } { result.clear(); pystring::rsplit(" //", result, "/", 1); PYSTRING_CHECK_EQUAL(result.size(), 2); if(result.size()==2) { PYSTRING_CHECK_EQUAL(result[0], " /"); PYSTRING_CHECK_EQUAL(result[1], ""); } } { result.clear(); pystring::rsplit("/root", result, "/", 1); PYSTRING_CHECK_EQUAL(result.size(), 2); if(result.size()==2) { PYSTRING_CHECK_EQUAL(result[0], ""); PYSTRING_CHECK_EQUAL(result[1], "root"); } } { result.clear(); pystring::rsplit("/root/world", result, "/", 0); PYSTRING_CHECK_EQUAL(result.size(), 1); if(result.size()==1) { PYSTRING_CHECK_EQUAL(result[0], "/root/world"); } } { result.clear(); pystring::rsplit("/root/world", result, "/", 1); PYSTRING_CHECK_EQUAL(result.size(), 2); if(result.size()==2) { PYSTRING_CHECK_EQUAL(result[0], "/root"); PYSTRING_CHECK_EQUAL(result[1], "world"); } } { result.clear(); pystring::rsplit("/root/world", result, "/", 2); PYSTRING_CHECK_EQUAL(result.size(), 3); if(result.size()==3) { PYSTRING_CHECK_EQUAL(result[0], ""); PYSTRING_CHECK_EQUAL(result[1], "root"); PYSTRING_CHECK_EQUAL(result[2], "world"); } } { result.clear(); pystring::rsplit("/root/world", result, "/", -1); PYSTRING_CHECK_EQUAL(result.size(), 3); if(result.size()==3) { PYSTRING_CHECK_EQUAL(result[0], ""); PYSTRING_CHECK_EQUAL(result[1], "root"); PYSTRING_CHECK_EQUAL(result[2], "world"); } } { // " root world".rsplit(None, 0) result.clear(); pystring::rsplit(" root world", result, "", 0); PYSTRING_CHECK_EQUAL(result.size(), 1); if(result.size()==1) { PYSTRING_CHECK_EQUAL(result[0], " root world"); } } { // " root world".rsplit(None, 1) result.clear(); pystring::rsplit(" root world", result, "", 1); PYSTRING_CHECK_EQUAL(result.size(), 2); if(result.size()==2) { PYSTRING_CHECK_EQUAL(result[0], " root"); PYSTRING_CHECK_EQUAL(result[1], "world"); } } { // " root world".rsplit(None, 2) result.clear(); pystring::rsplit(" root world", result, "", 2); PYSTRING_CHECK_EQUAL(result.size(), 2); if(result.size()==2) { PYSTRING_CHECK_EQUAL(result[0], "root"); PYSTRING_CHECK_EQUAL(result[1], "world"); } } { // " root world".rsplit(" ", 0) result.clear(); pystring::rsplit(" root world", result, " ", 0); PYSTRING_CHECK_EQUAL(result.size(), 1); if(result.size()==1) { PYSTRING_CHECK_EQUAL(result[0], " root world"); } } { // " root world".rsplit(" ", 1) result.clear(); pystring::rsplit(" root world", result, " ", 1); PYSTRING_CHECK_EQUAL(result.size(), 2); if(result.size()==2) { PYSTRING_CHECK_EQUAL(result[0], " root"); PYSTRING_CHECK_EQUAL(result[1], "world"); } } { // " root world".rsplit(" ", 2) result.clear(); pystring::rsplit(" root world", result, " ", 2); PYSTRING_CHECK_EQUAL(result.size(), 3); if(result.size()==3) { PYSTRING_CHECK_EQUAL(result[0], ""); PYSTRING_CHECK_EQUAL(result[1], "root"); PYSTRING_CHECK_EQUAL(result[2], "world"); } } } PYSTRING_ADD_TEST(pystring, startswith) { PYSTRING_CHECK_EQUAL(pystring::startswith("", ""), true); PYSTRING_CHECK_EQUAL(pystring::startswith("", "a"), false); PYSTRING_CHECK_EQUAL(pystring::startswith("a", ""), true); PYSTRING_CHECK_EQUAL(pystring::startswith("abc", "ab"), true); PYSTRING_CHECK_EQUAL(pystring::startswith("abc", "abc"), true); PYSTRING_CHECK_EQUAL(pystring::startswith("abc", "abcd"), false); PYSTRING_CHECK_EQUAL(pystring::startswith("abcdef", "abc"), true); PYSTRING_CHECK_EQUAL(pystring::startswith("abcdef", "abc", 1), false); PYSTRING_CHECK_EQUAL(pystring::startswith("abcdef", "bc", 1), true); } PYSTRING_ADD_TEST(pystring, strip) { PYSTRING_CHECK_EQUAL(pystring::strip(""), ""); PYSTRING_CHECK_EQUAL(pystring::strip("a"), "a"); PYSTRING_CHECK_EQUAL(pystring::strip("a "), "a"); PYSTRING_CHECK_EQUAL(pystring::strip(" a"), "a"); PYSTRING_CHECK_EQUAL(pystring::strip("\n a "), "a"); PYSTRING_CHECK_EQUAL(pystring::strip("\r\n a \r\n"), "a"); PYSTRING_CHECK_EQUAL(pystring::strip("\r\n a \r\n\t"), "a"); } PYSTRING_ADD_TEST(pystring, translate) { char t1data[256]; for(int i=0; i<256; ++i) t1data[i] = (char)i; std::string t1(t1data, 256); PYSTRING_CHECK_EQUAL(pystring::translate("", t1), ""); PYSTRING_CHECK_EQUAL(pystring::translate("cheese", t1), "cheese"); PYSTRING_CHECK_EQUAL(pystring::translate("cheese", t1, "e"), "chs"); char t2data[256]; for(int i=0; i<256; ++i) t2data[i] = (char)i; t2data[101] = 111; // Map e -> o std::string t2(t2data, 256); PYSTRING_CHECK_EQUAL(pystring::translate("", t2), ""); PYSTRING_CHECK_EQUAL(pystring::translate("cheese", t2), "chooso"); } PYSTRING_ADD_TEST(pystring, abspath) { PYSTRING_CHECK_EQUAL(pystring::os::path::abspath_posix("", "/net"), "/net"); PYSTRING_CHECK_EQUAL(pystring::os::path::abspath_posix("../jeremys", "/net/soft_scratch/users/stevel"), "/net/soft_scratch/users/jeremys"); PYSTRING_CHECK_EQUAL(pystring::os::path::abspath_posix("../../../../tmp/a", "/net/soft_scratch/users/stevel"), "/tmp/a"); PYSTRING_CHECK_EQUAL(pystring::os::path::abspath_nt("", "c:\\net"), "c:\\net"); PYSTRING_CHECK_EQUAL(pystring::os::path::abspath_nt("..\\jeremys", "c:\\net\\soft_scratch\\users\\stevel"), "c:\\net\\soft_scratch\\users\\jeremys"); PYSTRING_CHECK_EQUAL(pystring::os::path::abspath_nt("..\\..\\..\\..\\tmp\\a", "c:\\net\\soft_scratch\\users\\stevel"), "c:\\tmp\\a"); } PYSTRING_ADD_TEST(pystring_os_path, splitdrive) { using namespace pystring::os::path; std::string drivespec, pathspec; splitdrive_posix(drivespec, pathspec, "/Users/test"); PYSTRING_CHECK_EQUAL(drivespec, ""); PYSTRING_CHECK_EQUAL(pathspec, "/Users/test"); splitdrive_nt(drivespec, pathspec, "C:\\Users\\test"); PYSTRING_CHECK_EQUAL(drivespec, "C:" ); PYSTRING_CHECK_EQUAL(pathspec, "\\Users\\test" ); splitdrive_nt(drivespec, pathspec, "\\Users\\test"); PYSTRING_CHECK_EQUAL(drivespec, "" ); PYSTRING_CHECK_EQUAL(pathspec, "\\Users\\test" ); } PYSTRING_ADD_TEST(pystring_os_path, isabs) { using namespace pystring::os::path; PYSTRING_CHECK_EQUAL(isabs_posix("/Users/test"), true ); PYSTRING_CHECK_EQUAL(isabs_posix("\\Users\\test"), false); PYSTRING_CHECK_EQUAL(isabs_posix("../Users"), false); PYSTRING_CHECK_EQUAL(isabs_posix("Users"), false); PYSTRING_CHECK_EQUAL(isabs_nt("C:\\Users\\test"), true); PYSTRING_CHECK_EQUAL(isabs_nt("C:/Users"), true); PYSTRING_CHECK_EQUAL(isabs_nt("/Users"), true); PYSTRING_CHECK_EQUAL(isabs_nt("../Users"), false); PYSTRING_CHECK_EQUAL(isabs_nt("..\\Users"), false); } PYSTRING_ADD_TEST(pystring_os_path, join) { using namespace pystring::os::path; PYSTRING_CHECK_EQUAL(join_posix("a","b"), "a/b" ); PYSTRING_CHECK_EQUAL(join_posix("/a","b"), "/a/b" ); PYSTRING_CHECK_EQUAL(join_posix("/a","/b"), "/b" ); PYSTRING_CHECK_EQUAL(join_posix("a","/b"), "/b" ); PYSTRING_CHECK_EQUAL(join_posix("//a","b"), "//a/b" ); PYSTRING_CHECK_EQUAL(join_posix("//a","b//"), "//a/b//" ); PYSTRING_CHECK_EQUAL(join_posix("../a","/b"), "/b" ); PYSTRING_CHECK_EQUAL(join_posix("../a","b"), "../a/b" ); std::vector< std::string > paths; PYSTRING_CHECK_EQUAL(join_posix(paths), "" ); paths.push_back("/a"); PYSTRING_CHECK_EQUAL(join_posix(paths), "/a" ); paths.push_back("b"); PYSTRING_CHECK_EQUAL(join_posix(paths), "/a/b" ); paths.push_back("c/"); PYSTRING_CHECK_EQUAL(join_posix(paths), "/a/b/c/" ); paths.push_back("d"); PYSTRING_CHECK_EQUAL(join_posix(paths), "/a/b/c/d" ); paths.push_back("/e"); PYSTRING_CHECK_EQUAL(join_posix(paths), "/e" ); PYSTRING_CHECK_EQUAL(join_nt("c:","/a"), "c:/a" ); PYSTRING_CHECK_EQUAL(join_nt("c:/","/a"), "c:/a" ); PYSTRING_CHECK_EQUAL(join_nt("c:/a","/b"), "/b" ); PYSTRING_CHECK_EQUAL(join_nt("c:","d:/"), "d:/" ); PYSTRING_CHECK_EQUAL(join_nt("c:/","d:/"), "d:/" ); PYSTRING_CHECK_EQUAL(join_nt("a","b"), "a\\b" ); PYSTRING_CHECK_EQUAL(join_nt("\\a","b"), "\\a\\b" ); PYSTRING_CHECK_EQUAL(join_nt("c:\\a","b"), "c:\\a\\b" ); PYSTRING_CHECK_EQUAL(join_nt("c:\\a","c:\\b"), "c:\\b" ); PYSTRING_CHECK_EQUAL(join_nt("..\\a","b"), "..\\a\\b" ); } PYSTRING_ADD_TEST(pystring_os_path, normpath) { using namespace pystring::os::path; PYSTRING_CHECK_EQUAL(normpath_posix("A//B"), "A/B" ); PYSTRING_CHECK_EQUAL(normpath_posix("A/./B"), "A/B" ); PYSTRING_CHECK_EQUAL(normpath_posix("A/foo/../B"), "A/B" ); PYSTRING_CHECK_EQUAL(normpath_posix("/A//B"), "/A/B" ); PYSTRING_CHECK_EQUAL(normpath_posix("//A//B"), "//A/B" ); PYSTRING_CHECK_EQUAL(normpath_posix("///A//B"), "/A/B" ); PYSTRING_CHECK_EQUAL(normpath_posix("../A"), "../A" ); PYSTRING_CHECK_EQUAL(normpath_posix("../A../"), "../A.." ); PYSTRING_CHECK_EQUAL(normpath_posix("FOO/../A../././B"), "A../B" ); PYSTRING_CHECK_EQUAL(normpath_nt(""), "." ); PYSTRING_CHECK_EQUAL(normpath_nt("A"), "A" ); PYSTRING_CHECK_EQUAL(normpath_nt("A./B"), "A.\\B" ); PYSTRING_CHECK_EQUAL(normpath_nt("C:\\"), "C:\\" ); PYSTRING_CHECK_EQUAL(normpath_nt("C:\\A"), "C:\\A" ); PYSTRING_CHECK_EQUAL(normpath_nt("C:/A"), "C:\\A" ); PYSTRING_CHECK_EQUAL(normpath_nt("C:/A..\\"), "C:\\A.." ); PYSTRING_CHECK_EQUAL(normpath_nt("C:/A..\\..\\"), "C:\\" ); PYSTRING_CHECK_EQUAL(normpath_nt("C:\\\\A"), "C:\\A" ); PYSTRING_CHECK_EQUAL(normpath_nt("C:\\\\\\A\\\\B"), "C:\\A\\B" ); } PYSTRING_ADD_TEST(pystring_os_path, split) { using namespace pystring::os::path; std::string head, tail; split_posix(head, tail, ""); PYSTRING_CHECK_EQUAL(head, "" ); PYSTRING_CHECK_EQUAL(tail, "" ); split_posix(head, tail, "/"); PYSTRING_CHECK_EQUAL(head, "/" ); PYSTRING_CHECK_EQUAL(tail, "" ); split_posix(head, tail, "a"); PYSTRING_CHECK_EQUAL(head, "" ); PYSTRING_CHECK_EQUAL(tail, "a" ); split_posix(head, tail, "a/"); PYSTRING_CHECK_EQUAL(head, "a" ); PYSTRING_CHECK_EQUAL(tail, "" ); split_posix(head, tail, "/a"); PYSTRING_CHECK_EQUAL(head, "/" ); PYSTRING_CHECK_EQUAL(tail, "a" ); split_posix(head, tail, "/a/b/"); PYSTRING_CHECK_EQUAL(head, "/a/b" ); PYSTRING_CHECK_EQUAL(tail, "" ); split_posix(head, tail, "/a/b"); PYSTRING_CHECK_EQUAL(head, "/a" ); PYSTRING_CHECK_EQUAL(tail, "b" ); split_posix(head, tail, "/a/b//"); PYSTRING_CHECK_EQUAL(head, "/a/b" ); PYSTRING_CHECK_EQUAL(tail, "" ); split_posix(head, tail, "/a/b/////////////"); PYSTRING_CHECK_EQUAL(head, "/a/b" ); PYSTRING_CHECK_EQUAL(tail, "" ); split_nt(head, tail, ""); PYSTRING_CHECK_EQUAL(head, "" ); PYSTRING_CHECK_EQUAL(tail, "" ); split_nt(head, tail, "\\"); PYSTRING_CHECK_EQUAL(head, "\\" ); PYSTRING_CHECK_EQUAL(tail, "" ); split_nt(head, tail, "a"); PYSTRING_CHECK_EQUAL(head, "" ); PYSTRING_CHECK_EQUAL(tail, "a" ); split_nt(head, tail, "a\\"); PYSTRING_CHECK_EQUAL(head, "a" ); PYSTRING_CHECK_EQUAL(tail, "" ); split_nt(head, tail, "c:\\a"); PYSTRING_CHECK_EQUAL(head, "c:\\" ); PYSTRING_CHECK_EQUAL(tail, "a" ); split_nt(head, tail, "c:\\a\\b"); PYSTRING_CHECK_EQUAL(head, "c:\\a" ); PYSTRING_CHECK_EQUAL(tail, "b" ); split_nt(head, tail, "c:\\a\\b\\"); PYSTRING_CHECK_EQUAL(head, "c:\\a\\b" ); PYSTRING_CHECK_EQUAL(tail, "" ); split_nt(head, tail, "D:\\dir\\\\"); PYSTRING_CHECK_EQUAL(head, "D:\\dir" ); PYSTRING_CHECK_EQUAL(tail, "" ); } PYSTRING_ADD_TEST(pystring_os_path, splitext) { using namespace pystring::os::path; std::string root, ext; splitext_nt(root, ext, ""); PYSTRING_CHECK_EQUAL(root, ""); PYSTRING_CHECK_EQUAL(ext, ""); splitext_nt(root, ext, "."); PYSTRING_CHECK_EQUAL(root, "."); PYSTRING_CHECK_EQUAL(ext, ""); splitext_nt(root, ext, ".foo"); PYSTRING_CHECK_EQUAL(root, ".foo"); PYSTRING_CHECK_EQUAL(ext, ""); splitext_nt(root, ext, ".foo."); PYSTRING_CHECK_EQUAL(root, ".foo"); PYSTRING_CHECK_EQUAL(ext, "."); splitext_nt(root, ext, ".foo.e"); PYSTRING_CHECK_EQUAL(root, ".foo"); PYSTRING_CHECK_EQUAL(ext, ".e"); splitext_nt(root, ext, "c"); PYSTRING_CHECK_EQUAL(root, "c"); PYSTRING_CHECK_EQUAL(ext, ""); splitext_nt(root, ext, "a_b.c"); PYSTRING_CHECK_EQUAL(root, "a_b"); PYSTRING_CHECK_EQUAL(ext, ".c"); splitext_nt(root, ext, "c:\\a.b.c"); PYSTRING_CHECK_EQUAL(root, "c:\\a.b"); PYSTRING_CHECK_EQUAL(ext, ".c"); splitext_nt(root, ext, "c:\\a_b.c"); PYSTRING_CHECK_EQUAL(root, "c:\\a_b"); PYSTRING_CHECK_EQUAL(ext, ".c"); } // the python3_compat tests were auto generated from python // to make sure that our functions exactly match the values that are being returned in python 3.11 PYSTRING_ADD_TEST(python3_compat, capitalize) { PYSTRING_CHECK_EQUAL(pystring::capitalize(""), ""); PYSTRING_CHECK_EQUAL(pystring::capitalize("hello"), "Hello"); PYSTRING_CHECK_EQUAL(pystring::capitalize("HELLO"), "Hello"); PYSTRING_CHECK_EQUAL(pystring::capitalize("Hello World"), "Hello world"); PYSTRING_CHECK_EQUAL(pystring::capitalize("hello123"), "Hello123"); PYSTRING_CHECK_EQUAL(pystring::capitalize("123"), "123"); PYSTRING_CHECK_EQUAL(pystring::capitalize("hElLo"), "Hello"); PYSTRING_CHECK_EQUAL(pystring::capitalize("hello world!"), "Hello world!"); } PYSTRING_ADD_TEST(python3_compat, center) { PYSTRING_CHECK_EQUAL(pystring::center("hello", 10), " hello "); PYSTRING_CHECK_EQUAL(pystring::center("hello", 3), "hello"); PYSTRING_CHECK_EQUAL(pystring::center("", 5), " "); PYSTRING_CHECK_EQUAL(pystring::center("hello", 5), "hello"); } PYSTRING_ADD_TEST(python3_compat, count_python3) { PYSTRING_CHECK_EQUAL(pystring::count("", "", 0), 1); PYSTRING_CHECK_EQUAL(pystring::count("bob", ""), 4); PYSTRING_CHECK_EQUAL(pystring::count("", "bob"), 0); PYSTRING_CHECK_EQUAL(pystring::count("hello world", "o", 0), 2); PYSTRING_CHECK_EQUAL(pystring::count("hello world", "l", 0), 3); PYSTRING_CHECK_EQUAL(pystring::count("hello world", "xyz", 0), 0); PYSTRING_CHECK_EQUAL(pystring::count("hello world", "", 0), 12); PYSTRING_CHECK_EQUAL(pystring::count("aaa", "a", 0), 3); PYSTRING_CHECK_EQUAL(pystring::count("aaa", "aa", 0), 1); PYSTRING_CHECK_EQUAL(pystring::count("hello", "l", 2, 5), 2); } PYSTRING_ADD_TEST(python3_compat, endswith_python3) { PYSTRING_CHECK_EQUAL(pystring::endswith("hello world", "hello", 0), false); PYSTRING_CHECK_EQUAL(pystring::endswith("hello world", "world", 0), true); PYSTRING_CHECK_EQUAL(pystring::endswith("hello world", "", 0), true); PYSTRING_CHECK_EQUAL(pystring::endswith("hello world", "hello", 1), false); PYSTRING_CHECK_EQUAL(pystring::endswith("hello world", "ello", 1), false); PYSTRING_CHECK_EQUAL(pystring::endswith("hello", "hello world", 0), false); PYSTRING_CHECK_EQUAL(pystring::endswith("hello world", "world", 0, 5), false); PYSTRING_CHECK_EQUAL(pystring::endswith("hello world", "hello", 0, 5), true); PYSTRING_CHECK_EQUAL(pystring::endswith("", "", 0), true); PYSTRING_CHECK_EQUAL(pystring::endswith("hello", "hello", 0), true); } PYSTRING_ADD_TEST(python3_compat, expandtabs) { PYSTRING_CHECK_EQUAL(pystring::expandtabs("hello\tworld", 8), "hello world"); PYSTRING_CHECK_EQUAL(pystring::expandtabs("hello\tworld", 4), "hello world"); PYSTRING_CHECK_EQUAL(pystring::expandtabs("\t\t", 4), " "); PYSTRING_CHECK_EQUAL(pystring::expandtabs("no tabs", 4), "no tabs"); PYSTRING_CHECK_EQUAL(pystring::expandtabs("a\tb\tc", 4), "a b c"); } PYSTRING_ADD_TEST(python3_compat, find_python3) { PYSTRING_CHECK_EQUAL(pystring::find("hello world", "world", 0), 6); PYSTRING_CHECK_EQUAL(pystring::find("hello world", "hello", 0), 0); PYSTRING_CHECK_EQUAL(pystring::find("hello world", "xyz", 0), -1); PYSTRING_CHECK_EQUAL(pystring::find("hello world", "", 0), 0); PYSTRING_CHECK_EQUAL(pystring::find("hello world", "o", 0), 4); PYSTRING_CHECK_EQUAL(pystring::find("hello world", "o", 5), 7); PYSTRING_CHECK_EQUAL(pystring::find("hello world", "o", 0, 5), 4); PYSTRING_CHECK_EQUAL(pystring::find("", "hello", 0), -1); PYSTRING_CHECK_EQUAL(pystring::find("hello", "", 0), 0); PYSTRING_CHECK_EQUAL(pystring::find("abcabc", "bc", 0), 1); PYSTRING_CHECK_EQUAL(pystring::find("abcabc", "bc", 2), 4); } PYSTRING_ADD_TEST(python3_compat, isalnum) { PYSTRING_CHECK_EQUAL(pystring::isalnum(""), false); PYSTRING_CHECK_EQUAL(pystring::isalnum("abc"), true); PYSTRING_CHECK_EQUAL(pystring::isalnum("123"), true); PYSTRING_CHECK_EQUAL(pystring::isalnum("abc123"), true); PYSTRING_CHECK_EQUAL(pystring::isalnum("abc!"), false); PYSTRING_CHECK_EQUAL(pystring::isalnum(" "), false); PYSTRING_CHECK_EQUAL(pystring::isalnum("abc 123"), false); } PYSTRING_ADD_TEST(python3_compat, isalpha) { PYSTRING_CHECK_EQUAL(pystring::isalpha(""), false); PYSTRING_CHECK_EQUAL(pystring::isalpha("abc"), true); PYSTRING_CHECK_EQUAL(pystring::isalpha("ABC"), true); PYSTRING_CHECK_EQUAL(pystring::isalpha("abcABC"), true); PYSTRING_CHECK_EQUAL(pystring::isalpha("abc1"), false); PYSTRING_CHECK_EQUAL(pystring::isalpha("abc!"), false); PYSTRING_CHECK_EQUAL(pystring::isalpha(" "), false); PYSTRING_CHECK_EQUAL(pystring::isalpha("a b"), false); } PYSTRING_ADD_TEST(python3_compat, isdigit) { PYSTRING_CHECK_EQUAL(pystring::isdigit(""), false); PYSTRING_CHECK_EQUAL(pystring::isdigit("0"), true); PYSTRING_CHECK_EQUAL(pystring::isdigit("123"), true); PYSTRING_CHECK_EQUAL(pystring::isdigit("12.3"), false); PYSTRING_CHECK_EQUAL(pystring::isdigit("12 3"), false); PYSTRING_CHECK_EQUAL(pystring::isdigit("abc"), false); PYSTRING_CHECK_EQUAL(pystring::isdigit("123abc"), false); PYSTRING_CHECK_EQUAL(pystring::isdigit(" "), false); } PYSTRING_ADD_TEST(python3_compat, islower) { PYSTRING_CHECK_EQUAL(pystring::islower(""), false); PYSTRING_CHECK_EQUAL(pystring::islower("a"), true); PYSTRING_CHECK_EQUAL(pystring::islower("A"), false); PYSTRING_CHECK_EQUAL(pystring::islower("abc"), true); PYSTRING_CHECK_EQUAL(pystring::islower("ABC"), false); PYSTRING_CHECK_EQUAL(pystring::islower("abC"), false); PYSTRING_CHECK_EQUAL(pystring::islower("abc123"), true); PYSTRING_CHECK_EQUAL(pystring::islower("123"), false); PYSTRING_CHECK_EQUAL(pystring::islower("abc 123"), true); PYSTRING_CHECK_EQUAL(pystring::islower("hello!"), true); PYSTRING_CHECK_EQUAL(pystring::islower("HELLO!"), false); PYSTRING_CHECK_EQUAL(pystring::islower(" "), false); PYSTRING_CHECK_EQUAL(pystring::islower("abc\n"), true); PYSTRING_CHECK_EQUAL(pystring::islower("!@#$"), false); PYSTRING_CHECK_EQUAL(pystring::islower("a!b"), true); PYSTRING_CHECK_EQUAL(pystring::islower("1a"), true); } PYSTRING_ADD_TEST(python3_compat, isspace) { PYSTRING_CHECK_EQUAL(pystring::isspace(""), false); PYSTRING_CHECK_EQUAL(pystring::isspace(" "), true); PYSTRING_CHECK_EQUAL(pystring::isspace(" "), true); PYSTRING_CHECK_EQUAL(pystring::isspace("\t"), true); PYSTRING_CHECK_EQUAL(pystring::isspace("\n"), true); PYSTRING_CHECK_EQUAL(pystring::isspace("\r"), true); PYSTRING_CHECK_EQUAL(pystring::isspace(" \t\n"), true); PYSTRING_CHECK_EQUAL(pystring::isspace("a"), false); PYSTRING_CHECK_EQUAL(pystring::isspace(" a "), false); PYSTRING_CHECK_EQUAL(pystring::isspace(" a"), false); } PYSTRING_ADD_TEST(python3_compat, istitle) { PYSTRING_CHECK_EQUAL(pystring::istitle(""), false); PYSTRING_CHECK_EQUAL(pystring::istitle("Title"), true); PYSTRING_CHECK_EQUAL(pystring::istitle("Title Case"), true); PYSTRING_CHECK_EQUAL(pystring::istitle("title"), false); PYSTRING_CHECK_EQUAL(pystring::istitle("TITLE"), false); PYSTRING_CHECK_EQUAL(pystring::istitle("Title123"), true); PYSTRING_CHECK_EQUAL(pystring::istitle("A"), true); PYSTRING_CHECK_EQUAL(pystring::istitle("a"), false); PYSTRING_CHECK_EQUAL(pystring::istitle("Title Case Here"), true); PYSTRING_CHECK_EQUAL(pystring::istitle("Title Case"), true); PYSTRING_CHECK_EQUAL(pystring::istitle("123 Title"), true); PYSTRING_CHECK_EQUAL(pystring::istitle("Already A Title"), true); PYSTRING_CHECK_EQUAL(pystring::istitle("not A Title"), false); } PYSTRING_ADD_TEST(python3_compat, isupper) { PYSTRING_CHECK_EQUAL(pystring::isupper(""), false); PYSTRING_CHECK_EQUAL(pystring::isupper("A"), true); PYSTRING_CHECK_EQUAL(pystring::isupper("a"), false); PYSTRING_CHECK_EQUAL(pystring::isupper("ABC"), true); PYSTRING_CHECK_EQUAL(pystring::isupper("abc"), false); PYSTRING_CHECK_EQUAL(pystring::isupper("ABc"), false); PYSTRING_CHECK_EQUAL(pystring::isupper("ABC123"), true); PYSTRING_CHECK_EQUAL(pystring::isupper("123"), false); PYSTRING_CHECK_EQUAL(pystring::isupper("ABC 123"), true); PYSTRING_CHECK_EQUAL(pystring::isupper("HELLO!"), true); PYSTRING_CHECK_EQUAL(pystring::isupper("hello!"), false); PYSTRING_CHECK_EQUAL(pystring::isupper(" "), false); PYSTRING_CHECK_EQUAL(pystring::isupper("ABC\n"), true); PYSTRING_CHECK_EQUAL(pystring::isupper("ABC\tDEF"), true); PYSTRING_CHECK_EQUAL(pystring::isupper("!@#$"), false); PYSTRING_CHECK_EQUAL(pystring::isupper("A!B"), true); PYSTRING_CHECK_EQUAL(pystring::isupper("1A"), true); } PYSTRING_ADD_TEST(python3_compat, join_python3) { PYSTRING_CHECK_EQUAL(pystring::join(",", {}), ""); PYSTRING_CHECK_EQUAL(pystring::join(",", {""}), ""); PYSTRING_CHECK_EQUAL(pystring::join(",", {"a"}), "a"); PYSTRING_CHECK_EQUAL(pystring::join(",", {"a", "b", "c"}), "a,b,c"); PYSTRING_CHECK_EQUAL(pystring::join("", {"a", "b", "c"}), "abc"); PYSTRING_CHECK_EQUAL(pystring::join(", ", {"hello", "world"}), "hello, world"); PYSTRING_CHECK_EQUAL(pystring::join("-", {"one", "two", "three"}), "one-two-three"); PYSTRING_CHECK_EQUAL(pystring::join("/", {"path", "to", "file"}), "path/to/file"); } PYSTRING_ADD_TEST(python3_compat, ljust) { PYSTRING_CHECK_EQUAL(pystring::ljust("hello", 10), "hello "); PYSTRING_CHECK_EQUAL(pystring::ljust("hello", 3), "hello"); PYSTRING_CHECK_EQUAL(pystring::ljust("", 5), " "); PYSTRING_CHECK_EQUAL(pystring::ljust("hello", 5), "hello"); } PYSTRING_ADD_TEST(python3_compat, lower) { PYSTRING_CHECK_EQUAL(pystring::lower(""), ""); PYSTRING_CHECK_EQUAL(pystring::lower("hello"), "hello"); PYSTRING_CHECK_EQUAL(pystring::lower("HELLO"), "hello"); PYSTRING_CHECK_EQUAL(pystring::lower("Hello World"), "hello world"); PYSTRING_CHECK_EQUAL(pystring::lower("hello123"), "hello123"); PYSTRING_CHECK_EQUAL(pystring::lower("123"), "123"); PYSTRING_CHECK_EQUAL(pystring::lower("hElLo"), "hello"); PYSTRING_CHECK_EQUAL(pystring::lower("hello world!"), "hello world!"); } PYSTRING_ADD_TEST(python3_compat, lstrip) { PYSTRING_CHECK_EQUAL(pystring::lstrip(""), ""); PYSTRING_CHECK_EQUAL(pystring::lstrip(" hello "), "hello "); PYSTRING_CHECK_EQUAL(pystring::lstrip(" hello ", " "), "hello "); PYSTRING_CHECK_EQUAL(pystring::lstrip("xxhelloxx", "x"), "helloxx"); PYSTRING_CHECK_EQUAL(pystring::lstrip("\t\nhello\t\n"), "hello\t\n"); PYSTRING_CHECK_EQUAL(pystring::lstrip("hello"), "hello"); PYSTRING_CHECK_EQUAL(pystring::lstrip(" "), ""); PYSTRING_CHECK_EQUAL(pystring::lstrip("aabbcc", "ac"), "bbcc"); PYSTRING_CHECK_EQUAL(pystring::lstrip("hello world "), "hello world "); } PYSTRING_ADD_TEST(python3_compat, mul) { PYSTRING_CHECK_EQUAL(pystring::mul("ab", 3), "ababab"); PYSTRING_CHECK_EQUAL(pystring::mul("ab", 0), ""); PYSTRING_CHECK_EQUAL(pystring::mul("ab", 1), "ab"); PYSTRING_CHECK_EQUAL(pystring::mul("", 5), ""); PYSTRING_CHECK_EQUAL(pystring::mul("x", 5), "xxxxx"); } PYSTRING_ADD_TEST(python3_compat, removeprefix_python3) { PYSTRING_CHECK_EQUAL(pystring::removeprefix("hello world", "hello "), "world"); PYSTRING_CHECK_EQUAL(pystring::removeprefix("hello world", "xyz"), "hello world"); PYSTRING_CHECK_EQUAL(pystring::removeprefix("hello world", ""), "hello world"); PYSTRING_CHECK_EQUAL(pystring::removeprefix("hello", "hello"), ""); PYSTRING_CHECK_EQUAL(pystring::removeprefix("", "x"), ""); } PYSTRING_ADD_TEST(python3_compat, removesuffix_python3) { PYSTRING_CHECK_EQUAL(pystring::removesuffix("hello world", "hello "), "hello world"); PYSTRING_CHECK_EQUAL(pystring::removesuffix("hello world", "xyz"), "hello world"); PYSTRING_CHECK_EQUAL(pystring::removesuffix("hello world", ""), "hello world"); PYSTRING_CHECK_EQUAL(pystring::removesuffix("hello", "hello"), ""); PYSTRING_CHECK_EQUAL(pystring::removesuffix("", "x"), ""); } PYSTRING_ADD_TEST(python3_compat, replace_python3) { PYSTRING_CHECK_EQUAL(pystring::replace("hello world", "world", "python", -1), "hello python"); PYSTRING_CHECK_EQUAL(pystring::replace("hello world", "o", "0", -1), "hell0 w0rld"); PYSTRING_CHECK_EQUAL(pystring::replace("hello world", "o", "0", 1), "hell0 world"); PYSTRING_CHECK_EQUAL(pystring::replace("hello world", "xyz", "abc", -1), "hello world"); PYSTRING_CHECK_EQUAL(pystring::replace("hello world", "", "_", -1), "_h_e_l_l_o_ _w_o_r_l_d_"); PYSTRING_CHECK_EQUAL(pystring::replace("aaa", "a", "bb", -1), "bbbbbb"); PYSTRING_CHECK_EQUAL(pystring::replace("aaa", "a", "bb", 2), "bbbba"); PYSTRING_CHECK_EQUAL(pystring::replace("", "a", "b", -1), ""); PYSTRING_CHECK_EQUAL(pystring::replace("hello", "hello", "", -1), ""); } PYSTRING_ADD_TEST(python3_compat, rfind_python3) { PYSTRING_CHECK_EQUAL(pystring::rfind("hello world", "world", 0), 6); PYSTRING_CHECK_EQUAL(pystring::rfind("hello world", "hello", 0), 0); PYSTRING_CHECK_EQUAL(pystring::rfind("hello world", "xyz", 0), -1); PYSTRING_CHECK_EQUAL(pystring::rfind("hello world", "", 0), 11); PYSTRING_CHECK_EQUAL(pystring::rfind("hello world", "o", 0), 7); PYSTRING_CHECK_EQUAL(pystring::rfind("hello world", "o", 5), 7); PYSTRING_CHECK_EQUAL(pystring::rfind("hello world", "o", 0, 5), 4); PYSTRING_CHECK_EQUAL(pystring::rfind("", "hello", 0), -1); PYSTRING_CHECK_EQUAL(pystring::rfind("hello", "", 0), 5); PYSTRING_CHECK_EQUAL(pystring::rfind("abcabc", "bc", 0), 4); PYSTRING_CHECK_EQUAL(pystring::rfind("abcabc", "bc", 2), 4); } PYSTRING_ADD_TEST(python3_compat, rjust) { PYSTRING_CHECK_EQUAL(pystring::rjust("hello", 10), " hello"); PYSTRING_CHECK_EQUAL(pystring::rjust("hello", 3), "hello"); PYSTRING_CHECK_EQUAL(pystring::rjust("", 5), " "); PYSTRING_CHECK_EQUAL(pystring::rjust("hello", 5), "hello"); } PYSTRING_ADD_TEST(python3_compat, rstrip) { PYSTRING_CHECK_EQUAL(pystring::rstrip(""), ""); PYSTRING_CHECK_EQUAL(pystring::rstrip(" hello "), " hello"); PYSTRING_CHECK_EQUAL(pystring::rstrip(" hello ", " "), " hello"); PYSTRING_CHECK_EQUAL(pystring::rstrip("xxhelloxx", "x"), "xxhello"); PYSTRING_CHECK_EQUAL(pystring::rstrip("\t\nhello\t\n"), "\t\nhello"); PYSTRING_CHECK_EQUAL(pystring::rstrip("hello"), "hello"); PYSTRING_CHECK_EQUAL(pystring::rstrip(" "), ""); PYSTRING_CHECK_EQUAL(pystring::rstrip("aabbcc", "ac"), "aabb"); PYSTRING_CHECK_EQUAL(pystring::rstrip("hello world "), "hello world"); } PYSTRING_ADD_TEST(python3_compat, slice_python3) { PYSTRING_CHECK_EQUAL(pystring::slice("hello", 0, 5), "hello"); PYSTRING_CHECK_EQUAL(pystring::slice("hello", 1, 4), "ell"); PYSTRING_CHECK_EQUAL(pystring::slice("hello", 0, -1), "hell"); PYSTRING_CHECK_EQUAL(pystring::slice("hello", -3, -1), "ll"); PYSTRING_CHECK_EQUAL(pystring::slice("hello", 2, 2), ""); PYSTRING_CHECK_EQUAL(pystring::slice("hello", 3, 1), ""); PYSTRING_CHECK_EQUAL(pystring::slice("", 0, 0), ""); PYSTRING_CHECK_EQUAL(pystring::slice("hello world", 6, 11), "world"); } PYSTRING_ADD_TEST(python3_compat, startswith_python3) { PYSTRING_CHECK_EQUAL(pystring::startswith("hello world", "hello", 0), true); PYSTRING_CHECK_EQUAL(pystring::startswith("hello world", "world", 0), false); PYSTRING_CHECK_EQUAL(pystring::startswith("hello world", "", 0), true); PYSTRING_CHECK_EQUAL(pystring::startswith("hello world", "hello", 1), false); PYSTRING_CHECK_EQUAL(pystring::startswith("hello world", "ello", 1), true); PYSTRING_CHECK_EQUAL(pystring::startswith("hello", "hello world", 0), false); PYSTRING_CHECK_EQUAL(pystring::startswith("hello world", "world", 0, 5), false); PYSTRING_CHECK_EQUAL(pystring::startswith("hello world", "hello", 0, 5), true); PYSTRING_CHECK_EQUAL(pystring::startswith("", "", 0), true); PYSTRING_CHECK_EQUAL(pystring::startswith("hello", "hello", 0), true); } PYSTRING_ADD_TEST(python3_compat, strip) { PYSTRING_CHECK_EQUAL(pystring::strip(""), ""); PYSTRING_CHECK_EQUAL(pystring::strip(" hello "), "hello"); PYSTRING_CHECK_EQUAL(pystring::strip(" hello ", " "), "hello"); PYSTRING_CHECK_EQUAL(pystring::strip("xxhelloxx", "x"), "hello"); PYSTRING_CHECK_EQUAL(pystring::strip("\t\nhello\t\n"), "hello"); PYSTRING_CHECK_EQUAL(pystring::strip("hello"), "hello"); PYSTRING_CHECK_EQUAL(pystring::strip(" "), ""); PYSTRING_CHECK_EQUAL(pystring::strip("aabbcc", "ac"), "bb"); PYSTRING_CHECK_EQUAL(pystring::strip("hello world "), "hello world"); } PYSTRING_ADD_TEST(python3_compat, swapcase) { PYSTRING_CHECK_EQUAL(pystring::swapcase(""), ""); PYSTRING_CHECK_EQUAL(pystring::swapcase("hello"), "HELLO"); PYSTRING_CHECK_EQUAL(pystring::swapcase("HELLO"), "hello"); PYSTRING_CHECK_EQUAL(pystring::swapcase("Hello World"), "hELLO wORLD"); PYSTRING_CHECK_EQUAL(pystring::swapcase("hello123"), "HELLO123"); PYSTRING_CHECK_EQUAL(pystring::swapcase("123"), "123"); PYSTRING_CHECK_EQUAL(pystring::swapcase("hElLo"), "HeLlO"); PYSTRING_CHECK_EQUAL(pystring::swapcase("hello world!"), "HELLO WORLD!"); } PYSTRING_ADD_TEST(python3_compat, title) { PYSTRING_CHECK_EQUAL(pystring::title(""), ""); PYSTRING_CHECK_EQUAL(pystring::title("hello world"), "Hello World"); PYSTRING_CHECK_EQUAL(pystring::title("HELLO WORLD"), "Hello World"); PYSTRING_CHECK_EQUAL(pystring::title("hello"), "Hello"); PYSTRING_CHECK_EQUAL(pystring::title("it's a test"), "It'S A Test"); PYSTRING_CHECK_EQUAL(pystring::title("they're bill's friends from the UK"), "They'Re Bill'S Friends From The Uk"); PYSTRING_CHECK_EQUAL(pystring::title("hello123world"), "Hello123World"); } PYSTRING_ADD_TEST(python3_compat, upper) { PYSTRING_CHECK_EQUAL(pystring::upper(""), ""); PYSTRING_CHECK_EQUAL(pystring::upper("hello"), "HELLO"); PYSTRING_CHECK_EQUAL(pystring::upper("HELLO"), "HELLO"); PYSTRING_CHECK_EQUAL(pystring::upper("Hello World"), "HELLO WORLD"); PYSTRING_CHECK_EQUAL(pystring::upper("hello123"), "HELLO123"); PYSTRING_CHECK_EQUAL(pystring::upper("123"), "123"); PYSTRING_CHECK_EQUAL(pystring::upper("hElLo"), "HELLO"); PYSTRING_CHECK_EQUAL(pystring::upper("hello world!"), "HELLO WORLD!"); } PYSTRING_ADD_TEST(python3_compat, zfill) { PYSTRING_CHECK_EQUAL(pystring::zfill("42", 5), "00042"); PYSTRING_CHECK_EQUAL(pystring::zfill("42", 2), "42"); PYSTRING_CHECK_EQUAL(pystring::zfill("-42", 5), "-0042"); PYSTRING_CHECK_EQUAL(pystring::zfill("+42", 5), "+0042"); PYSTRING_CHECK_EQUAL(pystring::zfill("hello", 8), "000hello"); PYSTRING_CHECK_EQUAL(pystring::zfill("", 3), "000"); } PYSTRING_ADD_TEST(python3_compat, rpartition_python3) { { std::vector _e17 = {"hello", " ", "world"}; PYSTRING_CHECK_ASSERT((pystring_rpartition("hello world", " ")) == _e17); } { std::vector _e19 = {"", "", "hello world"}; PYSTRING_CHECK_ASSERT((pystring_rpartition("hello world", "xyz")) == _e19); } { std::vector _e21 = {"", "hello", ""}; PYSTRING_CHECK_ASSERT((pystring_rpartition("hello", "hello")) == _e21); } { std::vector _e23 = {"", "", ""}; PYSTRING_CHECK_ASSERT((pystring_rpartition("", "x")) == _e23); } { std::vector _e25 = {"aXb", "X", "c"}; PYSTRING_CHECK_ASSERT((pystring_rpartition("aXbXc", "X")) == _e25); } { std::vector _e27 = {"hello ", "world", ""}; PYSTRING_CHECK_ASSERT((pystring_rpartition("hello world", "world")) == _e27); } } PYSTRING_ADD_TEST(python3_compat, rsplit_python3) { { std::vector _e12 = {"hello world", "foo"}; PYSTRING_CHECK_ASSERT((pystring_rsplit("hello world foo", " ", 1)) == _e12); } { std::vector _e13 = {"a,b", "c", "d"}; PYSTRING_CHECK_ASSERT((pystring_rsplit("a,b,c,d", ",", 2)) == _e13); } { std::vector _e14 = {" hello", "world"}; PYSTRING_CHECK_ASSERT((pystring_rsplit(" hello world ", 1)) == _e14); } { std::vector _e15 = {"hello", "world"}; PYSTRING_CHECK_ASSERT((pystring_rsplit("hello world", -1)) == _e15); } } PYSTRING_ADD_TEST(python3_compat, split_python3) { { std::vector _e0 = {}; PYSTRING_CHECK_ASSERT((pystring_split("", -1)) == _e0); } { std::vector _e1 = {"hello", "world"}; PYSTRING_CHECK_ASSERT((pystring_split("hello world", -1)) == _e1); } { std::vector _e2 = {"hello", "world"}; PYSTRING_CHECK_ASSERT((pystring_split(" hello world ", -1)) == _e2); } { std::vector _e3 = {"hello", "world"}; PYSTRING_CHECK_ASSERT((pystring_split("hello world", " ", -1)) == _e3); } { std::vector _e4 = {"hello world"}; PYSTRING_CHECK_ASSERT((pystring_split("hello world", " ", 0)) == _e4); } { std::vector _e5 = {"hello", "world foo"}; PYSTRING_CHECK_ASSERT((pystring_split("hello world foo", " ", 1)) == _e5); } { std::vector _e6 = {"a", "b", "c", "d"}; PYSTRING_CHECK_ASSERT((pystring_split("a,b,c,d", ",", -1)) == _e6); } { std::vector _e7 = {"a", "b", "c,d"}; PYSTRING_CHECK_ASSERT((pystring_split("a,b,c,d", ",", 2)) == _e7); } { std::vector _e8 = {"a", "b", "c"}; PYSTRING_CHECK_ASSERT((pystring_split("aXXbXXc", "XX", -1)) == _e8); } { std::vector _e9 = {"hello"}; PYSTRING_CHECK_ASSERT((pystring_split("hello", "x", -1)) == _e9); } { std::vector _e10 = {}; PYSTRING_CHECK_ASSERT((pystring_split(" ", -1)) == _e10); } { std::vector _e11 = {"hello", "world"}; PYSTRING_CHECK_ASSERT((pystring_split("\thello\tworld\t", -1)) == _e11); } } PYSTRING_ADD_TEST(python3_compat, splitlines) { { std::vector _e28 = {}; PYSTRING_CHECK_ASSERT((pystring_splitlines("", false)) == _e28); } { std::vector _e29 = {}; PYSTRING_CHECK_ASSERT((pystring_splitlines("", true)) == _e29); } { std::vector _e30 = {"hello"}; PYSTRING_CHECK_ASSERT((pystring_splitlines("hello", false)) == _e30); } { std::vector _e31 = {"hello"}; PYSTRING_CHECK_ASSERT((pystring_splitlines("hello", true)) == _e31); } { std::vector _e32 = {"hello", "world"}; PYSTRING_CHECK_ASSERT((pystring_splitlines("hello\nworld", false)) == _e32); } { std::vector _e33 = {"hello\n", "world"}; PYSTRING_CHECK_ASSERT((pystring_splitlines("hello\nworld", true)) == _e33); } { std::vector _e34 = {"hello", "world"}; PYSTRING_CHECK_ASSERT((pystring_splitlines("hello\r\nworld", false)) == _e34); } { std::vector _e35 = {"hello\r\n", "world"}; PYSTRING_CHECK_ASSERT((pystring_splitlines("hello\r\nworld", true)) == _e35); } { std::vector _e36 = {"hello", "world"}; PYSTRING_CHECK_ASSERT((pystring_splitlines("hello\rworld", false)) == _e36); } { std::vector _e37 = {"hello\r", "world"}; PYSTRING_CHECK_ASSERT((pystring_splitlines("hello\rworld", true)) == _e37); } { std::vector _e38 = {"line1", "line2", "line3"}; PYSTRING_CHECK_ASSERT((pystring_splitlines("line1\nline2\nline3", false)) == _e38); } { std::vector _e39 = {"line1\n", "line2\n", "line3"}; PYSTRING_CHECK_ASSERT((pystring_splitlines("line1\nline2\nline3", true)) == _e39); } { std::vector _e40 = {"hello"}; PYSTRING_CHECK_ASSERT((pystring_splitlines("hello\n", false)) == _e40); } { std::vector _e41 = {"hello\n"}; PYSTRING_CHECK_ASSERT((pystring_splitlines("hello\n", true)) == _e41); } { std::vector _e42 = {"", "hello"}; PYSTRING_CHECK_ASSERT((pystring_splitlines("\nhello", false)) == _e42); } { std::vector _e43 = {"\n", "hello"}; PYSTRING_CHECK_ASSERT((pystring_splitlines("\nhello", true)) == _e43); } } imageworks-pystring-a055353/unittest.h000066400000000000000000000142131514717441600200510ustar00rootroot00000000000000// Copyright Contributors to the Pystring project. // SPDX-License-Identifier: BSD-3-Clause // https://github.com/imageworks/pystring/blob/master/LICENSE #ifndef INCLUDED_PYSTRING_UNITTEST_H #define INCLUDED_PYSTRING_UNITTEST_H #include #include #include extern int unit_test_failures; void unittest_fail(); typedef void (*PYSTRINGTestFunc)(); struct PYSTRINGTest { PYSTRINGTest(std::string testgroup, std::string testname, PYSTRINGTestFunc test) : group(testgroup), name(testname), function(test) { }; std::string group, name; PYSTRINGTestFunc function; }; typedef std::vector UnitTests; UnitTests& GetUnitTests(); struct AddTest { AddTest(PYSTRINGTest&& test); }; /// PYSTRING_CHECK_* macros checks if the conditions is met, and if not, /// prints an error message indicating the module and line where the /// error occurred, but does NOT abort. This is helpful for unit tests /// where we do not want one failure. #define PYSTRING_CHECK_ASSERT(x) \ ((x) ? ((void)0) \ : ((std::cout << __FILE__ << ":" << __LINE__ << ":\n" \ << "FAILED: " << #x << "\n"), \ (void)++unit_test_failures)) #define PYSTRING_CHECK_EQUAL(x,y) \ (((x) == (y)) ? ((void)0) \ : ((std::cout << __FILE__ << ":" << __LINE__ << ":\n" \ << "FAILED: " << #x << " == " << #y << "\n" \ << "\tvalues were '" << (x) << "' and '" << (y) << "'\n"), \ (void)++unit_test_failures)) #define PYSTRING_CHECK_NE(x,y) \ (((x) != (y)) ? ((void)0) \ : ((std::cout << __FILE__ << ":" << __LINE__ << ":\n" \ << "FAILED: " << #x << " != " << #y << "\n" \ << "\tvalues were '" << (x) << "' and '" << (y) << "'\n"), \ (void)++unit_test_failures)) #define PYSTRING_CHECK_LT(x,y) \ (((x) < (y)) ? ((void)0) \ : ((std::cout << __FILE__ << ":" << __LINE__ << ":\n" \ << "FAILED: " << #x << " < " << #y << "\n" \ << "\tvalues were '" << (x) << "' and '" << (y) << "'\n"), \ (void)++unit_test_failures)) #define PYSTRING_CHECK_GT(x,y) \ (((x) > (y)) ? ((void)0) \ : ((std::cout << __FILE__ << ":" << __LINE__ << ":\n" \ << "FAILED: " << #x << " > " << #y << "\n" \ << "\tvalues were '" << (x) << "' and '" << (y) << "'\n"), \ (void)++unit_test_failures)) #define PYSTRING_CHECK_LE(x,y) \ (((x) <= (y)) ? ((void)0) \ : ((std::cout << __FILE__ << ":" << __LINE__ << ":\n" \ << "FAILED: " << #x << " <= " << #y << "\n" \ << "\tvalues were '" << (x) << "' and '" << (y) << "'\n"), \ (void)++unit_test_failures)) #define PYSTRING_CHECK_GE(x,y) \ (((x) >= (y)) ? ((void)0) \ : ((std::cout << __FILE__ << ":" << __LINE__ << ":\n" \ << "FAILED: " << #x << " >= " << #y << "\n" \ << "\tvalues were '" << (x) << "' and '" << (y) << "'\n"), \ (void)++unit_test_failures)) #define PYSTRING_CHECK_CLOSE(x,y,tol) \ ((std::abs((x) - (y)) < tol) ? ((void)0) \ : ((std::cout << __FILE__ << ":" << __LINE__ << ":\n" \ << "FAILED: abs(" << #x << " - " << #y << ") < " << #tol << "\n" \ << "\tvalues were '" << (x) << "', '" << (y) << "' and '" << (tol) << "'\n"), \ (void)++unit_test_failures)) #define PYSTRING_CHECK_THOW(S, E) \ try { S; throw "throwanything"; } catch( E const& ex ) { } catch (...) { \ std::cout << __FILE__ << ":" << __LINE__ << ":\n" \ << "FAILED: " << #E << " is expected to be thrown\n"; \ ++unit_test_failures; } #define PYSTRING_CHECK_NO_THOW(S) \ try { S; } catch (...) { \ std::cout << __FILE__ << ":" << __LINE__ << ":\n" \ << "FAILED: exception thrown from " << #S <<"\n"; \ ++unit_test_failures; } #define PYSTRING_ADD_TEST(group, name) \ static void pystringtest_##group##_##name(); \ AddTest pystringaddtest_##group##_##name(PYSTRINGTest(#group, #name, pystringtest_##group##_##name)); \ static void pystringtest_##group##_##name() #define PYSTRING_TEST_SETUP() \ int unit_test_failures = 0 #define PYSTRING_TEST_APP(app) \ std::vector& GetUnitTests() { \ static std::vector pystring_unit_tests; \ return pystring_unit_tests; } \ AddTest::AddTest(PYSTRINGTest&& test){GetUnitTests().emplace_back(test);} \ PYSTRING_TEST_SETUP(); \ int main(int, char **) { std::cerr << "\n" << #app <<"\n\n"; \ for(size_t i = 0; i < GetUnitTests().size(); ++i) { \ int _tmp = unit_test_failures; GetUnitTests()[i].function(); \ std::cerr << "Test [" << GetUnitTests()[i].group << "] [" << GetUnitTests()[i].name << "] - "; \ std::cerr << (_tmp == unit_test_failures ? "PASSED" : "FAILED") << "\n"; } \ std::cerr << "\n" << unit_test_failures << " tests failed\n\n"; \ return unit_test_failures; } #endif