artichoke_backend/
intern.rs

1use std::borrow::Cow;
2
3use intaglio::SymbolOverflowError;
4use spinoso_exception::Fatal;
5
6use crate::Artichoke;
7use crate::core::{ClassRegistry, Intern, TryConvertMut};
8use crate::error::{Error, RubyException};
9use crate::ffi::InterpreterExtractError;
10use crate::sys;
11
12impl Artichoke {
13    pub fn lookup_symbol_with_trailing_nul(&self, symbol: u32) -> Result<Option<&[u8]>, Error> {
14        let state = self.state.as_deref().ok_or_else(InterpreterExtractError::new)?;
15        let Some(symbol) = symbol.checked_sub(1) else {
16            return Ok(None);
17        };
18        let Some(bytes) = state.symbols.get(symbol.into()) else {
19            return Ok(None);
20        };
21        Ok(Some(bytes))
22    }
23
24    pub fn intern_bytes_with_trailing_nul<T>(&mut self, bytes: T) -> Result<u32, Error>
25    where
26        T: Into<Cow<'static, [u8]>>,
27    {
28        let bytes = bytes.into();
29        let state = self.state.as_deref_mut().ok_or_else(InterpreterExtractError::new)?;
30        let symbol = state.symbols.intern(bytes)?;
31        let symbol = u32::from(symbol);
32        // mruby expects symbols to be non-zero.
33        let symbol = symbol
34            .checked_add(<Self as Intern>::SYMBOL_RANGE_START)
35            .ok_or_else(SymbolOverflowError::new)?;
36        Ok(symbol)
37    }
38
39    pub fn check_interned_bytes_with_trailing_nul(&self, bytes: &[u8]) -> Result<Option<u32>, Error> {
40        let state = self.state.as_deref().ok_or_else(InterpreterExtractError::new)?;
41        let symbol = state.symbols.check_interned(bytes);
42        let Some(symbol) = symbol else {
43            return Ok(None);
44        };
45        let symbol = u32::from(symbol);
46        // mruby expects symbols to be non-zero.
47        let symbol = symbol
48            .checked_add(<Self as Intern>::SYMBOL_RANGE_START)
49            .ok_or_else(SymbolOverflowError::new)?;
50        Ok(Some(symbol))
51    }
52}
53
54impl Intern for Artichoke {
55    type Symbol = u32;
56
57    type Error = Error;
58
59    const SYMBOL_RANGE_START: Self::Symbol = 1;
60
61    fn intern_bytes<T>(&mut self, bytes: T) -> Result<Self::Symbol, Self::Error>
62    where
63        T: Into<Cow<'static, [u8]>>,
64    {
65        let bytes = bytes.into();
66        let state = self.state.as_deref_mut().ok_or_else(InterpreterExtractError::new)?;
67        let mut bytes = bytes.into_owned();
68        bytes.push(b'\0');
69        let symbol = state.symbols.intern(bytes)?;
70        let symbol = u32::from(symbol);
71        // mruby expects symbols to be non-zero.
72        let symbol = symbol
73            .checked_add(Self::SYMBOL_RANGE_START)
74            .ok_or_else(SymbolOverflowError::new)?;
75        Ok(symbol)
76    }
77
78    fn check_interned_bytes(&self, bytes: &[u8]) -> Result<Option<Self::Symbol>, Self::Error> {
79        let state = self.state.as_deref().ok_or_else(InterpreterExtractError::new)?;
80        let mut bytes = bytes.to_vec();
81        bytes.push(b'\0');
82        let symbol = state.symbols.check_interned(&bytes);
83        let Some(symbol) = symbol else {
84            return Ok(None);
85        };
86        let symbol = u32::from(symbol);
87        // mruby expects symbols to be non-zero.
88        let symbol = symbol
89            .checked_add(Self::SYMBOL_RANGE_START)
90            .ok_or_else(SymbolOverflowError::new)?;
91        Ok(Some(symbol))
92    }
93
94    fn lookup_symbol(&self, symbol: Self::Symbol) -> Result<Option<&[u8]>, Self::Error> {
95        let state = self.state.as_deref().ok_or_else(InterpreterExtractError::new)?;
96        let Some(symbol) = symbol.checked_sub(Self::SYMBOL_RANGE_START) else {
97            return Ok(None);
98        };
99        let Some(bytes) = state.symbols.get(symbol.into()) else {
100            return Ok(None);
101        };
102        if bytes.is_empty() {
103            Ok(Some(bytes))
104        } else {
105            Ok(bytes.get(..bytes.len() - 1))
106        }
107    }
108
109    fn symbol_count(&self) -> usize {
110        let Some(state) = self.state.as_deref() else {
111            return 0;
112        };
113        state.symbols.len()
114    }
115}
116
117impl RubyException for SymbolOverflowError {
118    #[inline]
119    fn message(&self) -> Cow<'_, [u8]> {
120        self.to_string().into_bytes().into()
121    }
122
123    #[inline]
124    fn name(&self) -> Cow<'_, str> {
125        "fatal".into()
126    }
127
128    fn vm_backtrace(&self, interp: &mut Artichoke) -> Option<Vec<Vec<u8>>> {
129        let _ = interp;
130        None
131    }
132
133    fn as_mrb_value(&self, interp: &mut Artichoke) -> Option<sys::mrb_value> {
134        let message = interp.try_convert_mut(self.message()).ok()?;
135        let value = interp.new_instance::<Fatal>(&[message]).ok().flatten()?;
136        Some(value.inner())
137    }
138}
139
140impl From<SymbolOverflowError> for Error {
141    #[inline]
142    fn from(exception: SymbolOverflowError) -> Self {
143        let err: Box<dyn RubyException> = Box::new(exception);
144        Self::from(err)
145    }
146}