From 230bc855fc5954b2a8aaef624eac9a49bc60fadc Mon Sep 17 00:00:00 2001 From: ciciplusplus Date: Tue, 13 Jan 2026 23:13:29 +0100 Subject: [PATCH] NSMutableString deleteCharactersInRange: implementation + tests Change-Id: I711a6ba7518f1dc323c3df436c5b0aede3c50353 --- .github/workflows/touchHLE_release.yml | 2 +- src/frameworks/foundation/ns_string.rs | 44 ++++++++++++++++++++++---- tests/TestApp_source/cli_tests.m | 26 +++++++++++++++ tests/TestApp_source/system_headers.h | 8 +++++ 4 files changed, 72 insertions(+), 8 deletions(-) diff --git a/.github/workflows/touchHLE_release.yml b/.github/workflows/touchHLE_release.yml index 66325183e..979144424 100644 --- a/.github/workflows/touchHLE_release.yml +++ b/.github/workflows/touchHLE_release.yml @@ -75,7 +75,7 @@ jobs: CMAKE: ${{ github.workspace }}/vendor/cmake/bin/cmake # This step ensures that our non-iOS-specific tests are passing on macOS itself! - name: Sanity check tests - run: cc tests/TestApp_source/cli_tests.m tests/TestApp_source/CGAffineTransform.c tests/TestApp_source/SyncTester.m -framework CoreFoundation -framework CoreGraphics -DDEFINE_ME_WHEN_BUILDING_ON_MACOS -DTestApp_cli_tests_main=main -ObjC && ./a.out + run: cc tests/TestApp_source/cli_tests.m tests/TestApp_source/CGAffineTransform.c tests/TestApp_source/SyncTester.m -framework CoreFoundation -framework Foundation -framework CoreGraphics -DDEFINE_ME_WHEN_BUILDING_ON_MACOS -DTestApp_cli_tests_main=main -ObjC && ./a.out - name: Build run: cargo build --target x86_64-apple-darwin --release env: diff --git a/src/frameworks/foundation/ns_string.rs b/src/frameworks/foundation/ns_string.rs index 73b655837..e88a8ee09 100644 --- a/src/frameworks/foundation/ns_string.rs +++ b/src/frameworks/foundation/ns_string.rs @@ -1234,15 +1234,33 @@ pub const CLASSES: ClassExports = objc_classes! { } - (())deleteCharactersInRange:(NSRange)range { - // Below implementation handles a trivial case - - // whole string is deleted! let location = range.location; - assert_eq!(location, 0); // TODO - let len: NSUInteger = msg![env; this length]; let length = range.length; - assert_eq!(len, length); // TODO - let empty = get_static_str(env, ""); - () = msg![env; this setString:empty]; + + let left: id = if location == 0 { + get_static_str(env, "") + } else { + let left_range = NSRange { + location: 0, + length: location, + }; + msg![env; this substringWithRange:left_range] + }; + + let idx_after_removal = location + length; + let lenght_str: NSUInteger = msg![env; this length]; + let right: id = if idx_after_removal == lenght_str { + get_static_str(env, "") + } else { + let right_range = NSRange { + location: idx_after_removal, + length: lenght_str - idx_after_removal, + }; + msg![env; this substringWithRange:right_range] + }; + + let res: id = msg![env; left stringByAppendingString:right]; + () = msg![env; this setString:res]; } @end @@ -1532,6 +1550,18 @@ pub const CLASSES: ClassExports = objc_classes! { *env.objc.borrow_mut(this) = host_object; } +- (id)substringWithRange:(NSRange)range { + let host_object = env.objc.borrow_mut::(this); + let (orig_string, did_convert) = host_object.convert_to_utf16_inplace(); + if did_convert { + log_dbg!("[{:?} substringWithRange]: converted string to UTF-16", this); + } + let host_string = + orig_string[(range.location as usize)..((range.location + range.length) as usize)].to_vec(); + let res = from_u16_vec(env, host_string); + autorelease(env, res) +} + @end }; diff --git a/tests/TestApp_source/cli_tests.m b/tests/TestApp_source/cli_tests.m index 238444a9e..d8f00ec39 100644 --- a/tests/TestApp_source/cli_tests.m +++ b/tests/TestApp_source/cli_tests.m @@ -3070,6 +3070,31 @@ int test_CFURLHasDirectoryPath() { return 0; } +int test_NSMutableString_deleteCharactersInRange() { + NSMutableString *str = [NSMutableString stringWithUTF8String:"abc"]; + NSRange r1 = {0, 3}; + [str deleteCharactersInRange:r1]; + NSString *expected = [NSString stringWithUTF8String:""]; + if (!CFEqual(str, expected)) { + return -1; + } + str = [NSMutableString stringWithUTF8String:"abc"]; + NSRange r2 = {1, 1}; + [str deleteCharactersInRange:r2]; + expected = [NSString stringWithUTF8String:"ac"]; + if (!CFEqual(str, expected)) { + return -2; + } + str = [NSMutableString stringWithUTF8String:"abc"]; + NSRange r3 = {0, 2}; + [str deleteCharactersInRange:r3]; + expected = [NSString stringWithUTF8String:"c"]; + if (!CFEqual(str, expected)) { + return -3; + } + return 0; +} + // clang-format off #define FUNC_DEF(func) \ { &func, #func } @@ -3136,6 +3161,7 @@ struct { FUNC_DEF(test_CGGeometry), FUNC_DEF(test_CFURLHasDirectoryPath), FUNC_DEF(test_CGImage_JPEG), + FUNC_DEF(test_NSMutableString_deleteCharactersInRange), }; // clang-format on diff --git a/tests/TestApp_source/system_headers.h b/tests/TestApp_source/system_headers.h index eec30a72d..cf9e77c63 100644 --- a/tests/TestApp_source/system_headers.h +++ b/tests/TestApp_source/system_headers.h @@ -24,6 +24,11 @@ typedef signed char BOOL; typedef unsigned long NSUInteger; typedef signed long NSInteger; +typedef struct _NSRange { + NSUInteger location; + NSUInteger length; +} NSRange; + #define nil ((id)0) // id objc_msgSend(id, SEL, ...); @@ -65,6 +70,9 @@ typedef signed long NSInteger; + (instancetype)stringWithFormat:(NSString *)format, ...; + (instancetype)stringWithUTF8String:(const char *)string; @end +@interface NSMutableString : NSString +- (void)deleteCharactersInRange:(NSRange)range; +@end @interface NSValue : NSObject @end