artichoke_backend/
intern.rs1use 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 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 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 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 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}