From 9ba13c515e3b6cd0bf4cb0d565257c82dca78ddc Mon Sep 17 00:00:00 2001 From: Russell Greene Date: Wed, 3 Jan 2024 18:41:45 -0700 Subject: [PATCH 1/4] CxxVector: implement reserve() and capacity() --- gen/src/write.rs | 18 ++++++++++++++++++ macro/src/expand.rs | 19 +++++++++++++++++++ src/cxx.cc | 8 ++++++++ src/cxx_vector.rs | 39 +++++++++++++++++++++++++++++++++++++++ tests/test.rs | 5 +++++ 5 files changed, 89 insertions(+) diff --git a/gen/src/write.rs b/gen/src/write.rs index 8eef0a76b..a3689b241 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -1922,6 +1922,15 @@ fn write_cxx_vector(out: &mut OutFile, key: NamedImplKey) { writeln!(out, " return s.size();"); writeln!(out, "}}"); + begin_function_definition(out); + writeln!( + out, + "::std::size_t cxxbridge1$std$vector${}$capacity(::std::vector<{}> const &s) noexcept {{", + instance, inner, + ); + writeln!(out, " return s.capacity();"); + writeln!(out, "}}"); + begin_function_definition(out); writeln!( out, @@ -1931,6 +1940,15 @@ fn write_cxx_vector(out: &mut OutFile, key: NamedImplKey) { writeln!(out, " return &(*s)[pos];"); writeln!(out, "}}"); + begin_function_definition(out); + writeln!( + out, + "void cxxbridge1$std$vector${}$reserve(::std::vector<{}> *s, ::std::size_t new_cap) {{", + instance, inner, + ); + writeln!(out, " s->reserve(new_cap);"); + writeln!(out, "}}"); + if out.types.is_maybe_trivial(element) { begin_function_definition(out); writeln!( diff --git a/macro/src/expand.rs b/macro/src/expand.rs index ff1ed2076..30a671241 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -1678,7 +1678,9 @@ fn expand_cxx_vector( let prefix = format!("cxxbridge1$std$vector${}$", resolve.name.to_symbol()); let link_new = format!("{}new", prefix); let link_size = format!("{}size", prefix); + let link_capacity = format!("{}capacity", prefix); let link_get_unchecked = format!("{}get_unchecked", prefix); + let link_reserve = format!("{}reserve", prefix); let link_push_back = format!("{}push_back", prefix); let link_pop_back = format!("{}pop_back", prefix); let unique_ptr_prefix = format!( @@ -1760,6 +1762,13 @@ fn expand_cxx_vector( } unsafe { __vector_size(v) } } + fn __vector_capacity(v: &::cxx::CxxVector) -> usize { + extern "C" { + #[link_name = #link_capacity] + fn __vector_capacity #impl_generics(_: &::cxx::CxxVector<#elem #ty_generics>) -> usize; + } + unsafe { __vector_capacity(v) } + } unsafe fn __get_unchecked(v: *mut ::cxx::CxxVector, pos: usize) -> *mut Self { extern "C" { #[link_name = #link_get_unchecked] @@ -1770,6 +1779,16 @@ fn expand_cxx_vector( } unsafe { __get_unchecked(v, pos) as *mut Self } } + unsafe fn __reserve(v: ::cxx::core::pin::Pin<&mut ::cxx::CxxVector>, new_cap: usize) { + extern "C" { + #[link_name = #link_reserve] + fn __reserve #impl_generics( + v: ::cxx::core::pin::Pin<&mut ::cxx::CxxVector<#elem #ty_generics>>, + new_cap: usize, + ); + } + unsafe { __reserve(v, new_cap) } + } #by_value_methods fn __unique_ptr_null() -> ::cxx::core::mem::MaybeUninit<*mut ::cxx::core::ffi::c_void> { extern "C" { diff --git a/src/cxx.cc b/src/cxx.cc index 2522d61aa..cccbc64d1 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -600,10 +600,18 @@ static_assert(sizeof(std::string) <= kMaxExpectedWordsInString * sizeof(void *), const std::vector &s) noexcept { \ return s.size(); \ } \ + std::size_t cxxbridge1$std$vector$##RUST_TYPE##$capacity( \ + const std::vector &s) noexcept { \ + return s.capacity(); \ + } \ CXX_TYPE *cxxbridge1$std$vector$##RUST_TYPE##$get_unchecked( \ std::vector *s, std::size_t pos) noexcept { \ return &(*s)[pos]; \ } \ + void cxxbridge1$std$vector$##RUST_TYPE##$reserve( \ + std::vector *s, std::size_t new_cap) noexcept { \ + s->reserve(new_cap); \ + } \ void cxxbridge1$unique_ptr$std$vector$##RUST_TYPE##$null( \ std::unique_ptr> *ptr) noexcept { \ new (ptr) std::unique_ptr>(); \ diff --git a/src/cxx_vector.rs b/src/cxx_vector.rs index 5dcbe1c53..d3b3960a7 100644 --- a/src/cxx_vector.rs +++ b/src/cxx_vector.rs @@ -53,6 +53,15 @@ where T::__vector_size(self) } + /// Returns the capacity of the vector + /// + /// Matches the behavior of C++ [std::vector\::capacity][capacity]. + /// + /// [size]: https://en.cppreference.com/w/cpp/container/vector/capacity + pub fn capacity(&self) -> usize { + T::__vector_capacity(self) + } + /// Returns true if the vector contains no elements. /// /// Matches the behavior of C++ [std::vector\::empty][empty]. @@ -196,6 +205,18 @@ where }) } } + + /// Reserve additional space in the vector + /// + /// Note that this follows Rust semantics of being *additional* + /// capacity instead of absolute capacity. Equivalent to `vec.reserve(vec.size() + additional)` + /// in C++ + pub fn reserve(self: Pin<&mut Self>, additional: usize) { + unsafe { + let len = self.as_ref().len(); + T::__reserve(self, len + additional); + } + } } /// Iterator over elements of a `CxxVector` by shared reference. @@ -350,8 +371,12 @@ pub unsafe trait VectorElement: Sized { #[doc(hidden)] fn __vector_size(v: &CxxVector) -> usize; #[doc(hidden)] + fn __vector_capacity(v: &CxxVector) -> usize; + #[doc(hidden)] unsafe fn __get_unchecked(v: *mut CxxVector, pos: usize) -> *mut Self; #[doc(hidden)] + unsafe fn __reserve(v: Pin<&mut CxxVector>, new_capacity: usize); + #[doc(hidden)] unsafe fn __push_back(v: Pin<&mut CxxVector>, value: &mut ManuallyDrop) { // Opaque C type vector elements do not get this method because they can // never exist by value on the Rust side of the bridge. @@ -422,6 +447,13 @@ macro_rules! impl_vector_element { } unsafe { __vector_size(v) } } + fn __vector_capacity(v: &CxxVector<$ty>) -> usize { + extern "C" { + #[link_name = concat!("cxxbridge1$std$vector$", $segment, "$capacity")] + fn __vector_capacity(_: &CxxVector<$ty>) -> usize; + } + unsafe { __vector_capacity(v) } + } unsafe fn __get_unchecked(v: *mut CxxVector<$ty>, pos: usize) -> *mut $ty { extern "C" { #[link_name = concat!("cxxbridge1$std$vector$", $segment, "$get_unchecked")] @@ -429,6 +461,13 @@ macro_rules! impl_vector_element { } unsafe { __get_unchecked(v, pos) } } + unsafe fn __reserve(v: Pin<&mut CxxVector<$ty>>, pos: usize) { + extern "C" { + #[link_name = concat!("cxxbridge1$std$vector$", $segment, "$reserve")] + fn __reserve(_: Pin<&mut CxxVector<$ty>>, _: usize); + } + unsafe { __reserve(v, pos) } + } vector_element_by_value_methods!($kind, $segment, $ty); fn __unique_ptr_null() -> MaybeUninit<*mut c_void> { extern "C" { diff --git a/tests/test.rs b/tests/test.rs index 6ef9a8293..f9f86b191 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -56,6 +56,7 @@ fn test_c_return() { assert_eq!("Hello \u{fffd}World", ffi::c_return_rust_string_lossy()); assert_eq!("2020", ffi::c_return_unique_ptr_string().to_str().unwrap()); assert_eq!(4, ffi::c_return_unique_ptr_vector_u8().len()); + assert!(4 <= ffi::c_return_unique_ptr_vector_u8().capacity()); assert_eq!( 200_u8, ffi::c_return_unique_ptr_vector_u8().into_iter().sum(), @@ -65,6 +66,7 @@ fn test_c_return() { ffi::c_return_unique_ptr_vector_f64().into_iter().sum(), ); assert_eq!(2, ffi::c_return_unique_ptr_vector_shared().len()); + assert!(2 <= ffi::c_return_unique_ptr_vector_shared().capacity()); assert_eq!( 2021_usize, ffi::c_return_unique_ptr_vector_shared() @@ -160,6 +162,9 @@ fn test_c_take() { check!(ffi::c_take_unique_ptr_vector_u8(vector)); let mut vector = ffi::c_return_unique_ptr_vector_f64(); vector.pin_mut().push(9.0); + assert!(vector.pin_mut().capacity() >= 1); + vector.pin_mut().reserve(100); + assert!(vector.pin_mut().capacity() >= 101); check!(ffi::c_take_unique_ptr_vector_f64(vector)); let mut vector = ffi::c_return_unique_ptr_vector_shared(); vector.pin_mut().push(ffi::Shared { z: 9 }); From 974a2364220f1281a8102188bd8c8ba584711082 Mon Sep 17 00:00:00 2001 From: Russell Greene Date: Wed, 3 Jan 2024 18:51:21 -0700 Subject: [PATCH 2/4] fix docs --- src/cxx_vector.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cxx_vector.rs b/src/cxx_vector.rs index d3b3960a7..4dc9d5d52 100644 --- a/src/cxx_vector.rs +++ b/src/cxx_vector.rs @@ -57,7 +57,7 @@ where /// /// Matches the behavior of C++ [std::vector\::capacity][capacity]. /// - /// [size]: https://en.cppreference.com/w/cpp/container/vector/capacity + /// [capacity]: https://en.cppreference.com/w/cpp/container/vector/capacity pub fn capacity(&self) -> usize { T::__vector_capacity(self) } From 64625ac9f0a9e2dc5d63628f017caa64753811e4 Mon Sep 17 00:00:00 2001 From: Russell Greene Date: Wed, 3 Jan 2024 19:00:09 -0700 Subject: [PATCH 3/4] also do extend --- src/cxx_vector.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/cxx_vector.rs b/src/cxx_vector.rs index 4dc9d5d52..894687929 100644 --- a/src/cxx_vector.rs +++ b/src/cxx_vector.rs @@ -219,6 +219,18 @@ where } } +impl Extend for Pin<&mut CxxVector> +where + A: ExternType, + A: VectorElement, +{ + fn extend>(&mut self, iter: T) { + for i in iter { + self.as_mut().push(i); + } + } +} + /// Iterator over elements of a `CxxVector` by shared reference. /// /// The iterator element type is `&'a T`. From 56ee0c26a5dc4916cb242785e904d322e6e035bc Mon Sep 17 00:00:00 2001 From: Russell Greene Date: Wed, 3 Jan 2024 19:11:53 -0700 Subject: [PATCH 4/4] slightly smarter extend --- src/cxx_vector.rs | 2 ++ tests/test.rs | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/cxx_vector.rs b/src/cxx_vector.rs index 894687929..1d952b11c 100644 --- a/src/cxx_vector.rs +++ b/src/cxx_vector.rs @@ -225,6 +225,8 @@ where A: VectorElement, { fn extend>(&mut self, iter: T) { + let iter = iter.into_iter(); + self.as_mut().reserve(iter.size_hint().0); for i in iter { self.as_mut().push(i); } diff --git a/tests/test.rs b/tests/test.rs index f9f86b191..6faa95cec 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -161,7 +161,7 @@ fn test_c_take() { assert_eq!(vector.pin_mut().pop(), Some(9)); check!(ffi::c_take_unique_ptr_vector_u8(vector)); let mut vector = ffi::c_return_unique_ptr_vector_f64(); - vector.pin_mut().push(9.0); + vector.pin_mut().extend(Some(9.0)); assert!(vector.pin_mut().capacity() >= 1); vector.pin_mut().reserve(100); assert!(vector.pin_mut().capacity() >= 101);