upb: implement immutability

PiperOrigin-RevId: 601848001
diff --git a/upb/message/internal/array.h b/upb/message/internal/array.h
index dc3eea9..21a8256 100644
--- a/upb/message/internal/array.h
+++ b/upb/message/internal/array.h
@@ -33,13 +33,21 @@
   //   2 maps to elem size 8
   //   3 maps to elem size 16
   //
-  // Bit #2 contains the frozen/immutable flag (currently unimplemented).
+  // Bit #2 contains the frozen/immutable flag.
   uintptr_t UPB_ONLYBITS(data);
 
   size_t UPB_ONLYBITS(size);     // The number of elements in the array.
   size_t UPB_PRIVATE(capacity);  // Allocated storage. Measured in elements.
 };
 
+UPB_INLINE void UPB_PRIVATE(_upb_Array_ShallowFreeze)(struct upb_Array* arr) {
+  arr->UPB_ONLYBITS(data) |= _UPB_ARRAY_MASK_IMM;
+}
+
+UPB_INLINE bool UPB_PRIVATE(_upb_Array_IsFrozen)(const struct upb_Array* arr) {
+  return (arr->UPB_ONLYBITS(data) & _UPB_ARRAY_MASK_IMM) != 0;
+}
+
 UPB_INLINE void UPB_PRIVATE(_upb_Array_SetTaggedPtr)(struct upb_Array* array,
                                                      void* data, size_t lg2) {
   UPB_ASSERT(lg2 != 1);
@@ -89,6 +97,7 @@
 
 UPB_INLINE bool UPB_PRIVATE(_upb_Array_Reserve)(struct upb_Array* array,
                                                 size_t size, upb_Arena* arena) {
+  UPB_ASSERT(!UPB_PRIVATE(_upb_Array_IsFrozen)(array));
   if (array->UPB_PRIVATE(capacity) < size)
     return UPB_PRIVATE(_upb_Array_Realloc)(array, size, arena);
   return true;
@@ -97,6 +106,7 @@
 // Resize without initializing new elements.
 UPB_INLINE bool UPB_PRIVATE(_upb_Array_ResizeUninitialized)(
     struct upb_Array* array, size_t size, upb_Arena* arena) {
+  UPB_ASSERT(!UPB_PRIVATE(_upb_Array_IsFrozen)(array));
   UPB_ASSERT(size <= array->UPB_ONLYBITS(size) ||
              arena);  // Allow NULL arena when shrinking.
   if (!UPB_PRIVATE(_upb_Array_Reserve)(array, size, arena)) return false;
@@ -110,6 +120,7 @@
 UPB_INLINE void UPB_PRIVATE(_upb_Array_Set)(struct upb_Array* array, size_t i,
                                             const void* data,
                                             size_t elem_size) {
+  UPB_ASSERT(!UPB_PRIVATE(_upb_Array_IsFrozen)(array));
   UPB_ASSERT(i < array->UPB_ONLYBITS(size));
   UPB_ASSERT(elem_size == 1U << UPB_PRIVATE(_upb_Array_ElemSizeLg2)(array));
   char* arr_data = (char*)UPB_PRIVATE(_upb_Array_MutableDataPtr)(array);