Skip to content

Commit

Permalink
Implement mutators for singular primitive fields without presence
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 560649893
  • Loading branch information
Necior authored and copybara-github committed Sep 4, 2023
1 parent 8f1c050 commit 49d0a89
Show file tree
Hide file tree
Showing 10 changed files with 261 additions and 14 deletions.
1 change: 1 addition & 0 deletions rust/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ PROTOBUF_SHARED = [
"internal.rs",
"macros.rs",
"optional.rs",
"primitive.rs",
"proxied.rs",
"shared.rs",
"string.rs",
Expand Down
1 change: 1 addition & 0 deletions rust/cpp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ impl fmt::Debug for SerializedData {
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]>;
pub type InnerPrimitiveMut<'a, T> = crate::vtable::RawVTableMutator<'a, T>;

/// The raw contents of every generated message.
#[derive(Debug)]
Expand Down
3 changes: 2 additions & 1 deletion rust/internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@
//! generated code.

pub use crate::vtable::{
new_vtable_field_entry, BytesMutVTable, BytesOptionalMutVTable, RawVTableMutator,
new_vtable_field_entry, BytesMutVTable, BytesOptionalMutVTable, PrimitiveVTable,
RawVTableMutator,
};
use std::ptr::NonNull;
use std::slice;
Expand Down
10 changes: 7 additions & 3 deletions rust/optional.rs
Original file line number Diff line number Diff line change
Expand Up @@ -539,7 +539,11 @@ mod tests {
fn set_absent_to_default<'a>(
absent_mutator: Self::AbsentMutData<'a>,
) -> Self::PresentMutData<'a> {
absent_mutator.as_view().val().set_on_absent(Private, absent_mutator)
SettableValue::<VtableProxied>::set_on_absent(
absent_mutator.as_view().val(),
Private,
absent_mutator,
)
}
}

Expand Down Expand Up @@ -609,15 +613,15 @@ mod tests {

impl SettableValue<VtableProxied> for View<'_, VtableProxied> {
fn set_on(self, _private: Private, mutator: Mut<VtableProxied>) {
self.val().set_on(Private, mutator)
SettableValue::<VtableProxied>::set_on(self.val(), Private, mutator)
}

fn set_on_absent<'a>(
self,
_private: Private,
absent_mutator: <VtableProxied as ProxiedWithPresence>::AbsentMutData<'a>,
) -> <VtableProxied as ProxiedWithPresence>::PresentMutData<'a> {
self.val().set_on_absent(Private, absent_mutator)
SettableValue::<VtableProxied>::set_on_absent(self.val(), Private, absent_mutator)
}
}

Expand Down
134 changes: 134 additions & 0 deletions rust/primitive.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// 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;
use crate::__runtime::InnerPrimitiveMut;
use crate::vtable::{PrimitiveVTable, ProxiedWithRawVTable};
use crate::{Mut, MutProxy, Proxied, SettableValue, View, ViewProxy};

#[derive(Debug)]
pub struct PrimitiveMut<'a, T: ProxiedWithRawVTable> {
inner: InnerPrimitiveMut<'a, T>,
}

impl<'a, T: ProxiedWithRawVTable> PrimitiveMut<'a, T> {
#[doc(hidden)]
pub fn from_inner(_private: Private, inner: InnerPrimitiveMut<'a, T>) -> Self {
Self { inner }
}
}

unsafe impl<'a, T: ProxiedWithRawVTable> Sync for PrimitiveMut<'a, T> {}

macro_rules! impl_singular_primitives {
($($t:ty),*) => {
$(
impl Proxied for $t {
type View<'a> = $t;
type Mut<'a> = PrimitiveMut<'a, $t>;
}

impl<'a> ViewProxy<'a> for $t {
type Proxied = $t;

fn as_view(&self) -> View<'_, Self::Proxied> {
*self
}

fn into_view<'shorter>(self) -> View<'shorter, Self::Proxied> {
self
}
}

impl<'a> ViewProxy<'a> for PrimitiveMut<'a, $t> {
type Proxied = $t;

fn as_view(&self) -> View<'_, Self::Proxied> {
self.get()
}

fn into_view<'shorter>(self) -> View<'shorter, Self::Proxied> {
self.get()
}
}

