Implement v0.6 Optional Bytes
This makes a few changes:
- It changes generated messages to reference message innards as a type in `__runtime` instead of branching on what fields should be there. That results in much less bifurcation in gencode and lets runtime-agnostic code reference raw message innards.
- It adds a generic mechanism for creating vtable-based mutators. These vtables point to thunks generated for interacting with C++ or upb fields. Right now, the design results in 2-word (msg+vtable) mutators for C++ and 3-word mutators (msg+arena+vtable) for UPB. See upb.rs for an explanation of the design options. I chose the `RawMessage+&Arena` design for mutator data as opposed to a `&MessageInner` design because it did not result in extra-indirection layout changes for message mutators. We could revisit this in the future with performance data, since this results in all field mutators being 3 words large instead of the register-friendly 2 words.
- And lastly, as a nearby change that touches on many of the same topics, it adds some extra SAFETY comments for Send/Sync in message gencode.
PiperOrigin-RevId: 559483437
diff --git a/rust/BUILD b/rust/BUILD
index 889e00e..4feba32 100644
--- a/rust/BUILD
+++ b/rust/BUILD
@@ -57,6 +57,7 @@
"shared.rs",
"string.rs",
"upb.rs",
+ "vtable.rs",
],
crate_root = "shared.rs",
rustc_flags = ["--cfg=upb_kernel"],
@@ -92,6 +93,7 @@
"proxied.rs",
"shared.rs",
"string.rs",
+ "vtable.rs",
],
crate_root = "shared.rs",
rustc_flags = ["--cfg=cpp_kernel"],
diff --git a/rust/cpp.rs b/rust/cpp.rs
index 46cead7..58939a3 100644
--- a/rust/cpp.rs
+++ b/rust/cpp.rs
@@ -30,7 +30,7 @@
// Rust Protobuf runtime using the C++ kernel.
-use crate::__internal::RawArena;
+use crate::__internal::{Private, RawArena, RawMessage};
use std::alloc::Layout;
use std::cell::UnsafeCell;
use std::fmt;
@@ -156,6 +156,47 @@
}
}
+pub type BytesPresentMutData<'msg> = crate::vtable::RawVTableOptionalMutatorData<'msg, [u8]>;
+pub type BytesAbsentMutData<'msg> = crate::vtable::RawVTableOptionalMutatorData<'msg, [u8]>;
+pub type InnerBytesMut<'msg> = crate::vtable::RawVTableMutator<'msg, [u8]>;
+
+/// The raw contents of every generated message.
+#[derive(Debug)]
+pub struct MessageInner {
+ pub msg: RawMessage,
+}
+
+/// Mutators that point to their original message use this to do so.
+///
+/// Since C++ messages manage their own memory, this can just copy the
+/// `RawMessage` instead of referencing an arena like UPB must.
+///
+/// Note: even though this type is `Copy`, it should only be copied by
+/// protobuf internals that can maintain mutation invariants.
+#[derive(Clone, Copy, Debug)]
+pub struct MutatorMessageRef<'msg> {
+ msg: RawMessage,
+ _phantom: PhantomData<&'msg mut ()>,
+}
+impl<'msg> MutatorMessageRef<'msg> {
+ #[allow(clippy::needless_pass_by_ref_mut)] // Sound construction requires mutable access.
+ pub fn new(_private: Private, msg: &'msg mut MessageInner) -> Self {
+ MutatorMessageRef { msg: msg.msg, _phantom: PhantomData }
+ }
+
+ pub fn msg(&self) -> RawMessage {
+ self.msg
+ }
+}
+
+pub fn copy_bytes_in_arena_if_needed_by_runtime<'a>(
+ _msg_ref: MutatorMessageRef<'a>,
+ val: &'a [u8],
+) -> &'a [u8] {
+ // Nothing to do, the message manages its own string memory for C++.
+ val
+}
+
#[cfg(test)]
mod tests {
use super::*;
diff --git a/rust/internal.rs b/rust/internal.rs
index 7423a3c..c1d8dca 100644
--- a/rust/internal.rs
+++ b/rust/internal.rs
@@ -32,6 +32,9 @@
//! exposed to through the `protobuf` path but must be public for use by
//! generated code.
+pub use crate::vtable::{
+ new_vtable_field_entry, BytesMutVTable, BytesOptionalMutVTable, RawVTableMutator,
+};
use std::slice;
/// Used to protect internal-only items from being used accidentally.
diff --git a/rust/shared.rs b/rust/shared.rs
index dfee341..5abd8be 100644
--- a/rust/shared.rs
+++ b/rust/shared.rs
@@ -67,6 +67,7 @@
mod optional;
mod proxied;
mod string;
+mod vtable;
/// An error that happened during deserialization.
#[derive(Debug, Clone)]
diff --git a/rust/string.rs b/rust/string.rs
index 143d284..fe78871 100644
--- a/rust/string.rs
+++ b/rust/string.rs
@@ -32,7 +32,8 @@
#![allow(dead_code)]
#![allow(unused)]
-use crate::__internal::Private;
+use crate::__internal::{Private, PtrAndLen, RawMessage};
+use crate::__runtime::{BytesAbsentMutData, BytesPresentMutData, InnerBytesMut};
use crate::{Mut, MutProxy, Proxied, ProxiedWithPresence, SettableValue, View, ViewProxy};
use std::borrow::Cow;
use std::cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd};
@@ -43,10 +44,6 @@
use std::ops::{Deref, DerefMut};
use utf8::Utf8Chunks;
-/// This type will be replaced by something else in a future revision.
-// TODO(b/285309330): remove this and any `impl`s using it.
-pub type Todo<'msg> = (std::convert::Infallible, std::marker::PhantomData<&'msg mut ()>);
-
/// A mutator for `bytes` fields - this type is `protobuf::Mut<'msg, [u8]>`.
///
/// This type implements `Deref<Target = [u8]>`, so many operations are
@@ -62,9 +59,29 @@
/// recommended to instead build a `Vec<u8>` or `String` and pass that directly
/// to `set`, which will reuse the allocation if supported by the runtime.
#[derive(Debug)]
-pub struct BytesMut<'msg>(Todo<'msg>);
+pub struct BytesMut<'msg> {
+ inner: InnerBytesMut<'msg>,
+}
+
+// SAFETY:
+// - Protobuf Rust messages don't allow shared mutation across threads.
+// - Protobuf Rust messages don't share arenas.
+// - All access that touches an arena occurs behind a `&mut`.
+// - All mutators that store an arena are `!Send`.
+unsafe impl Sync for BytesMut<'_> {}
impl<'msg> BytesMut<'msg> {
+ /// Constructs a new `BytesMut` from its internal, runtime-dependent part.
+ #[doc(hidden)]
+ pub fn from_inner(_private: Private, inner: InnerBytesMut<'msg>) -> Self {
+ Self { inner }
+ }
+
+ /// Gets the current value of the field.
+ pub fn get(&self) -> &[u8] {
+ self.as_view()
+ }
+
/// Sets the byte string to the given `val`, cloning any borrowed data.
///
/// This method accepts both owned and borrowed byte strings; if the runtime
@@ -78,7 +95,7 @@
///
/// Has no effect if `new_len` is larger than the current `len`.
pub fn truncate(&mut self, new_len: usize) {
- todo!("b/285309330")
+ self.inner.truncate(new_len)
}
/// Clears the byte string to the empty string.
@@ -93,7 +110,7 @@
/// `BytesMut::clear` results in the accessor returning an empty string
/// while `FieldEntry::clear` results in the non-empty default.
///
- /// However, for a proto3 `bytes` that has implicit presence, there is no
+ /// However, for a proto3 `bytes` that have implicit presence, there is no
/// distinction between these states: unset `bytes` is the same as empty
/// `bytes` and the default is always the empty string.
///
@@ -117,7 +134,7 @@
impl AsRef<[u8]> for BytesMut<'_> {
fn as_ref(&self) -> &[u8] {
- todo!("b/285309330")
+ unsafe { self.inner.get() }
}
}
@@ -126,45 +143,20 @@
type Mut<'msg> = BytesMut<'msg>;
}
-impl<'msg> ViewProxy<'msg> for Todo<'msg> {
- type Proxied = [u8];
- fn as_view(&self) -> &[u8] {
- unreachable!()
- }
- fn into_view<'shorter>(self) -> &'shorter [u8]
- where
- 'msg: 'shorter,
- {
- unreachable!()
- }
-}
-
-impl<'msg> MutProxy<'msg> for Todo<'msg> {
- fn as_mut(&mut self) -> BytesMut<'msg> {
- unreachable!()
- }
- fn into_mut<'shorter>(self) -> BytesMut<'shorter>
- where
- 'msg: 'shorter,
- {
- unreachable!()
- }
-}
-
impl ProxiedWithPresence for [u8] {
- type PresentMutData<'msg> = Todo<'msg>;
- type AbsentMutData<'msg> = Todo<'msg>;
+ type PresentMutData<'msg> = BytesPresentMutData<'msg>;
+ type AbsentMutData<'msg> = BytesAbsentMutData<'msg>;
fn clear_present_field<'a>(
present_mutator: Self::PresentMutData<'a>,
) -> Self::AbsentMutData<'a> {
- todo!("b/285309330")
+ present_mutator.clear()
}
fn set_absent_to_default<'a>(
absent_mutator: Self::AbsentMutData<'a>,
) -> Self::PresentMutData<'a> {
- todo!("b/285309330")
+ absent_mutator.set_absent_to_default()
}
}
@@ -194,48 +186,89 @@
where
'msg: 'shorter,
{
- todo!("b/285309330")
+ self.inner.get()
}
}
impl<'msg> MutProxy<'msg> for BytesMut<'msg> {
fn as_mut(&mut self) -> BytesMut<'_> {
- todo!("b/285309330")
+ BytesMut { inner: self.inner }
}
fn into_mut<'shorter>(self) -> BytesMut<'shorter>
where
'msg: 'shorter,
{
- todo!("b/285309330")
+ BytesMut { inner: self.inner }
}
}
-impl SettableValue<[u8]> for &'_ [u8] {
+impl<'bytes> SettableValue<[u8]> for &'bytes [u8] {
fn set_on(self, _private: Private, mutator: BytesMut<'_>) {
- todo!("b/285309330")
+ // SAFETY: this is a `bytes` field with no restriction on UTF-8.
+ unsafe { mutator.inner.set(self) }
+ }
+
+ fn set_on_absent(
+ self,
+ _private: Private,
+ absent_mutator: <[u8] as ProxiedWithPresence>::AbsentMutData<'_>,
+ ) -> <[u8] as ProxiedWithPresence>::PresentMutData<'_> {
+ // SAFETY: this is a `bytes` field with no restriction on UTF-8.
+ unsafe { absent_mutator.set(self) }
+ }
+
+ fn set_on_present(
+ self,
+ _private: Private,
+ present_mutator: <[u8] as ProxiedWithPresence>::PresentMutData<'_>,
+ ) {
+ // SAFETY: this is a `bytes` field with no restriction on UTF-8.
+ unsafe {
+ present_mutator.set(self);
+ }
}
}
-impl<const N: usize> SettableValue<[u8]> for &'_ [u8; N] {
- fn set_on(self, _private: Private, mutator: BytesMut<'_>) {
- self[..].set_on(Private, mutator)
- }
+macro_rules! impl_forwarding_settable_value {
+ ($proxied:ty, $self:ident => $self_forwarding_expr:expr) => {
+ fn set_on($self, _private: Private, mutator: BytesMut<'_>) {
+ ($self_forwarding_expr).set_on(Private, mutator)
+ }
+
+ fn set_on_absent(
+ $self,
+ _private: Private,
+ absent_mutator: <$proxied as ProxiedWithPresence>::AbsentMutData<'_>,
+ ) -> <$proxied as ProxiedWithPresence>::PresentMutData<'_> {
+ ($self_forwarding_expr).set_on_absent(Private, absent_mutator)
+ }
+
+ fn set_on_present(
+ $self,
+ _private: Private,
+ present_mutator: <$proxied as ProxiedWithPresence>::PresentMutData<'_>,
+ ) {
+ ($self_forwarding_expr).set_on_present(Private, present_mutator)
+ }
+ };
+}
+
+impl<'a, const N: usize> SettableValue<[u8]> for &'a [u8; N] {
+ // forward to `self[..]`
+ impl_forwarding_settable_value!([u8], self => &self[..]);
}
impl SettableValue<[u8]> for Vec<u8> {
- fn set_on(self, _private: Private, mutator: BytesMut<'_>) {
- todo!("b/285309330")
- }
+ // TODO(b/293956360): Investigate taking ownership of this when allowed by the
+ // runtime.
+ impl_forwarding_settable_value!([u8], self => &self[..]);
}
impl SettableValue<[u8]> for Cow<'_, [u8]> {
- fn set_on(self, _private: Private, mutator: BytesMut<'_>) {
- match self {
- Cow::Borrowed(s) => s.set_on(Private, mutator),
- Cow::Owned(v) => v.set_on(Private, mutator),
- }
- }
+ // TODO(b/293956360): Investigate taking ownership of this when allowed by the
+ // runtime.
+ impl_forwarding_settable_value!([u8], self => &self[..]);
}
impl Hash for BytesMut<'_> {
diff --git a/rust/test/BUILD b/rust/test/BUILD
index 2ff1bc8..84566ff 100644
--- a/rust/test/BUILD
+++ b/rust/test/BUILD
@@ -10,6 +10,8 @@
UNITTEST_CC_PROTO_TARGET = "//src/google/protobuf:cc_test_protos"
UNITTEST_PROTO3_TARGET = "//src/google/protobuf:test_protos"
UNITTEST_PROTO3_CC_TARGET = "//src/google/protobuf:cc_test_protos"
+UNITTEST_PROTO3_OPTIONAL_TARGET = "//src/google/protobuf:test_protos"
+UNITTEST_PROTO3_OPTIONAL_CC_TARGET = "//src/google/protobuf:cc_test_protos"
alias(
name = "unittest_cc_proto",
@@ -46,6 +48,68 @@
deps = [UNITTEST_CC_PROTO_TARGET],
)
+rust_proto_library(
+ name = "unittest_proto3_rust_proto",
+ testonly = True,
+ visibility = [
+ "//rust/test/shared:__subpackages__",
+ ],
+ deps = [
+ UNITTEST_PROTO3_TARGET,
+ ],
+)
+
+rust_cc_proto_library(
+ name = "unittest_proto3_cc_rust_proto",
+ testonly = True,
+ visibility = [
+ "//rust/test/cpp:__subpackages__",
+ "//rust/test/shared:__subpackages__",
+ ],
+ deps = [UNITTEST_PROTO3_CC_TARGET],
+)
+
+rust_upb_proto_library(
+ name = "unittest_proto3_upb_rust_proto",
+ testonly = True,
+ visibility = [
+ "//rust/test/cpp:__subpackages__",
+ "//rust/test/shared:__subpackages__",
+ ],
+ deps = [UNITTEST_PROTO3_TARGET],
+)
+
+rust_proto_library(
+ name = "unittest_proto3_optional_rust_proto",
+ testonly = True,
+ visibility = [
+ "//rust/test/shared:__subpackages__",
+ ],
+ deps = [
+ UNITTEST_PROTO3_OPTIONAL_TARGET,
+ ],
+)
+
+rust_cc_proto_library(
+ name = "unittest_proto3_optional_cc_rust_proto",
+ testonly = True,
+ visibility = [
+ "//rust/test/cpp:__subpackages__",
+ "//rust/test/shared:__subpackages__",
+ ],
+ deps = [UNITTEST_PROTO3_OPTIONAL_CC_TARGET],
+)
+
+rust_upb_proto_library(
+ name = "unittest_proto3_optional_upb_rust_proto",
+ testonly = True,
+ visibility = [
+ "//rust/test/cpp:__subpackages__",
+ "//rust/test/shared:__subpackages__",
+ ],
+ deps = [UNITTEST_PROTO3_OPTIONAL_TARGET],
+)
+
proto_library(
name = "parent_proto",
srcs = ["parent.proto"],
diff --git a/rust/test/cpp/interop/main.rs b/rust/test/cpp/interop/main.rs
index 3158430..1ab03f5 100644
--- a/rust/test/cpp/interop/main.rs
+++ b/rust/test/cpp/interop/main.rs
@@ -64,7 +64,7 @@
let mut msg2 = TestAllTypes::new();
msg2.optional_int64_set(Some(42));
- msg2.optional_bytes_set(Some(b"something mysterious"));
+ msg2.optional_bytes_mut().set(b"something mysterious");
msg2.optional_bool_set(Some(false));
proto_assert_eq!(msg1, msg2);
@@ -74,7 +74,7 @@
fn deserialize_in_rust() {
let mut msg1 = TestAllTypes::new();
msg1.optional_int64_set(Some(-1));
- msg1.optional_bytes_set(Some(b"some cool data I guess"));
+ msg1.optional_bytes_mut().set(b"some cool data I guess");
let serialized =
unsafe { SerializeTestAllTypes(msg1.__unstable_cpp_repr_grant_permission_to_break()) };
@@ -87,7 +87,7 @@
fn deserialize_in_cpp() {
let mut msg1 = TestAllTypes::new();
msg1.optional_int64_set(Some(-1));
- msg1.optional_bytes_set(Some(b"some cool data I guess"));
+ msg1.optional_bytes_mut().set(b"some cool data I guess");
let data = msg1.serialize();
let msg2 = unsafe {
diff --git a/rust/test/shared/BUILD b/rust/test/shared/BUILD
index ac1486e..1ade4ce 100644
--- a/rust/test/shared/BUILD
+++ b/rust/test/shared/BUILD
@@ -87,25 +87,51 @@
rust_test(
name = "accessors_cpp_test",
srcs = ["accessors_test.rs"],
- deps = ["//rust/test:unittest_cc_rust_proto"],
+ aliases = {
+ "//rust:protobuf_cpp": "protobuf",
+ },
+ deps = [
+ "//rust:protobuf_cpp",
+ "//rust/test:unittest_cc_rust_proto",
+ ],
)
rust_test(
name = "accessors_upb_test",
srcs = ["accessors_test.rs"],
- deps = ["//rust/test:unittest_upb_rust_proto"],
+ aliases = {
+ "//rust:protobuf_upb": "protobuf",
+ },
+ deps = [
+ "//rust:protobuf_upb",
+ "//rust/test:unittest_upb_rust_proto",
+ ],
)
rust_test(
name = "accessors_proto3_cpp_test",
srcs = ["accessors_proto3_test.rs"],
- deps = ["//rust/test:unittest_proto3_cc_rust_proto"],
+ aliases = {
+ "//rust:protobuf_cpp": "protobuf",
+ },
+ deps = [
+ "//rust:protobuf_cpp",
+ "//rust/test:unittest_proto3_cc_rust_proto",
+ "//rust/test:unittest_proto3_optional_cc_rust_proto",
+ ],
)
rust_test(
name = "accessors_proto3_upb_test",
srcs = ["accessors_proto3_test.rs"],
- deps = ["//rust/test:unittest_proto3_upb_rust_proto"],
+ aliases = {
+ "//rust:protobuf_upb": "protobuf",
+ },
+ deps = [
+ "//rust:protobuf_upb",
+ "//rust/test:unittest_proto3_optional_upb_rust_proto",
+ "//rust/test:unittest_proto3_upb_rust_proto",
+ ],
)
rust_test(
diff --git a/rust/test/shared/accessors_proto3_test.rs b/rust/test/shared/accessors_proto3_test.rs
index 9a002b6..2789f11 100644
--- a/rust/test/shared/accessors_proto3_test.rs
+++ b/rust/test/shared/accessors_proto3_test.rs
@@ -30,7 +30,9 @@
/// Tests covering accessors for singular bool, int32, int64, and bytes fields
/// on proto3.
+use protobuf::Optional;
use unittest_proto3::proto3_unittest::TestAllTypes;
+use unittest_proto3_optional::proto2_unittest::TestProto3Optional;
#[test]
fn test_fixed32_accessors() {
@@ -45,18 +47,75 @@
}
#[test]
-fn test_optional_bytes_accessors() {
+fn test_bytes_accessors() {
let mut msg = TestAllTypes::new();
// Note: even though its named 'optional_bytes' the field is actually not proto3
// optional, so it does not support presence.
assert_eq!(msg.optional_bytes(), b"");
+ assert_eq!(msg.optional_bytes_mut().get(), b"");
- msg.optional_bytes_set(Some(b"accessors_test"));
+ msg.optional_bytes_mut().set(b"accessors_test");
assert_eq!(msg.optional_bytes(), b"accessors_test");
+ assert_eq!(msg.optional_bytes_mut().get(), b"accessors_test");
- msg.optional_bytes_set(None);
- assert_eq!(msg.optional_bytes(), b"");
+ {
+ let s = Vec::from(&b"hello world"[..]);
+ msg.optional_bytes_mut().set(&s[..]);
+ }
+ assert_eq!(msg.optional_bytes(), b"hello world");
+ assert_eq!(msg.optional_bytes_mut().get(), b"hello world");
- msg.optional_bytes_set(Some(b""));
+ msg.optional_bytes_mut().clear();
assert_eq!(msg.optional_bytes(), b"");
+ assert_eq!(msg.optional_bytes_mut().get(), b"");
+
+ msg.optional_bytes_mut().set(b"");
+ assert_eq!(msg.optional_bytes(), b"");
+ assert_eq!(msg.optional_bytes_mut().get(), b"");
+}
+
+#[test]
+fn test_optional_bytes_accessors() {
+ let mut msg = TestProto3Optional::new();
+ assert_eq!(msg.optional_bytes(), b"");
+ assert_eq!(msg.optional_bytes_opt(), Optional::Unset(&b""[..]));
+ assert_eq!(msg.optional_bytes_mut().get(), b"");
+ assert!(msg.optional_bytes_mut().is_unset());
+
+ {
+ let s = Vec::from(&b"hello world"[..]);
+ msg.optional_bytes_mut().set(&s[..]);
+ }
+ assert_eq!(msg.optional_bytes(), b"hello world");
+ assert_eq!(msg.optional_bytes_opt(), Optional::Set(&b"hello world"[..]));
+ assert!(msg.optional_bytes_mut().is_set());
+ assert_eq!(msg.optional_bytes_mut().get(), b"hello world");
+
+ msg.optional_bytes_mut().or_default().set(b"accessors_test");
+ assert_eq!(msg.optional_bytes(), b"accessors_test");
+ assert_eq!(msg.optional_bytes_opt(), Optional::Set(&b"accessors_test"[..]));
+ assert!(msg.optional_bytes_mut().is_set());
+ assert_eq!(msg.optional_bytes_mut().get(), b"accessors_test");
+ assert_eq!(msg.optional_bytes_mut().or_default().get(), b"accessors_test");
+
+ msg.optional_bytes_mut().clear();
+ assert_eq!(msg.optional_bytes(), b"");
+ assert_eq!(msg.optional_bytes_opt(), Optional::Unset(&b""[..]));
+ assert!(msg.optional_bytes_mut().is_unset());
+
+ msg.optional_bytes_mut().set(b"");
+ assert_eq!(msg.optional_bytes(), b"");
+ assert_eq!(msg.optional_bytes_opt(), Optional::Set(&b""[..]));
+
+ msg.optional_bytes_mut().clear();
+ msg.optional_bytes_mut().or_default();
+ assert_eq!(msg.optional_bytes(), b"");
+ assert_eq!(msg.optional_bytes_opt(), Optional::Set(&b""[..]));
+
+ msg.optional_bytes_mut().or_default().set(b"\xffbinary\x85non-utf8");
+ assert_eq!(msg.optional_bytes(), b"\xffbinary\x85non-utf8");
+ assert_eq!(msg.optional_bytes_opt(), Optional::Set(&b"\xffbinary\x85non-utf8"[..]));
+ assert!(msg.optional_bytes_mut().is_set());
+ assert_eq!(msg.optional_bytes_mut().get(), b"\xffbinary\x85non-utf8");
+ assert_eq!(msg.optional_bytes_mut().or_default().get(), b"\xffbinary\x85non-utf8");
}
diff --git a/rust/test/shared/accessors_test.rs b/rust/test/shared/accessors_test.rs
index 55e700d..32679ea 100644
--- a/rust/test/shared/accessors_test.rs
+++ b/rust/test/shared/accessors_test.rs
@@ -28,7 +28,9 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-/// Tests covering accessors for singular bool, int32, int64, and bytes fields.
+//! Tests covering accessors for singular bool, int32, int64, and bytes fields.
+
+use protobuf::Optional;
use unittest_proto::proto2_unittest::TestAllTypes;
#[test]
@@ -216,16 +218,93 @@
#[test]
fn test_optional_bytes_accessors() {
let mut msg = TestAllTypes::new();
- assert_eq!(msg.optional_bytes_opt(), None);
+ assert_eq!(msg.optional_bytes(), b"");
+ assert_eq!(msg.optional_bytes_opt(), Optional::Unset(&b""[..]));
+ assert_eq!(msg.optional_bytes_mut().get(), b"");
+ assert!(msg.optional_bytes_mut().is_unset());
- msg.optional_bytes_set(Some(b"accessors_test"));
- assert_eq!(msg.optional_bytes_opt().unwrap(), b"accessors_test");
+ {
+ let s = Vec::from(&b"hello world"[..]);
+ msg.optional_bytes_mut().set(&s[..]);
+ }
+ assert_eq!(msg.optional_bytes(), b"hello world");
+ assert_eq!(msg.optional_bytes_opt(), Optional::Set(&b"hello world"[..]));
+ assert!(msg.optional_bytes_mut().is_set());
+ assert_eq!(msg.optional_bytes_mut().get(), b"hello world");
- msg.optional_bytes_set(None);
- assert_eq!(msg.optional_bytes_opt(), None);
+ msg.optional_bytes_mut().or_default().set(b"accessors_test");
+ assert_eq!(msg.optional_bytes(), b"accessors_test");
+ assert_eq!(msg.optional_bytes_opt(), Optional::Set(&b"accessors_test"[..]));
+ assert!(msg.optional_bytes_mut().is_set());
+ assert_eq!(msg.optional_bytes_mut().get(), b"accessors_test");
+ assert_eq!(msg.optional_bytes_mut().or_default().get(), b"accessors_test");
- msg.optional_bytes_set(Some(b""));
- assert_eq!(msg.optional_bytes_opt().unwrap(), b"");
+ msg.optional_bytes_mut().clear();
+ assert_eq!(msg.optional_bytes(), b"");
+ assert_eq!(msg.optional_bytes_opt(), Optional::Unset(&b""[..]));
+ assert!(msg.optional_bytes_mut().is_unset());
+
+ msg.optional_bytes_mut().set(b"");
+ assert_eq!(msg.optional_bytes(), b"");
+ assert_eq!(msg.optional_bytes_opt(), Optional::Set(&b""[..]));
+
+ msg.optional_bytes_mut().clear();
+ msg.optional_bytes_mut().or_default();
+ assert_eq!(msg.optional_bytes(), b"");
+ assert_eq!(msg.optional_bytes_opt(), Optional::Set(&b""[..]));
+
+ msg.optional_bytes_mut().or_default().set(b"\xffbinary\x85non-utf8");
+ assert_eq!(msg.optional_bytes(), b"\xffbinary\x85non-utf8");
+ assert_eq!(msg.optional_bytes_opt(), Optional::Set(&b"\xffbinary\x85non-utf8"[..]));
+ assert!(msg.optional_bytes_mut().is_set());
+ assert_eq!(msg.optional_bytes_mut().get(), b"\xffbinary\x85non-utf8");
+ assert_eq!(msg.optional_bytes_mut().or_default().get(), b"\xffbinary\x85non-utf8");
+}
+
+#[test]
+fn test_nonempty_default_bytes_accessors() {
+ let mut msg = TestAllTypes::new();
+ assert_eq!(msg.default_bytes(), b"world");
+ assert_eq!(msg.default_bytes_opt(), Optional::Unset(&b"world"[..]));
+ assert_eq!(msg.default_bytes_mut().get(), b"world");
+ assert!(msg.default_bytes_mut().is_unset());
+
+ {
+ let s = String::from("hello world");
+ msg.default_bytes_mut().set(s.as_bytes());
+ }
+ assert_eq!(msg.default_bytes(), b"hello world");
+ assert_eq!(msg.default_bytes_opt(), Optional::Set(&b"hello world"[..]));
+ assert!(msg.default_bytes_mut().is_set());
+ assert_eq!(msg.default_bytes_mut().get(), b"hello world");
+
+ msg.default_bytes_mut().or_default().set(b"accessors_test");
+ assert_eq!(msg.default_bytes(), b"accessors_test");
+ assert_eq!(msg.default_bytes_opt(), Optional::Set(&b"accessors_test"[..]));
+ assert!(msg.default_bytes_mut().is_set());
+ assert_eq!(msg.default_bytes_mut().get(), b"accessors_test");
+ assert_eq!(msg.default_bytes_mut().or_default().get(), b"accessors_test");
+
+ msg.default_bytes_mut().clear();
+ assert_eq!(msg.default_bytes(), b"world");
+ assert_eq!(msg.default_bytes_opt(), Optional::Unset(&b"world"[..]));
+ assert!(msg.default_bytes_mut().is_unset());
+
+ msg.default_bytes_mut().set(b"");
+ assert_eq!(msg.default_bytes(), b"");
+ assert_eq!(msg.default_bytes_opt(), Optional::Set(&b""[..]));
+
+ msg.default_bytes_mut().clear();
+ msg.default_bytes_mut().or_default();
+ assert_eq!(msg.default_bytes(), b"world");
+ assert_eq!(msg.default_bytes_opt(), Optional::Set(&b"world"[..]));
+
+ msg.default_bytes_mut().or_default().set(b"\xffbinary\x85non-utf8");
+ assert_eq!(msg.default_bytes(), b"\xffbinary\x85non-utf8");
+ assert_eq!(msg.default_bytes_opt(), Optional::Set(&b"\xffbinary\x85non-utf8"[..]));
+ assert!(msg.default_bytes_mut().is_set());
+ assert_eq!(msg.default_bytes_mut().get(), b"\xffbinary\x85non-utf8");
+ assert_eq!(msg.default_bytes_mut().or_default().get(), b"\xffbinary\x85non-utf8");
}
#[test]
diff --git a/rust/test/shared/serialization_test.rs b/rust/test/shared/serialization_test.rs
index ddfce52..c265c16 100644
--- a/rust/test/shared/serialization_test.rs
+++ b/rust/test/shared/serialization_test.rs
@@ -35,7 +35,7 @@
let mut msg = TestAllTypes::new();
msg.optional_int64_set(Some(42));
msg.optional_bool_set(Some(true));
- msg.optional_bytes_set(Some(b"serialize deserialize test"));
+ msg.optional_bytes_mut().set(b"serialize deserialize test");
let serialized = msg.serialize();
diff --git a/rust/upb.rs b/rust/upb.rs
index a887052..957f426 100644
--- a/rust/upb.rs
+++ b/rust/upb.rs
@@ -30,7 +30,7 @@
//! UPB FFI wrapper code for use by Rust Protobuf.
-use crate::__internal::RawArena;
+use crate::__internal::{Private, RawArena, RawMessage};
use std::alloc;
use std::alloc::Layout;
use std::cell::UnsafeCell;
@@ -200,6 +200,72 @@
}
}
+// TODO(b/293919363): Investigate replacing this with direct access to UPB bits.
+pub type BytesPresentMutData<'msg> = crate::vtable::RawVTableOptionalMutatorData<'msg, [u8]>;
+pub type BytesAbsentMutData<'msg> = crate::vtable::RawVTableOptionalMutatorData<'msg, [u8]>;
+pub type InnerBytesMut<'msg> = crate::vtable::RawVTableMutator<'msg, [u8]>;
+
+/// The raw contents of every generated message.
+#[derive(Debug)]
+pub struct MessageInner {
+ pub msg: RawMessage,
+ pub arena: Arena,
+}
+
+/// Mutators that point to their original message use this to do so.
+///
+/// Since UPB expects runtimes to manage their own arenas, this needs to have
+/// access to an `Arena`.
+///
+/// This has two possible designs:
+/// - Store two pointers here, `RawMessage` and `&'msg Arena`. This doesn't
+/// place any restriction on the layout of generated messages and their
+/// mutators. This makes a vtable-based mutator three pointers, which can no
+/// longer be returned in registers on most platforms.
+/// - Store one pointer here, `&'msg MessageInner`, where `MessageInner` stores
+/// a `RawMessage` and an `Arena`. This would require all generated messages
+/// to store `MessageInner`, and since their mutators need to be able to
+/// generate `BytesMut`, would also require `BytesMut` to store a `&'msg
+/// MessageInner` since they can't store an owned `Arena`.
+///
+/// Note: even though this type is `Copy`, it should only be copied by
+/// protobuf internals that can maintain mutation invariants.
+#[derive(Clone, Copy, Debug)]
+pub struct MutatorMessageRef<'msg> {
+ msg: RawMessage,
+ arena: &'msg Arena,
+}
+
+impl<'msg> MutatorMessageRef<'msg> {
+ #[doc(hidden)]
+ #[allow(clippy::needless_pass_by_ref_mut)] // Sound construction requires mutable access.
+ pub fn new(_private: Private, msg: &'msg mut MessageInner) -> Self {
+ MutatorMessageRef { msg: msg.msg, arena: &msg.arena }
+ }
+
+ pub fn msg(&self) -> RawMessage {
+ self.msg
+ }
+}
+
+pub fn copy_bytes_in_arena_if_needed_by_runtime<'a>(
+ msg_ref: MutatorMessageRef<'a>,
+ val: &'a [u8],
+) -> &'a [u8] {
+ // SAFETY: the alignment of `[u8]` is less than `UPB_MALLOC_ALIGN`.
+ let new_alloc = unsafe { msg_ref.arena.alloc(Layout::for_value(val)) };
+ debug_assert_eq!(new_alloc.len(), val.len());
+
+ let start: *mut u8 = new_alloc.as_mut_ptr().cast();
+ // SAFETY:
+ // - `new_alloc` is writeable for `val.len()` bytes.
+ // - After the copy, `new_alloc` is initialized for `val.len()` bytes.
+ unsafe {
+ val.as_ptr().copy_to_nonoverlapping(start, val.len());
+ &*(new_alloc as *mut _ as *mut [u8])
+ }
+}
+
#[cfg(test)]
mod tests {
use super::*;
diff --git a/rust/vtable.rs b/rust/vtable.rs
new file mode 100644
index 0000000..293710b
--- /dev/null
+++ b/rust/vtable.rs
@@ -0,0 +1,378 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2023 Google LLC. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google LLC. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+use crate::__internal::{Private, PtrAndLen, RawMessage};
+use crate::__runtime::{copy_bytes_in_arena_if_needed_by_runtime, MutatorMessageRef};
+use crate::{
+ AbsentField, FieldEntry, Mut, MutProxy, Optional, PresentField, Proxied, ProxiedWithPresence,
+ View, ViewProxy,
+};
+use std::fmt::{self, Debug};
+
+/// A proxied type that can use a vtable to provide get/set access for a
+/// present field.
+///
+/// This vtable should consist of `unsafe fn`s that call thunks that operate on
+/// `RawMessage`. The structure of this vtable is different per proxied type.
+pub trait ProxiedWithRawVTable: Proxied {
+ /// The vtable for get/set access, stored in static memory.
+ type VTable: Debug + 'static;
+
+ fn make_view(_private: Private, mut_inner: RawVTableMutator<'_, Self>) -> View<'_, Self>;
+ fn make_mut(_private: Private, inner: RawVTableMutator<'_, Self>) -> Mut<'_, Self>;
+}
+
+/// A proxied type that can use a vtable to provide get/set/clear access for
+/// an optional field.
+///
+/// This vtable should consist of `unsafe fn`s that call thunks that operate on
+/// `RawMessage`. The structure of this vtable is different per-proxied type.
+pub trait ProxiedWithRawOptionalVTable: ProxiedWithRawVTable + ProxiedWithPresence {
+ /// The vtable for get/set/clear, must contain `Self::VTable`.
+ type OptionalVTable: Debug + 'static;
+
+ /// Cast from a static reference of `OptionalVTable` to `VTable`.
+ /// This should mean `OptionalVTable` contains a `VTable`.
+ fn upcast_vtable(
+ _private: Private,
+ optional_vtable: &'static Self::OptionalVTable,
+ ) -> &'static Self::VTable;
+}
+
+/// Constructs a new field entry from a raw message, a vtable for manipulation,
+/// and an eager check for whether the value is present or not.
+///
+/// # Safety
+/// - `msg_ref` must be valid to provide as an argument for `vtable`'s methods
+/// for `'msg`.
+/// - If given `msg_ref` as an argument, any values returned by `vtable` methods
+/// must be valid for `'msg`.
+/// - Operations on the vtable must be thread-compatible.
+#[doc(hidden)]
+pub unsafe fn new_vtable_field_entry<'msg, T: ProxiedWithRawOptionalVTable + ?Sized>(
+ _private: Private,
+ msg_ref: MutatorMessageRef<'msg>,
+ optional_vtable: &'static T::OptionalVTable,
+ is_set: bool,
+) -> FieldEntry<'msg, T>
+where
+ T: ProxiedWithPresence<
+ PresentMutData<'msg> = RawVTableOptionalMutatorData<'msg, T>,
+ AbsentMutData<'msg> = RawVTableOptionalMutatorData<'msg, T>,
+ >,
+{
+ let data = RawVTableOptionalMutatorData { msg_ref, vtable: optional_vtable };
+ if is_set {
+ Optional::Set(PresentField::from_inner(Private, data))
+ } else {
+ Optional::Unset(AbsentField::from_inner(Private, data))
+ }
+}
+
+/// The internal implementation type for a vtable-based `protobuf::Mut<T>`.
+///
+/// This stores the two components necessary to mutate the field:
+/// borrowed message data and a vtable reference.
+///
+/// The borrowed message data varies per runtime: C++ needs a message pointer,
+/// while UPB needs a message pointer and an `&Arena`.
+///
+/// Implementations of `ProxiedWithRawVTable` implement get/set
+/// on top of `RawVTableMutator<T>`, and the top-level mutator (e.g.
+/// `BytesMut`) calls these methods.
+///
+/// [`RawVTableOptionalMutatorData`] is similar, but also includes the
+/// capability to has/clear.
+pub struct RawVTableMutator<'msg, T: ProxiedWithRawVTable + ?Sized> {
+ msg_ref: MutatorMessageRef<'msg>,
+ vtable: &'static T::VTable,
+}
+
+// These use manual impls instead of derives to avoid unnecessary bounds on `T`.
+// This problem is referred to as "perfect derive".
+// https://smallcultfollowing.com/babysteps/blog/2022/04/12/implied-bounds-and-perfect-derive/
+impl<'msg, T: ProxiedWithRawVTable + ?Sized> Clone for RawVTableMutator<'msg, T> {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+impl<'msg, T: ProxiedWithRawVTable + ?Sized> Copy for RawVTableMutator<'msg, T> {}
+
+impl<'msg, T: ProxiedWithRawVTable + ?Sized> Debug for RawVTableMutator<'msg, T> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("RawVTableMutator")
+ .field("msg_ref", &self.msg_ref)
+ .field("vtable", &self.vtable)
+ .finish()
+ }
+}
+
+impl<'msg, T: ProxiedWithRawVTable + ?Sized> RawVTableMutator<'msg, T> {
+ /// # Safety
+ /// - `msg_ref` must be valid to provide as an argument for `vtable`'s
+ /// methods for `'msg`.
+ /// - If given `msg_ref` as an argument, any values returned by `vtable`
+ /// methods must be valid for `'msg`.
+ #[doc(hidden)]
+ pub unsafe fn new(
+ _private: Private,
+ msg_ref: MutatorMessageRef<'msg>,
+ vtable: &'static T::VTable,
+ ) -> Self {
+ RawVTableMutator { msg_ref, vtable }
+ }
+}
+
+/// [`RawVTableMutator`], but also includes has/clear.
+///
+/// This is used as the `PresentData` and `AbsentData` for `impl
+/// ProxiedWithPresence for T`. In that implementation, `clear_present_field`
+/// and `set_absent_to_default` will use methods implemented on
+/// `RawVTableOptionalMutatorData<T>` to do the setting and clearing.
+///
+/// This has the same representation for "present" and "absent" data;
+/// differences like default values are obviated by the vtable.
+pub struct RawVTableOptionalMutatorData<'msg, T: ProxiedWithRawOptionalVTable + ?Sized> {
+ msg_ref: MutatorMessageRef<'msg>,
+ vtable: &'static T::OptionalVTable,
+}
+
+unsafe impl<'msg, T: ProxiedWithRawOptionalVTable + ?Sized> Sync
+ for RawVTableOptionalMutatorData<'msg, T>
+{
+}
+
+// These use manual impls instead of derives to avoid unnecessary bounds on `T`.
+// This problem is referred to as "perfect derive".
+// https://smallcultfollowing.com/babysteps/blog/2022/04/12/implied-bounds-and-perfect-derive/
+impl<'msg, T: ProxiedWithRawOptionalVTable + ?Sized> Clone
+ for RawVTableOptionalMutatorData<'msg, T>
+{
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+impl<'msg, T: ProxiedWithRawOptionalVTable + ?Sized> Copy
+ for RawVTableOptionalMutatorData<'msg, T>
+{
+}
+
+impl<'msg, T: ProxiedWithRawOptionalVTable + ?Sized> Debug
+ for RawVTableOptionalMutatorData<'msg, T>
+{
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("RawVTableOptionalMutatorData")
+ .field("msg_ref", &self.msg_ref)
+ .field("vtable", &self.vtable)
+ .finish()
+ }
+}
+
+impl<'msg, T: ProxiedWithRawOptionalVTable + ?Sized> RawVTableOptionalMutatorData<'msg, T> {
+ /// # Safety
+ /// - `msg_ref` must be valid to provide as an argument for `vtable`'s
+ /// methods for `'msg`.
+ /// - If given `msg_ref` as an argument, any values returned by `vtable`
+ /// methods must be valid for `'msg`.
+ #[doc(hidden)]
+ pub unsafe fn new(
+ _private: Private,
+ msg_ref: MutatorMessageRef<'msg>,
+ vtable: &'static T::OptionalVTable,
+ ) -> Self {
+ Self { msg_ref, vtable }
+ }
+
+ fn into_raw_mut(self) -> RawVTableMutator<'msg, T> {
+ RawVTableMutator { msg_ref: self.msg_ref, vtable: T::upcast_vtable(Private, self.vtable) }
+ }
+}
+
+impl<'msg, T: ProxiedWithRawOptionalVTable + ?Sized + 'msg> ViewProxy<'msg>
+ for RawVTableOptionalMutatorData<'msg, T>
+{
+ type Proxied = T;
+
+ fn as_view(&self) -> View<'_, T> {
+ T::make_view(Private, self.into_raw_mut())
+ }
+
+ fn into_view<'shorter>(self) -> View<'shorter, T>
+ where
+ 'msg: 'shorter,
+ {
+ T::make_view(Private, self.into_raw_mut())
+ }
+}
+
+// Note: though this raw value implements `MutProxy`, the `as_mut` is only valid
+// when the field is known to be present. `FieldEntry` enforces this in its
+// design: `AbsentField { inner: RawVTableOptionalMutatorData<T> }` does not
+// implement `MutProxy`.
+impl<'msg, T: ProxiedWithRawOptionalVTable + ?Sized + 'msg> MutProxy<'msg>
+ for RawVTableOptionalMutatorData<'msg, T>
+{
+ fn as_mut(&mut self) -> Mut<'_, T> {
+ T::make_mut(Private, self.into_raw_mut())
+ }
+
+ fn into_mut<'shorter>(self) -> Mut<'shorter, T>
+ where
+ 'msg: 'shorter,
+ {
+ T::make_mut(Private, self.into_raw_mut())
+ }
+}
+
+impl ProxiedWithRawVTable for [u8] {
+ type VTable = BytesMutVTable;
+
+ fn make_view(_private: Private, mut_inner: RawVTableMutator<'_, Self>) -> View<'_, Self> {
+ mut_inner.get()
+ }
+
+ fn make_mut(_private: Private, inner: RawVTableMutator<'_, Self>) -> Mut<'_, Self> {
+ crate::string::BytesMut::from_inner(Private, inner)
+ }
+}
+
+impl ProxiedWithRawOptionalVTable for [u8] {
+ type OptionalVTable = BytesOptionalMutVTable;
+ fn upcast_vtable(
+ _private: Private,
+ optional_vtable: &'static Self::OptionalVTable,
+ ) -> &'static Self::VTable {
+ &optional_vtable.base
+ }
+}
+
+/// A generic thunk vtable for mutating a present `bytes` or `string` field.
+#[doc(hidden)]
+#[derive(Debug)]
+pub struct BytesMutVTable {
+ pub(crate) setter: unsafe extern "C" fn(msg: RawMessage, val: *const u8, len: usize),
+ pub(crate) getter: unsafe extern "C" fn(msg: RawMessage) -> PtrAndLen,
+}
+
+/// A generic thunk vtable for mutating an `optional` `bytes` or `string` field.
+#[derive(Debug)]
+pub struct BytesOptionalMutVTable {
+ pub(crate) base: BytesMutVTable,
+ pub(crate) clearer: unsafe extern "C" fn(msg: RawMessage),
+ pub(crate) default: &'static [u8],
+}
+
+impl BytesMutVTable {
+ #[doc(hidden)]
+ pub const fn new(
+ _private: Private,
+ getter: unsafe extern "C" fn(msg: RawMessage) -> PtrAndLen,
+ setter: unsafe extern "C" fn(msg: RawMessage, val: *const u8, len: usize),
+ ) -> Self {
+ Self { getter, setter }
+ }
+}
+
+impl BytesOptionalMutVTable {
+ /// # Safety
+ /// The `default` value must be UTF-8 if required by
+ /// the runtime and this is for a `string` field.
+ #[doc(hidden)]
+ pub const unsafe fn new(
+ _private: Private,
+ getter: unsafe extern "C" fn(msg: RawMessage) -> PtrAndLen,
+ setter: unsafe extern "C" fn(msg: RawMessage, val: *const u8, len: usize),
+ clearer: unsafe extern "C" fn(msg: RawMessage),
+ default: &'static [u8],
+ ) -> Self {
+ Self { base: BytesMutVTable { getter, setter }, clearer, default }
+ }
+}
+
+impl<'msg> RawVTableMutator<'msg, [u8]> {
+ pub(crate) fn get(self) -> &'msg [u8] {
+ // SAFETY:
+ // - `msg_ref` is valid for `'msg` as promised by the caller of `new`.
+ // - The caller of `BytesMutVTable` promised that the returned `PtrAndLen` is
+ // valid for `'msg`.
+ unsafe { (self.vtable.getter)(self.msg_ref.msg()).as_ref() }
+ }
+
+ /// # Safety
+ /// - `msg_ref` must be valid for `'msg`
+ /// - If this is for a `string` field, `val` must be valid UTF-8 if the
+ /// runtime requires it.
+ pub(crate) unsafe fn set(self, val: &[u8]) {
+ let val = copy_bytes_in_arena_if_needed_by_runtime(self.msg_ref, val);
+ // SAFETY:
+ // - `msg_ref` is valid for `'msg` as promised by the caller of `new`.
+ unsafe { (self.vtable.setter)(self.msg_ref.msg(), val.as_ptr(), val.len()) }
+ }
+
+ pub(crate) fn truncate(&self, len: usize) {
+ if len == 0 {
+ // SAFETY: The empty string is valid UTF-8.
+ unsafe {
+ self.set(b"");
+ }
+ return;
+ }
+ todo!("b/294252563")
+ }
+}
+
+impl<'msg> RawVTableOptionalMutatorData<'msg, [u8]> {
+ /// Sets an absent `bytes`/`string` field to its default value.
+ pub(crate) fn set_absent_to_default(self) -> Self {
+ // SAFETY: The default value is UTF-8 if required by the
+ // runtime as promised by the caller of `BytesOptionalMutVTable::new`.
+ unsafe { self.set(self.vtable.default) }
+ }
+
+ /// # Safety
+ /// - If this is a `string` field, `val` must be valid UTF-8 if required by
+ /// the runtime.
+ pub(crate) unsafe fn set(self, val: &[u8]) -> Self {
+ let val = copy_bytes_in_arena_if_needed_by_runtime(self.msg_ref, val);
+ // SAFETY:
+ // - `msg_ref` is valid for `'msg` as promised by the caller.
+ unsafe { (self.vtable.base.setter)(self.msg_ref.msg(), val.as_ptr(), val.len()) }
+ self
+ }
+
+ pub(crate) fn clear(self) -> Self {
+ // SAFETY:
+ // - `msg_ref` is valid for `'msg` as promised by the caller.
+ // - The caller of `new` promised that the returned `PtrAndLen` is valid for
+ // `'msg`.
+ unsafe { (self.vtable.clearer)(self.msg_ref.msg()) }
+ self
+ }
+}
diff --git a/src/google/protobuf/compiler/rust/accessors/singular_bytes.cc b/src/google/protobuf/compiler/rust/accessors/singular_bytes.cc
index 3deb5a6..089ff63 100644
--- a/src/google/protobuf/compiler/rust/accessors/singular_bytes.cc
+++ b/src/google/protobuf/compiler/rust/accessors/singular_bytes.cc
@@ -28,6 +28,9 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#include <string>
+
+#include "absl/strings/escaping.h"
#include "absl/strings/string_view.h"
#include "google/protobuf/compiler/cpp/helpers.h"
#include "google/protobuf/compiler/rust/accessors/accessor_generator.h"
@@ -41,45 +44,107 @@
namespace rust {
void SingularBytes::InMsgImpl(Context<FieldDescriptor> field) const {
+ std::string hazzer_thunk = Thunk(field, "has");
+ std::string getter_thunk = Thunk(field, "get");
+ std::string setter_thunk = Thunk(field, "set");
field.Emit(
{
{"field", field.desc().name()},
- {"hazzer_thunk", Thunk(field, "has")},
- {"getter_thunk", Thunk(field, "get")},
- {"setter_thunk", Thunk(field, "set")},
- {"clearer_thunk", Thunk(field, "clear")},
- {"getter_opt",
+ {"hazzer_thunk", hazzer_thunk},
+ {"getter_thunk", getter_thunk},
+ {"setter_thunk", setter_thunk},
+ {"field_optional_getter",
[&] {
if (!field.desc().is_optional()) return;
if (!field.desc().has_presence()) return;
- field.Emit({}, R"rs(
- pub fn $field$_opt(&self) -> Option<&[u8]> {
- if !unsafe { $hazzer_thunk$(self.msg) } {
- return None;
- }
- unsafe {
- Some($getter_thunk$(self.msg).as_ref())
- }
- })rs");
+ field.Emit({{"hazzer_thunk", hazzer_thunk},
+ {"getter_thunk", getter_thunk}},
+ R"rs(
+ pub fn $field$_opt(&self) -> $pb$::Optional<&[u8]> {
+ unsafe {
+ $pb$::Optional::new(
+ $getter_thunk$(self.inner.msg).as_ref(),
+ $hazzer_thunk$(self.inner.msg)
+ )
+ }
+ }
+ )rs");
+ }},
+ {"field_mutator_getter",
+ [&] {
+ if (field.desc().has_presence()) {
+ field.Emit(
+ {
+ {"field", field.desc().name()},
+ {"default_val",
+ absl::CHexEscape(field.desc().default_value_string())},
+ {"hazzer_thunk", hazzer_thunk},
+ {"getter_thunk", getter_thunk},
+ {"setter_thunk", setter_thunk},
+ {"clearer_thunk", Thunk(field, "clear")},
+ },
+ R"rs(
+ pub fn $field$_mut(&mut self) -> $pb$::FieldEntry<'_, [u8]> {
+ static VTABLE: $pbi$::BytesOptionalMutVTable = unsafe {
+ $pbi$::BytesOptionalMutVTable::new(
+ $pbi$::Private,
+ $getter_thunk$,
+ $setter_thunk$,
+ $clearer_thunk$,
+ b"$default_val$",
+ )
+ };
+ unsafe {
+ let has = $hazzer_thunk$(self.inner.msg);
+ $pbi$::new_vtable_field_entry(
+ $pbi$::Private,
+ $pbr$::MutatorMessageRef::new(
+ $pbi$::Private, &mut self.inner),
+ &VTABLE,
+ has,
+ )
+ }
+ }
+ )rs");
+ } else {
+ field.Emit({{"field", field.desc().name()},
+ {"getter_thunk", getter_thunk},
+ {"setter_thunk", setter_thunk}},
+ R"rs(
+ pub fn $field$_mut(&mut self) -> $pb$::BytesMut<'_> {
+ static VTABLE: $pbi$::BytesMutVTable = unsafe {
+ $pbi$::BytesMutVTable::new(
+ $pbi$::Private,
+ $getter_thunk$,
+ $setter_thunk$,
+ )
+ };
+ unsafe {
+ $pb$::BytesMut::from_inner(
+ $pbi$::Private,
+ $pbi$::RawVTableMutator::new(
+ $pbi$::Private,
+ $pbr$::MutatorMessageRef::new(
+ $pbi$::Private, &mut self.inner),
+ &VTABLE,
+ )
+ )
+ }
+ }
+ )rs");
+ }
}},
},
R"rs(
- pub fn r#$field$(&self) -> &[u8] {
- unsafe { $getter_thunk$(self.msg).as_ref() }
+ pub fn r#$field$(&self) -> &[u8] {
+ unsafe {
+ $getter_thunk$(self.inner.msg).as_ref()
}
- $getter_opt$
- pub fn $field$_set(&mut self, val: Option<&[u8]>) {
- match val {
- Some(val) =>
- if val.len() == 0 {
- unsafe { $setter_thunk$(self.msg, $std$::ptr::null(), 0) }
- } else {
- unsafe { $setter_thunk$(self.msg, val.as_ptr(), val.len()) }
- },
- None => unsafe { $clearer_thunk$(self.msg) },
- }
- }
- )rs");
+ }
+
+ $field_optional_getter$
+ $field_mutator_getter$
+ )rs");
}
void SingularBytes::InExternC(Context<FieldDescriptor> field) const {
diff --git a/src/google/protobuf/compiler/rust/accessors/singular_message.cc b/src/google/protobuf/compiler/rust/accessors/singular_message.cc
index c939714..038abe7 100644
--- a/src/google/protobuf/compiler/rust/accessors/singular_message.cc
+++ b/src/google/protobuf/compiler/rust/accessors/singular_message.cc
@@ -46,7 +46,7 @@
R"rs(
// inMsgImpl
pub fn r#$field$(&self) -> $Msg$View {
- $Msg$View { msg: self.msg, _phantom: std::marker::PhantomData }
+ $Msg$View { msg: self.inner.msg, _phantom: std::marker::PhantomData }
}
)rs");
}
diff --git a/src/google/protobuf/compiler/rust/accessors/singular_scalar.cc b/src/google/protobuf/compiler/rust/accessors/singular_scalar.cc
index 6be8f44..93a5d41 100644
--- a/src/google/protobuf/compiler/rust/accessors/singular_scalar.cc
+++ b/src/google/protobuf/compiler/rust/accessors/singular_scalar.cc
@@ -50,7 +50,7 @@
[&] {
field.Emit({}, R"rs(
pub fn r#$field$(&self) -> $Scalar$ {
- unsafe { $getter_thunk$(self.msg) }
+ unsafe { $getter_thunk$(self.inner.msg) }
}
)rs");
}},
@@ -60,10 +60,10 @@
if (!field.desc().has_presence()) return;
field.Emit({}, R"rs(
pub fn r#$field$_opt(&self) -> Option<$Scalar$> {
- if !unsafe { $hazzer_thunk$(self.msg) } {
+ if !unsafe { $hazzer_thunk$(self.inner.msg) } {
return None;
}
- Some(unsafe { $getter_thunk$(self.msg) })
+ Some(unsafe { $getter_thunk$(self.inner.msg) })
}
)rs");
}},
@@ -77,8 +77,8 @@
pub fn $field$_set(&mut self, val: Option<$Scalar$>) {
match val {
- Some(val) => unsafe { $setter_thunk$(self.msg, val) },
- None => unsafe { $clearer_thunk$(self.msg) },
+ Some(val) => unsafe { $setter_thunk$(self.inner.msg, val) },
+ None => unsafe { $clearer_thunk$(self.inner.msg) },
}
}
)rs");
diff --git a/src/google/protobuf/compiler/rust/message.cc b/src/google/protobuf/compiler/rust/message.cc
index 64027f9..6f9ce5a 100644
--- a/src/google/protobuf/compiler/rust/message.cc
+++ b/src/google/protobuf/compiler/rust/message.cc
@@ -45,33 +45,12 @@
namespace compiler {
namespace rust {
namespace {
-void MessageStructFields(Context<Descriptor> msg) {
- switch (msg.opts().kernel) {
- case Kernel::kCpp:
- msg.Emit(R"rs(
- msg: $pbi$::RawMessage,
- )rs");
- return;
-
- case Kernel::kUpb:
- msg.Emit(R"rs(
- msg: $pbi$::RawMessage,
- //~ rustc incorrectly thinks this field is never read, even though
- //~ it has a destructor!
- #[allow(dead_code)]
- arena: $pbr$::Arena,
- )rs");
- return;
- }
-
- ABSL_LOG(FATAL) << "unreachable";
-}
void MessageNew(Context<Descriptor> msg) {
switch (msg.opts().kernel) {
case Kernel::kCpp:
msg.Emit({{"new_thunk", Thunk(msg, "new")}}, R"rs(
- Self { msg: unsafe { $new_thunk$() } }
+ Self { inner: $pbr$::MessageInner { msg: unsafe { $new_thunk$() } } }
)rs");
return;
@@ -79,8 +58,10 @@
msg.Emit({{"new_thunk", Thunk(msg, "new")}}, R"rs(
let arena = $pbr$::Arena::new();
Self {
- msg: unsafe { $new_thunk$(arena.raw()) },
- arena,
+ inner: $pbr$::MessageInner {
+ msg: unsafe { $new_thunk$(arena.raw()) },
+ arena,
+ }
}
)rs");
return;
@@ -93,7 +74,7 @@
switch (msg.opts().kernel) {
case Kernel::kCpp:
msg.Emit({{"serialize_thunk", Thunk(msg, "serialize")}}, R"rs(
- unsafe { $serialize_thunk$(self.msg) }
+ unsafe { $serialize_thunk$(self.inner.msg) }
)rs");
return;
@@ -102,7 +83,7 @@
let arena = $pbr$::Arena::new();
let mut len = 0;
unsafe {
- let data = $serialize_thunk$(self.msg, arena.raw(), &mut len);
+ let data = $serialize_thunk$(self.inner.msg, arena.raw(), &mut len);
$pbr$::SerializedData::from_raw_parts(arena, data, len)
}
)rs");
@@ -126,7 +107,7 @@
data.len(),
);
- $deserialize_thunk$(self.msg, data)
+ $deserialize_thunk$(self.inner.msg, data)
};
success.then_some(()).ok_or($pb$::ParseError)
)rs");
@@ -143,9 +124,9 @@
None => Err($pb$::ParseError),
Some(msg) => {
// This assignment causes self.arena to be dropped and to deallocate
- // any previous message pointed/owned to by self.msg.
- self.arena = arena;
- self.msg = msg;
+ // any previous message pointed/owned to by self.inner.msg.
+ self.inner.arena = arena;
+ self.inner.msg = msg;
Ok(())
}
}
@@ -200,7 +181,7 @@
}
msg.Emit({{"delete_thunk", Thunk(msg, "delete")}}, R"rs(
- unsafe { $delete_thunk$(self.msg); }
+ unsafe { $delete_thunk$(self.inner.msg); }
)rs");
}
} // namespace
@@ -213,7 +194,6 @@
msg.Emit(
{
{"Msg", msg.desc().name()},
- {"Msg.fields", [&] { MessageStructFields(msg); }},
{"Msg::new", [&] { MessageNew(msg); }},
{"Msg::serialize", [&] { MessageSerialize(msg); }},
{"Msg::deserialize", [&] { MessageDeserialize(msg); }},
@@ -264,12 +244,15 @@
#[allow(non_camel_case_types)]
#[derive(Debug)]
pub struct $Msg$ {
- $Msg.fields$
+ inner: $pbr$::MessageInner
}
+ // SAFETY:
+ // - `$Msg$` does not provide shared mutation with its arena.
+ // - `$Msg$Mut` is not `Send`, and so even in the presence of mutator
+ // splitting, synchronous access of an arena that would conflict with
+ // field access is impossible.
unsafe impl Sync for $Msg$ {}
- unsafe impl Sync for $Msg$View<'_> {}
- unsafe impl Send for $Msg$View<'_> {}
impl $pb$::Proxied for $Msg$ {
type View<'a> = $Msg$View<'a>;
@@ -283,6 +266,15 @@
_phantom: $Phantom$<&'a ()>,
}
+ // SAFETY:
+ // - `$Msg$View` does not perform any mutation.
+ // - While a `$Msg$View` exists, a `$Msg$Mut` can't exist to mutate
+ // the arena that would conflict with field access.
+ // - `$Msg$Mut` is not `Send`, and so even in the presence of mutator
+ // splitting, synchronous access of an arena is impossible.
+ unsafe impl Sync for $Msg$View<'_> {}
+ unsafe impl Send for $Msg$View<'_> {}
+
impl<'a> $pb$::ViewProxy<'a> for $Msg$View<'a> {
type Proxied = $Msg$;
@@ -303,15 +295,18 @@
#[derive(Debug, Copy, Clone)]
#[allow(dead_code)]
pub struct $Msg$Mut<'a> {
- msg: $pbi$::RawMessage,
- _phantom: $Phantom$<&'a mut ()>,
+ inner: $pbr$::MutatorMessageRef<'a>,
}
+ // SAFETY:
+ // - `$Msg$Mut` does not perform any shared mutation.
+ // - `$Msg$Mut` is not `Send`, and so even in the presence of mutator
+ // splitting, synchronous access of an arena is impossible.
unsafe impl Sync for $Msg$Mut<'_> {}
impl<'a> $pb$::MutProxy<'a> for $Msg$Mut<'a> {
fn as_mut(&mut self) -> $pb$::Mut<'_, $Msg$> {
- $Msg$Mut { msg: self.msg, _phantom: self._phantom }
+ $Msg$Mut { inner: self.inner }
}
fn into_mut<'shorter>(self) -> $pb$::Mut<'shorter, $Msg$> where 'a : 'shorter { self }
}
@@ -319,10 +314,10 @@
impl<'a> $pb$::ViewProxy<'a> for $Msg$Mut<'a> {
type Proxied = $Msg$;
fn as_view(&self) -> $pb$::View<'_, $Msg$> {
- $Msg$View { msg: self.msg, _phantom: std::marker::PhantomData }
+ $Msg$View { msg: self.inner.msg(), _phantom: std::marker::PhantomData }
}
fn into_view<'shorter>(self) -> $pb$::View<'shorter, $Msg$> where 'a: 'shorter {
- $Msg$View { msg: self.msg, _phantom: std::marker::PhantomData }
+ $Msg$View { msg: self.inner.msg(), _phantom: std::marker::PhantomData }
}
}
@@ -363,10 +358,10 @@
msg.Emit({{"Msg", msg.desc().name()}}, R"rs(
impl $Msg$ {
pub fn __unstable_wrap_cpp_grant_permission_to_break(msg: $pbi$::RawMessage) -> Self {
- Self { msg }
+ Self { inner: $pbr$::MessageInner { msg } }
}
pub fn __unstable_cpp_repr_grant_permission_to_break(&mut self) -> $pbi$::RawMessage {
- self.msg
+ self.inner.msg
}
}
)rs");