Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize RustStr's Equatable implementation #151

Merged
merged 5 commits into from
Jan 29, 2023

Conversation

NiwakaDev
Copy link
Collaborator

Addresses #148.

Copy link
Owner

@chinedufn chinedufn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor notes then looks good to me.

Great work here, thanks for figuring this out!

}
XCTContext.runActivity(named: "Should not be equal"){
_ in
//Equal length
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor: The Equal length and Not equal length are on the wrong tests. Need to switch them.

struct RustStr __swift_bridge__$RustString$trim(void* self);
bool __swift_bridge__equality_operator_for_RustStr(struct RustStr lhs, struct RustStr rhs);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

__swift_bridge__$RustStr$partial_eq

src/lib.rs Outdated
@@ -92,5 +92,25 @@ pub extern "C" fn __swift_bridge__null_pointer() -> *const std::ffi::c_void {
std::ptr::null()
}

#[no_mangle]
#[doc(hidden)]
pub extern "C" fn __swift_bridge__equality_operator_for_RustStr(
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

__swift_bridge__RustStr__partial_eq

src/lib.rs Outdated
return false;
}

unsafe {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another alternative that I thought of was:

unsafe { 
    std::slice::from_raw_parts(lhs.start, lhs.len) ==
    std::slice::from_raw_parts(rhs.start, rhs.len)
}

I was thinking that the stdlib might have an optimization that I was unfamiliar with, but I think that desugars to https://doc.rust-lang.org/1.52.1/src/core/slice/cmp.rs.html#69-75

Which looks like the same as what you're doing. So this looks good to me.

src/lib.rs Outdated
@@ -92,5 +92,25 @@ pub extern "C" fn __swift_bridge__null_pointer() -> *const std::ffi::c_void {
std::ptr::null()
}

#[no_mangle]
Copy link
Owner

@chinedufn chinedufn Jan 29, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can put this in std_bridge/string.rs under here

impl RustStr {
pub fn len(&self) -> usize {
self.len
}
// TODO: Think through these lifetimes and the implications of them...
pub fn to_str<'a>(self) -> &'a str {
let bytes = unsafe { std::slice::from_raw_parts(self.start, self.len) };
std::str::from_utf8(bytes).expect("Failed to convert RustStr to &str")
}
pub fn to_string(self) -> String {
self.to_str().to_string()
}
pub fn from_str(str: &str) -> Self {
RustStr {
start: str.as_ptr(),
len: str.len(),
}
}
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I got the link error like:

Testing failed:
        Undefined symbol: ___swift_bridge__$RustStr$partial_eq
        Testing cancelled because the build failed.

How does the Swift-compiler know where methods are, such as len, to_string.

pub fn len(&self) -> usize {
self.len
}
// TODO: Think through these lifetimes and the implications of them...
pub fn to_str<'a>(self) -> &'a str {
let bytes = unsafe { std::slice::from_raw_parts(self.start, self.len) };
std::str::from_utf8(bytes).expect("Failed to convert RustStr to &str")
}
pub fn to_string(self) -> String {
self.to_str().to_string()
}
pub fn from_str(str: &str) -> Self {
RustStr {
start: str.as_ptr(),
len: str.len(),
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seaching for RustStr's methods with a tool like grep, I couldn't find any declarations of them in any c-header files.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pub(super) fn write_core_swift_and_c(out_dir: &Path) {
let core_swift_out = out_dir.join("SwiftBridgeCore.swift");
let mut swift = core_swift();
swift += "\n";
swift += &RUST_STRING_SWIFT;
swift += "\n";
swift += &SWIFT_CALLBACK_SUPPORT_NO_ARGS_NO_RETURN;
swift += "\n";
swift += &SWIFT_RUST_RESULT;
std::fs::write(core_swift_out, swift).unwrap();
let core_c_header_out = out_dir.join("SwiftBridgeCore.h");
let mut c_header = core_c_header().to_string();
c_header += "\n";
c_header += &RUST_STRING_C;
c_header += "\n";
c_header += &C_CALLBACK_SUPPORT_NO_ARGS_NO_RETURN;
c_header += "\n";
c_header += &C_RESULT_SUPPORT;
std::fs::write(core_c_header_out, c_header).unwrap();
}

struct RustStr __swift_bridge__$RustString$as_str(void* self);
struct RustStr __swift_bridge__$RustString$trim(void* self);

Copy link
Owner

@chinedufn chinedufn Jan 29, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh wait, it looks like none of the RustStr methods like len or to_string are actually bridged. So that's why you couldn't find them.

But yeah you can declare the ___swift_bridge__$RustStr$partial_eq inside of rust_string.c.h

Copy link
Owner

@chinedufn chinedufn Jan 29, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The build script might not be getting re-ran.

Try running touch crates/swift-integration-tests/build.rs or otherwise modifying and saving (then removing the modification of) the build.rs so that it gets re-run and seeing if that works.

https://doc.rust-lang.org/cargo/reference/build-scripts.html#life-cycle-of-a-build-script

Copy link
Collaborator Author

@NiwakaDev NiwakaDev Jan 29, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Try running touch crates/swift-integration-tests/build.rs or otherwise modifying and saving

I got the link error again.

RustString's methods, such as len() and as_str(), seem to be imported on the Swift side.
スクリーンショット 2023-01-30 1 33 35

However, RustStr's methods don't seem to be imported on the Swift side.

スクリーンショット 2023-01-30 1 33 44

I tried to dive into this problem, and run objdump -d libswift_integration_tests.a | grep RustStr:

0000000000000328 <__ZN12swift_bridge10std_bridge6string7RustStr10partial_eq17h39b2f23e5457666eE

patial_eq might be not __swift_bridge__$RustStr$partial_eq but __ZN12swift_bridge10std_bridge6string7RustStr10partial_eq17h39b2f23e5457666eE.

Copy link
Owner

@chinedufn chinedufn Jan 29, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just fetched your branch and I can reproduce the error. Give me a sec

Copy link
Owner

@chinedufn chinedufn Jan 29, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should work:

// std_bridge/string.rs

impl RustStr {
    // ...
}

impl PartialEq for RustStr {
    fn eq(&self, other: &Self) -> bool {
        unsafe {
            std::slice::from_raw_parts(self.start, self.len)
                == std::slice::from_raw_parts(other.start, other.len)
        }
    }
}

#[export_name = "__swift_bridge__$RustStr$partial_eq"]
#[allow(non_snake_case)]
pub extern "C" fn __swift_bridge__RustStr_partial_eq(lhs: RustStr, rhs: RustStr) -> bool {
    lhs == rhs
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm sorry for taking up your time. I'm going to study rust hard.

// on the Rust side that compares the underlying byte slices.
return
lhs.toString() == rhs.toString()
return __swift_bridge__equality_operator_for_RustStr(lhs, rhs);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Beautiful

@chinedufn chinedufn changed the title Fix RustStr's == operator calling toString Optimize RustStr's Equatable implementation Jan 29, 2023
src/lib.rs Outdated
lhs: string::RustStr,
rhs: string::RustStr,
) -> bool {
if lhs.len != rhs.len {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's make this lhs == rhs

And then you can

impl PartialEq for RustStr { .. }

And handle the comparison inside of the PartialEq impl.

@@ -79,4 +81,15 @@ impl RustStr {
len: str.len(),
}
}

pub fn partial_eq(
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should move this into a PartialEq trait implementation.

impl PartialEq for RustStr {
    // ...
}

https://doc.rust-lang.org/std/cmp/trait.PartialEq.html

Copy link
Collaborator Author

@NiwakaDev NiwakaDev Jan 29, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should move this into a PartialEq trait implementation.

Sorry...I know that I should do it, but I want to solve the link error first. So, I pushed 03245ec to share this problem.

@NiwakaDev
Copy link
Collaborator Author

I addressed your review. Sorry...

@chinedufn chinedufn merged commit 5428646 into chinedufn:master Jan 29, 2023
@chinedufn
Copy link
Owner

Great work!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants