makepad/libs/relax/base/src/arena.rs
Admin b22a714840 makepad run: the Rust JIT (libs/relax) and makepad-remote
The JIT that runs makepad apps from source: lexer, front end, type checker,
lazy borrow checker (Gandalf; `--check-first` opts in), codegen, session and
warm/cold caches keyed by compiler stamp, provided crates bound late,
startup images, live patching. User-facing name is `makepad run` (binary
`makepad`, MAKEPAD_RUN_* env, ~/.makepad-run); libs/relax stays the internal
library name. `makepad check` reports rustc-style diagnostics with rustc
error codes and checks a whole workspace (`--workspace`). Runs on macOS,
Linux, Windows and Android. makepad-remote: one tool schema for apps and the
WM (app/1): grabs, captures, traces, frame profiles and tweaks.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 12:26:21 +02:00

145 lines
3.9 KiB
Rust

//! Arrays of the front end's per-file data that are either built in this process (owned) or
//! read from a mapped front-end image (plans/memory-use.md, "Image design"): a slice into the
//! mapping costs no heap and its pages stay clean (the OS can drop and re-read them).
//!
//! Reads go through `Deref<Target = [T]>`, so consumers do not care which one they have. A
//! mutable access to a mapped array copies it into the heap first (copy on write) and counts
//! the copy (`COPIES`): files loaded from an image are not meant to change.
use std::sync::atomic::{AtomicU64, Ordering};
/// mapped arrays copied into the heap by a mutable access, and their bytes
pub static COPIES: AtomicU64 = AtomicU64::new(0);
pub static COPIED_BYTES: AtomicU64 = AtomicU64::new(0);
pub enum Arena<T: 'static> {
Owned(Vec<T>),
/// into a mapping that lives as long as the process
Mapped(&'static [T]),
}
impl<T: 'static> Default for Arena<T> {
fn default() -> Self {
Arena::Owned(Vec::new())
}
}
impl<T: 'static> From<Vec<T>> for Arena<T> {
fn from(v: Vec<T>) -> Self {
Arena::Owned(v)
}
}
impl<T: Clone + 'static> Clone for Arena<T> {
fn clone(&self) -> Self {
match self {
Arena::Owned(v) => Arena::Owned(v.clone()),
Arena::Mapped(s) => Arena::Mapped(s),
}
}
}
impl<T: std::fmt::Debug + 'static> std::fmt::Debug for Arena<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
(**self).fmt(f)
}
}
impl<T: 'static> std::ops::Deref for Arena<T> {
type Target = [T];
#[inline]
fn deref(&self) -> &[T] {
match self {
Arena::Owned(v) => v,
Arena::Mapped(s) => s,
}
}
}
impl<T: Clone + 'static> std::ops::DerefMut for Arena<T> {
#[inline]
fn deref_mut(&mut self) -> &mut [T] {
self.vec_mut()
}
}
impl<T: 'static> Arena<T> {
pub fn is_mapped(&self) -> bool {
matches!(self, Arena::Mapped(_))
}
/// Heap bytes this array holds (0 when mapped).
pub fn heap_bytes(&self) -> usize {
match self {
Arena::Owned(v) => v.capacity() * std::mem::size_of::<T>(),
Arena::Mapped(_) => 0,
}
}
pub fn shrink_to_fit(&mut self) {
if let Arena::Owned(v) = self {
v.shrink_to_fit();
}
}
/// The owned array (None when mapped).
pub fn owned(&self) -> Option<&Vec<T>> {
match self {
Arena::Owned(v) => Some(v),
Arena::Mapped(_) => None,
}
}
}
impl<T: Clone + 'static> Arena<T> {
/// The array as a Vec to change (a mapped one is copied into the heap first).
#[inline]
pub fn vec_mut(&mut self) -> &mut Vec<T> {
if let Arena::Mapped(s) = *self {
COPIES.fetch_add(1, Ordering::Relaxed);
COPIED_BYTES.fetch_add(std::mem::size_of_val(s) as u64, Ordering::Relaxed);
*self = Arena::Owned(s.to_vec());
}
match self {
Arena::Owned(v) => v,
Arena::Mapped(_) => unreachable!(),
}
}
#[inline]
pub fn push(&mut self, x: T) {
self.vec_mut().push(x)
}
pub fn extend_from_slice(&mut self, x: &[T]) {
self.vec_mut().extend_from_slice(x)
}
pub fn truncate(&mut self, n: usize) {
if n < self.len() {
self.vec_mut().truncate(n)
}
}
/// The owned Vec, copying a mapped array.
pub fn into_vec(self) -> Vec<T> {
match self {
Arena::Owned(v) => v,
Arena::Mapped(s) => s.to_vec(),
}
}
}
impl<T: Clone + 'static> Extend<T> for Arena<T> {
fn extend<I: IntoIterator<Item = T>>(&mut self, it: I) {
self.vec_mut().extend(it)
}
}
impl<'a, T: 'static> IntoIterator for &'a Arena<T> {
type Item = &'a T;
type IntoIter = std::slice::Iter<'a, T>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}