impl<'a> MutProxy<'a> for PrimitiveMut<'a, $t> {
fn as_mut(&mut self) -> Mut<'_, Self::Proxied> {
PrimitiveMut::from_inner(Private, self.inner)
}

fn into_mut<'shorter>(self) -> Mut<'shorter, Self::Proxied>
where 'a: 'shorter,
{
self
}
}

impl SettableValue<$t> for $t {
fn set_on(self, _private: Private, mutator: Mut<'_, $t>) {
unsafe { (mutator.inner).set(self) };
}
}

impl<'a> PrimitiveMut<'a, $t> {
pub fn set(&mut self, val: impl SettableValue<$t>) {
val.set_on(Private, self.as_mut());
}

pub fn get(&self) -> $t {
self.inner.get()
}

pub fn clear(&mut self) {
// The default value for a boolean field is false and 0 for numerical types. It
// matches the Rust default values for corresponding types. Let's use this fact.
SettableValue::<$t>::set_on(<$t>::default(), Private, MutProxy::as_mut(self));
}
}

impl ProxiedWithRawVTable for $t {
type VTable = PrimitiveVTable<$t>;

fn make_view(
_private: Private,
mut_inner: InnerPrimitiveMut<'_, Self>
) -> View<'_, Self> {
mut_inner.get()
}

fn make_mut(_private: Private, inner: InnerPrimitiveMut<'_, Self>) -> Mut<'_, Self> {
PrimitiveMut::from_inner(Private, inner)
}
}
)*
}
}

impl_singular_primitives!(bool, f32, f64, i32, i64, u32, u64);
2 changes: 2 additions & 0 deletions rust/shared.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ use std::fmt;
#[doc(hidden)]
pub mod __public {
pub use crate::optional::{AbsentField, FieldEntry, Optional, PresentField};
pub use crate::primitive::PrimitiveMut;
pub use crate::proxied::{
Mut, MutProxy, Proxied, ProxiedWithPresence, SettableValue, View, ViewProxy,
};
Expand All @@ -66,6 +67,7 @@ pub mod __runtime;

mod macros;
mod optional;
mod primitive;
mod proxied;
mod string;
mod vtable;
Expand Down
24 changes: 21 additions & 3 deletions rust/test/shared/accessors_proto3_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,30 @@ use unittest_proto3_optional::proto2_unittest::TestProto3Optional;
fn test_fixed32_accessors() {
let mut msg = TestAllTypes::new();
assert_eq!(msg.optional_fixed32(), 0);
assert_eq!(msg.optional_fixed32_mut().get(), 0);

msg.optional_fixed32_set(Some(99));
assert_eq!(msg.optional_fixed32(), 99);
msg.optional_fixed32_mut().set(42);
assert_eq!(msg.optional_fixed32_mut().get(), 42);
assert_eq!(msg.optional_fixed32(), 42);

msg.optional_fixed32_set(None);
msg.optional_fixed32_mut().clear();
assert_eq!(msg.optional_fixed32(), 0);
assert_eq!(msg.optional_fixed32_mut().get(), 0);
}

#[test]
fn test_bool_accessors() {
let mut msg = TestAllTypes::new();
assert!(!msg.optional_bool());
assert!(!msg.optional_bool_mut().get());

msg.optional_bool_mut().set(true);
assert!(msg.optional_bool());
assert!(msg.optional_bool_mut().get());

msg.optional_bool_mut().clear();
assert!(!msg.optional_bool());
assert!(!msg.optional_bool_mut().get());
}

#[test]
Expand Down
1 change: 1 addition & 0 deletions rust/upb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ impl fmt::Debug for SerializedData {
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]>;
pub type InnerPrimitiveMut<'a, T> = crate::vtable::RawVTableMutator<'a, T>;

/// The raw contents of every generated message.
#[derive(Debug)]
Expand Down
45 changes: 45 additions & 0 deletions rust/vtable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,51 @@ impl ProxiedWithRawOptionalVTable for [u8] {
}
}

