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,