From e28c6e04984a6608734bb3766c43da71c8602de7 Mon Sep 17 00:00:00 2001 From: Mei Chu Date: Thu, 14 May 2026 18:40:48 -0700 Subject: [PATCH 01/16] Replaced split(), startswith() and endswith() pystring usages Signed-off-by: Mei Chu --- src/OpenColorIO/Config.cpp | 5 +++-- vendor/openfx/OCIOUtils.cpp | 11 ++++++----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/OpenColorIO/Config.cpp b/src/OpenColorIO/Config.cpp index 1b5a8c70db..17b2005204 100644 --- a/src/OpenColorIO/Config.cpp +++ b/src/OpenColorIO/Config.cpp @@ -2561,7 +2561,8 @@ const char * Config::getInactiveColorSpaces() const bool Config::isInactiveColorSpace(const char * colorspace) const noexcept { StringUtils::StringVec svec; - pystring::split(getImpl()->m_inactiveColorSpaceNamesConf.c_str(), svec, ", "); + svec = StringUtils::Split(getImpl()->m_inactiveColorSpaceNamesConf.c_str(), ','); + StringUtils::Trim(svec); for (size_t i = 0; i < svec.size(); i++) { @@ -6026,7 +6027,7 @@ bool Config::isArchivable() const // 1) Path may not be absolute. pystring::os::path::isabs(normPath) || // 2) Path may not start with double dot ".." (going above working directory). - pystring::startswith(normPath, "..") || + StringUtils::StartsWith(normPath, "..") || // 3) A context variable may not be located at the start of the path. (ContainsContextVariables(path) && (StringUtils::Find(path, "$") == 0 || diff --git a/vendor/openfx/OCIOUtils.cpp b/vendor/openfx/OCIOUtils.cpp index 90f6e42527..c4a532821b 100644 --- a/vendor/openfx/OCIOUtils.cpp +++ b/vendor/openfx/OCIOUtils.cpp @@ -2,6 +2,7 @@ // Copyright Contributors to the OpenColorIO Project. #include "OCIOUtils.h" +#include "utils/StringUtils.h" namespace OCIO = OCIO_NAMESPACE; @@ -37,13 +38,13 @@ ContextMap deserializeContextStore(const std::string & contextStoreRaw) // Format: key0:value0;key1:value1;... std::vector contextPairsRaw; - pystring::split(contextStoreRaw, contextPairsRaw, ";"); + contextPairsRaw = StringUtils::Split(contextStoreRaw, ";"); for (size_t i = 0; i < contextPairsRaw.size(); i++) { std::vector contextPair; - pystring::split(contextPairsRaw[i], contextPair, ":"); - + contextPair = StringUtils::Split(contextPairsRaw[i], ":"); + if (contextPair.size() == 2) { contextMap[contextPair[0]] = contextPair[1]; @@ -437,7 +438,7 @@ void contextParamChanged(OFX::ImageEffect & instance, const std::string & paramName) { // Is changed param a context variable? - if (!pystring::startswith(paramName, "context_") + if (!StringUtils::StartsWith(paramName, "context_") || paramName == "context_store") { return; @@ -513,7 +514,7 @@ void choiceParamChanged(OFX::ImageEffect & instance, const std::string & paramName) { // Ignore sibling *_store params - if (pystring::endswith(paramName, "_store")) + if (StringUtils::EndsWith(paramName, "_store")) { return; } From ce13451013995e6d0ce676a60bcd1006e75b7646 Mon Sep 17 00:00:00 2001 From: Mei Chu Date: Thu, 14 May 2026 19:08:54 -0700 Subject: [PATCH 02/16] Add utils as include path for openfx build Signed-off-by: Mei Chu --- vendor/openfx/CMakeLists.txt | 1 + vendor/openfx/OCIOUtils.cpp | 1 - vendor/openfx/OCIOUtils.h | 2 ++ 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/vendor/openfx/CMakeLists.txt b/vendor/openfx/CMakeLists.txt index 2d2f880b2a..66c12f812c 100644 --- a/vendor/openfx/CMakeLists.txt +++ b/vendor/openfx/CMakeLists.txt @@ -54,6 +54,7 @@ target_include_directories(ofxplugin ${openfx_INCLUDE_DIR} ${PROJECT_SOURCE_DIR}/include/ ${PROJECT_BINARY_DIR}/include/ + ${PROJECT_SOURCE_DIR}/src/ ) target_link_libraries(ofxplugin diff --git a/vendor/openfx/OCIOUtils.cpp b/vendor/openfx/OCIOUtils.cpp index c4a532821b..e71d20dc4a 100644 --- a/vendor/openfx/OCIOUtils.cpp +++ b/vendor/openfx/OCIOUtils.cpp @@ -2,7 +2,6 @@ // Copyright Contributors to the OpenColorIO Project. #include "OCIOUtils.h" -#include "utils/StringUtils.h" namespace OCIO = OCIO_NAMESPACE; diff --git a/vendor/openfx/OCIOUtils.h b/vendor/openfx/OCIOUtils.h index 24f51a3f2d..398b3d7c31 100644 --- a/vendor/openfx/OCIOUtils.h +++ b/vendor/openfx/OCIOUtils.h @@ -9,6 +9,8 @@ #include "ofxsImageEffect.h" +#include "utils/StringUtils.h" + #include namespace OCIO = OCIO_NAMESPACE; From 8de61b87a64e52e6dcb1f76c5136b1a03a1fa787 Mon Sep 17 00:00:00 2001 From: Mei Chu Date: Thu, 14 May 2026 19:18:08 -0700 Subject: [PATCH 03/16] Fix silly char issue Signed-off-by: Mei Chu --- vendor/openfx/OCIOUtils.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vendor/openfx/OCIOUtils.cpp b/vendor/openfx/OCIOUtils.cpp index e71d20dc4a..1750b6e324 100644 --- a/vendor/openfx/OCIOUtils.cpp +++ b/vendor/openfx/OCIOUtils.cpp @@ -37,12 +37,12 @@ ContextMap deserializeContextStore(const std::string & contextStoreRaw) // Format: key0:value0;key1:value1;... std::vector contextPairsRaw; - contextPairsRaw = StringUtils::Split(contextStoreRaw, ";"); + contextPairsRaw = StringUtils::Split(contextStoreRaw, ';'); for (size_t i = 0; i < contextPairsRaw.size(); i++) { std::vector contextPair; - contextPair = StringUtils::Split(contextPairsRaw[i], ":"); + contextPair = StringUtils::Split(contextPairsRaw[i], ':'); if (contextPair.size() == 2) { From f68f55a5fd9b8e27fb0c9521a9baea782536cccf Mon Sep 17 00:00:00 2001 From: Mei Chu Date: Fri, 15 May 2026 00:56:36 -0700 Subject: [PATCH 04/16] Update StringUtils Trim functionalities. Signed-off-by: Mei Chu --- src/OpenColorIO/OCIOZArchive.cpp | 2 +- .../fileformats/FileFormatIridasLook.cpp | 2 +- src/utils/StringUtils.h | 21 +++++++++++++++++ tests/utils/StringUtils_tests.cpp | 23 +++++++++++++++++++ vendor/openfx/OCIOUtils.cpp | 4 +--- 5 files changed, 47 insertions(+), 5 deletions(-) diff --git a/src/OpenColorIO/OCIOZArchive.cpp b/src/OpenColorIO/OCIOZArchive.cpp index 1caed9ecba..12d839a265 100644 --- a/src/OpenColorIO/OCIOZArchive.cpp +++ b/src/OpenColorIO/OCIOZArchive.cpp @@ -174,7 +174,7 @@ void addSupportedFiles(void * archiver, const char * path, const char * configWo std::string root, ext; pystring::os::path::splitext(root, ext, std::string(entry->d_name)); // Strip leading dot character in order to get the extension name only. - ext = pystring::lstrip(ext, "."); + ext = StringUtils::LeftTrim(ext, '.'); // Check if the extension is supported. Using logic from LoadFileUncached(). FormatRegistry & formatRegistry = FormatRegistry::GetInstance(); diff --git a/src/OpenColorIO/fileformats/FileFormatIridasLook.cpp b/src/OpenColorIO/fileformats/FileFormatIridasLook.cpp index e2c30cbf5f..d5f164a588 100755 --- a/src/OpenColorIO/fileformats/FileFormatIridasLook.cpp +++ b/src/OpenColorIO/fileformats/FileFormatIridasLook.cpp @@ -416,7 +416,7 @@ class XMLParserHelper if (pImpl->m_size) { std::string size_raw = std::string(s, len); - std::string size_clean = pystring::strip(size_raw, "'\" "); // strip quotes and space + std::string size_clean = StringUtils::Trim(size_raw, "'\" "); // strip quotes and space long int size_3d{}; diff --git a/src/utils/StringUtils.h b/src/utils/StringUtils.h index 25600a2760..d1ee19fefa 100644 --- a/src/utils/StringUtils.h +++ b/src/utils/StringUtils.h @@ -110,6 +110,14 @@ inline bool StartsWith(const std::string& str, char prefix) } // Starting from the left, trim the character. +inline std::string LeftTrim(std::string str, const std::string & prefix) +{ + size_t first_good_char = str.find_first_not_of(prefix); + if (first_good_char == std::string::npos) { return str; } + str.erase(0, first_good_char); + return str; +} + inline std::string LeftTrim(std::string str, char c) { const auto it = std::find_if(str.begin(), str.end(), [&c](char ch) { return c!=ch; }); @@ -126,6 +134,14 @@ inline std::string LeftTrim(std::string str) } // Starting from the right, trim the character. +inline std::string RightTrim(std::string str, const std::string & suffix) +{ + size_t last_good_char = str.find_last_not_of(suffix); + if (last_good_char == std::string::npos) { return str; } + str.erase(last_good_char + 1); + return str; +} + inline std::string RightTrim(std::string str, char c) { const auto it = std::find_if(str.rbegin(), str.rend(), [&c](char ch) { return c!=ch; }); @@ -149,6 +165,11 @@ inline std::string Trim(std::string str, char c) } // From the left and right, trim all the space characters i.e. space, tabulation, etc. +inline std::string Trim(std::string str, const std::string & chars) +{ + return LeftTrim(RightTrim(str, chars), chars); +} + inline std::string Trim(std::string str) { return LeftTrim(RightTrim(str)); diff --git a/tests/utils/StringUtils_tests.cpp b/tests/utils/StringUtils_tests.cpp index ccf15c7f4f..bc05cfe739 100644 --- a/tests/utils/StringUtils_tests.cpp +++ b/tests/utils/StringUtils_tests.cpp @@ -40,16 +40,39 @@ OCIO_ADD_TEST(StringUtils, trim) const std::string str = StringUtils::LeftTrim(ref); OCIO_CHECK_EQUAL(str, "lOwEr 1*& ctfG \n\n "); } + { + consst std::string str = StringUtils::LeftTrim(ref, ' '); + OCIO_CHECK_EQUAL(str, "\t\n lOwEr 1*& ctfG \n\n "); + } + // Test to validate the former pystring::lstrip() behavior. + { + consst std::string str = StringUtils::LeftTrim(ref, " \t\n "); + OCIO_CHECK_EQUAL(str, "lOwEr 1*& ctfG \n\n "); + } { const std::string str = StringUtils::RightTrim(ref); OCIO_CHECK_EQUAL(str, " \t\n lOwEr 1*& ctfG"); } + { + consst std::string str = StringUtils::RightTrim(ref, ' '); + OCIO_CHECK_EQUAL(str, " \t\n lOwEr 1*& ctfG \n\n"); + } + // Test to validate the former pystring::rstrip() behavior. + { + consst std::string str = StringUtils::RightTrim(ref, " \n\n "); + OCIO_CHECK_EQUAL(str, " \t\n lOwEr 1*& ctfG"); + } { const std::string str = StringUtils::Trim(ref); OCIO_CHECK_EQUAL(str, "lOwEr 1*& ctfG"); } + // Test to validate the former pystring::strip() behavior. + { + const std::string str = StringUtils::Trim(ref, " "); + OCIO_CHECK_EQUAL(str, "\t\n lOwEr 1*& ctfG \n\n"); + } { // Test that no assert happens when the Trim argument is not an unsigned char (see issue #1874). diff --git a/vendor/openfx/OCIOUtils.cpp b/vendor/openfx/OCIOUtils.cpp index 1750b6e324..6fc37b5c18 100644 --- a/vendor/openfx/OCIOUtils.cpp +++ b/vendor/openfx/OCIOUtils.cpp @@ -8,8 +8,6 @@ namespace OCIO = OCIO_NAMESPACE; #include #include -#include - #include "ofxsLog.h" namespace @@ -67,7 +65,7 @@ std::string serializeContextStore(const ContextMap & contextMap) contextStoreRaw = os.str(); - return pystring::rstrip(contextStoreRaw, ";"); + return StringUtils::RightTrim(contextStoreRaw, ";"); } } // namespace From e425152b2a5991619b074e89b6dd30856a32fcdf Mon Sep 17 00:00:00 2001 From: Mei Chu Date: Fri, 15 May 2026 15:54:16 -0700 Subject: [PATCH 05/16] Add tests and Replace Signed-off-by: Mei Chu --- src/utils/StringUtils.h | 52 ++++++++++++++++++++++--------- tests/utils/StringUtils_tests.cpp | 8 ++--- 2 files changed, 42 insertions(+), 18 deletions(-) diff --git a/src/utils/StringUtils.h b/src/utils/StringUtils.h index d1ee19fefa..3d152d169a 100644 --- a/src/utils/StringUtils.h +++ b/src/utils/StringUtils.h @@ -270,39 +270,52 @@ inline std::string::size_type ReverseFind(const std::string & subject, const std return subject.rfind(search); } -// In place replace the 'search' substring by the 'replace' string in 'str'. -inline bool ReplaceInPlace(std::string & subject, const std::string & search, const std::string & replace) +// In place replace the 'search' substring by the 'replace' string in 'subject'. +inline bool ReplaceInPlace(std::string & subject, const std::string & search, const std::string & replace, int count) { if (search.empty()) return false; bool changed = false; - size_t pos = 0; + size_t pos = 0; + int iter = 0; while ((pos = subject.find(search, pos)) != std::string::npos) { + if ( count > -1 && iter >= count ) { break; } subject.replace(pos, search.length(), replace); pos += replace.length(); changed = true; + ++iter; } return changed; } -// Replace the 'search' substring by the 'replace' string in 'str'. -inline std::string Replace(const std::string & subject, const std::string & search, const std::string & replace) +inline bool ReplaceInPlace(std::string & subject, const std::string & search, const std::string & replace) +{ + return ReplaceInPlace(subject, search, replace, -1); +} + +// Replace the 'search' substring by the 'replace' string in 'subject'. +inline std::string Replace(const std::string & subject, const std::string & search, const std::string & replace, int count) { std::string str{subject}; - ReplaceInPlace(str, search, replace); + ReplaceInPlace(str, search, replace, count); return str; } +inline std::string Replace(const std::string & subject, const std::string & search, const std::string & replace) +{ + return Replace(subject, search, replace, -1); +} + // Check if the 'entry' is in the 'list' using a case insensitive comparison. inline bool Contain(const StringVec & list, const std::string & entry) { - const auto it = std::find_if(list.begin(), list.end(), - [entry](const std::string & ent) - { - return Compare(ent.c_str(), entry.c_str()); + const auto it = std::find_if(list.begin(), list.end(), + [entry](const std::string & ent) + { + return Compare(ent.c_str(), entry.c_str()); }); return it!=list.end(); } @@ -311,10 +324,10 @@ inline bool Contain(const StringVec & list, const std::string & entry) // It returns true if found. inline bool Remove(StringVec & list, const std::string & entry) { - const auto it = std::find_if(list.begin(), list.end(), - [entry](const std::string & ent) - { - return Compare(ent.c_str(), entry.c_str()); + const auto it = std::find_if(list.begin(), list.end(), + [entry](const std::string & ent) + { + return Compare(ent.c_str(), entry.c_str()); }); if (it!=list.end()) { @@ -325,6 +338,17 @@ inline bool Remove(StringVec & list, const std::string & entry) return false; } +inline std::string Multiply(const std::string & str, int n) +{ + // Early exits + if (n <= 0) return ""; + if (n == 1) return str; + + std::ostringstream os; + for(int i = 0; i < n; ++i) { os << str; } + return os.str(); +} + } // namespace StringUtils #endif // INCLUDED_STRINGUTILS_H diff --git a/tests/utils/StringUtils_tests.cpp b/tests/utils/StringUtils_tests.cpp index bc05cfe739..9a916bc118 100644 --- a/tests/utils/StringUtils_tests.cpp +++ b/tests/utils/StringUtils_tests.cpp @@ -41,12 +41,12 @@ OCIO_ADD_TEST(StringUtils, trim) OCIO_CHECK_EQUAL(str, "lOwEr 1*& ctfG \n\n "); } { - consst std::string str = StringUtils::LeftTrim(ref, ' '); + const std::string str = StringUtils::LeftTrim(ref, ' '); OCIO_CHECK_EQUAL(str, "\t\n lOwEr 1*& ctfG \n\n "); } // Test to validate the former pystring::lstrip() behavior. { - consst std::string str = StringUtils::LeftTrim(ref, " \t\n "); + const std::string str = StringUtils::LeftTrim(ref, " \t\n "); OCIO_CHECK_EQUAL(str, "lOwEr 1*& ctfG \n\n "); } @@ -55,12 +55,12 @@ OCIO_ADD_TEST(StringUtils, trim) OCIO_CHECK_EQUAL(str, " \t\n lOwEr 1*& ctfG"); } { - consst std::string str = StringUtils::RightTrim(ref, ' '); + const std::string str = StringUtils::RightTrim(ref, ' '); OCIO_CHECK_EQUAL(str, " \t\n lOwEr 1*& ctfG \n\n"); } // Test to validate the former pystring::rstrip() behavior. { - consst std::string str = StringUtils::RightTrim(ref, " \n\n "); + const std::string str = StringUtils::RightTrim(ref, " \n\n "); OCIO_CHECK_EQUAL(str, " \t\n lOwEr 1*& ctfG"); } From 19b47dc6941d3b34e4efe8af0b7d1b5bf48647bd Mon Sep 17 00:00:00 2001 From: Mei Chu Date: Fri, 15 May 2026 16:31:13 -0700 Subject: [PATCH 06/16] Add more Replace tests. Signed-off-by: Mei Chu --- src/utils/StringUtils.h | 7 +++++-- tests/utils/StringUtils_tests.cpp | 15 +++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/utils/StringUtils.h b/src/utils/StringUtils.h index 3d152d169a..c3f78ce627 100644 --- a/src/utils/StringUtils.h +++ b/src/utils/StringUtils.h @@ -270,7 +270,7 @@ inline std::string::size_type ReverseFind(const std::string & subject, const std return subject.rfind(search); } -// In place replace the 'search' substring by the 'replace' string in 'subject'. +// In place replace the 'search' substring by the 'replace' string in 'subject'. Limited by 'count'. inline bool ReplaceInPlace(std::string & subject, const std::string & search, const std::string & replace, int count) { if (search.empty()) return false; @@ -291,12 +291,13 @@ inline bool ReplaceInPlace(std::string & subject, const std::string & search, co return changed; } +// In place replace the 'search' substring by the 'replace' string in 'subject'. inline bool ReplaceInPlace(std::string & subject, const std::string & search, const std::string & replace) { return ReplaceInPlace(subject, search, replace, -1); } -// Replace the 'search' substring by the 'replace' string in 'subject'. +// Replace the 'search' substring by the 'replace' string in 'subject'. Limited by 'count'. inline std::string Replace(const std::string & subject, const std::string & search, const std::string & replace, int count) { std::string str{subject}; @@ -304,6 +305,7 @@ inline std::string Replace(const std::string & subject, const std::string & sear return str; } +// Replace the 'search' substring by the 'replace' string in 'subject'. inline std::string Replace(const std::string & subject, const std::string & search, const std::string & replace) { return Replace(subject, search, replace, -1); @@ -338,6 +340,7 @@ inline bool Remove(StringVec & list, const std::string & entry) return false; } +// Repeat the 'str' by the 'n' value. inline std::string Multiply(const std::string & str, int n) { // Early exits diff --git a/tests/utils/StringUtils_tests.cpp b/tests/utils/StringUtils_tests.cpp index 9a916bc118..7950d8afec 100644 --- a/tests/utils/StringUtils_tests.cpp +++ b/tests/utils/StringUtils_tests.cpp @@ -175,15 +175,30 @@ OCIO_ADD_TEST(StringUtils, replace) ref = StringUtils::Replace(ref, "345 1*", "ABC"); OCIO_CHECK_EQUAL(ref, "lO12ABC& ctfG"); + ref = StringUtils::Replace(ref, "&", "^^^", 0); + OCIO_CHECK_EQUAL(ref, "lO12ABC& ctfG") + + ref = StringUtils::Replace(ref, "&", "^^^", 1); + OCIO_CHECK_EQUAL(ref, "lO12ABC^^^ ctfG") + + ref = StringUtils::Replace(ref, "^", "&", 2); + OCIO_CHECK_EQUAL(ref, "lO12ABC&&^ ctfG") + // Test a not existing subbstring. ref = StringUtils::Replace(ref, "ZY", "TO"); OCIO_CHECK_EQUAL(ref, "lO12ABC& ctfG"); + ref = StringUtils::Replace(ref, "hEllo", "TO", 1); + OCIO_CHECK_EQUAL(ref, "lO12ABC& ctfG"); + OCIO_CHECK_ASSERT(StringUtils::ReplaceInPlace(ref, "ct", "TO")); OCIO_CHECK_EQUAL(ref, "lO12ABC& TOfG"); OCIO_CHECK_ASSERT(!StringUtils::ReplaceInPlace(ref, "12345", "TO")); OCIO_CHECK_EQUAL(ref, "lO12ABC& TOfG"); + + OCIO_CHECK_ASSERT(!StringUtils::ReplaceInPlace(ref, "hEllo", "TO", 1)); + OCIO_CHECK_EQUAL(ref, "lO12ABC& TOfG"); } OCIO_ADD_TEST(StringUtils, split_whitespaces) From fda46686516c4fce489a173a4617b70587a8d081 Mon Sep 17 00:00:00 2001 From: Mei Chu Date: Fri, 15 May 2026 16:39:37 -0700 Subject: [PATCH 07/16] Fix test Signed-off-by: Mei Chu --- tests/utils/StringUtils_tests.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/utils/StringUtils_tests.cpp b/tests/utils/StringUtils_tests.cpp index 7950d8afec..7b6e6754b8 100644 --- a/tests/utils/StringUtils_tests.cpp +++ b/tests/utils/StringUtils_tests.cpp @@ -176,13 +176,13 @@ OCIO_ADD_TEST(StringUtils, replace) OCIO_CHECK_EQUAL(ref, "lO12ABC& ctfG"); ref = StringUtils::Replace(ref, "&", "^^^", 0); - OCIO_CHECK_EQUAL(ref, "lO12ABC& ctfG") + OCIO_CHECK_EQUAL(ref, "lO12ABC& ctfG"); ref = StringUtils::Replace(ref, "&", "^^^", 1); - OCIO_CHECK_EQUAL(ref, "lO12ABC^^^ ctfG") + OCIO_CHECK_EQUAL(ref, "lO12ABC^^^ ctfG"); ref = StringUtils::Replace(ref, "^", "&", 2); - OCIO_CHECK_EQUAL(ref, "lO12ABC&&^ ctfG") + OCIO_CHECK_EQUAL(ref, "lO12ABC&&^ ctfG"); // Test a not existing subbstring. ref = StringUtils::Replace(ref, "ZY", "TO"); From bee3dec7e896cde9337bd786ca18796aaa5fdd4e Mon Sep 17 00:00:00 2001 From: Mei Chu Date: Fri, 15 May 2026 16:51:03 -0700 Subject: [PATCH 08/16] Fix test Signed-off-by: Mei Chu --- tests/utils/StringUtils_tests.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/utils/StringUtils_tests.cpp b/tests/utils/StringUtils_tests.cpp index 7b6e6754b8..35800d0578 100644 --- a/tests/utils/StringUtils_tests.cpp +++ b/tests/utils/StringUtils_tests.cpp @@ -186,19 +186,19 @@ OCIO_ADD_TEST(StringUtils, replace) // Test a not existing subbstring. ref = StringUtils::Replace(ref, "ZY", "TO"); - OCIO_CHECK_EQUAL(ref, "lO12ABC& ctfG"); + OCIO_CHECK_EQUAL(ref, "lO12ABC&&^ ctfG"); ref = StringUtils::Replace(ref, "hEllo", "TO", 1); - OCIO_CHECK_EQUAL(ref, "lO12ABC& ctfG"); + OCIO_CHECK_EQUAL(ref, "lO12ABC&&^ ctfG"); OCIO_CHECK_ASSERT(StringUtils::ReplaceInPlace(ref, "ct", "TO")); - OCIO_CHECK_EQUAL(ref, "lO12ABC& TOfG"); + OCIO_CHECK_EQUAL(ref, "lO12ABC&&^ ctfG"); OCIO_CHECK_ASSERT(!StringUtils::ReplaceInPlace(ref, "12345", "TO")); - OCIO_CHECK_EQUAL(ref, "lO12ABC& TOfG"); + OCIO_CHECK_EQUAL(ref, "lO12ABC&&^ ctfG"); OCIO_CHECK_ASSERT(!StringUtils::ReplaceInPlace(ref, "hEllo", "TO", 1)); - OCIO_CHECK_EQUAL(ref, "lO12ABC& TOfG"); + OCIO_CHECK_EQUAL(ref, "lO12ABC&&^ ctfG"); } OCIO_ADD_TEST(StringUtils, split_whitespaces) From 58a8ed49996ba4fa140fc30d1b570c5ec236d691 Mon Sep 17 00:00:00 2001 From: Mei Chu Date: Fri, 15 May 2026 17:04:15 -0700 Subject: [PATCH 09/16] Fix replace test Signed-off-by: Mei Chu --- tests/utils/StringUtils_tests.cpp | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/tests/utils/StringUtils_tests.cpp b/tests/utils/StringUtils_tests.cpp index 35800d0578..13205f4249 100644 --- a/tests/utils/StringUtils_tests.cpp +++ b/tests/utils/StringUtils_tests.cpp @@ -169,6 +169,7 @@ OCIO_ADD_TEST(StringUtils, replace) { std::string ref{"lOwEr 1*& ctfG"}; + // Test Replace. ref = StringUtils::Replace(ref, "wEr", "12345"); OCIO_CHECK_EQUAL(ref, "lO12345 1*& ctfG"); @@ -184,21 +185,32 @@ OCIO_ADD_TEST(StringUtils, replace) ref = StringUtils::Replace(ref, "^", "&", 2); OCIO_CHECK_EQUAL(ref, "lO12ABC&&^ ctfG"); - // Test a not existing subbstring. + // Test Replace with non-existing subbstring. ref = StringUtils::Replace(ref, "ZY", "TO"); OCIO_CHECK_EQUAL(ref, "lO12ABC&&^ ctfG"); ref = StringUtils::Replace(ref, "hEllo", "TO", 1); OCIO_CHECK_EQUAL(ref, "lO12ABC&&^ ctfG"); + // Test ReplaceInPlace. OCIO_CHECK_ASSERT(StringUtils::ReplaceInPlace(ref, "ct", "TO")); - OCIO_CHECK_EQUAL(ref, "lO12ABC&&^ ctfG"); + OCIO_CHECK_EQUAL(ref, "lO12ABC&&^ TOfG"); + + OCIO_CHECK_ASSERT(StringUtils::ReplaceInPlace(ref, "ct", "TO", 0)); + OCIO_CHECK_EQUAL(ref, "lO12ABC&&^ TOfG"); + + OCIO_CHECK_ASSERT(!StringUtils::ReplaceInPlace(ref, "O", "P", 1)); + OCIO_CHECK_EQUAL(ref, "lP12ABC&&^ TOfG"); + // Test ReplaceInPlace with non-existing subbstring. OCIO_CHECK_ASSERT(!StringUtils::ReplaceInPlace(ref, "12345", "TO")); - OCIO_CHECK_EQUAL(ref, "lO12ABC&&^ ctfG"); + OCIO_CHECK_EQUAL(ref, "lP12ABC&&^ TOfG"); + + OCIO_CHECK_ASSERT(!StringUtils::ReplaceInPlace(ref, "hEllo", "TO", 0)); + OCIO_CHECK_EQUAL(ref, "lP12ABC&&^ TOfG"); OCIO_CHECK_ASSERT(!StringUtils::ReplaceInPlace(ref, "hEllo", "TO", 1)); - OCIO_CHECK_EQUAL(ref, "lO12ABC&&^ ctfG"); + OCIO_CHECK_EQUAL(ref, "lP12ABC&&^ TOfG"); } OCIO_ADD_TEST(StringUtils, split_whitespaces) From 0afc79b3fa9ef57b8b69f6705ffccd07a33e0dea Mon Sep 17 00:00:00 2001 From: Mei Chu Date: Fri, 15 May 2026 17:19:21 -0700 Subject: [PATCH 10/16] Fix replace test Signed-off-by: Mei Chu --- tests/utils/StringUtils_tests.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/utils/StringUtils_tests.cpp b/tests/utils/StringUtils_tests.cpp index 13205f4249..8552a67c3a 100644 --- a/tests/utils/StringUtils_tests.cpp +++ b/tests/utils/StringUtils_tests.cpp @@ -196,10 +196,10 @@ OCIO_ADD_TEST(StringUtils, replace) OCIO_CHECK_ASSERT(StringUtils::ReplaceInPlace(ref, "ct", "TO")); OCIO_CHECK_EQUAL(ref, "lO12ABC&&^ TOfG"); - OCIO_CHECK_ASSERT(StringUtils::ReplaceInPlace(ref, "ct", "TO", 0)); + OCIO_CHECK_ASSERT(StringUtils::ReplaceInPlace(ref, "TO", "ct", 0)); OCIO_CHECK_EQUAL(ref, "lO12ABC&&^ TOfG"); - OCIO_CHECK_ASSERT(!StringUtils::ReplaceInPlace(ref, "O", "P", 1)); + OCIO_CHECK_ASSERT(StringUtils::ReplaceInPlace(ref, "O", "P", 1)); OCIO_CHECK_EQUAL(ref, "lP12ABC&&^ TOfG"); // Test ReplaceInPlace with non-existing subbstring. From 5ef69227eee27d0873cce8a3f4358a2ec55a69e8 Mon Sep 17 00:00:00 2001 From: Mei Chu Date: Fri, 15 May 2026 17:29:50 -0700 Subject: [PATCH 11/16] More replace test fix Signed-off-by: Mei Chu --- tests/utils/StringUtils_tests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/utils/StringUtils_tests.cpp b/tests/utils/StringUtils_tests.cpp index 8552a67c3a..15b9efe9b9 100644 --- a/tests/utils/StringUtils_tests.cpp +++ b/tests/utils/StringUtils_tests.cpp @@ -196,7 +196,7 @@ OCIO_ADD_TEST(StringUtils, replace) OCIO_CHECK_ASSERT(StringUtils::ReplaceInPlace(ref, "ct", "TO")); OCIO_CHECK_EQUAL(ref, "lO12ABC&&^ TOfG"); - OCIO_CHECK_ASSERT(StringUtils::ReplaceInPlace(ref, "TO", "ct", 0)); + OCIO_CHECK_ASSERT(!StringUtils::ReplaceInPlace(ref, "TO", "ct", 0)); OCIO_CHECK_EQUAL(ref, "lO12ABC&&^ TOfG"); OCIO_CHECK_ASSERT(StringUtils::ReplaceInPlace(ref, "O", "P", 1)); From 4bf0853bf5c28e0897098deeb9842851209984c6 Mon Sep 17 00:00:00 2001 From: Mei Chu Date: Sun, 17 May 2026 01:17:11 -0700 Subject: [PATCH 12/16] Replace pystring::replace in FileTransform Signed-off-by: Mei Chu --- src/OpenColorIO/transforms/FileTransform.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OpenColorIO/transforms/FileTransform.cpp b/src/OpenColorIO/transforms/FileTransform.cpp index 6fd1549d61..d9c7e33720 100755 --- a/src/OpenColorIO/transforms/FileTransform.cpp +++ b/src/OpenColorIO/transforms/FileTransform.cpp @@ -613,7 +613,7 @@ void LoadFileUncached(FileFormat * & returnFormat, std::string root, extension; pystring::os::path::splitext(root, extension, filepath); // remove the leading '.' - extension = pystring::replace(extension,".","",1); + extension = StringUtils::Replace(extension, "." , "", 1); FormatRegistry & formatRegistry = FormatRegistry::GetInstance(); From 235ecc9e536ccae09e174b35c9612018861c7269 Mon Sep 17 00:00:00 2001 From: Mei Chu Date: Sun, 17 May 2026 01:29:57 -0700 Subject: [PATCH 13/16] Test changed cicd Signed-off-by: Mei Chu --- .github/workflows/ci_workflow.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci_workflow.yml b/.github/workflows/ci_workflow.yml index 2d6ba3a12e..5a1b5fcd27 100644 --- a/.github/workflows/ci_workflow.yml +++ b/.github/workflows/ci_workflow.yml @@ -61,7 +61,8 @@ jobs: strategy: fail-fast: true matrix: - build: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] + # build: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] + build: [1, 2, 4, 5, 7, 8, 10, 11, 12] include: # ------------------------------------------------------------------- # VFX CY2026 (Python 3.13) From bb69820fea9ebfa6f8ec12abe3bb6c14185ea53e Mon Sep 17 00:00:00 2001 From: Mei Chu Date: Sun, 17 May 2026 01:33:00 -0700 Subject: [PATCH 14/16] Update CICD test Signed-off-by: Mei Chu --- .github/workflows/ci_workflow.yml | 78 +++++++++++++++---------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/.github/workflows/ci_workflow.yml b/.github/workflows/ci_workflow.yml index 5a1b5fcd27..937b22604b 100644 --- a/.github/workflows/ci_workflow.yml +++ b/.github/workflows/ci_workflow.yml @@ -109,19 +109,19 @@ jobs: # ------------------------------------------------------------------- # VFX CY2025 (Python 3.11) # ------------------------------------------------------------------- - - build: 9 - build-type: Debug - build-shared: 'ON' - build-docs: 'OFF' - build-openfx: 'ON' - use-simd: 'ON' - use-oiio: 'ON' - cxx-standard: 20 - cxx-compiler: clang++ - cc-compiler: clang - compiler-desc: Clang - vfx-cy: 2025 - install-ext-packages: MISSING + # - build: 9 + # build-type: Debug + # build-shared: 'ON' + # build-docs: 'OFF' + # build-openfx: 'ON' + # use-simd: 'ON' + # use-oiio: 'ON' + # cxx-standard: 20 + # cxx-compiler: clang++ + # cc-compiler: clang + # compiler-desc: Clang + # vfx-cy: 2025 + # install-ext-packages: MISSING - build: 8 build-type: Release build-shared: 'ON' @@ -151,19 +151,19 @@ jobs: # ------------------------------------------------------------------- # VFX CY2024 (Python 3.11) # ------------------------------------------------------------------- - - build: 6 - build-type: Debug - build-shared: 'ON' - build-docs: 'OFF' - build-openfx: 'ON' - use-simd: 'ON' - use-oiio: 'ON' - cxx-standard: 20 - cxx-compiler: clang++ - cc-compiler: clang - compiler-desc: Clang - vfx-cy: 2024 - install-ext-packages: MISSING + # - build: 6 + # build-type: Debug + # build-shared: 'ON' + # build-docs: 'OFF' + # build-openfx: 'ON' + # use-simd: 'ON' + # use-oiio: 'ON' + # cxx-standard: 20 + # cxx-compiler: clang++ + # cc-compiler: clang + # compiler-desc: Clang + # vfx-cy: 2024 + # install-ext-packages: MISSING - build: 5 build-type: Release build-shared: 'ON' @@ -193,19 +193,19 @@ jobs: # ------------------------------------------------------------------- # VFX CY2023 (Python 3.10) # ------------------------------------------------------------------- - - build: 3 - build-type: Debug - build-shared: 'ON' - build-docs: 'OFF' - build-openfx: 'ON' - use-simd: 'ON' - use-oiio: 'ON' - cxx-standard: 20 - cxx-compiler: clang++ - cc-compiler: clang - compiler-desc: Clang - vfx-cy: 2023 - install-ext-packages: MISSING + # - build: 3 + # build-type: Debug + # build-shared: 'ON' + # build-docs: 'OFF' + # build-openfx: 'ON' + # use-simd: 'ON' + # use-oiio: 'ON' + # cxx-standard: 20 + # cxx-compiler: clang++ + # cc-compiler: clang + # compiler-desc: Clang + # vfx-cy: 2023 + # install-ext-packages: MISSING - build: 2 build-type: Release build-shared: 'ON' From 182dbf8acf29377570f09c5386b8f5f89e7a9cfe Mon Sep 17 00:00:00 2001 From: Mei Chu Date: Mon, 18 May 2026 18:30:04 -0700 Subject: [PATCH 15/16] Integrate StringUtils::Multiply Signed-off-by: Mei Chu --- src/OpenColorIO/Op.cpp | 5 ++--- tests/utils/StringUtils_tests.cpp | 13 +++++++++++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/OpenColorIO/Op.cpp b/src/OpenColorIO/Op.cpp index 7e95baecfc..88e76f5bad 100755 --- a/src/OpenColorIO/Op.cpp +++ b/src/OpenColorIO/Op.cpp @@ -4,8 +4,6 @@ #include #include -#include - #include #include "Logging.h" @@ -23,6 +21,7 @@ #include "ops/lut1d/Lut1DOp.h" #include "ops/lut3d/Lut3DOp.h" #include "ops/range/RangeOp.h" +#include "utils/StringUtils.h" namespace OCIO_NAMESPACE { @@ -478,7 +477,7 @@ std::string SerializeOpVec(const OpRcPtrVec & ops, int indent) { const OpRcPtr & op = ops[idx]; - oss << pystring::mul(" ", indent); + oss << StringUtils::Multiply(" ", indent); oss << "Op " << idx << ": " << *op << " "; oss << op->getCacheID(); diff --git a/tests/utils/StringUtils_tests.cpp b/tests/utils/StringUtils_tests.cpp index 15b9efe9b9..4751e654d7 100644 --- a/tests/utils/StringUtils_tests.cpp +++ b/tests/utils/StringUtils_tests.cpp @@ -275,3 +275,16 @@ OCIO_ADD_TEST(StringUtils, remove_contain) OCIO_CHECK_ASSERT(!StringUtils::Contain(values, "2")); } } + +OCIO_ADD_TEST(StringUtils, multiply) +{ + constexpr char ref[]{"10.0 9. 1 er\t1e-5f"}; + + OCIO_CHECK_EQUAL(StringUtils::Multiply(ref, 0), ""); + OCIO_CHECK_EQUAL(StringUtils::Multiply(ref, 1), "10.0 9. 1 er\t1e-5f"); + OCIO_CHECK_EQUAL(StringUtils::Multiply(ref, 2), "10.0 9. 1 er\t1e-5f10.0 9. 1 er\t1e-5f"); + + OCIO_CHECK_EQUAL(StringUtils::Multiply(" ", 0), ""); + OCIO_CHECK_EQUAL(StringUtils::Multiply(" ", 1), " "); + OCIO_CHECK_EQUAL(StringUtils::Multiply(" ", 2), " "); +} From 5f511b6c5004fb65be4913cb78425c8d043bca6b Mon Sep 17 00:00:00 2001 From: Mei Chu Date: Mon, 18 May 2026 19:07:35 -0700 Subject: [PATCH 16/16] Small syntax update. Signed-off-by: Mei Chu --- src/OpenColorIO/transforms/FileTransform.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OpenColorIO/transforms/FileTransform.cpp b/src/OpenColorIO/transforms/FileTransform.cpp index d9c7e33720..4c8635cd22 100755 --- a/src/OpenColorIO/transforms/FileTransform.cpp +++ b/src/OpenColorIO/transforms/FileTransform.cpp @@ -613,7 +613,7 @@ void LoadFileUncached(FileFormat * & returnFormat, std::string root, extension; pystring::os::path::splitext(root, extension, filepath); // remove the leading '.' - extension = StringUtils::Replace(extension, "." , "", 1); + extension = StringUtils::Replace(extension, ".", "", 1); FormatRegistry & formatRegistry = FormatRegistry::GetInstance();