├── .travis.yml ├── CMakeLists.txt ├── LICENCE ├── README.md ├── cmake ├── GoogleTest-CMakeLists.txt.in └── tinyutf8Config.cmake.in ├── deps ├── install-deps-linux.sh ├── install-deps-osx.sh └── install-deps-win32.bat ├── docs ├── CMakeLists.txt ├── Layout.xlsx └── UTF8.png ├── include └── tinyutf8 │ └── tinyutf8.h └── test ├── CMakeLists.txt └── src ├── helpers ├── helpers_ssotestutils.cpp └── helpers_ssotestutils.h ├── mocks ├── mock_nothrowallocator.cpp ├── mock_nothrowallocator.h ├── mock_throwallocator.cpp └── mock_throwallocator.h ├── test_construction.cpp ├── test_conversion.cpp ├── test_iterators.cpp ├── test_manipulation.cpp ├── test_noexceptions.cpp └── test_search.cpp /.travis.yml: -------------------------------------------------------------------------------- 1 | os: 2 | - linux 3 | - osx 4 | - windows 5 | 6 | dist: focal 7 | 8 | language: cpp 9 | 10 | compiler: 11 | - gcc 12 | - clang 13 | 14 | install: 15 | - |- 16 | case $TRAVIS_OS_NAME in 17 | "windows") 18 | ./deps/install-deps-win32.bat 19 | ;; 20 | "linux") 21 | ./deps/install-deps-linux.sh 22 | ;; 23 | "osx") 24 | ./deps/install-deps-osx.sh 25 | ;; 26 | esac 27 | 28 | cache: 29 | directories: 30 | - vcpkg/installed 31 | 32 | script: 33 | - mkdir build 34 | - cd build 35 | - |- 36 | case $TRAVIS_OS_NAME in 37 | "windows") 38 | cmake -G "Visual Studio 15 2017" -T v141 -A x64 -DCMAKE_TOOLCHAIN_FILE=..\\vcpkg\\scripts\\buildsystems\\vcpkg.cmake .. 39 | cmake --build . --target ALL_BUILD --config Release 40 | cmake --build . --target RUN_TESTS --config Release 41 | ;; 42 | "linux"|"osx") 43 | cmake .. 44 | make 45 | make test 46 | ;; 47 | esac 48 | 49 | jobs: 50 | include: 51 | - os: linux 52 | dist: focal 53 | - os: osx 54 | osx_image: xcode9.4 55 | - os: windows 56 | dist: server-2016 57 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.8) 2 | project(tinyutf8 3 | VERSION 4.0.2 4 | DESCRIPTION "Unicode (UTF-8) capable std::string" 5 | HOMEPAGE_URL "https://github.com/DuffsDevice/tiny-utf8" 6 | LANGUAGES CXX 7 | ) 8 | 9 | # Make sure that custom modules are found 10 | list(INSERT CMAKE_MODULE_PATH 0 ${CMAKE_SOURCE_DIR}/cmake) 11 | 12 | ############################################## 13 | # Declare dependencies 14 | 15 | include(GNUInstallDirs) 16 | include(CTest) 17 | 18 | if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) 19 | set(IS_TOPLEVEL_PROJECT TRUE) 20 | else() 21 | set(IS_TOPLEVEL_PROJECT FALSE) 22 | endif() 23 | 24 | option(TINYUTF8_BUILD_TESTING "Build and run TinyUTF8 tests " ${IS_TOPLEVEL_PROJECT}) 25 | option(TINYUTF8_BUILD_DOC "Generate TinyUTF8 documentation" ${IS_TOPLEVEL_PROJECT}) 26 | 27 | # Set conformance with C++11 (with no compiler/vendor extensions) 28 | set(CMAKE_CXX_STANDARD 11) 29 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 30 | set(CMAKE_CXX_EXTENSIONS OFF) 31 | 32 | ############################################## 33 | # Create target and set properties 34 | 35 | add_library(${PROJECT_NAME} INTERFACE) 36 | 37 | # Add an alias so that library can be used inside the build tree, e.g. when testing 38 | add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) 39 | 40 | target_include_directories( 41 | ${PROJECT_NAME} 42 | INTERFACE 43 | $ 44 | $ 45 | ) 46 | 47 | target_compile_features( 48 | ${PROJECT_NAME} 49 | INTERFACE 50 | cxx_std_17 51 | ) 52 | 53 | ############################################## 54 | ## Add test 55 | 56 | if(BUILD_TESTING AND TINYUTF8_BUILD_TESTING) 57 | add_subdirectory(test) 58 | endif() 59 | 60 | ############################################## 61 | ## Add documentation 62 | 63 | if(TINYUTF8_BUILD_DOC) 64 | add_subdirectory(docs) 65 | endif() 66 | 67 | ############################################## 68 | # Installation instructions 69 | 70 | install( 71 | TARGETS ${PROJECT_NAME} 72 | # locations are provided by GNUInstallDirs 73 | EXPORT ${PROJECT_NAME}_Targets 74 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} 75 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 76 | RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} 77 | ) 78 | 79 | # Create a ConfigVersion.cmake file 80 | include(CMakePackageConfigHelpers) 81 | 82 | write_basic_package_version_file( 83 | "${PROJECT_NAME}ConfigVersion.cmake" 84 | VERSION ${PROJECT_VERSION} 85 | COMPATIBILITY SameMajorVersion 86 | ) 87 | 88 | configure_package_config_file( 89 | "${PROJECT_SOURCE_DIR}/cmake/${PROJECT_NAME}Config.cmake.in" 90 | "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" 91 | INSTALL_DESTINATION 92 | ${CMAKE_INSTALL_DATAROOTDIR}/${PROJECT_NAME}/cmake 93 | ) 94 | 95 | # Export the targets to a script 96 | install( 97 | EXPORT 98 | ${PROJECT_NAME}_Targets 99 | FILE 100 | ${PROJECT_NAME}Targets.cmake 101 | NAMESPACE 102 | ${PROJECT_NAME}:: 103 | DESTINATION 104 | ${CMAKE_INSTALL_DATAROOTDIR}/${PROJECT_NAME}/cmake 105 | ) 106 | 107 | install( 108 | FILES 109 | "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" 110 | "${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" 111 | DESTINATION 112 | ${CMAKE_INSTALL_DATAROOTDIR}/${PROJECT_NAME}/cmake 113 | ) 114 | 115 | install( 116 | DIRECTORY 117 | ${PROJECT_SOURCE_DIR}/include/${PROJECT_NAME} 118 | DESTINATION 119 | include) 120 | 121 | ############################################## 122 | ## Packaging the library 123 | 124 | set(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENCE") 125 | 126 | include(CPack) 127 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2015-2021 Jakob Riedle (DuffsDevice) 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TINY UTF8 Art 4.4 2 | 3 | [![Build Status](https://api.travis-ci.com/DuffsDevice/tiny-utf8.svg?branch=master)](https://travis-ci.com/github/DuffsDevice/tiny-utf8)  [![Licence](https://img.shields.io/badge/licence-BSD--3-e20000.svg)](https://github.com/DuffsDevice/tiny-utf8/blob/master/LICENCE)  [![Donation](https://img.shields.io/badge/buy%20me%20a%20coffee-paypal-fcd303.svg)](https://www.paypal.me/jakobriedle) 4 | 5 | ### DESCRIPTION 6 | **Tiny-utf8** is a library for extremely easy integration of Unicode into an arbitrary C++11 project. 7 | The library consists solely of the class `utf8_string`, which acts as a drop-in replacement for `std::string`. 8 | Its implementation is successfully in the middle between small memory footprint and fast access. All functionality of `std::string` is therefore replaced by the corresponding codepoint-based UTF-32 version - translating every access to UTF-8 under the hood. 9 | 10 | #### *CHANGES BETWEEN Version 4.4 and 4.3* 11 | 12 | - **tiny-utf8** used to only work with byte-index-based iterator types. The set of iterator types has now been completed with codepoint-based versions and 13 | - the **default has been changed**. That means (`c`)(`r`)`begin`/`end` now return codepoint-based iterators, while `raw_`(`c`)(`r`)`begin`/`end` now return byte-based iterators. 14 | - The upside with byte-based iterators is: they are usually quicker than code-point-based iterators. The downside is: They get invalidated **very quickly**. Example: 15 | `str.erase( std::remove( str.begin() , str.end() , U'W' ) , str.end() )` will work, but `str.erase( std::remove(`**`str.raw_begin()`**`,`**`str.raw_end()`**`, U'W' ) ,`**`str.raw_end()`**`)` will not (at least not always). The reason is: after the call to `std::remove`, the size of the string data might have changed and the second call to `str.raw_end()` might have yielded a now-invalidated iterator. 16 | 17 | ### FEATURES 18 | - **Drop-in replacement for `std::string`** 19 | - **Lightweight and self-contained** (~5K SLOC) 20 | - **Very fast**, i.e. highly optimized decoder, encoder and traversal routines 21 | - **Advanced Memory Layout**, i.e. Random Access is 22 | - ***O(1) for ASCII-only strings (!)*** and 23 | - O(#Codepoints ∉ ASCII) for the average case. 24 | - O(n) for strings with a high amount of non-ASCII code points (>25%) 25 | - **Small String Optimization** (SSO) for strings up to an UTF8-encoded length of `sizeof(utf8_string)`! That is, including the trailing `\0` 26 | - **Growth in Constant Time** (Amortized) 27 | - **On-the-fly Conversion between UTF32 and UTF8** 28 | - **`size()`** returns the size of the data **in bytes**, **`length()`** returns the number of **codepoints** contained. 29 | - Codepoint Range of `0x0` - `0xFFFFFFFF`, i.e. 1-7 Code Units/Bytes per Codepoint (Note: This is more than specified by UTF8, but until now otherwise considered out of scope) 30 | - Complete support for **embedded zeros** (Note: all methods taking `const char*`/`const char32_t*` also have an overload for `const char (&)[N]`/`const char32_t (&)[N]`, allowing correct interpretation of string literals with embedded zeros) 31 | - Single Header File 32 | - Straightforward C++11 Design 33 | - Possibility to prepend the UTF8 BOM (Byte Order Mark) to any string when converting it to an std::string 34 | - Supports raw (Byte-based) access for occasions where Speed is needed 35 | - Supports `shrink_to_fit()` 36 | - Malformed UTF8 sequences will **lead to defined behaviour** 37 | 38 | ## THE PURPOSE OF TINY-UTF8 39 | Back when I decided to write a UTF8 solution for C++, I knew I wanted a drop-in replacement for `std::string`. At the time mostly because I found it neat to have one and felt C++ always lacked accessible support for UTF8. Since then, several years have passed and the situation has not improved much. That said, things currently look like they are about to improve - but that doesn't say much, eh? 40 | 41 | The opinion shared by many "experienced Unicode programmers" (e.g. published on [UTF-8 Everywhere](https://www.utf8everywhere.org)) is that "non-experienced" programmers both *under* and *over*estimate the need for Unicode- and encoding-specific treatment: This need is... 42 | 1. **overestimated**, because many times we really should care less about codepoint/grapheme borders within string data; 43 | 2. **underestimated**, because if we really want to "support" unicode, we need to think about *normalizations*, *visual character comparisons*, *reserved codepoint values*, *illegal code unit sequences* and so on and so forth. 44 | 45 | Unicode is not rocket science but nonetheless hard to get *right*. **Tiny-utf8** does not intend to be an enterprise solution like [ICU](http://site.icu-project.org/) for C++. The goal of **tiny-utf8** is to 46 | - bridge as many gaps to "supporting Unicode" as possible by 'just' replacing `std::string` with a custom class which means to 47 | - provide you with a Codepoint Abstraction Layer that takes care of the Run-Length Encoding, without you noticing. 48 | 49 | **Tiny-utf8** aims to be the simple-and-dependable groundwork which you build Unicode infrastructure upon. And, if *1)* C++2xyz should happen to make your Unicode life easier than **tiny-utf8** or *2)* you decide to go enterprise, you have not wasted much time replacing `std::string` with `tiny_utf8::string` either. That's what makes **tiny-utf8** so agreeable. 50 | 51 | #### WHAT TINY-UTF8 IS NOT AIMED AT 52 | - Conversion between ISO encodings and UTF8 53 | - Interfacing with UTF16 54 | - Visible character comparison (`'ch'` vs. `'c'+'h'`) 55 | - Codepoint Normalization 56 | - Correction of invalid Code Unit sequences 57 | - Detection of Grapheme Clusters 58 | 59 | Note: ANSI suppport was dropped in Version 2.0 in favor of execution speed. 60 | 61 | ## EXAMPLE 62 | 63 | ```cpp 64 | #include 65 | #include 66 | #include 67 | using namespace std; 68 | 69 | int main() 70 | { 71 | tiny_utf8::string str = u8"!🌍 olleH"; 72 | for_each( str.rbegin() , str.rend() , []( char32_t codepoint ){ 73 | cout << codepoint; 74 | } ); 75 | return 0; 76 | } 77 | ``` 78 | 79 | ## EXCEPTION BEHAVIOR 80 | 81 | - **Tiny-utf8** should automatically detect, whether your build system allows the use of exceptions or not. This is done by checking for the feature test macro `__cpp_exceptions`. 82 | - If you would like **tiny-utf8** to be `noexcept` anyway, `#define` the macro `TINY_UTF8_NOEXCEPT`. 83 | - If you would like **tiny-utf8** to use a different exception strategy, `#define` the macro `TINY_UTF8_THROW( location , failing_predicate )`. For using assertions, you would write `#define TINY_UTF8_THROW( _ , pred ) assert( pred )`. 84 | - *Hint:* If exceptions are disabled, `TINY_UTF8_THROW( ... )` is automatically defined as `void()`. This works well, because all uses of `TINY_UTF8_THROW` are immediately followed by a `;` as well as a proper `return` statement with a fallback value. That also means, `TINY_UTF8_THROW` can safely be a NO-OP. 85 | 86 | ## BACKWARDS-COMPATIBILITY 87 | 88 | #### *CHANGES BETWEEN Version 4.3 and 4.2* 89 | 90 | - Class `tiny_utf8::basic_utf8_string` has been renamed to `basic_string`, which better resembles its drop-in-capabilities for `std::string`. 91 | 92 | #### *CHANGES BETWEEN Version 4.1 and 4.0* 93 | 94 | - `tinyutf8.h` has been moved into the folder `include/tinyutf8/` in order to mimic the structuring of many other C++-based open source projects. 95 | 96 | #### *CHANGES BETWEEN Version 4.0 and 3.2.4* 97 | 98 | - Class `utf8_string` is now defined inside `namespace tiny_utf8`. If you want the old declaration in the global namespace, `#define TINY_UTF8_GLOBAL_NAMESPACE` 99 | - Support for C++20: Use class `tiny_utf8::u8string`, which uses `char8_t` as underlying data type (instead of `char`) 100 | 101 | #### *CHANGES BETWEEN Version 4.0 and Version 3.2* 102 | 103 | - If you would like to stay compatible with 3.2.* and have `utf8_string` defined in the global namespace, `#define` the macro `TINY_UTF8_GLOBAL_NAMESPACE`. 104 | 105 | ## BUGS 106 | 107 | If you encounter any bugs, please file a bug report through the "Issues" tab. 108 | I'll try to answer it soon! 109 | 110 | ## THANK YOU 111 | 112 | - @iainchesworth 113 | - @vadim-berman 114 | - @MattHarrington 115 | - @evanmoran 116 | - @bakerstu 117 | - @revel8n 118 | - @githubuser0xFFFF 119 | - @marekfoltyn 120 | - @Megaxela 121 | - @vfiksdal 122 | - @maddouri 123 | - @Abdullah-AlAttar 124 | - @s9w 125 | 126 | for taking your time to improve **tiny-utf8**. 127 | 128 | Cheers, 129 | Jakob 130 | -------------------------------------------------------------------------------- /cmake/GoogleTest-CMakeLists.txt.in: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5.0) 2 | 3 | project(googletest-download NONE) 4 | 5 | include(ExternalProject) 6 | ExternalProject_Add(googletest 7 | GIT_REPOSITORY https://github.com/google/googletest.git 8 | GIT_TAG master 9 | SOURCE_DIR "${CMAKE_BINARY_DIR}/googletest-src" 10 | BINARY_DIR "${CMAKE_BINARY_DIR}/googletest-build" 11 | CONFIGURE_COMMAND "" 12 | BUILD_COMMAND "" 13 | INSTALL_COMMAND "" 14 | TEST_COMMAND "" 15 | ) 16 | -------------------------------------------------------------------------------- /cmake/tinyutf8Config.cmake.in: -------------------------------------------------------------------------------- 1 | @PACKAGE_INIT@ 2 | 3 | include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") 4 | check_required_components("@PROJECT_NAME@") 5 | -------------------------------------------------------------------------------- /deps/install-deps-linux.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo "Installing dependencies..." 3 | sudo apt update 4 | sudo apt -y install googletest google-mock libgtest-dev libgmock-dev doxygen graphviz 5 | -------------------------------------------------------------------------------- /deps/install-deps-osx.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo "Installing dependencies..." 3 | 4 | mkdir -p googletest 5 | cd googletest 6 | 7 | git init 8 | git remote add origin https://github.com/google/googletest 9 | git fetch origin 10 | git checkout -b master --track origin/master 11 | 12 | mkdir -p build 13 | cd build 14 | 15 | cmake -DCMAKE_CXX_STANDARD=17 .. 16 | 17 | make 18 | make install 19 | 20 | cd ../.. 21 | -------------------------------------------------------------------------------- /deps/install-deps-win32.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | echo "Installing dependencies..." 3 | 4 | if not exist "vcpkg" mkdir "vcpkg" 5 | cd vcpkg 6 | 7 | git init 8 | git remote add origin https://github.com/microsoft/vcpkg.git 9 | git fetch origin 10 | git checkout -b master --track origin/master 11 | 12 | cmd /C bootstrap-vcpkg.bat 13 | 14 | vcpkg.exe install gtest:x86-windows gtest:x64-windows 15 | 16 | cd .. 17 | -------------------------------------------------------------------------------- /docs/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.8) 2 | 3 | find_package(Doxygen) 4 | 5 | if(NOT Doxygen_FOUND) 6 | message( 7 | WARNING 8 | "Doxygen not found. Targets for building documentation are not available") 9 | return() 10 | endif() 11 | 12 | set(DOXYGEN_EXTRACT_ALL YES) 13 | set(DOXYGEN_BUILTIN_STL_SUPPORT YES) 14 | set(DOXYGEN_USE_MDFILE_AS_MAINPAGE "${PROJECT_SOURCE_DIR}/README.md") 15 | set(DOXYGEN_PROJECT_LOGO "${PROJECT_SOURCE_DIR}/docs/UTF8.png") 16 | 17 | doxygen_add_docs( 18 | api_doc "${PROJECT_SOURCE_DIR}/README.md" "${PROJECT_SOURCE_DIR}/include" 19 | WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/include") 20 | -------------------------------------------------------------------------------- /docs/Layout.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DuffsDevice/tiny-utf8/263f7de70b14324988eae4e0e08c9f36037384ad/docs/Layout.xlsx -------------------------------------------------------------------------------- /docs/UTF8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DuffsDevice/tiny-utf8/263f7de70b14324988eae4e0e08c9f36037384ad/docs/UTF8.png -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.8) 2 | 3 | find_package(GTest REQUIRED) 4 | include(GoogleTest) 5 | 6 | add_executable(tinyutf8_test) 7 | 8 | target_sources( 9 | tinyutf8_test 10 | PRIVATE 11 | src/test_construction.cpp 12 | src/test_conversion.cpp 13 | src/test_iterators.cpp 14 | src/test_manipulation.cpp 15 | src/test_noexceptions.cpp 16 | src/test_search.cpp 17 | src/mocks/mock_nothrowallocator.cpp 18 | src/mocks/mock_throwallocator.cpp 19 | src/helpers/helpers_ssotestutils.cpp 20 | ) 21 | 22 | target_include_directories( 23 | tinyutf8_test 24 | PRIVATE 25 | $ 26 | $ 27 | ) 28 | 29 | target_compile_features(tinyutf8_test PRIVATE cxx_std_11) 30 | 31 | target_link_libraries( 32 | tinyutf8_test 33 | PRIVATE 34 | tinyutf8::tinyutf8 35 | GTest::GTest 36 | GTest::Main) 37 | 38 | 39 | set_target_properties( 40 | tinyutf8_test 41 | PROPERTIES 42 | CXX_STANDARD 11 43 | CXX_STANDARD_REQUIRED YES 44 | CXX_EXTENSIONS NO 45 | ) 46 | 47 | enable_testing() 48 | 49 | gtest_discover_tests(tinyutf8_test) 50 | -------------------------------------------------------------------------------- /test/src/helpers/helpers_ssotestutils.cpp: -------------------------------------------------------------------------------- 1 | #include "helpers_ssotestutils.h" 2 | 3 | -------------------------------------------------------------------------------- /test/src/helpers/helpers_ssotestutils.h: -------------------------------------------------------------------------------- 1 | #ifndef HELPERS_SSOTESTUTILS 2 | #define HELPERS_SSOTESTUTILS 3 | 4 | #include 5 | 6 | #include 7 | 8 | #include 9 | 10 | namespace Helpers_SSOTestUtils 11 | { 12 | 13 | template 14 | class SSO_Capacity : public BASIC_STRING 15 | { 16 | public: 17 | static constexpr typename BASIC_STRING::size_type get_sso_capacity() noexcept 18 | { 19 | return BASIC_STRING::get_sso_capacity(); 20 | } 21 | }; 22 | 23 | } 24 | // namespace Helpers_SSOTestUtils 25 | 26 | #endif // HELPERS_SSOTESTUTILS 27 | -------------------------------------------------------------------------------- /test/src/mocks/mock_nothrowallocator.cpp: -------------------------------------------------------------------------------- 1 | #include "mock_nothrowallocator.h" 2 | -------------------------------------------------------------------------------- /test/src/mocks/mock_nothrowallocator.h: -------------------------------------------------------------------------------- 1 | #ifndef MOCK_NOTHROWALLOCATOR_H_ 2 | #define MOCK_NOTHROWALLOCATOR_H_ 3 | 4 | #include 5 | 6 | template 7 | class NoThrowAllocator 8 | { 9 | public: 10 | using value_type = T; 11 | 12 | template 13 | struct rebind 14 | { 15 | using other = NoThrowAllocator; 16 | }; 17 | 18 | NoThrowAllocator() noexcept 19 | { 20 | } 21 | 22 | NoThrowAllocator(const NoThrowAllocator&) noexcept 23 | { 24 | } 25 | 26 | NoThrowAllocator(NoThrowAllocator&&) noexcept 27 | { 28 | } 29 | 30 | template 31 | NoThrowAllocator(const NoThrowAllocator&) noexcept 32 | { 33 | } 34 | 35 | template 36 | NoThrowAllocator(NoThrowAllocator&&) noexcept 37 | { 38 | } 39 | 40 | T* allocate(std::size_t n, const void* hint = 0) noexcept 41 | { 42 | (void)n; 43 | return nullptr; 44 | } 45 | 46 | void deallocate(T* p, std::size_t n) noexcept 47 | { 48 | (void)p; 49 | (void)n; 50 | } 51 | }; 52 | 53 | template 54 | constexpr bool operator== (const NoThrowAllocator&, const NoThrowAllocator&) noexcept 55 | { 56 | return true; 57 | } 58 | 59 | template 60 | constexpr bool operator!= (const NoThrowAllocator&, const NoThrowAllocator&) noexcept 61 | { 62 | return false; 63 | } 64 | 65 | #endif // MOCK_NOTHROWALLOCATOR_H_ 66 | -------------------------------------------------------------------------------- /test/src/mocks/mock_throwallocator.cpp: -------------------------------------------------------------------------------- 1 | #include "mock_throwallocator.h" 2 | -------------------------------------------------------------------------------- /test/src/mocks/mock_throwallocator.h: -------------------------------------------------------------------------------- 1 | #ifndef MOCK_THROWALLOCATOR_H_ 2 | #define MOCK_THROWALLOCATOR_H_ 3 | 4 | #include 5 | #include // For std::bad_alloc (Linux and Windows) 6 | #include // For std::bad_alloc (MacOS) 7 | 8 | template 9 | class ThrowAllocator 10 | { 11 | public: 12 | using value_type = T; 13 | 14 | template 15 | struct rebind 16 | { 17 | using other = ThrowAllocator; 18 | }; 19 | 20 | ThrowAllocator() 21 | { 22 | } 23 | 24 | ThrowAllocator(const ThrowAllocator&) 25 | { 26 | } 27 | 28 | ThrowAllocator(ThrowAllocator&&) 29 | { 30 | } 31 | 32 | template 33 | ThrowAllocator(const ThrowAllocator&) 34 | { 35 | } 36 | 37 | template 38 | ThrowAllocator(ThrowAllocator&&) 39 | { 40 | } 41 | 42 | T* allocate(std::size_t n, const void* hint = 0) 43 | { 44 | throw std::bad_alloc(); 45 | } 46 | 47 | void deallocate(T* p, std::size_t n) 48 | { 49 | (void)p; 50 | (void)n; 51 | } 52 | }; 53 | 54 | template 55 | constexpr bool operator== (const ThrowAllocator&, const ThrowAllocator&) 56 | { 57 | return true; 58 | } 59 | 60 | template 61 | constexpr bool operator!= (const ThrowAllocator&, const ThrowAllocator&) 62 | { 63 | return false; 64 | } 65 | 66 | #endif // MOCK_THROWALLOCATOR_H_ 67 | -------------------------------------------------------------------------------- /test/src/test_construction.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | #include "helpers/helpers_ssotestutils.h" 9 | 10 | TEST(TinyUTF8, CTor_TakeALiteral) 11 | { 12 | tiny_utf8::string str(U"TEST: ツ♫"); 13 | 14 | EXPECT_EQ(str.length(), 8); 15 | EXPECT_EQ(str.size(), 12); 16 | EXPECT_TRUE(str.requires_unicode()); 17 | EXPECT_TRUE(str.sso_active()); 18 | EXPECT_FALSE(str.lut_active()); 19 | EXPECT_EQ(static_cast(str[6]), 12484); 20 | } 21 | 22 | TEST(TinyUTF8, CTor_TakeALiteral_SSOAndNoSSO) 23 | { 24 | using test_utf8_string = tiny_utf8::basic_string>; 25 | 26 | constexpr uint32_t TEST_LITERAL_U_LENGTH = 1; 27 | constexpr uint32_t TEST_STRING_LENGTH = 100; 28 | 29 | const char TEST_LITERAL_T = 'T'; 30 | const char TEST_LITERAL_U[TEST_LITERAL_U_LENGTH] = { 'U' }; 31 | const char TEST_STRING[TEST_STRING_LENGTH] = "This is a test string...This is a test string...This is a test string...This is a test string..."; 32 | 33 | // Calls -> Constructor that fills the string with the supplied character 34 | test_utf8_string str_literal(TEST_LITERAL_T, std::allocator()); 35 | 36 | // Calls -> Constructor taking an utf8 char literal (SSO) 37 | ASSERT_LT(TEST_LITERAL_U_LENGTH, Helpers_SSOTestUtils::SSO_Capacity::get_sso_capacity()); 38 | test_utf8_string str_literal_sso(TEST_LITERAL_U, std::allocator()); 39 | EXPECT_EQ(TEST_LITERAL_U_LENGTH, str_literal_sso.length()); 40 | EXPECT_EQ(TEST_LITERAL_U_LENGTH, str_literal_sso.size()); 41 | 42 | // Calls -> Constructor taking an utf8 char literal (NO SSO) 43 | ASSERT_GT(TEST_STRING_LENGTH, Helpers_SSOTestUtils::SSO_Capacity::get_sso_capacity()); 44 | test_utf8_string str_literal_nosso(TEST_STRING, std::allocator()); 45 | EXPECT_EQ(TEST_STRING_LENGTH-1, str_literal_nosso.length()); // Note that -1 accounts for null terminator 46 | EXPECT_EQ(TEST_STRING_LENGTH-1, str_literal_nosso.size()); // Note that -1 accounts for null terminator 47 | 48 | } 49 | 50 | TEST(TinyUTF8, CTor_TakeALiteralWithMaxCodePoints) 51 | { 52 | tiny_utf8::string str(U"ツ♫", 1); 53 | 54 | EXPECT_EQ(str.length(), 1); 55 | EXPECT_EQ(str.size(), 3); 56 | EXPECT_TRUE(str.requires_unicode()); 57 | EXPECT_TRUE(str.sso_active()); 58 | EXPECT_FALSE(str.lut_active()); 59 | EXPECT_EQ(static_cast(str[0]), 12484); 60 | } 61 | 62 | TEST(TinyUTF8, CTor_TakeAnAnsiString) 63 | { 64 | const std::string ansi_str("Loewen, Boeren, Voegel und Koefer sind Tiere."); 65 | tiny_utf8::string str(ansi_str); 66 | 67 | EXPECT_EQ(ansi_str.length(), 45); 68 | EXPECT_EQ(ansi_str.size(), 45); 69 | EXPECT_EQ(str.length(), 45); 70 | EXPECT_EQ(str.size(), 45); 71 | 72 | EXPECT_FALSE(str.requires_unicode()); 73 | EXPECT_FALSE(str.sso_active()); 74 | EXPECT_TRUE(str.lut_active()); 75 | } 76 | 77 | TEST(TinyUTF8, CTor_TakeAStringView) 78 | { 79 | const std::string str_orig("Löwen, Bären, Vögel und Käfer sind Tiere."); 80 | const std::string_view str_view(str_orig); 81 | tiny_utf8::string str(str_view); 82 | 83 | EXPECT_EQ(str_view.length(), 45); 84 | EXPECT_EQ(str_view.size(), 45); 85 | EXPECT_EQ(str.length(), 41); 86 | EXPECT_EQ(str.size(), 45); 87 | 88 | EXPECT_TRUE(str.requires_unicode()); 89 | EXPECT_FALSE(str.sso_active()); 90 | EXPECT_TRUE(str.lut_active()); 91 | } 92 | 93 | TEST(TinyUTF8, CopyCTor) 94 | { 95 | tiny_utf8::string str_orig(U"Hello ツ World"); 96 | tiny_utf8::string str(str_orig); 97 | 98 | EXPECT_EQ(str.length(), 15); 99 | EXPECT_EQ(str.size(), 17); 100 | EXPECT_TRUE(str.requires_unicode()); 101 | EXPECT_TRUE(str.sso_active()); 102 | EXPECT_FALSE(str.lut_active()); 103 | EXPECT_EQ(static_cast(str[8]), 32); 104 | } 105 | -------------------------------------------------------------------------------- /test/src/test_conversion.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include 6 | 7 | TEST(TinyUTF8, ToWideLiteral) 8 | { 9 | tiny_utf8::string str(std::string("Löwen, Bären, Vögel und Käfer sind Tiere.")); 10 | 11 | std::unique_ptr ptr{ new char32_t[str.length() + 1] }; 12 | str.to_wide_literal(ptr.get()); 13 | 14 | tiny_utf8::string::const_iterator it_fwd = str.begin(); 15 | for (size_t chcount = 0; chcount < str.length(); ++chcount) 16 | { 17 | EXPECT_TRUE((static_cast(str[chcount])) == (static_cast(ptr[chcount]))); 18 | ++it_fwd; 19 | } 20 | } 21 | 22 | TEST(TinyUTF8, ToCppStr) 23 | { 24 | const std::string str_orig("Löwen, Bären, Vögel und Käfer sind Tiere."); 25 | tiny_utf8::string str(str_orig); 26 | EXPECT_EQ(str.cpp_str(), str_orig); 27 | EXPECT_EQ(str.cpp_str_view(), str_orig); 28 | } 29 | -------------------------------------------------------------------------------- /test/src/test_iterators.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include 6 | 7 | TEST(TinyUTF8, IteratorAccess) 8 | { 9 | const tiny_utf8::string str = U"Hello ツ World ♫"; 10 | 11 | EXPECT_TRUE(str.requires_unicode()); 12 | EXPECT_TRUE(str.sso_active()); 13 | 14 | tiny_utf8::string str_fwd; 15 | tiny_utf8::string str_rev; 16 | 17 | std::copy(str.begin(), str.end(), str_fwd.begin()); // Copy using forward iterators 18 | std::copy(str.rbegin(), str.rend(), str_rev.begin()); // Copy using reverse iterators 19 | 20 | EXPECT_TRUE( 21 | std::equal(str.begin(), str.end(), str_fwd.begin(), 22 | [](const tiny_utf8::string::value_type& a, const tiny_utf8::string::value_type& b) -> bool 23 | { 24 | return ((static_cast(a)) == (static_cast(b))); 25 | } 26 | ) 27 | ); 28 | 29 | tiny_utf8::string::const_iterator it_src = str.end(); 30 | tiny_utf8::string::const_iterator it_rev = str_rev.begin(); 31 | for ( /* NOTHING HERE */; it_src != str.begin(); /* NOTHING HERE */) 32 | { 33 | --it_src; 34 | 35 | EXPECT_TRUE((static_cast(*it_src)) == (static_cast(*it_rev))); 36 | 37 | ++it_rev; 38 | } 39 | 40 | tiny_utf8::string::const_iterator it_fwd = str.begin(); 41 | for (size_t chcount = 0; chcount < str.length(); ++chcount) 42 | { 43 | EXPECT_TRUE((static_cast(str[chcount])) == (static_cast(*it_fwd))); 44 | ++it_fwd; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /test/src/test_manipulation.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | TEST(TinyUTF8, AppendString) 9 | { 10 | // Test 2 11 | tiny_utf8::string str = U"Hello ツ"; 12 | str.append(U" ♫ World"); 13 | 14 | EXPECT_EQ(str.length(), 15); 15 | EXPECT_EQ(str.size(), 19); 16 | EXPECT_TRUE(str.requires_unicode()); 17 | EXPECT_TRUE(str.sso_active()); 18 | EXPECT_FALSE(str.lut_active()); 19 | 20 | tiny_utf8::string tmp = U" ♫ World"; 21 | str.append(tmp); 22 | 23 | EXPECT_EQ(str.length(), 23); 24 | EXPECT_EQ(str.size(), 29); 25 | EXPECT_TRUE(str.requires_unicode()); 26 | EXPECT_TRUE(str.sso_active()); 27 | EXPECT_FALSE(str.lut_active()); 28 | } 29 | 30 | TEST(TinyUTF8, AppendAndShrinkString) 31 | { 32 | tiny_utf8::string str(U"TEST: ツ♫"); 33 | 34 | EXPECT_EQ(str.capacity(), 31); 35 | 36 | for (uint64_t iteration = 0; iteration < 5; ++iteration) 37 | { 38 | str.append(str); // yes, yes...self-append! 39 | 40 | switch (iteration) 41 | { 42 | case 0: 43 | EXPECT_EQ(str.capacity(), 31); 44 | EXPECT_EQ(str.length(), 16); 45 | EXPECT_EQ(str.size(), 24); 46 | EXPECT_FALSE(str.lut_active()); 47 | break; 48 | case 1: 49 | EXPECT_EQ(str.capacity(), 72); 50 | EXPECT_EQ(str.length(), 32); 51 | EXPECT_EQ(str.size(), 48); 52 | EXPECT_TRUE(str.lut_active()); 53 | break; 54 | case 2: 55 | EXPECT_EQ(str.capacity(), 72); 56 | EXPECT_EQ(str.length(), 64); 57 | EXPECT_EQ(str.size(), 96); 58 | EXPECT_TRUE(str.lut_active()); 59 | break; 60 | case 3: 61 | EXPECT_EQ(str.capacity(), 231); 62 | EXPECT_EQ(str.length(), 128); 63 | EXPECT_EQ(str.size(), 192); 64 | EXPECT_TRUE(str.lut_active()); 65 | break; 66 | case 4: 67 | EXPECT_EQ(str.capacity(), 519); 68 | EXPECT_EQ(str.length(), 256); 69 | EXPECT_EQ(str.size(), 384); 70 | EXPECT_TRUE(str.lut_active()); 71 | break; 72 | case 5: 73 | EXPECT_EQ(str.capacity(), 519); 74 | EXPECT_EQ(str.length(), 512); 75 | EXPECT_EQ(str.size(), 768); 76 | EXPECT_TRUE(str.lut_active()); 77 | break; 78 | } 79 | } 80 | 81 | str.shrink_to_fit(); 82 | 83 | EXPECT_EQ(str.capacity(), 259); 84 | EXPECT_EQ(str.length(), 256); 85 | EXPECT_EQ(str.size(), 384); 86 | EXPECT_TRUE(str.lut_active()); 87 | } 88 | 89 | TEST(TinyUTF8, EraseString) 90 | { 91 | tiny_utf8::string str = U"Hello ツ World ♫"; 92 | 93 | EXPECT_EQ(str.length(), 15); 94 | EXPECT_EQ(str.size(), 19); 95 | EXPECT_TRUE(str.requires_unicode()); 96 | EXPECT_TRUE(str.sso_active()); 97 | EXPECT_FALSE(str.lut_active()); 98 | 99 | auto res1 = str.erase(14); 100 | 101 | EXPECT_EQ(res1, str.end()); 102 | EXPECT_EQ(str.length(), 14); 103 | EXPECT_EQ(str.size(), 16); 104 | EXPECT_TRUE(str.requires_unicode()); 105 | EXPECT_TRUE(str.sso_active()); 106 | 107 | auto res2 = str.erase(str.begin(), str.begin() + 9); 108 | 109 | EXPECT_EQ(res2, str.begin()); 110 | EXPECT_EQ(str.length(), 5); 111 | EXPECT_EQ(str.size(), 5); 112 | EXPECT_FALSE(str.requires_unicode()); 113 | EXPECT_TRUE(str.sso_active()); 114 | 115 | auto res3 = str.erase(str.begin() + 2); 116 | 117 | EXPECT_EQ(res3, str.begin() + 2); 118 | EXPECT_EQ(str.length(), 4); 119 | EXPECT_EQ(str.size(), 4); 120 | EXPECT_FALSE(str.requires_unicode()); 121 | EXPECT_TRUE(str.sso_active()); 122 | } 123 | 124 | TEST(TinyUTF8, SubString) 125 | { 126 | const tiny_utf8::string fullstr(U"Hello ツ World rg rth rt he rh we gxgre"); 127 | 128 | const tiny_utf8::string str = fullstr.substr(3, 16); 129 | const tiny_utf8::string substr(U"lo ツ World rg rt"); 130 | 131 | tiny_utf8::string::const_iterator it_str = str.begin(); 132 | tiny_utf8::string::const_iterator it_sub = substr.begin(); 133 | for ( /* NOTHING HERE */; it_sub != substr.end(); /* NOTHING HERE */) 134 | { 135 | EXPECT_TRUE((static_cast(*it_str)) == (static_cast(*it_sub))); 136 | 137 | ++it_str; 138 | ++it_sub; 139 | } 140 | 141 | EXPECT_TRUE(str.sso_active()); 142 | EXPECT_FALSE(str.lut_active()); 143 | 144 | EXPECT_EQ(static_cast(str[6]), static_cast(U'o')); 145 | } 146 | 147 | TEST(TinyUTF8, ReplaceString) 148 | { 149 | tiny_utf8::string str(U"Hello ツ World"); 150 | 151 | const std::string str_repl1("Hello World"); 152 | const tiny_utf8::string str_repl2(U"Hello~ 🤝 ~World"); 153 | 154 | const char32_t ch_repl1 = U'ツ'; 155 | const char32_t ch_repl2 = U'🤝'; 156 | 157 | EXPECT_EQ(static_cast(str[6]), static_cast(ch_repl1)); 158 | EXPECT_EQ(str.length(), 13); 159 | EXPECT_TRUE(str.requires_unicode()); 160 | EXPECT_TRUE(str.sso_active()); 161 | EXPECT_FALSE(str.lut_active()); 162 | 163 | str[6] = ch_repl2; 164 | EXPECT_EQ(static_cast(str[6]), static_cast(ch_repl2)); 165 | EXPECT_EQ(str.length(), 13); 166 | EXPECT_TRUE(str.requires_unicode()); 167 | 168 | str.replace(5, 3, " "); 169 | EXPECT_TRUE( 170 | std::equal(str.begin(), str.end(), str_repl1.begin(), 171 | [](const tiny_utf8::string::value_type& a, const tiny_utf8::string::value_type& b) -> bool 172 | { 173 | return ((static_cast(a)) == (static_cast(b))); 174 | } 175 | ) 176 | ); 177 | EXPECT_EQ(str.length(), 11); 178 | EXPECT_FALSE(str.requires_unicode()); 179 | 180 | str.replace(str.begin() + 5, str.begin() + 6, U"~ 🤝 ~"); // Orig: 181 | EXPECT_TRUE( 182 | std::equal(str.begin(), str.end(), str_repl2.begin(), 183 | [](const tiny_utf8::string::value_type& a, const tiny_utf8::string::value_type& b) -> bool 184 | { 185 | return ((static_cast(a)) == (static_cast(b))); 186 | } 187 | ) 188 | ); 189 | EXPECT_EQ(str.length(), 15); 190 | EXPECT_TRUE(str.requires_unicode()); 191 | } 192 | -------------------------------------------------------------------------------- /test/src/test_noexceptions.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #define TINY_UTF8_NOEXCEPT true 7 | #include 8 | 9 | #include "mocks/mock_nothrowallocator.h" 10 | #include "mocks/mock_throwallocator.h" 11 | 12 | TEST(TinyUTF8, CTor_TakeALiteral_WithAllocator_Throw) 13 | { 14 | // Make sure that the mock allocator will throw an exception... 15 | 16 | ASSERT_FALSE(std::is_nothrow_constructible>::value); 17 | ASSERT_FALSE(std::is_nothrow_copy_constructible>::value); 18 | ASSERT_FALSE(std::is_nothrow_move_constructible>::value); 19 | 20 | using alloc_utf8_string = tiny_utf8::basic_string>; 21 | 22 | const char TEST_LITERAL[1] = { 'R' }; 23 | const char TEST_STRING[29] = "This Is A Const Char* String"; 24 | 25 | auto x = alloc_utf8_string(TEST_LITERAL, ThrowAllocator()); 26 | const bool ThrowCTor = std::is_nothrow_constructible::value; 27 | const bool ThrowCopyCTor = std::is_nothrow_constructible::value; 28 | const bool ThrowMoveCTor = std::is_nothrow_move_constructible::value; 29 | 30 | EXPECT_FALSE(ThrowCTor); 31 | EXPECT_FALSE(ThrowCopyCTor); 32 | EXPECT_FALSE(ThrowMoveCTor); 33 | alloc_utf8_string str(TEST_LITERAL); 34 | } 35 | 36 | TEST(TinyUTF8, CTor_TakeALiteral_WithAllocator_NoThrow) 37 | { 38 | // Make sure that the mock allocator will NOT throw an exception... 39 | 40 | ASSERT_TRUE(std::is_nothrow_constructible>::value); 41 | ASSERT_TRUE(std::is_nothrow_copy_constructible>::value); 42 | ASSERT_TRUE(std::is_nothrow_move_constructible>::value); 43 | 44 | using alloc_utf8_string = tiny_utf8::basic_string>; 45 | 46 | const char TEST_LITERAL[1] = { 'R' }; 47 | const char TEST_STRING[29] = "This Is A Const Char* String"; 48 | 49 | auto x = alloc_utf8_string(TEST_LITERAL, NoThrowAllocator()); 50 | const bool NoThrowCTor = std::is_nothrow_constructible::value; 51 | const bool NoThrowCopyCTor = std::is_nothrow_constructible::value; 52 | const bool NoThrowMoveCTor = std::is_nothrow_move_constructible::value; 53 | 54 | EXPECT_TRUE(NoThrowCTor); 55 | EXPECT_TRUE(NoThrowCopyCTor); 56 | EXPECT_TRUE(NoThrowMoveCTor); 57 | alloc_utf8_string str(TEST_LITERAL); 58 | } 59 | -------------------------------------------------------------------------------- /test/src/test_search.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | TEST(TinyUTF8, FindSubstr) 6 | { 7 | tiny_utf8::string str = U"Hello World ツ♫"; 8 | 9 | const char32_t* find_last_not_of = U"ツ♫"; 10 | const char32_t* find_last_of = U"e"; 11 | const char32_t find = U'l'; 12 | const char32_t rfind = U'l'; 13 | 14 | EXPECT_EQ(str.find_last_not_of(find_last_not_of), 11); 15 | EXPECT_EQ(str.find_last_of(find_last_of), 1); 16 | EXPECT_EQ(str.find(find), 2); 17 | EXPECT_EQ(str.rfind(rfind), 9); 18 | } 19 | 20 | TEST(TinyUTF8, StartsEndsWith) 21 | { 22 | tiny_utf8::string str = U"Hello World ツ♫"; 23 | 24 | const char32_t* ends_with_positive = U"ツ♫"; 25 | const char32_t* ends_with_negative = U"e"; 26 | const char32_t* starts_with_positive = U"Hello "; 27 | const char32_t* starts_with_negative = U"Hell "; 28 | 29 | EXPECT_EQ(str.ends_with(ends_with_positive), true); 30 | EXPECT_EQ(str.ends_with(ends_with_negative), false); 31 | EXPECT_EQ(str.ends_with(tiny_utf8::string(ends_with_positive)), true); 32 | EXPECT_EQ(str.ends_with(tiny_utf8::string(ends_with_negative)), false); 33 | EXPECT_EQ(str.starts_with(starts_with_positive), true); 34 | EXPECT_EQ(str.starts_with(starts_with_negative), false); 35 | EXPECT_EQ(str.starts_with(tiny_utf8::string(starts_with_positive)), true); 36 | EXPECT_EQ(str.starts_with(tiny_utf8::string(starts_with_negative)), false); 37 | } 38 | --------------------------------------------------------------------------------