diff --git a/crates/cxx-qt-lib-headers/include/qbytearray.h b/crates/cxx-qt-lib-headers/include/qbytearray.h index d962c0406..419c668e2 100644 --- a/crates/cxx-qt-lib-headers/include/qbytearray.h +++ b/crates/cxx-qt-lib-headers/include/qbytearray.h @@ -6,6 +6,8 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 #pragma once +#include + #include #include "rust/cxx.h" @@ -19,9 +21,14 @@ namespace rust { namespace cxxqtlib1 { QByteArray -qbytearrayFromRustString(rust::Str string); -rust::String -qbytearrayToRustString(const QByteArray& string); +qbytearrayFromSliceU8(::rust::Slice slice); +::rust::Vec<::std::uint8_t> +qbytearrayToVecU8(const QByteArray& byteArray); + +QByteArray +qbytearrayFromRawData(::rust::Slice slice); +::rust::Slice +qbytearrayAsSlice(const QByteArray& byteArray); } } diff --git a/crates/cxx-qt-lib/src/types/qbytearray.cpp b/crates/cxx-qt-lib/src/types/qbytearray.cpp index 4c0dc8c86..1d74ae69d 100644 --- a/crates/cxx-qt-lib/src/types/qbytearray.cpp +++ b/crates/cxx-qt-lib/src/types/qbytearray.cpp @@ -38,16 +38,44 @@ namespace rust { namespace cxxqtlib1 { QByteArray -qbytearrayFromRustString(rust::Str string) +qbytearrayFromSliceU8(::rust::Slice slice) { - // Note that rust::Str here is borrowed - return QByteArray(string.data(), string.size()); + // Note that rust::Slice here is borrowed +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + return QByteArray(reinterpret_cast(slice.data()), + static_cast(slice.size())); +#else + return QByteArray(reinterpret_cast(slice.data()), + static_cast(slice.size())); +#endif +} + +::rust::Vec<::std::uint8_t> +qbytearrayToVecU8(const QByteArray& byteArray) +{ + ::rust::Vec<::std::uint8_t> vec; + std::copy(byteArray.cbegin(), byteArray.cend(), std::back_inserter(vec)); + return vec; +} + +QByteArray +qbytearrayFromRawData(::rust::Slice slice) +{ +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + return QByteArray::fromRawData(reinterpret_cast(slice.data()), + static_cast(slice.size())); +#else + return QByteArray::fromRawData(reinterpret_cast(slice.data()), + static_cast(slice.size())); +#endif } -rust::String -qbytearrayToRustString(const QByteArray& byteArray) +::rust::Slice +qbytearrayAsSlice(const QByteArray& byteArray) { - return rust::String(byteArray.constData(), byteArray.size()); + return ::rust::Slice( + reinterpret_cast(byteArray.data()), + static_cast<::std::size_t>(byteArray.size())); } } diff --git a/crates/cxx-qt-lib/src/types/qbytearray.rs b/crates/cxx-qt-lib/src/types/qbytearray.rs index b7396c168..4f2ccfe9a 100644 --- a/crates/cxx-qt-lib/src/types/qbytearray.rs +++ b/crates/cxx-qt-lib/src/types/qbytearray.rs @@ -19,21 +19,28 @@ mod ffi { #[doc(hidden)] #[rust_name = "qbytearray_drop"] - fn drop(string: &mut QByteArray); + fn drop(bytearray: &mut QByteArray); #[doc(hidden)] #[rust_name = "qbytearray_default"] fn construct() -> QByteArray; #[doc(hidden)] - #[rust_name = "qbytearray_from_rust_string"] - fn qbytearrayFromRustString(string: &str) -> QByteArray; - #[doc(hidden)] #[rust_name = "qbytearray_clone"] - fn construct(string: &QByteArray) -> QByteArray; + fn construct(bytearray: &QByteArray) -> QByteArray; + + #[doc(hidden)] + #[rust_name = "qbytearray_from_slice_u8"] + fn qbytearrayFromSliceU8(slice: &[u8]) -> QByteArray; + #[doc(hidden)] + #[rust_name = "qbytearray_to_vec_u8"] + fn qbytearrayToVecU8(string: &QByteArray) -> Vec; #[doc(hidden)] - #[rust_name = "qbytearray_to_rust_string"] - fn qbytearrayToRustString(string: &QByteArray) -> String; + #[rust_name = "qbytearray_from_raw_data"] + fn qbytearrayFromRawData(slice: &[u8]) -> QByteArray; + #[doc(hidden)] + #[rust_name = "qbytearray_as_slice"] + fn qbytearrayAsSlice(bytearray: &QByteArray) -> &[u8]; } } @@ -52,7 +59,7 @@ pub struct QByteArray { impl Clone for QByteArray { /// Constructs a copy of other. /// - /// This operation takes constant time, because QByteArray is implicitly shared. + /// This operation takes constant time, because QByteArray is implicitly shared similar to a [std::borrow::Cow]. /// This makes returning a QByteArray from a function very fast. /// If a shared instance is modified, it will be copied (copy-on-write), and that takes linear time. fn clone(&self) -> Self { @@ -70,7 +77,11 @@ impl Default for QByteArray { impl std::fmt::Display for QByteArray { /// Convert the QByteArray to a Rust string fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "{}", <&QByteArray as Into>::into(self)) + if let Ok(string) = String::from_utf8(self.into()) { + write!(f, "{}", string) + } else { + write!(f, "{:?}", self.as_slice()) + } } } @@ -82,23 +93,46 @@ impl Drop for QByteArray { } impl From<&str> for QByteArray { - /// Constructs a QByteArray from a Rust string + /// Constructs a QByteArray from a Rust string slice. This makes a deep copy of the data. fn from(str: &str) -> Self { - ffi::qbytearray_from_rust_string(str) + ffi::qbytearray_from_slice_u8(str.as_bytes()) } } impl From<&String> for QByteArray { - /// Constructs a QByteArray from a Rust string + /// Constructs a QByteArray from a Rust string. This makes a deep copy of the data. fn from(str: &String) -> Self { - ffi::qbytearray_from_rust_string(str) + ffi::qbytearray_from_slice_u8(str.as_bytes()) } } -impl From<&QByteArray> for String { - /// Convert the QByteArray to a Rust string - fn from(qbytearray: &QByteArray) -> Self { - ffi::qbytearray_to_rust_string(qbytearray) +impl From<&[u8]> for QByteArray { + /// Constructs a QByteArray from a `&[u8]`. This makes a deep copy of the data. + fn from(bytes: &[u8]) -> Self { + ffi::qbytearray_from_slice_u8(bytes) + } +} + +impl From<&QByteArray> for Vec { + /// Convert the QByteArray to a `Vec`. This makes a deep copy of the data. + fn from(bytearray: &QByteArray) -> Self { + ffi::qbytearray_to_vec_u8(bytearray) + } +} + +impl QByteArray { + /// Construct a slice of u8 from a QByteArray + pub fn as_slice(&self) -> &[u8] { + ffi::qbytearray_as_slice(self) + } + + /// Construct a QByteArray from a `&[u8]` without a deep copy + /// + /// # Safety + /// + /// The caller must ensure that the original slice outlives the QByteArray + pub unsafe fn from_raw_data(bytes: &[u8]) -> QByteArray { + ffi::qbytearray_from_raw_data(bytes) } } diff --git a/tests/qt_types_standalone/cpp/qbytearray.h b/tests/qt_types_standalone/cpp/qbytearray.h index 51117113b..8f6bdc90d 100644 --- a/tests/qt_types_standalone/cpp/qbytearray.h +++ b/tests/qt_types_standalone/cpp/qbytearray.h @@ -52,4 +52,6 @@ private Q_SLOTS: modify_qbytearray(s); QCOMPARE(s, QByteArrayLiteral("Updated string value")); } + + void can_use_as_slice_cpp() { QVERIFY(can_use_as_slice()); } }; diff --git a/tests/qt_types_standalone/rust/src/qbytearray.rs b/tests/qt_types_standalone/rust/src/qbytearray.rs index e72ea88b5..2fa5a0d9f 100644 --- a/tests/qt_types_standalone/rust/src/qbytearray.rs +++ b/tests/qt_types_standalone/rust/src/qbytearray.rs @@ -20,6 +20,7 @@ mod qbytearray_cxx { fn modify_qbytearray(s: Pin<&mut QByteArray>); fn can_handle_qbytearray_change() -> bool; fn clone_qbytearray(s: &QByteArray) -> QByteArray; + fn can_use_as_slice() -> bool; } } @@ -57,3 +58,10 @@ fn can_handle_qbytearray_change() -> bool { fn clone_qbytearray(s: &QByteArray) -> QByteArray { s.clone() } + +fn can_use_as_slice() -> bool { + let string = "String slice"; + let slice = unsafe { QByteArray::from_raw_data(string.as_bytes()) }; + + slice.as_slice() == string.as_bytes() +}