1use alloc::vec::Vec;
6use core::hint::unreachable_unchecked;
7use core::marker::PhantomData;
8use core::mem::{align_of, forget, replace, size_of, transmute, ManuallyDrop, MaybeUninit};
9use core::num::NonZeroU8;
10use core::ops::Range;
11
12use allocated::Allocated;
13use borrowed::Borrowed;
14
15use crate::Backend;
16
17pub mod allocated;
18pub mod borrowed;
19pub mod inline;
20#[cfg(test)]
21mod tests;
22
23const TAG_BITS: u8 = 2;
25
26const MASK: u8 = (1 << TAG_BITS) - 1;
28
29const TAG_INLINE: u8 = 1;
31
32const TAG_BORROWED: u8 = 2;
34
35const TAG_ALLOCATED: u8 = 3;
37
38const INLINE_CAPACITY: usize = size_of::<Borrowed>() - 1;
40
41const WORD_SIZE_M1: usize = size_of::<usize>() - 1;
43
44pub type Inline = inline::Inline<INLINE_CAPACITY>;
46
47#[repr(C)]
100pub struct HipByt<'borrow, B: Backend> {
101 pivot: Pivot,
102 _marker: PhantomData<&'borrow B>,
103}
104
105#[derive(Clone, Copy)]
106#[repr(C)]
107pub(super) struct Pivot {
108 #[cfg(target_endian = "little")]
109 tag_byte: NonZeroU8,
110 #[cfg(target_endian = "little")]
111 _word_remainder: MaybeUninit<[u8; WORD_SIZE_M1]>,
112 #[cfg(target_endian = "little")]
113 _word1: MaybeUninit<*mut ()>,
114
115 _word2: MaybeUninit<*mut ()>,
116
117 #[cfg(target_endian = "big")]
118 _word1: MaybeUninit<*mut ()>,
119 #[cfg(target_endian = "big")]
120 _word_remainder: MaybeUninit<[u8; WORD_SIZE_M1]>,
121 #[cfg(target_endian = "big")]
122 tag_byte: NonZeroU8,
123}
124
125unsafe impl<B: Backend + Sync> Sync for HipByt<'_, B> {}
126unsafe impl<B: Backend + Send> Send for HipByt<'_, B> {}
127
128#[repr(C)]
132pub union Union<'borrow, B: Backend> {
133 pub inline: Inline,
135
136 pub allocated: Allocated<B>,
138
139 pub borrowed: Borrowed<'borrow>,
141
142 pivot: Pivot,
144}
145
146impl<'borrow, B: Backend> Union<'borrow, B> {
147 const ASSERTS: () = {
148 assert!(size_of::<Self>() == size_of::<HipByt<'borrow, B>>());
149 assert!(align_of::<Self>() == align_of::<HipByt<'borrow, B>>());
150 };
151
152 #[inline]
153 pub const fn into_raw(self) -> HipByt<'borrow, B> {
154 let () = Self::ASSERTS;
156
157 let pivot = unsafe { self.pivot };
159 HipByt {
160 pivot,
161 _marker: PhantomData,
162 }
163 }
164}
165
166#[repr(u8)]
170#[derive(Clone, Copy, Debug, PartialEq, Eq)]
171pub enum Tag {
172 Inline = TAG_INLINE,
173 Borrowed = TAG_BORROWED,
174 Allocated = TAG_ALLOCATED,
175}
176
177pub enum Split<'a, 'borrow, B: Backend> {
179 Inline(&'a Inline),
181 Allocated(&'a Allocated<B>),
183 Borrowed(&'a Borrowed<'borrow>),
185}
186
187pub enum SplitMut<'a, 'borrow, B: Backend> {
189 Inline(&'a mut Inline),
191 Allocated(&'a mut Allocated<B>),
193 Borrowed(&'a mut Borrowed<'borrow>),
195}
196
197impl<'borrow, B: Backend> HipByt<'borrow, B> {
198 #[inline]
200 pub(super) const fn union(&self) -> &Union<'borrow, B> {
201 let raw_ptr: *const _ = &self.pivot;
202 let union_ptr: *const Union<'borrow, B> = raw_ptr.cast();
203 unsafe { &*union_ptr }
205 }
206
207 #[inline]
209 pub(super) const fn union_mut(&mut self) -> &mut Union<'borrow, B> {
210 let raw_ptr: *mut _ = &mut self.pivot;
211 let union_ptr: *mut Union<'borrow, B> = raw_ptr.cast();
212 unsafe { &mut *union_ptr }
214 }
215
216 pub(super) fn union_move(self) -> Union<'borrow, B> {
218 let this = ManuallyDrop::new(self);
220 Union { pivot: this.pivot }
221 }
222
223 #[inline]
230 pub(super) const fn from_allocated(allocated: Allocated<B>) -> Self {
231 Union { allocated }.into_raw()
232 }
233
234 #[inline]
236 pub(super) const fn from_inline(inline: Inline) -> Self {
237 Union { inline }.into_raw()
238 }
239
240 #[inline]
242 pub(super) const fn from_borrowed(borrowed: Borrowed<'borrow>) -> Self {
243 Union { borrowed }.into_raw()
244 }
245
246 pub(super) const fn tag(&self) -> Tag {
248 match self.pivot.tag_byte.get() & MASK {
249 TAG_INLINE => Tag::Inline,
250 TAG_BORROWED => Tag::Borrowed,
251 TAG_ALLOCATED => Tag::Allocated,
252 _ => unsafe { unreachable_unchecked() },
254 }
255 }
256
257 #[inline]
259 pub(super) const fn split(&self) -> Split<'_, 'borrow, B> {
260 let tag = self.tag();
261 let union = self.union();
262 match tag {
263 Tag::Inline => {
264 Split::Inline(unsafe { &union.inline })
266 }
267 Tag::Borrowed => {
268 Split::Borrowed(unsafe { &union.borrowed })
270 }
271 Tag::Allocated => {
272 Split::Allocated(unsafe { &union.allocated })
274 }
275 }
276 }
277
278 #[inline]
280 pub(super) const fn split_mut(&mut self) -> SplitMut<'_, 'borrow, B> {
281 let tag = self.tag();
282 let union = self.union_mut();
283 match tag {
284 Tag::Inline => {
285 SplitMut::Inline(unsafe { &mut union.inline })
287 }
288 Tag::Borrowed => {
289 SplitMut::Borrowed(unsafe { &mut union.borrowed })
291 }
292 Tag::Allocated => {
293 SplitMut::Allocated(unsafe { &mut union.allocated })
295 }
296 }
297 }
298
299 pub(super) fn from_vec(vec: Vec<u8>) -> Self {
301 let allocated = Allocated::new(vec);
302 Self::from_allocated(allocated)
303 }
304
305 #[inline]
307 pub(super) const fn inline_empty() -> Self {
308 const { Self::from_inline(Inline::empty()) }
309 }
310
311 pub(super) const unsafe fn inline_unchecked(bytes: &[u8]) -> Self {
317 let inline = unsafe { Inline::new_unchecked(bytes) };
319 Self::from_inline(inline)
320 }
321
322 #[inline]
328 pub(crate) fn normalized_from_vec(vec: Vec<u8>) -> Self {
329 let len = vec.len();
330 if len <= INLINE_CAPACITY {
331 unsafe { Self::inline_unchecked(&vec) }
333 } else {
334 Self::from_vec(vec)
335 }
336 }
337
338 pub(crate) fn from_slice(bytes: &[u8]) -> Self {
342 let len = bytes.len();
343 if len == 0 {
344 Self::inline_empty()
345 } else if len <= INLINE_CAPACITY {
346 unsafe { Self::inline_unchecked(bytes) }
348 } else {
349 Self::from_allocated(Allocated::from_slice(bytes))
350 }
351 }
352
353 #[inline]
361 pub(super) unsafe fn range_unchecked(&self, range: Range<usize>) -> Self {
362 debug_assert!(range.start <= range.end);
363 debug_assert!(range.end <= self.len());
364
365 let result = match self.split() {
366 Split::Inline(inline) => {
367 unsafe { Self::inline_unchecked(&inline.as_slice()[range]) }
370 }
371 Split::Borrowed(borrowed) => Self::borrowed(&borrowed.as_slice()[range]),
372 Split::Allocated(allocated) => {
373 if range.len() <= INLINE_CAPACITY {
375 unsafe { Self::inline_unchecked(&allocated.as_slice()[range]) }
377 } else {
378 unsafe {
380 let allocated = allocated.slice_unchecked(range);
381 Self::from_allocated(allocated)
382 }
383 }
384 }
385 };
386
387 debug_assert!(self.is_normalized());
388 result
389 }
390
391 #[must_use]
401 pub unsafe fn slice_ref_unchecked(&self, slice: &[u8]) -> Self {
402 #[cfg(debug_assertions)]
403 {
404 let range = self.as_slice().as_ptr_range();
405 let slice_range = slice.as_ptr_range();
406 assert!(range.contains(&slice_range.start) || range.end == slice_range.start);
407 assert!(range.contains(&slice_range.end) || range.end == slice_range.end);
408 }
409
410 let result = match self.split() {
411 Split::Inline(_) => {
412 unsafe { Self::inline_unchecked(slice) }
415 }
416 Split::Borrowed(_) => {
417 let sl: &'borrow [u8] = unsafe { transmute(slice) };
420 Self::borrowed(sl)
421 }
422 Split::Allocated(allocated) => {
423 if slice.len() <= INLINE_CAPACITY {
425 unsafe { Self::inline_unchecked(slice) }
427 } else {
428 let range = unsafe { range_of_unchecked(self.as_slice(), slice) };
430 unsafe {
432 let allocated = allocated.slice_unchecked(range);
433 Self::from_allocated(allocated)
434 }
435 }
436 }
437 };
438
439 debug_assert!(self.is_normalized());
440 result
441 }
442
443 #[inline]
447 pub(crate) fn take_vec(&mut self) -> Vec<u8> {
448 if self.is_allocated() {
449 let allocated = unsafe { self.union_mut().allocated };
451 if let Ok(owned) = allocated.try_into_vec() {
452 forget(replace(self, Self::new()));
455 return owned;
456 }
457 }
458 let owned = Vec::from(self.as_slice());
459 *self = Self::new();
460 owned
461 }
462
463 #[inline]
470 pub(super) const fn take_allocated(&mut self) -> Option<Allocated<B>> {
471 match self.split() {
472 Split::Allocated(&allocated) => {
473 forget(replace(self, Self::new()));
478
479 Some(allocated)
480 }
481 _ => None,
482 }
483 }
484
485 #[inline]
487 pub(super) fn make_unique(&mut self) {
488 let tag = self.tag();
489 match tag {
490 Tag::Inline => {}
491 Tag::Borrowed => {
492 let old = replace(self, Self::new()).union_move();
493
494 let borrowed = unsafe { old.borrowed };
496
497 *self = Self::from_slice(borrowed.as_slice());
498 }
499 Tag::Allocated => {
500 if unsafe { self.union().allocated }.is_unique() {
502 return;
503 }
504
505 let old = replace(self, Self::new());
506
507 let allocated = unsafe { old.union_move().allocated };
509
510 let new = Self::from_vec(allocated.as_slice().to_vec());
513
514 allocated.explicit_drop();
516
517 *self = new;
518 }
519 }
520 }
521
522 #[inline(never)]
524 pub(crate) fn inherent_eq<B2: Backend>(&self, other: &HipByt<B2>) -> bool {
525 extern "C" {
527 fn memcmp(a: *const u8, b: *const u8, size: usize) -> core::ffi::c_int;
528 }
529
530 let len = self.len();
531 if len != other.len() {
532 return false;
533 }
534
535 let self_ptr = self.as_ptr();
536 let other_ptr = other.as_ptr();
537 if core::ptr::eq(self_ptr, other_ptr) {
538 return true;
539 }
540
541 let size = len * size_of::<u8>();
543
544 unsafe { memcmp(self_ptr, other_ptr, size) == 0 }
546 }
547}
548
549impl<B: Backend> Drop for HipByt<'_, B> {
550 #[inline]
551 fn drop(&mut self) {
552 if let Some(allocated) = self.take_allocated() {
554 allocated.explicit_drop();
555 }
556 }
557}
558
559impl<B: Backend> Clone for HipByt<'_, B> {
560 fn clone(&self) -> Self {
561 match self.split() {
562 Split::Inline(&inline) => Self::from_inline(inline),
563 Split::Borrowed(&borrowed) => Self::from_borrowed(borrowed),
564 Split::Allocated(allocated) => {
565 let clone = allocated.explicit_clone();
567 Self::from_allocated(clone)
568 }
569 }
570 }
571}
572
573unsafe fn range_of_unchecked(whole: &[u8], slice: &[u8]) -> Range<usize> {
579 unsafe {
580 let offset = slice.as_ptr().offset_from(whole.as_ptr());
581 let offset: usize = offset.try_into().unwrap_unchecked();
582 offset..offset + slice.len()
583 }
584}
585
586pub fn try_range_of(whole: &[u8], slice: &[u8]) -> Option<Range<usize>> {
587 let len = whole.len();
588 let Range { start, end } = whole.as_ptr_range();
589 let slice_len = slice.len();
590 let slice_start = slice.as_ptr();
591
592 if slice_start < start || slice_start > end {
594 return None;
595 }
596
597 let offset = unsafe { slice_start.offset_from(start) };
600 let offset: usize = unsafe { offset.try_into().unwrap_unchecked() };
602 if offset + slice_len > len {
603 None
604 } else {
605 Some(offset..offset + slice_len)
606 }
607}