Create a upb rust directory.
The intent of this directory would be for a layer of Rust bindings that directly map to upb semantics; Rust Protobuf runtime would be layer on top of that Rust, instead of directly on the upb C api.
PiperOrigin-RevId: 624282429
diff --git a/rust/BUILD b/rust/BUILD
index df5b8a3..9bc42f3 100644
--- a/rust/BUILD
+++ b/rust/BUILD
@@ -78,7 +78,7 @@
],
deps = [
":utf8",
- "//rust/upb_kernel:upb_c_api",
+ "//rust/upb",
],
)
diff --git a/rust/cpp.rs b/rust/cpp.rs
index 9329113..225c97e 100644
--- a/rust/cpp.rs
+++ b/rust/cpp.rs
@@ -7,15 +7,13 @@
// Rust Protobuf runtime using the C++ kernel.
-use crate::__internal::{Enum, Private, PtrAndLen, RawMap, RawMessage, RawRepeatedField};
+use crate::__internal::{Enum, Private};
use crate::{
Map, MapIter, Mut, ProtoStr, Proxied, ProxiedInMapValue, ProxiedInRepeated, Repeated,
RepeatedMut, RepeatedView, SettableValue, View,
};
use core::fmt::Debug;
use paste::paste;
-use std::alloc::Layout;
-use std::cell::UnsafeCell;
use std::convert::identity;
use std::ffi::{c_int, c_void};
use std::fmt;
@@ -23,6 +21,111 @@
use std::mem::MaybeUninit;
use std::ops::Deref;
use std::ptr::{self, NonNull};
+use std::slice;
+
+/// Defines a set of opaque, unique, non-accessible pointees.
+///
+/// The [Rustonomicon][nomicon] currently recommends a zero-sized struct,
+/// though this should use [`extern type`] when that is stabilized.
+/// [nomicon]: https://doc.rust-lang.org/nomicon/ffi.html#representing-opaque-structs
+/// [`extern type`]: https://github.com/rust-lang/rust/issues/43467
+mod _opaque_pointees {
+ /// Opaque pointee for [`RawMessage`]
+ ///
+ /// This type is not meant to be dereferenced in Rust code.
+ /// It is only meant to provide type safety for raw pointers
+ /// which are manipulated behind FFI.
+ ///
+ /// [`RawMessage`]: super::RawMessage
+ #[repr(C)]
+ pub struct RawMessageData {
+ _data: [u8; 0],
+ _marker: std::marker::PhantomData<(*mut u8, ::std::marker::PhantomPinned)>,
+ }
+
+ /// Opaque pointee for [`RawRepeatedField`]
+ ///
+ /// This type is not meant to be dereferenced in Rust code.
+ /// It is only meant to provide type safety for raw pointers
+ /// which are manipulated behind FFI.
+ #[repr(C)]
+ pub struct RawRepeatedFieldData {
+ _data: [u8; 0],
+ _marker: std::marker::PhantomData<(*mut u8, ::std::marker::PhantomPinned)>,
+ }
+
+ /// Opaque pointee for [`RawMap`]
+ ///
+ /// This type is not meant to be dereferenced in Rust code.
+ /// It is only meant to provide type safety for raw pointers
+ /// which are manipulated behind FFI.
+ #[repr(C)]
+ pub struct RawMapData {
+ _data: [u8; 0],
+ _marker: std::marker::PhantomData<(*mut u8, ::std::marker::PhantomPinned)>,
+ }
+}
+
+/// A raw pointer to the underlying message for this runtime.
+pub type RawMessage = NonNull<_opaque_pointees::RawMessageData>;
+
+/// A raw pointer to the underlying repeated field container for this runtime.
+pub type RawRepeatedField = NonNull<_opaque_pointees::RawRepeatedFieldData>;
+
+/// A raw pointer to the underlying arena for this runtime.
+pub type RawMap = NonNull<_opaque_pointees::RawMapData>;
+
+/// Represents an ABI-stable version of `NonNull<[u8]>`/`string_view` (a
+/// borrowed slice of bytes) for FFI use only.
+///
+/// Has semantics similar to `std::string_view` in C++ and `&[u8]` in Rust,
+/// but is not ABI-compatible with either.
+///
+/// If `len` is 0, then `ptr` can be null or dangling. C++ considers a dangling
+/// 0-len `std::string_view` to be invalid, and Rust considers a `&[u8]` with a
+/// null data pointer to be invalid.
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct PtrAndLen {
+ /// Pointer to the first byte.
+ /// Borrows the memory.
+ pub ptr: *const u8,
+
+ /// Length of the `[u8]` pointed to by `ptr`.
+ pub len: usize,
+}
+
+impl PtrAndLen {
+ /// Unsafely dereference this slice.
+ ///
+ /// # Safety
+ /// - `self.ptr` must be dereferencable and immutable for `self.len` bytes
+ /// for the lifetime `'a`. It can be null or dangling if `self.len == 0`.
+ pub unsafe fn as_ref<'a>(self) -> &'a [u8] {
+ if self.ptr.is_null() {
+ assert_eq!(self.len, 0, "Non-empty slice with null data pointer");
+ &[]
+ } else {
+ // SAFETY:
+ // - `ptr` is non-null
+ // - `ptr` is valid for `len` bytes as promised by the caller.
+ unsafe { slice::from_raw_parts(self.ptr, self.len) }
+ }
+ }
+}
+
+impl From<&[u8]> for PtrAndLen {
+ fn from(slice: &[u8]) -> Self {
+ Self { ptr: slice.as_ptr(), len: slice.len() }
+ }
+}
+
+impl From<&ProtoStr> for PtrAndLen {
+ fn from(s: &ProtoStr) -> Self {
+ let bytes = s.as_bytes();
+ Self { ptr: bytes.as_ptr(), len: bytes.len() }
+ }
+}
/// Serialized Protobuf wire format data. It's typically produced by
/// `<Message>.serialize()`.
diff --git a/rust/internal.rs b/rust/internal.rs
index b1b77db..041c42d 100644
--- a/rust/internal.rs
+++ b/rust/internal.rs
@@ -16,128 +16,11 @@
RawVTableMutator, RawVTableOptionalMutatorData,
};
pub use crate::ProtoStr;
-use std::ptr::NonNull;
-use std::slice;
+
+// TODO: Temporarily re-export these symbols which are now under
+// __runtime under __internal since some external callers using it through
+// __internal.
+pub use crate::__runtime::{PtrAndLen, RawMap, RawMessage, RawRepeatedField};
/// Used to protect internal-only items from being used accidentally.
pub struct Private;
-
-/// Defines a set of opaque, unique, non-accessible pointees.
-///
-/// The [Rustonomicon][nomicon] currently recommends a zero-sized struct,
-/// though this should use [`extern type`] when that is stabilized.
-/// [nomicon]: https://doc.rust-lang.org/nomicon/ffi.html#representing-opaque-structs
-/// [`extern type`]: https://github.com/rust-lang/rust/issues/43467
-mod _opaque_pointees {
- /// Opaque pointee for [`RawMessage`]
- ///
- /// This type is not meant to be dereferenced in Rust code.
- /// It is only meant to provide type safety for raw pointers
- /// which are manipulated behind FFI.
- ///
- /// [`RawMessage`]: super::RawMessage
- #[repr(C)]
- pub struct RawMessageData {
- _data: [u8; 0],
- _marker: std::marker::PhantomData<(*mut u8, ::std::marker::PhantomPinned)>,
- }
-
- /// Opaque pointee for [`RawArena`]
- ///
- /// This type is not meant to be dereferenced in Rust code.
- /// It is only meant to provide type safety for raw pointers
- /// which are manipulated behind FFI.
- ///
- /// [`RawArena`]: super::RawArena
- #[repr(C)]
- pub struct RawArenaData {
- _data: [u8; 0],
- _marker: std::marker::PhantomData<(*mut u8, ::std::marker::PhantomPinned)>,
- }
-
- /// Opaque pointee for [`RawRepeatedField`]
- ///
- /// This type is not meant to be dereferenced in Rust code.
- /// It is only meant to provide type safety for raw pointers
- /// which are manipulated behind FFI.
- #[repr(C)]
- pub struct RawRepeatedFieldData {
- _data: [u8; 0],
- _marker: std::marker::PhantomData<(*mut u8, ::std::marker::PhantomPinned)>,
- }
-
- /// Opaque pointee for [`RawMap`]
- ///
- /// This type is not meant to be dereferenced in Rust code.
- /// It is only meant to provide type safety for raw pointers
- /// which are manipulated behind FFI.
- #[repr(C)]
- pub struct RawMapData {
- _data: [u8; 0],
- _marker: std::marker::PhantomData<(*mut u8, ::std::marker::PhantomPinned)>,
- }
-}
-
-/// A raw pointer to the underlying message for this runtime.
-pub type RawMessage = NonNull<_opaque_pointees::RawMessageData>;
-
-/// A raw pointer to the underlying arena for this runtime.
-pub type RawArena = NonNull<_opaque_pointees::RawArenaData>;
-
-/// A raw pointer to the underlying repeated field container for this runtime.
-pub type RawRepeatedField = NonNull<_opaque_pointees::RawRepeatedFieldData>;
-
-/// A raw pointer to the underlying arena for this runtime.
-pub type RawMap = NonNull<_opaque_pointees::RawMapData>;
-
-/// Represents an ABI-stable version of `NonNull<[u8]>`/`string_view` (a
-/// borrowed slice of bytes) for FFI use only.
-///
-/// Has semantics similar to `std::string_view` in C++ and `&[u8]` in Rust,
-/// but is not ABI-compatible with either.
-///
-/// If `len` is 0, then `ptr` can be null or dangling. C++ considers a dangling
-/// 0-len `std::string_view` to be invalid, and Rust considers a `&[u8]` with a
-/// null data pointer to be invalid.
-#[repr(C)]
-#[derive(Copy, Clone)]
-pub struct PtrAndLen {
- /// Pointer to the first byte.
- /// Borrows the memory.
- pub ptr: *const u8,
-
- /// Length of the `[u8]` pointed to by `ptr`.
- pub len: usize,
-}
-
-impl PtrAndLen {
- /// Unsafely dereference this slice.
- ///
- /// # Safety
- /// - `self.ptr` must be dereferencable and immutable for `self.len` bytes
- /// for the lifetime `'a`. It can be null or dangling if `self.len == 0`.
- pub unsafe fn as_ref<'a>(self) -> &'a [u8] {
- if self.ptr.is_null() {
- assert_eq!(self.len, 0, "Non-empty slice with null data pointer");
- &[]
- } else {
- // SAFETY:
- // - `ptr` is non-null
- // - `ptr` is valid for `len` bytes as promised by the caller.
- unsafe { slice::from_raw_parts(self.ptr, self.len) }
- }
- }
-}
-
-impl From<&[u8]> for PtrAndLen {
- fn from(slice: &[u8]) -> Self {
- Self { ptr: slice.as_ptr(), len: slice.len() }
- }
-}
-
-impl From<&ProtoStr> for PtrAndLen {
- fn from(s: &ProtoStr) -> Self {
- let bytes = s.as_bytes();
- Self { ptr: bytes.as_ptr(), len: bytes.len() }
- }
-}
diff --git a/rust/map.rs b/rust/map.rs
index 05285d0..5ce7404 100644
--- a/rust/map.rs
+++ b/rust/map.rs
@@ -7,8 +7,8 @@
use crate::{
Mut, MutProxy, Proxied, SettableValue, View, ViewProxy,
- __internal::{Private, RawMap},
- __runtime::{InnerMap, InnerMapMut, RawMapIter},
+ __internal::Private,
+ __runtime::{InnerMap, InnerMapMut, RawMap, RawMapIter},
};
use std::marker::PhantomData;
diff --git a/rust/repeated.rs b/rust/repeated.rs
index 8c7c1cc..068dd9a 100644
--- a/rust/repeated.rs
+++ b/rust/repeated.rs
@@ -16,8 +16,8 @@
use crate::{
Mut, MutProxy, Proxied, SettableValue, View, ViewProxy,
- __internal::{Private, RawRepeatedField},
- __runtime::{InnerRepeated, InnerRepeatedMut},
+ __internal::Private,
+ __runtime::{InnerRepeated, InnerRepeatedMut, RawRepeatedField},
};
/// Views the elements in a `repeated` field of `T`.
diff --git a/rust/string.rs b/rust/string.rs
index d2b5a5d..a6277d4 100644
--- a/rust/string.rs
+++ b/rust/string.rs
@@ -9,8 +9,10 @@
#![allow(dead_code)]
#![allow(unused)]
-use crate::__internal::{Private, PtrAndLen, RawMessage};
-use crate::__runtime::{BytesAbsentMutData, BytesPresentMutData, InnerBytesMut};
+use crate::__internal::Private;
+use crate::__runtime::{
+ BytesAbsentMutData, BytesPresentMutData, InnerBytesMut, PtrAndLen, RawMessage,
+};
use crate::macros::impl_forwarding_settable_value;
use crate::{
AbsentField, FieldEntry, Mut, MutProxy, Optional, PresentField, Proxied, ProxiedWithPresence,
diff --git a/rust/test/cpp/interop/main.rs b/rust/test/cpp/interop/main.rs
index 36c021c..f8e0527 100644
--- a/rust/test/cpp/interop/main.rs
+++ b/rust/test/cpp/interop/main.rs
@@ -6,7 +6,7 @@
// https://developers.google.com/open-source/licenses/bsd
use googletest::prelude::*;
-use protobuf_cpp::__internal::{PtrAndLen, RawMessage};
+use protobuf_cpp::__runtime::{PtrAndLen, RawMessage};
use unittest_proto::{TestAllExtensions, TestAllTypes, TestAllTypesMut, TestAllTypesView};
macro_rules! proto_assert_eq {
diff --git a/rust/upb.rs b/rust/upb.rs
index ce57a48..eec3cc1 100644
--- a/rust/upb.rs
+++ b/rust/upb.rs
@@ -7,7 +7,7 @@
//! UPB FFI wrapper code for use by Rust Protobuf.
-use crate::__internal::{Enum, Private, PtrAndLen, RawArena, RawMap, RawMessage, RawRepeatedField};
+use crate::__internal::{Enum, Private};
use crate::{
Map, MapIter, MapMut, MapView, Mut, ProtoStr, Proxied, ProxiedInMapValue, ProxiedInRepeated,
Repeated, RepeatedMut, RepeatedView, SettableValue, View, ViewProxy,
@@ -16,7 +16,6 @@
use std::alloc;
use std::alloc::Layout;
use std::cell::UnsafeCell;
-use std::ffi::c_int;
use std::fmt;
use std::marker::PhantomData;
use std::mem::{align_of, size_of, ManuallyDrop, MaybeUninit};
@@ -25,6 +24,25 @@
use std::slice;
use std::sync::OnceLock;
+extern crate upb;
+
+// Temporarily 'pub' since a lot of gencode is directly calling any of the ffi
+// fns.
+pub use upb::*;
+
+pub type RawArena = upb::RawArena;
+pub type RawMessage = upb::RawMessage;
+pub type RawRepeatedField = upb::RawArray;
+pub type RawMap = upb::RawMap;
+pub type PtrAndLen = upb::StringView;
+
+impl From<&ProtoStr> for PtrAndLen {
+ fn from(s: &ProtoStr) -> Self {
+ let bytes = s.as_bytes();
+ Self { ptr: bytes.as_ptr(), len: bytes.len() }
+ }
+}
+
/// See `upb/port/def.inc`.
const UPB_MALLOC_ALIGN: usize = 8;
const _CHECK_UPB_MALLOC_ALIGN_AT_LEAST_POINTER_ALIGNED: () =
@@ -46,14 +64,6 @@
_not_sync: PhantomData<UnsafeCell<()>>,
}
-extern "C" {
- // `Option<NonNull<T: Sized>>` is ABI-compatible with `*mut T`
- pub fn upb_Arena_New() -> Option<RawArena>;
- pub fn upb_Arena_Free(arena: RawArena);
- pub fn upb_Arena_Malloc(arena: RawArena, size: usize) -> *mut u8;
- pub fn upb_Arena_Realloc(arena: RawArena, ptr: *mut u8, old: usize, new: usize) -> *mut u8;
-}
-
impl Arena {
/// Allocates a fresh arena.
#[inline]
@@ -332,89 +342,6 @@
}
}
-// A macro for opaque pointees.
-// This wrapper is a workaround until stabilization of [`extern type`].
-// TODO: convert to extern type once stabilized.
-// [`extern type`]: https://github.com/rust-lang/rust/issues/43467
-macro_rules! opaque_pointee {
- ($name:ident) => {
- #[repr(C)]
- pub struct $name {
- _data: [u8; 0],
- _marker: std::marker::PhantomData<(*mut u8, ::std::marker::PhantomPinned)>,
- }
- };
-}
-
-// Opaque pointee for upb_MiniTable.
-// TODO: consider importing a minitable struct declared in
-// google3/third_party/upb/bits.
-opaque_pointee!(OpaqueMiniTable);
-
-// Opaque pointee for upb_ExtensionRegistry
-opaque_pointee!(OpaqueExtensionRegistry);
-
-extern "C" {
- pub fn upb_Message_DeepCopy(
- dst: RawMessage,
- src: RawMessage,
- mini_table: *const OpaqueMiniTable,
- arena: RawArena,
- );
-
- pub fn upb_Message_DeepClone(
- m: RawMessage,
- mini_table: *const OpaqueMiniTable,
- arena: RawArena,
- ) -> Option<RawMessage>;
-}
-
-// LINT.IfChange(encode_status)
-#[repr(C)]
-#[derive(PartialEq, Eq, Copy, Clone)]
-pub enum EncodeStatus {
- Ok = 0,
- OutOfMemory = 1,
- MaxDepthExceeded = 2,
- MissingRequired = 3,
-}
-// LINT.ThenChange()
-
-// LINT.IfChange(decode_status)
-#[repr(C)]
-#[derive(PartialEq, Eq, Copy, Clone)]
-pub enum DecodeStatus {
- Ok = 0,
- Malformed = 1,
- OutOfMemory = 2,
- BadUtf8 = 3,
- MaxDepthExceeded = 4,
- MissingRequired = 5,
- UnlinkedSubMessage = 6,
-}
-// LINT.ThenChange()
-
-extern "C" {
- pub fn upb_Encode(
- msg: RawMessage,
- mini_table: *const OpaqueMiniTable,
- options: i32,
- arena: RawArena,
- buf: *mut *mut u8,
- buf_size: *mut usize,
- ) -> EncodeStatus;
-
- pub fn upb_Decode(
- buf: *const u8,
- buf_size: usize,
- msg: RawMessage,
- mini_table: *const OpaqueMiniTable,
- extreg: *const OpaqueExtensionRegistry,
- options: i32,
- arena: RawArena,
- ) -> DecodeStatus;
-}
-
/// The raw type-erased version of an owned `Repeated`.
#[derive(Debug)]
pub struct InnerRepeated {
@@ -442,70 +369,13 @@
}
}
-// Transcribed from google3/third_party/upb/upb/message/value.h
-#[repr(C)]
-#[derive(Clone, Copy)]
-pub union upb_MessageValue {
- pub bool_val: bool,
- pub float_val: std::ffi::c_float,
- pub double_val: std::ffi::c_double,
- pub uint32_val: u32,
- pub int32_val: i32,
- pub uint64_val: u64,
- pub int64_val: i64,
- pub array_val: Option<RawRepeatedField>,
- pub map_val: Option<RawMap>,
- // TODO: Replace this `RawMessage` with the const type.
- pub msg_val: Option<RawMessage>,
- pub str_val: PtrAndLen,
-
- tagged_msg_val: *const std::ffi::c_void,
-}
-
-#[repr(C)]
-#[derive(Clone, Copy)]
-pub union upb_MutableMessageValue {
- pub array: Option<RawRepeatedField>,
- pub map: Option<RawMap>,
- pub msg: Option<RawMessage>,
-}
-
-// Transcribed from google3/third_party/upb/upb/base/descriptor_constants.h
-#[repr(C)]
-#[allow(dead_code)]
-pub enum UpbCType {
- Bool = 1,
- Float = 2,
- Int32 = 3,
- UInt32 = 4,
- Enum = 5,
- Message = 6,
- Double = 7,
- Int64 = 8,
- UInt64 = 9,
- String = 10,
- Bytes = 11,
-}
-
-extern "C" {
- fn upb_Array_New(a: RawArena, r#type: std::ffi::c_int) -> RawRepeatedField;
- pub fn upb_Array_Size(arr: RawRepeatedField) -> usize;
- pub fn upb_Array_Set(arr: RawRepeatedField, i: usize, val: upb_MessageValue);
- pub fn upb_Array_Get(arr: RawRepeatedField, i: usize) -> upb_MessageValue;
- pub fn upb_Array_Append(arr: RawRepeatedField, val: upb_MessageValue, arena: RawArena);
- pub fn upb_Array_Resize(arr: RawRepeatedField, size: usize, arena: RawArena) -> bool;
- fn upb_Array_MutableDataPtr(arr: RawRepeatedField) -> *mut std::ffi::c_void;
- fn upb_Array_DataPtr(arr: RawRepeatedField) -> *const std::ffi::c_void;
- pub fn upb_Array_GetMutable(arr: RawRepeatedField, i: usize) -> upb_MutableMessageValue;
-}
-
macro_rules! impl_repeated_base {
($t:ty, $elem_t:ty, $ufield:ident, $upb_tag:expr) => {
#[allow(dead_code)]
fn repeated_new(_: Private) -> Repeated<$t> {
let arena = Arena::new();
Repeated::from_inner(InnerRepeated {
- raw: unsafe { upb_Array_New(arena.raw(), $upb_tag as c_int) },
+ raw: unsafe { upb_Array_New(arena.raw(), $upb_tag) },
arena,
})
}
@@ -519,11 +389,11 @@
fn repeated_push(mut f: Mut<Repeated<$t>>, v: View<$t>) {
let arena = f.raw_arena(Private);
unsafe {
- upb_Array_Append(
+ assert!(upb_Array_Append(
f.as_raw(Private),
<$t as UpbTypeConversions>::to_message_value_copy_if_required(arena, v),
arena,
- )
+ ));
}
}
fn repeated_clear(mut f: Mut<Repeated<$t>>) {
@@ -624,17 +494,17 @@
}
impl_repeated_primitives!(
- // proxied type, element type, upb_MessageValue field name, UpbCType variant
- (bool, bool, bool_val, UpbCType::Bool),
- (f32, f32, float_val, UpbCType::Float),
- (f64, f64, double_val, UpbCType::Double),
- (i32, i32, int32_val, UpbCType::Int32),
- (u32, u32, uint32_val, UpbCType::UInt32),
- (i64, i64, int64_val, UpbCType::Int64),
- (u64, u64, uint64_val, UpbCType::UInt64),
+ // proxied type, element type, upb_MessageValue field name, upb::CType variant
+ (bool, bool, bool_val, upb::CType::Bool),
+ (f32, f32, float_val, upb::CType::Float),
+ (f64, f64, double_val, upb::CType::Double),
+ (i32, i32, int32_val, upb::CType::Int32),
+ (u32, u32, uint32_val, upb::CType::UInt32),
+ (i64, i64, int64_val, upb::CType::Int64),
+ (u64, u64, uint64_val, upb::CType::UInt64),
);
-impl_repeated_bytes!((ProtoStr, UpbCType::String), ([u8], UpbCType::Bytes),);
+impl_repeated_bytes!((ProtoStr, upb::CType::String), ([u8], upb::CType::Bytes),);
/// Copy the contents of `src` into `dest`.
///
@@ -643,7 +513,7 @@
pub unsafe fn repeated_message_copy_from<T: ProxiedInRepeated>(
src: View<Repeated<T>>,
mut dest: Mut<Repeated<T>>,
- minitable: *const OpaqueMiniTable,
+ minitable: *const upb_MiniTable,
) {
// SAFETY:
// - `src.as_raw()` is a valid `const upb_Array*`.
@@ -801,7 +671,7 @@
}
pub trait UpbTypeConversions: Proxied {
- fn upb_type() -> UpbCType;
+ fn upb_type() -> upb::CType;
fn to_message_value(val: View<'_, Self>) -> upb_MessageValue;
/// # Safety
@@ -822,7 +692,7 @@
$(
impl UpbTypeConversions for $t {
#[inline(always)]
- fn upb_type() -> UpbCType {
+ fn upb_type() -> upb::CType {
$upb_tag
}
@@ -846,18 +716,18 @@
}
impl_upb_type_conversions_for_scalars!(
- f32, float_val, UpbCType::Float, 0f32;
- f64, double_val, UpbCType::Double, 0f64;
- i32, int32_val, UpbCType::Int32, 0i32;
- u32, uint32_val, UpbCType::UInt32, 0u32;
- i64, int64_val, UpbCType::Int64, 0i64;
- u64, uint64_val, UpbCType::UInt64, 0u64;
- bool, bool_val, UpbCType::Bool, false;
+ f32, float_val, upb::CType::Float, 0f32;
+ f64, double_val, upb::CType::Double, 0f64;
+ i32, int32_val, upb::CType::Int32, 0i32;
+ u32, uint32_val, upb::CType::UInt32, 0u32;
+ i64, int64_val, upb::CType::Int64, 0i64;
+ u64, uint64_val, upb::CType::UInt64, 0u64;
+ bool, bool_val, upb::CType::Bool, false;
);
impl UpbTypeConversions for [u8] {
- fn upb_type() -> UpbCType {
- UpbCType::Bytes
+ fn upb_type() -> upb::CType {
+ upb::CType::Bytes
}
fn to_message_value(val: View<'_, [u8]>) -> upb_MessageValue {
@@ -881,8 +751,8 @@
}
impl UpbTypeConversions for ProtoStr {
- fn upb_type() -> UpbCType {
- UpbCType::String
+ fn upb_type() -> upb::CType {
+ upb::CType::String
}
fn to_message_value(val: View<'_, ProtoStr>) -> upb_MessageValue {
@@ -1033,14 +903,6 @@
impl_ProxiedInMapValue_for_key_types!(i32, u32, i64, u64, bool, ProtoStr);
-#[repr(C)]
-#[allow(dead_code)]
-pub enum upb_MapInsertStatus {
- Inserted = 0,
- Replaced = 1,
- OutOfMemory = 2,
-}
-
/// `upb_Map_Insert`, but returns a `bool` for whether insert occurred.
///
/// Returns `true` if the entry was newly inserted.
@@ -1061,39 +923,12 @@
arena: RawArena,
) -> bool {
match unsafe { upb_Map_Insert(map, key, value, arena) } {
- upb_MapInsertStatus::Inserted => true,
- upb_MapInsertStatus::Replaced => false,
- upb_MapInsertStatus::OutOfMemory => panic!("map arena is out of memory"),
+ upb::MapInsertStatus::Inserted => true,
+ upb::MapInsertStatus::Replaced => false,
+ upb::MapInsertStatus::OutOfMemory => panic!("map arena is out of memory"),
}
}
-extern "C" {
- pub fn upb_Map_New(arena: RawArena, key_type: UpbCType, value_type: UpbCType) -> RawMap;
- pub fn upb_Map_Size(map: RawMap) -> usize;
- pub fn upb_Map_Insert(
- map: RawMap,
- key: upb_MessageValue,
- value: upb_MessageValue,
- arena: RawArena,
- ) -> upb_MapInsertStatus;
- pub fn upb_Map_Get(map: RawMap, key: upb_MessageValue, value: *mut upb_MessageValue) -> bool;
- pub fn upb_Map_Delete(
- map: RawMap,
- key: upb_MessageValue,
- removed_value: *mut upb_MessageValue,
- ) -> bool;
- pub fn upb_Map_Clear(map: RawMap);
-
- static __rust_proto_kUpb_Map_Begin: usize;
-
- pub fn upb_Map_Next(
- map: RawMap,
- key: *mut upb_MessageValue,
- value: *mut upb_MessageValue,
- iter: &mut usize,
- ) -> bool;
-}
-
#[cfg(test)]
mod tests {
use super::*;
diff --git a/rust/upb/BUILD b/rust/upb/BUILD
new file mode 100644
index 0000000..df43ce9
--- /dev/null
+++ b/rust/upb/BUILD
@@ -0,0 +1,36 @@
+# Copyright 2024 Google LLC
+#
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file or at
+# https://developers.google.com/open-source/licenses/bsd
+
+load("@rules_rust//rust:defs.bzl", "rust_library", "rust_test")
+
+# begin:google_only
+# package(default_applicable_licenses = ["//upb:license"])
+# end:google_only
+
+rust_library(
+ name = "upb",
+ srcs = ["shared.rs"],
+ visibility = [
+ "//rust:__subpackages__",
+ "//src/google/protobuf:__subpackages__",
+ ],
+ deps = [":upb_c_api"],
+)
+
+rust_test(
+ name = "upb_rs_crate_test",
+ crate = ":upb",
+)
+
+cc_library(
+ name = "upb_c_api",
+ srcs = ["upb_api.c"],
+ deps = [
+ "//upb:mem",
+ "//upb:message",
+ "//upb:message_copy",
+ ],
+)
diff --git a/rust/upb/README.md b/rust/upb/README.md
new file mode 100644
index 0000000..1e29faf
--- /dev/null
+++ b/rust/upb/README.md
@@ -0,0 +1,7 @@
+# Rust upb bindings
+
+This directory contains Rust bindings for upb, attempting to be the a direct
+mapping to expose upb to Rust. This should not be used directly by application
+code, instead is an implementation detail for Rust Protobuf, which is still
+under development but can be found here:
+[here](https://github.com/protocolbuffers/protobuf/tree/main/rust).
diff --git a/rust/upb/shared.rs b/rust/upb/shared.rs
new file mode 100644
index 0000000..b61028b
--- /dev/null
+++ b/rust/upb/shared.rs
@@ -0,0 +1,333 @@
+use std::ptr::NonNull;
+use std::slice;
+
+// Macro to create structs that will act as opaque pointees. These structs are
+// never intended to be dereferenced in Rust.
+// This is a workaround until stabilization of [`extern type`].
+// TODO: convert to extern type once stabilized.
+// [`extern type`]: https://github.com/rust-lang/rust/issues/43467
+macro_rules! opaque_pointee {
+ ($name: ident) => {
+ #[repr(C)]
+ pub struct $name {
+ _data: [u8; 0],
+ _marker: std::marker::PhantomData<(*mut u8, std::marker::PhantomPinned)>,
+ }
+ };
+}
+
+opaque_pointee!(upb_Arena);
+pub type RawArena = NonNull<upb_Arena>;
+
+opaque_pointee!(upb_Message);
+pub type RawMessage = NonNull<upb_Message>;
+
+opaque_pointee!(upb_MiniTable);
+pub type RawMiniTable = NonNull<upb_MiniTable>;
+
+opaque_pointee!(upb_ExtensionRegistry);
+pub type RawExtensionRegistry = NonNull<upb_ExtensionRegistry>;
+
+opaque_pointee!(upb_Map);
+pub type RawMap = NonNull<upb_Map>;
+
+opaque_pointee!(upb_Array);
+pub type RawArray = NonNull<upb_Array>;
+
+extern "C" {
+ // `Option<NonNull<T: Sized>>` is ABI-compatible with `*mut T`
+ pub fn upb_Arena_New() -> Option<RawArena>;
+ pub fn upb_Arena_Free(arena: RawArena);
+ pub fn upb_Arena_Malloc(arena: RawArena, size: usize) -> *mut u8;
+ pub fn upb_Arena_Realloc(arena: RawArena, ptr: *mut u8, old: usize, new: usize) -> *mut u8;
+}
+
+extern "C" {
+ pub fn upb_Message_DeepCopy(
+ dst: RawMessage,
+ src: RawMessage,
+ mini_table: *const upb_MiniTable,
+ arena: RawArena,
+ );
+
+ pub fn upb_Message_DeepClone(
+ m: RawMessage,
+ mini_table: *const upb_MiniTable,
+ arena: RawArena,
+ ) -> Option<RawMessage>;
+}
+
+// LINT.IfChange(encode_status)
+#[repr(C)]
+#[derive(PartialEq, Eq, Copy, Clone)]
+pub enum EncodeStatus {
+ Ok = 0,
+ OutOfMemory = 1,
+ MaxDepthExceeded = 2,
+ MissingRequired = 3,
+}
+// LINT.ThenChange()
+
+// LINT.IfChange(decode_status)
+#[repr(C)]
+#[derive(PartialEq, Eq, Copy, Clone)]
+pub enum DecodeStatus {
+ Ok = 0,
+ Malformed = 1,
+ OutOfMemory = 2,
+ BadUtf8 = 3,
+ MaxDepthExceeded = 4,
+ MissingRequired = 5,
+ UnlinkedSubMessage = 6,
+}
+// LINT.ThenChange()
+
+extern "C" {
+ pub fn upb_Encode(
+ msg: RawMessage,
+ mini_table: *const upb_MiniTable,
+ options: i32,
+ arena: RawArena,
+ buf: *mut *mut u8,
+ buf_size: *mut usize,
+ ) -> EncodeStatus;
+
+ pub fn upb_Decode(
+ buf: *const u8,
+ buf_size: usize,
+ msg: RawMessage,
+ mini_table: *const upb_MiniTable,
+ extreg: *const upb_ExtensionRegistry,
+ options: i32,
+ arena: RawArena,
+ ) -> DecodeStatus;
+}
+
+/// ABI compatible struct with upb_StringView.
+///
+/// Note that this has semantics similar to `std::string_view` in C++ and
+/// `&[u8]` in Rust, but is not ABI-compatible with either.
+///
+/// If `len` is 0, then `ptr` is allowed to be either null or dangling. C++
+/// considers a dangling 0-len `std::string_view` to be invalid, and Rust
+/// considers a `&[u8]` with a null data pointer to be invalid.
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct StringView {
+ /// Pointer to the first byte.
+ /// Borrows the memory.
+ pub ptr: *const u8,
+
+ /// Length of the `[u8]` pointed to by `ptr`.
+ pub len: usize,
+}
+
+impl StringView {
+ /// Unsafely dereference this slice.
+ ///
+ /// # Safety
+ /// - `self.ptr` must be dereferencable and immutable for `self.len` bytes
+ /// for the lifetime `'a`. It can be null or dangling if `self.len == 0`.
+ pub unsafe fn as_ref<'a>(self) -> &'a [u8] {
+ if self.ptr.is_null() {
+ assert_eq!(self.len, 0, "Non-empty slice with null data pointer");
+ &[]
+ } else {
+ // SAFETY:
+ // - `ptr` is non-null
+ // - `ptr` is valid for `len` bytes as promised by the caller.
+ unsafe { slice::from_raw_parts(self.ptr, self.len) }
+ }
+ }
+}
+
+impl From<&[u8]> for StringView {
+ fn from(slice: &[u8]) -> Self {
+ Self { ptr: slice.as_ptr(), len: slice.len() }
+ }
+}
+
+// Transcribed from google3/third_party/upb/upb/message/value.h
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub union upb_MessageValue {
+ pub bool_val: bool,
+ pub float_val: std::ffi::c_float,
+ pub double_val: std::ffi::c_double,
+ pub uint32_val: u32,
+ pub int32_val: i32,
+ pub uint64_val: u64,
+ pub int64_val: i64,
+ // TODO: Replace this `RawMessage` with the const type.
+ pub array_val: Option<RawArray>,
+ pub map_val: Option<RawMap>,
+ pub msg_val: Option<RawMessage>,
+ pub str_val: StringView,
+
+ tagged_msg_val: *const std::ffi::c_void,
+}
+
+impl upb_MessageValue {
+ pub fn zeroed() -> Self {
+ // SAFETY: zero bytes is a valid representation for at least one value in the
+ // union (actually valid for all of them).
+ unsafe { std::mem::zeroed() }
+ }
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub union upb_MutableMessageValue {
+ pub array: Option<RawArray>,
+ pub map: Option<RawMap>,
+ pub msg: Option<RawMessage>,
+}
+
+// Transcribed from google3/third_party/upb/upb/base/descriptor_constants.h
+#[repr(C)]
+#[allow(dead_code)]
+#[derive(PartialEq, Eq, Clone, Copy, Debug)]
+pub enum CType {
+ Bool = 1,
+ Float = 2,
+ Int32 = 3,
+ UInt32 = 4,
+ Enum = 5,
+ Message = 6,
+ Double = 7,
+ Int64 = 8,
+ UInt64 = 9,
+ String = 10,
+ Bytes = 11,
+}
+
+extern "C" {
+ pub fn upb_Array_New(a: RawArena, r#type: CType) -> RawArray;
+ pub fn upb_Array_Size(arr: RawArray) -> usize;
+ pub fn upb_Array_Set(arr: RawArray, i: usize, val: upb_MessageValue);
+ pub fn upb_Array_Get(arr: RawArray, i: usize) -> upb_MessageValue;
+ pub fn upb_Array_Append(arr: RawArray, val: upb_MessageValue, arena: RawArena) -> bool;
+ pub fn upb_Array_Resize(arr: RawArray, size: usize, arena: RawArena) -> bool;
+ pub fn upb_Array_MutableDataPtr(arr: RawArray) -> *mut std::ffi::c_void;
+ pub fn upb_Array_DataPtr(arr: RawArray) -> *const std::ffi::c_void;
+ pub fn upb_Array_GetMutable(arr: RawArray, i: usize) -> upb_MutableMessageValue;
+}
+
+#[repr(C)]
+#[allow(dead_code)]
+#[derive(PartialEq, Eq, Clone, Copy, Debug)]
+pub enum MapInsertStatus {
+ Inserted = 0,
+ Replaced = 1,
+ OutOfMemory = 2,
+}
+
+extern "C" {
+ pub fn upb_Map_New(arena: RawArena, key_type: CType, value_type: CType) -> RawMap;
+ pub fn upb_Map_Size(map: RawMap) -> usize;
+ pub fn upb_Map_Insert(
+ map: RawMap,
+ key: upb_MessageValue,
+ value: upb_MessageValue,
+ arena: RawArena,
+ ) -> MapInsertStatus;
+ pub fn upb_Map_Get(map: RawMap, key: upb_MessageValue, value: *mut upb_MessageValue) -> bool;
+ pub fn upb_Map_Delete(
+ map: RawMap,
+ key: upb_MessageValue,
+ removed_value: *mut upb_MessageValue,
+ ) -> bool;
+ pub fn upb_Map_Clear(map: RawMap);
+
+ pub static __rust_proto_kUpb_Map_Begin: usize;
+
+ pub fn upb_Map_Next(
+ map: RawMap,
+ key: *mut upb_MessageValue,
+ value: *mut upb_MessageValue,
+ iter: &mut usize,
+ ) -> bool;
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn arena_ffi_test() {
+ // SAFETY: FFI unit test uses C API under expected patterns.
+ unsafe {
+ let arena = upb_Arena_New().unwrap();
+ let bytes = upb_Arena_Malloc(arena, 3);
+ let bytes = upb_Arena_Realloc(arena, bytes, 3, 512);
+ *bytes.add(511) = 7;
+ upb_Arena_Free(arena);
+ }
+ }
+
+ #[test]
+ fn array_ffi_test() {
+ // SAFETY: FFI unit test uses C API under expected patterns.
+ unsafe {
+ let arena = upb_Arena_New().unwrap();
+ let array = upb_Array_New(arena, CType::Float);
+
+ assert!(upb_Array_Append(array, upb_MessageValue { float_val: 7.0 }, arena));
+ assert!(upb_Array_Append(array, upb_MessageValue { float_val: 42.0 }, arena));
+ assert_eq!(upb_Array_Size(array), 2);
+ assert!(matches!(upb_Array_Get(array, 1), upb_MessageValue { float_val: 42.0 }));
+
+ assert!(upb_Array_Resize(array, 3, arena));
+ assert_eq!(upb_Array_Size(array), 3);
+ assert!(matches!(upb_Array_Get(array, 2), upb_MessageValue { float_val: 0.0 }));
+
+ upb_Arena_Free(arena);
+ }
+ }
+
+ #[test]
+ fn map_ffi_test() {
+ // SAFETY: FFI unit test uses C API under expected patterns.
+ unsafe {
+ let arena = upb_Arena_New().unwrap();
+ let map = upb_Map_New(arena, CType::Bool, CType::Double);
+ assert_eq!(upb_Map_Size(map), 0);
+ assert_eq!(
+ upb_Map_Insert(
+ map,
+ upb_MessageValue { bool_val: true },
+ upb_MessageValue { double_val: 2.0 },
+ arena
+ ),
+ MapInsertStatus::Inserted
+ );
+ assert_eq!(
+ upb_Map_Insert(
+ map,
+ upb_MessageValue { bool_val: true },
+ upb_MessageValue { double_val: 3.0 },
+ arena,
+ ),
+ MapInsertStatus::Replaced,
+ );
+ assert_eq!(upb_Map_Size(map), 1);
+ upb_Map_Clear(map);
+ assert_eq!(upb_Map_Size(map), 0);
+ assert_eq!(
+ upb_Map_Insert(
+ map,
+ upb_MessageValue { bool_val: true },
+ upb_MessageValue { double_val: 4.0 },
+ arena
+ ),
+ MapInsertStatus::Inserted
+ );
+
+ let mut out = upb_MessageValue::zeroed();
+ assert!(upb_Map_Get(map, upb_MessageValue { bool_val: true }, &mut out));
+ assert!(matches!(out, upb_MessageValue { double_val: 4.0 }));
+
+ upb_Arena_Free(arena);
+ }
+ }
+}
diff --git a/rust/upb_kernel/upb_api.c b/rust/upb/upb_api.c
similarity index 96%
rename from rust/upb_kernel/upb_api.c
rename to rust/upb/upb_api.c
index 527d4b0..6208be2 100644
--- a/rust/upb_kernel/upb_api.c
+++ b/rust/upb/upb_api.c
@@ -6,6 +6,8 @@
// https://developers.google.com/open-source/licenses/bsd
//
+#include <stddef.h>
+
#define UPB_BUILD_API
#include "upb/mem/arena.h" // IWYU pragma: keep
diff --git a/rust/upb_kernel/BUILD b/rust/upb_kernel/BUILD
deleted file mode 100644
index c98774a..0000000
--- a/rust/upb_kernel/BUILD
+++ /dev/null
@@ -1,15 +0,0 @@
-# This package contains Rust protobuf runtime implementation built on top of UPB.
-
-cc_library(
- name = "upb_c_api",
- srcs = ["upb_api.c"],
- visibility = [
- "//rust:__subpackages__",
- "//src/google/protobuf:__subpackages__",
- ],
- deps = [
- "//upb:mem",
- "//upb:message",
- "//upb:message_copy",
- ],
-)
diff --git a/rust/vtable.rs b/rust/vtable.rs
index 3e8802a..52a0d87 100644
--- a/rust/vtable.rs
+++ b/rust/vtable.rs
@@ -5,9 +5,10 @@
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
-use crate::__internal::{Private, PtrAndLen, RawMessage};
+use crate::__internal::Private;
use crate::__runtime::{
- copy_bytes_in_arena_if_needed_by_runtime, InnerPrimitiveMut, MutatorMessageRef,
+ copy_bytes_in_arena_if_needed_by_runtime, InnerPrimitiveMut, MutatorMessageRef, PtrAndLen,
+ RawMessage,
};
use crate::{
AbsentField, FieldEntry, Mut, MutProxy, Optional, PresentField, PrimitiveMut, Proxied,