1#[cfg(all(feature = "alloc", not(feature = "std")))]
14use alloc::borrow::Cow;
15use core::fmt;
16#[cfg(feature = "std")]
17use std::{borrow::Cow, error};
18
19use crate::{
20 error::{Error, ErrorKind},
21 ConstantTy, Section, SectionMut,
22};
23
24const BOOL_FALSE_INDEX: u16 = 0;
25const BOOL_TRUE_INDEX: u16 = 1;
26
27const MAX_DATA_LEN: usize = 4096;
28const BYTES_TY_BITMASK: u16 = 3 << 13;
29const NUM_TY_BITMASK: u16 = 2 << 13;
30
31#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
33pub(crate) struct BytesId(pub(crate) u16);
34
35#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
37pub(crate) struct NumId(pub(crate) u16);
38
39#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
41pub(crate) struct ScalarId(u16);
42
43impl From<bool> for ScalarId {
44 fn from(value: bool) -> Self {
45 if value {
46 Self(1)
47 } else {
48 Self(0)
49 }
50 }
51}
52
53impl From<NumId> for ScalarId {
54 fn from(value: NumId) -> Self {
55 Self(value.0)
56 }
57}
58
59impl From<BytesId> for ScalarId {
60 fn from(value: BytesId) -> Self {
61 Self(value.0)
62 }
63}
64
65#[derive(Debug, Default, Clone, Copy, Hash, PartialEq, Eq)]
67pub struct ConstantId(pub(crate) u16);
68
69impl From<bool> for ConstantId {
70 fn from(value: bool) -> Self {
71 Self(ScalarId::from(value).0)
72 }
73}
74
75impl From<NumId> for ConstantId {
76 fn from(value: NumId) -> Self {
77 Self(value.0)
78 }
79}
80
81impl From<BytesId> for ConstantId {
82 fn from(value: BytesId) -> Self {
83 Self(value.0)
84 }
85}
86
87impl From<ScalarId> for ConstantId {
88 fn from(value: ScalarId) -> Self {
89 Self(value.0)
90 }
91}
92
93#[derive(Debug, Clone, Hash, PartialEq, Eq)]
95pub enum Constant<'a> {
96 Bool(bool),
98 Num(i64),
100 Bytes(&'a [u8]),
102}
103
104impl<'a> Default for Constant<'a> {
105 fn default() -> Self {
106 Self::Bool(false)
107 }
108}
109
110impl<'a> From<bool> for Constant<'a> {
111 fn from(value: bool) -> Self {
112 Constant::Bool(value)
113 }
114}
115
116impl<'a> From<i64> for Constant<'a> {
117 fn from(value: i64) -> Self {
118 Constant::Num(value)
119 }
120}
121
122impl<'a> From<&'a str> for Constant<'a> {
123 fn from(value: &'a str) -> Self {
124 Constant::Bytes(value.as_bytes())
125 }
126}
127
128impl<'a> From<&'a [u8]> for Constant<'a> {
129 fn from(value: &'a [u8]) -> Self {
130 Constant::Bytes(value)
131 }
132}
133
134#[derive(Clone, Copy)]
136pub struct InvalidType;
137
138#[cfg(feature = "std")]
139impl error::Error for InvalidType {
140 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
141 None
142 }
143}
144
145impl fmt::Display for InvalidType {
146 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
147 f.write_str("invalid type")
148 }
149}
150
151impl fmt::Debug for InvalidType {
152 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
153 f.write_str("invalid type")
154 }
155}
156
157impl<'a> TryFrom<Constant<'a>> for &'a [u8] {
158 type Error = InvalidType;
159
160 fn try_from(value: Constant<'a>) -> Result<Self, Self::Error> {
161 match value {
162 Constant::Bool(_) | Constant::Num(_) => Err(InvalidType),
163 Constant::Bytes(bytes) => Ok(bytes),
164 }
165 }
166}
167
168impl<'a> TryFrom<Constant<'a>> for Cow<'a, [u8]> {
169 type Error = InvalidType;
170
171 fn try_from(value: Constant<'a>) -> Result<Self, Self::Error> {
172 match value {
173 Constant::Bool(_) | Constant::Num(_) => Err(InvalidType),
174 Constant::Bytes(bytes) => Ok(Cow::from(bytes)),
175 }
176 }
177}
178
179impl<'a> TryFrom<Constant<'a>> for &'a str {
180 type Error = InvalidType;
181
182 fn try_from(value: Constant<'a>) -> Result<Self, Self::Error> {
183 match value {
184 Constant::Bool(_) | Constant::Num(_) => Err(InvalidType),
185 Constant::Bytes(bytes) => Ok(core::str::from_utf8(bytes).map_err(|_| InvalidType)?),
186 }
187 }
188}
189
190impl<'a> TryFrom<Constant<'a>> for Cow<'a, str> {
191 type Error = InvalidType;
192
193 fn try_from(value: Constant<'a>) -> Result<Self, Self::Error> {
194 match value {
195 Constant::Bool(_) | Constant::Num(_) => Err(InvalidType),
196 Constant::Bytes(bytes) => Ok(Cow::Borrowed(
197 core::str::from_utf8(bytes).map_err(|_| InvalidType)?,
198 )),
199 }
200 }
201}
202
203impl<'a> TryFrom<Constant<'a>> for i64 {
204 type Error = InvalidType;
205
206 fn try_from(value: Constant<'a>) -> Result<Self, Self::Error> {
207 match value {
208 Constant::Bool(_) | Constant::Bytes(_) => Err(InvalidType),
209 Constant::Num(n) => Ok(n),
210 }
211 }
212}
213
214impl<'a> TryFrom<Constant<'a>> for bool {
215 type Error = InvalidType;
216
217 fn try_from(value: Constant<'a>) -> Result<Self, Self::Error> {
218 match value {
219 Constant::Bool(b) => Ok(b),
220 Constant::Num(_) | Constant::Bytes(_) => Err(InvalidType),
221 }
222 }
223}
224
225#[derive(Debug)]
226pub(crate) struct Iter<'a> {
227 data: &'a [u8],
228}
229
230fn iter<'a>(ctx: &Section<'a>) -> Iter<'a> {
231 let data = ctx.data();
232 if data.is_empty() {
233 return Iter { data: &[] };
234 }
235 Iter { data: &data[2..] }
236}
237
238impl<'a> Iterator for Iter<'a> {
239 type Item = (ConstantTy, &'a [u8]);
240
241 fn next(&mut self) -> Option<Self::Item> {
242 if self.data.is_empty() {
243 return None;
244 }
245
246 let first_len_byte = self.data[0];
247 let ty = match first_len_byte >> 5 {
248 2 => ConstantTy::Num,
249 3 => ConstantTy::Bytes,
250 _ => unreachable!(),
251 };
252
253 let first_len_byte = first_len_byte & 0x1F;
254 let len = u16::from_be_bytes([first_len_byte, self.data[1]]);
255
256 let len = usize::from(len);
257 let slice = &self.data[2..2 + len];
258 self.data = &self.data[2 + len..];
259
260 Some((ty, slice))
261 }
262}
263
264const SECTION_INIT: [u8; 5] = [1, 0, 2, 0, 0];
265
266#[must_use]
267#[inline]
268fn elem_len(section: &SectionMut<'_>) -> u16 {
269 u16::from_be_bytes([
270 section.data[section.start + 3],
271 section.data[section.start + 4],
272 ])
273}
274
275#[inline]
276fn set_elem_len(section: &mut SectionMut<'_>, len: u16) {
277 let len_bytes = len.to_be_bytes();
278 section.data[section.start + 3] = len_bytes[0];
279 section.data[section.start + 4] = len_bytes[1];
280}
281
282#[must_use]
283pub(crate) fn bytes_id(ctx: &Section<'_>, needle: &[u8]) -> Option<BytesId> {
284 iter(ctx)
285 .position(|(ty, value)| ty == ConstantTy::Bytes && value == needle)
286 .map(|pos| {
287 BytesId(2 + u16::try_from(pos).expect("greater than u16::MAX byte slices in context"))
288 })
289}
290
291#[must_use]
293pub(crate) fn bytes<'a>(ctx: &Section<'a>, id: BytesId) -> &'a [u8] {
294 let idx = usize::from(id.0 - 2);
295 let Some((ty, value)) = iter(ctx).nth(idx) else {
296 unreachable!()
297 };
298 assert_eq!(ty, ConstantTy::Bytes);
299 value
300}
301
302pub(crate) fn get_or_insert_bytes_id(
308 ctx: &mut SectionMut<'_>,
309 needle: &[u8],
310) -> Result<BytesId, Error> {
311 if needle.len() > MAX_DATA_LEN {
312 return Err(Error::with_kind(ErrorKind::InvalidContextValue));
313 }
314
315 if let Some(bytes_id) = bytes_id(&ctx.as_ref(), needle) {
316 Ok(bytes_id)
317 } else {
318 ctx.init(&SECTION_INIT);
319
320 let len = elem_len(ctx);
321 if len >= u16::MAX - 2 {
322 return Err(Error::with_kind(ErrorKind::ContextFull));
323 }
324 let len = len + 1;
325
326 let needle_len = needle.len();
327 assert!(needle_len <= 4096);
328 let needle_len_with_ty = u16::try_from(needle_len).unwrap() | BYTES_TY_BITMASK;
329 ctx.splice(needle_len_with_ty.to_be_bytes());
330 ctx.splice(needle.iter().copied());
331
332 set_elem_len(ctx, len);
333 ctx.update_len();
334
335 Ok(BytesId(2 + len - 1))
336 }
337}
338
339#[must_use]
341fn num_id(ctx: &Section<'_>, needle: i64) -> Option<NumId> {
342 iter(ctx)
343 .position(|(ty, value)| {
344 ty == ConstantTy::Num
345 && i64::from_be_bytes(<[u8; 8]>::try_from(value).unwrap()) == needle
346 })
347 .map(|pos| NumId(2 + u16::try_from(pos).expect("greater than u16::MAX numbers in context")))
348}
349
350#[must_use]
352#[allow(unused)]
353fn num(ctx: &Section<'_>, id: NumId) -> i64 {
354 let idx = usize::from(id.0 - 2);
355 let Some((ty, value)) = iter(ctx).nth(idx) else {
356 unreachable!()
357 };
358 assert_eq!(ty, ConstantTy::Num);
359 i64::from_be_bytes(<[u8; 8]>::try_from(value).unwrap())
360}
361
362pub(crate) fn get_or_insert_num_id(ctx: &mut SectionMut<'_>, needle: i64) -> Result<NumId, Error> {
368 if let Some(id) = num_id(&ctx.as_ref(), needle) {
369 Ok(id)
370 } else {
371 ctx.init(&SECTION_INIT);
372
373 let len = elem_len(ctx);
374 if len >= u16::MAX - 2 {
375 return Err(Error::with_kind(ErrorKind::ContextFull));
376 }
377 let len = len + 1;
378
379 let needle_len = 8u16;
380 let needle_len = needle_len | NUM_TY_BITMASK;
381 ctx.splice(needle_len.to_be_bytes());
382 ctx.splice(needle.to_be_bytes());
383
384 set_elem_len(ctx, len);
385 ctx.update_len();
386
387 Ok(NumId(2 + len - 1))
388 }
389}
390
391pub(crate) fn constant<'a, T>(ctx: &Section<'a>, id: T) -> Constant<'a>
393where
394 ConstantId: From<T>,
395{
396 let id = ConstantId::from(id);
397
398 if id.0 == BOOL_FALSE_INDEX {
399 return Constant::Bool(false);
400 }
401
402 if id.0 == BOOL_TRUE_INDEX {
403 return Constant::Bool(true);
404 }
405
406 let Some((ty, value)) = iter(ctx).nth(usize::from(id.0 - 2)) else {
407 unreachable!()
408 };
409
410 match ty {
411 ConstantTy::Unknown | ConstantTy::Bool => unreachable!(),
412 ConstantTy::Num => Constant::Num(i64::from_be_bytes(<[u8; 8]>::try_from(value).unwrap())),
413 ConstantTy::Bytes => Constant::Bytes(value),
414 }
415}