From 30bdb8ec95be9de6077070c29295392caa3dfe36 Mon Sep 17 00:00:00 2001 From: Protobuf Team Bot Date: Tue, 16 Apr 2024 06:42:54 -0700 Subject: [PATCH] Refactor upb/rust directory to introduce separate files for each concept instead of a single blob. PiperOrigin-RevId: 625312955 --- rust/upb/BUILD | 9 ++++ rust/upb/array.rs | 42 ++++++++++++++++ rust/upb/ctype.rs | 17 +++++++ rust/upb/extension_registry.rs | 5 ++ rust/upb/map.rs | 91 ++++++++++++++++++++++++++++++++++ rust/upb/message.rs | 21 ++++++++ rust/upb/message_value.rs | 37 ++++++++++++++ rust/upb/mini_table.rs | 5 ++ rust/upb/string_view.rs | 43 ++++++++++++++++ rust/upb/wire.rs | 47 ++++++++++++++++++ 10 files changed, 317 insertions(+) create mode 100644 rust/upb/array.rs create mode 100644 rust/upb/ctype.rs create mode 100644 rust/upb/extension_registry.rs create mode 100644 rust/upb/map.rs create mode 100644 rust/upb/message.rs create mode 100644 rust/upb/message_value.rs create mode 100644 rust/upb/mini_table.rs create mode 100644 rust/upb/string_view.rs create mode 100644 rust/upb/wire.rs diff --git a/rust/upb/BUILD b/rust/upb/BUILD index 2c19acc55ff4b..894d4950e0637 100644 --- a/rust/upb/BUILD +++ b/rust/upb/BUILD @@ -14,8 +14,17 @@ rust_library( name = "upb", srcs = [ "arena.rs", + "array.rs", + "ctype.rs", + "extension_registry.rs", "lib.rs", + "map.rs", + "message.rs", + "message_value.rs", + "mini_table.rs", "opaque_pointee.rs", + "string_view.rs", + "wire.rs", ], visibility = [ "//rust:__subpackages__", diff --git a/rust/upb/array.rs b/rust/upb/array.rs new file mode 100644 index 0000000000000..8c0abf8c81d28 --- /dev/null +++ b/rust/upb/array.rs @@ -0,0 +1,42 @@ +use crate::opaque_pointee::opaque_pointee; +use crate::{upb_MessageValue, upb_MutableMessageValue, CType, RawArena}; +use std::ptr::NonNull; + +opaque_pointee!(upb_Array); +pub type RawArray = NonNull; + +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; +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn array_ffi_test() { + // SAFETY: FFI unit test uses C API under expected patterns. + unsafe { + let arena = crate::Arena::new(); + let raw_arena = arena.raw(); + let array = upb_Array_New(raw_arena, CType::Float); + + assert!(upb_Array_Append(array, upb_MessageValue { float_val: 7.0 }, raw_arena)); + assert!(upb_Array_Append(array, upb_MessageValue { float_val: 42.0 }, raw_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, raw_arena)); + assert_eq!(upb_Array_Size(array), 3); + assert!(matches!(upb_Array_Get(array, 2), upb_MessageValue { float_val: 0.0 })); + } + } +} diff --git a/rust/upb/ctype.rs b/rust/upb/ctype.rs new file mode 100644 index 0000000000000..346706ac23f51 --- /dev/null +++ b/rust/upb/ctype.rs @@ -0,0 +1,17 @@ +// 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, +} diff --git a/rust/upb/extension_registry.rs b/rust/upb/extension_registry.rs new file mode 100644 index 0000000000000..b58b3f62aaac4 --- /dev/null +++ b/rust/upb/extension_registry.rs @@ -0,0 +1,5 @@ +use crate::opaque_pointee::opaque_pointee; +use std::ptr::NonNull; + +opaque_pointee!(upb_ExtensionRegistry); +pub type RawExtensionRegistry = NonNull; diff --git a/rust/upb/map.rs b/rust/upb/map.rs new file mode 100644 index 0000000000000..14b8a7a486fe5 --- /dev/null +++ b/rust/upb/map.rs @@ -0,0 +1,91 @@ +use crate::opaque_pointee::opaque_pointee; +use crate::{upb_MessageValue, CType, RawArena}; +use std::ptr::NonNull; + +opaque_pointee!(upb_Map); +pub type RawMap = NonNull; + +#[repr(C)] +#[allow(dead_code)] +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub enum MapInsertStatus { + Inserted = 0, + Replaced = 1, + OutOfMemory = 2, +} + +extern "C" { + pub static __rust_proto_kUpb_Map_Begin: usize; + + 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 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 map_ffi_test() { + // SAFETY: FFI unit test uses C API under expected patterns. + unsafe { + let arena = crate::Arena::new(); + let raw_arena = arena.raw(); + let map = upb_Map_New(raw_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 }, + raw_arena + ), + MapInsertStatus::Inserted + ); + assert_eq!( + upb_Map_Insert( + map, + upb_MessageValue { bool_val: true }, + upb_MessageValue { double_val: 3.0 }, + raw_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 }, + raw_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 })); + } + } +} diff --git a/rust/upb/message.rs b/rust/upb/message.rs new file mode 100644 index 0000000000000..2a9fe91e30302 --- /dev/null +++ b/rust/upb/message.rs @@ -0,0 +1,21 @@ +use crate::opaque_pointee::opaque_pointee; +use crate::{upb_MiniTable, RawArena}; +use std::ptr::NonNull; + +opaque_pointee!(upb_Message); +pub type RawMessage = NonNull; + +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; +} diff --git a/rust/upb/message_value.rs b/rust/upb/message_value.rs new file mode 100644 index 0000000000000..38380106bc3cc --- /dev/null +++ b/rust/upb/message_value.rs @@ -0,0 +1,37 @@ +use crate::{RawArray, RawMap, RawMessage, StringView}; + +// 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, + pub map_val: Option, + pub msg_val: Option, + 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, + pub map: Option, + pub msg: Option, +} diff --git a/rust/upb/mini_table.rs b/rust/upb/mini_table.rs new file mode 100644 index 0000000000000..dc704caf0e61a --- /dev/null +++ b/rust/upb/mini_table.rs @@ -0,0 +1,5 @@ +use crate::opaque_pointee::opaque_pointee; +use std::ptr::NonNull; + +opaque_pointee!(upb_MiniTable); +pub type RawMiniTable = NonNull; diff --git a/rust/upb/string_view.rs b/rust/upb/string_view.rs new file mode 100644 index 0000000000000..029c599714ea9 --- /dev/null +++ b/rust/upb/string_view.rs @@ -0,0 +1,43 @@ +/// 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 { std::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() } + } +} diff --git a/rust/upb/wire.rs b/rust/upb/wire.rs new file mode 100644 index 0000000000000..2b68145cc293d --- /dev/null +++ b/rust/upb/wire.rs @@ -0,0 +1,47 @@ +use crate::{upb_ExtensionRegistry, upb_MiniTable, RawArena, 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; +}