typst_library/foundations/
bytes.rs1use std::any::Any;
2use std::fmt::{self, Debug, Formatter};
3use std::hash::{Hash, Hasher};
4use std::ops::{Add, AddAssign, Deref};
5use std::str::Utf8Error;
6use std::sync::Arc;
7
8use ecow::{eco_format, EcoString};
9use serde::{Serialize, Serializer};
10use typst_utils::LazyHash;
11
12use crate::diag::{bail, StrResult};
13use crate::foundations::{cast, func, scope, ty, Array, Reflect, Repr, Str, Value};
14
15#[ty(scope, cast)]
44#[derive(Clone, Hash)]
45#[allow(clippy::derived_hash_with_manual_eq)]
46pub struct Bytes(Arc<LazyHash<dyn Bytelike>>);
47
48impl Bytes {
49 pub fn new<T>(data: T) -> Self
61 where
62 T: AsRef<[u8]> + Send + Sync + 'static,
63 {
64 Self(Arc::new(LazyHash::new(data)))
65 }
66
67 pub fn from_string<T>(data: T) -> Self
73 where
74 T: AsRef<str> + Send + Sync + 'static,
75 {
76 Self(Arc::new(LazyHash::new(StrWrapper(data))))
77 }
78
79 pub fn is_empty(&self) -> bool {
81 self.as_slice().is_empty()
82 }
83
84 pub fn as_slice(&self) -> &[u8] {
86 self
87 }
88
89 pub fn as_str(&self) -> Result<&str, Utf8Error> {
94 self.inner().as_str()
95 }
96
97 pub fn to_vec(&self) -> Vec<u8> {
99 self.as_slice().to_vec()
100 }
101
102 pub fn to_str(&self) -> Result<Str, Utf8Error> {
109 match self.inner().as_any().downcast_ref::<Str>() {
110 Some(string) => Ok(string.clone()),
111 None => self.as_str().map(Into::into),
112 }
113 }
114
115 fn locate(&self, index: i64) -> StrResult<usize> {
117 self.locate_opt(index).ok_or_else(|| out_of_bounds(index, self.len()))
118 }
119
120 fn locate_opt(&self, index: i64) -> Option<usize> {
124 let len = self.as_slice().len();
125 let wrapped =
126 if index >= 0 { Some(index) } else { (len as i64).checked_add(index) };
127 wrapped.and_then(|v| usize::try_from(v).ok()).filter(|&v| v <= len)
128 }
129
130 fn inner(&self) -> &dyn Bytelike {
132 &**self.0
133 }
134}
135
136#[scope]
137impl Bytes {
138 #[func(constructor)]
151 pub fn construct(
152 value: ToBytes,
154 ) -> Bytes {
155 value.0
156 }
157
158 #[func(title = "Length")]
160 pub fn len(&self) -> usize {
161 self.as_slice().len()
162 }
163
164 #[func]
168 pub fn at(
169 &self,
170 index: i64,
172 #[named]
174 default: Option<Value>,
175 ) -> StrResult<Value> {
176 self.locate_opt(index)
177 .and_then(|i| self.as_slice().get(i).map(|&b| Value::Int(b.into())))
178 .or(default)
179 .ok_or_else(|| out_of_bounds_no_default(index, self.len()))
180 }
181
182 #[func]
185 pub fn slice(
186 &self,
187 start: i64,
189 #[default]
192 end: Option<i64>,
193 #[named]
197 count: Option<i64>,
198 ) -> StrResult<Bytes> {
199 let mut end = end;
200 if end.is_none() {
201 end = count.map(|c: i64| start + c);
202 }
203
204 let start = self.locate(start)?;
205 let end = self.locate(end.unwrap_or(self.len() as i64))?.max(start);
206 let slice = &self.as_slice()[start..end];
207
208 Ok(Bytes::new(slice.to_vec()))
214 }
215}
216
217impl Debug for Bytes {
218 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
219 write!(f, "Bytes({})", self.len())
220 }
221}
222
223impl Repr for Bytes {
224 fn repr(&self) -> EcoString {
225 eco_format!("bytes({})", self.len())
226 }
227}
228
229impl Deref for Bytes {
230 type Target = [u8];
231
232 fn deref(&self) -> &Self::Target {
233 self.inner().as_bytes()
234 }
235}
236
237impl Eq for Bytes {}
238
239impl PartialEq for Bytes {
240 fn eq(&self, other: &Self) -> bool {
241 self.0.eq(&other.0)
242 }
243}
244
245impl AsRef<[u8]> for Bytes {
246 fn as_ref(&self) -> &[u8] {
247 self
248 }
249}
250
251impl Add for Bytes {
252 type Output = Self;
253
254 fn add(mut self, rhs: Self) -> Self::Output {
255 self += rhs;
256 self
257 }
258}
259
260impl AddAssign for Bytes {
261 fn add_assign(&mut self, rhs: Self) {
262 if rhs.is_empty() {
263 } else if self.is_empty() {
265 *self = rhs;
266 } else if let Some(vec) = Arc::get_mut(&mut self.0)
267 .and_then(|unique| unique.as_any_mut().downcast_mut::<Vec<u8>>())
268 {
269 vec.extend_from_slice(&rhs);
270 } else {
271 *self = Self::new([self.as_slice(), rhs.as_slice()].concat());
272 }
273 }
274}
275
276impl Serialize for Bytes {
277 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
278 where
279 S: Serializer,
280 {
281 if serializer.is_human_readable() {
282 serializer.serialize_str(&eco_format!("{self:?}"))
283 } else {
284 serializer.serialize_bytes(self)
285 }
286 }
287}
288
289trait Bytelike: Send + Sync {
291 fn as_bytes(&self) -> &[u8];
292 fn as_str(&self) -> Result<&str, Utf8Error>;
293 fn as_any(&self) -> &dyn Any;
294 fn as_any_mut(&mut self) -> &mut dyn Any;
295}
296
297impl<T> Bytelike for T
298where
299 T: AsRef<[u8]> + Send + Sync + 'static,
300{
301 fn as_bytes(&self) -> &[u8] {
302 self.as_ref()
303 }
304
305 fn as_str(&self) -> Result<&str, Utf8Error> {
306 std::str::from_utf8(self.as_ref())
307 }
308
309 fn as_any(&self) -> &dyn Any {
310 self
311 }
312
313 fn as_any_mut(&mut self) -> &mut dyn Any {
314 self
315 }
316}
317
318impl Hash for dyn Bytelike {
319 fn hash<H: Hasher>(&self, state: &mut H) {
320 self.as_bytes().hash(state);
321 }
322}
323
324struct StrWrapper<T>(T);
326
327impl<T> Bytelike for StrWrapper<T>
328where
329 T: AsRef<str> + Send + Sync + 'static,
330{
331 fn as_bytes(&self) -> &[u8] {
332 self.0.as_ref().as_bytes()
333 }
334
335 fn as_str(&self) -> Result<&str, Utf8Error> {
336 Ok(self.0.as_ref())
337 }
338
339 fn as_any(&self) -> &dyn Any {
340 self
341 }
342
343 fn as_any_mut(&mut self) -> &mut dyn Any {
344 self
345 }
346}
347
348pub struct ToBytes(Bytes);
350
351cast! {
352 ToBytes,
353 v: Str => Self(Bytes::from_string(v)),
354 v: Array => Self(v.iter()
355 .map(|item| match item {
356 Value::Int(byte @ 0..=255) => Ok(*byte as u8),
357 Value::Int(_) => bail!("number must be between 0 and 255"),
358 value => Err(<u8 as Reflect>::error(value)),
359 })
360 .collect::<Result<Vec<u8>, _>>()
361 .map(Bytes::new)?
362 ),
363 v: Bytes => Self(v),
364}
365
366#[cold]
368fn out_of_bounds(index: i64, len: usize) -> EcoString {
369 eco_format!("byte index out of bounds (index: {index}, len: {len})")
370}
371
372#[cold]
374fn out_of_bounds_no_default(index: i64, len: usize) -> EcoString {
375 eco_format!(
376 "byte index out of bounds (index: {index}, len: {len}) \
377 and no default value was specified",
378 )
379}