/// A generic thunk vtable for mutating a present primitive field.
#[doc(hidden)]
#[derive(Debug)]
pub struct PrimitiveVTable<T> {
pub(crate) setter: unsafe extern "C" fn(msg: RawMessage, val: T),
pub(crate) getter: unsafe extern "C" fn(msg: RawMessage) -> T,
}

impl<T> PrimitiveVTable<T> {
#[doc(hidden)]
pub const fn new(
_private: Private,
getter: unsafe extern "C" fn(msg: RawMessage) -> T,
setter: unsafe extern "C" fn(msg: RawMessage, val: T),
) -> Self {
Self { getter, setter }
}
}

macro_rules! impl_raw_vtable_mutator_get_set {
($($t:ty),*) => {
$(
impl RawVTableMutator<'_, $t> {
pub(crate) fn get(self) -> $t {
// SAFETY:
// - `msg_ref` is valid for the lifetime of `RawVTableMutator` as promised by the
// caller of `new`.
unsafe { (self.vtable.getter)(self.msg_ref.msg()) }
}

/// # Safety
/// - `msg_ref` must be valid for the lifetime of `RawVTableMutator`.
pub(crate) unsafe fn set(self, val: $t) {
// SAFETY:
// - `msg_ref` is valid for the lifetime of `RawVTableMutator` as promised by the
// caller of `new`.
unsafe { (self.vtable.setter)(self.msg_ref.msg(), val) }
}
}
)*
}
}

impl_raw_vtable_mutator_get_set!(bool, f32, f64, i32, i64, u32, u64);

/// A generic thunk vtable for mutating a present `bytes` or `string` field.
#[doc(hidden)]
#[derive(Debug)]
Expand Down
54 changes: 47 additions & 7 deletions src/google/protobuf/compiler/rust/accessors/singular_scalar.cc
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ void SingularScalar::InMsgImpl(Context<FieldDescriptor> field) const {
[&] {
if (!field.desc().is_optional()) return;
if (!field.desc().has_presence()) return;
// TODO(b/285309449): use Optional instead of Option
field.Emit({}, R"rs(
pub fn r#$field$_opt(&self) -> Option<$Scalar$> {
if !unsafe { $hazzer_thunk$(self.inner.msg) } {
Expand All @@ -70,17 +71,56 @@ void SingularScalar::InMsgImpl(Context<FieldDescriptor> field) const {
{"getter_thunk", Thunk(field, "get")},
{"setter_thunk", Thunk(field, "set")},
{"clearer_thunk", Thunk(field, "clear")},
{"field_setter",
[&] {
if (field.desc().has_presence()) {
field.Emit({}, R"rs(
pub fn r#$field$_set(&mut self, val: Option<$Scalar$>) {
match val {
Some(val) => unsafe { $setter_thunk$(self.inner.msg, val) },
None => unsafe { $clearer_thunk$(self.inner.msg) },
}
}
)rs");
}
}},
{"field_mutator_getter",
[&] {
if (field.desc().has_presence()) {
// TODO(b/285309449): implement mutator for fields with presence.
return;
} else {
field.Emit({}, R"rs(
pub fn r#$field$_mut(&mut self) -> $pb$::PrimitiveMut<'_, $Scalar$> {
static VTABLE: $pbi$::PrimitiveVTable<$Scalar$> =
$pbi$::PrimitiveVTable::new(
$pbi$::Private,
$getter_thunk$,
$setter_thunk$,
);
$pb$::PrimitiveMut::from_inner(
$pbi$::Private,
unsafe {
$pbi$::RawVTableMutator::new(
$pbi$::Private,
$pbr$::MutatorMessageRef::new(
$pbi$::Private, &mut self.inner
),
&VTABLE,
)
},
)
}
)rs");
}
}},
},
R"rs(
$getter$
$getter_opt$
pub fn $field$_set(&mut self, val: Option<$Scalar$>) {
match val {
Some(val) => unsafe { $setter_thunk$(self.inner.msg, val) },
None => unsafe { $clearer_thunk$(self.inner.msg) },
}
}
$field_setter$
$field_mutator_getter$
)rs");
}

Expand Down

0 comments on commit 49d0a89

Please sign in to comment.