From 769126e2046e2927a5b03083577e0aa2b7e14b04 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Sat, 3 Jan 2026 19:51:39 -0700 Subject: [PATCH 1/3] [buffer] Unsafe vector --- src/hb/buffer.rs | 19 ++-- src/hb/mod.rs | 1 + src/hb/unsafe_vec.rs | 225 +++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 2 +- 4 files changed, 239 insertions(+), 8 deletions(-) create mode 100644 src/hb/unsafe_vec.rs diff --git a/src/hb/buffer.rs b/src/hb/buffer.rs index adc69ac3..af3f54d7 100644 --- a/src/hb/buffer.rs +++ b/src/hb/buffer.rs @@ -1,5 +1,6 @@ +use crate::hb::unsafe_vec::UnsafeVec; use crate::U32Set; -use alloc::{string::String, vec::Vec}; +use alloc::string::String; use core::cmp::min; use core::convert::TryFrom; use read_fonts::types::{GlyphId, GlyphId16}; @@ -420,8 +421,8 @@ pub struct hb_buffer_t { pub len: usize, pub out_len: usize, - pub info: Vec, - pub pos: Vec, + pub info: UnsafeVec, + pub pos: UnsafeVec, // Text before / after the main buffer contents. // Always in Unicode, and ordered outward. @@ -472,8 +473,8 @@ impl hb_buffer_t { idx: 0, len: 0, out_len: 0, - info: Vec::new(), - pos: Vec::new(), + info: UnsafeVec::new(), + pos: UnsafeVec::new(), have_separate_output: false, allocated_var_bits: 0, serial: 0, @@ -770,8 +771,12 @@ impl hb_buffer_t { if self.have_separate_output { // Swap info and pos buffers. - let info: Vec = bytemuck::cast_vec(core::mem::take(&mut self.info)); - let pos: Vec = bytemuck::cast_vec(core::mem::take(&mut self.pos)); + let info: UnsafeVec = UnsafeVec { + inner: bytemuck::cast_vec(core::mem::take(&mut self.info)), + }; + let pos: UnsafeVec = UnsafeVec { + inner: bytemuck::cast_vec(core::mem::take(&mut self.pos)), + }; self.pos = info; self.info = pos; self.have_separate_output = false; diff --git a/src/hb/mod.rs b/src/hb/mod.rs index 4dc1f8c3..ba89c230 100644 --- a/src/hb/mod.rs +++ b/src/hb/mod.rs @@ -71,6 +71,7 @@ mod text_parser; #[rustfmt::skip] mod ucd_table; mod unicode; +mod unsafe_vec; use read_fonts::types::Tag as hb_tag_t; diff --git a/src/hb/unsafe_vec.rs b/src/hb/unsafe_vec.rs new file mode 100644 index 00000000..77fa7d56 --- /dev/null +++ b/src/hb/unsafe_vec.rs @@ -0,0 +1,225 @@ +use std::fmt; +use std::ops::{ + Deref, DerefMut, Index, IndexMut, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, + RangeToInclusive, +}; + +#[repr(transparent)] +pub struct UnsafeVec { + pub inner: Vec, +} + +impl UnsafeVec { + #[inline] + pub fn new() -> Self { + Self { inner: Vec::new() } + } + + #[inline] + pub fn with_capacity(cap: usize) -> Self { + Self { + inner: Vec::with_capacity(cap), + } + } + + #[inline] + pub fn len(&self) -> usize { + self.inner.len() + } + + #[inline] + pub fn is_empty(&self) -> bool { + self.inner.is_empty() + } + + #[inline] + pub fn push(&mut self, value: T) { + self.inner.push(value); + } + + #[inline] + pub fn as_ptr(&self) -> *const T { + self.inner.as_ptr() + } + + #[inline] + pub fn as_mut_ptr(&mut self) -> *mut T { + self.inner.as_mut_ptr() + } + + #[inline] + pub fn into_vec(self) -> Vec { + self.inner + } + + #[inline] + pub fn from_vec(v: Vec) -> Self { + Self { inner: v } + } +} + +impl Default for UnsafeVec { + #[inline] + fn default() -> Self { + Self::new() + } +} + +impl fmt::Debug for UnsafeVec { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.inner.fmt(f) + } +} + +/// Expose full Vec API (resize/truncate/extend/etc.) +impl Deref for UnsafeVec { + type Target = Vec; + #[inline] + fn deref(&self) -> &Vec { + &self.inner + } +} +impl DerefMut for UnsafeVec { + #[inline] + fn deref_mut(&mut self) -> &mut Vec { + &mut self.inner + } +} + +/// Unchecked single-element indexing (UB if out of bounds) +impl Index for UnsafeVec { + type Output = T; + #[inline] + fn index(&self, i: usize) -> &T { + unsafe { self.inner.get_unchecked(i) } + } +} +impl IndexMut for UnsafeVec { + #[inline] + fn index_mut(&mut self, i: usize) -> &mut T { + unsafe { self.inner.get_unchecked_mut(i) } + } +} + +/// Unchecked range indexing (UB if invalid range) +impl Index> for UnsafeVec { + type Output = [T]; + #[inline] + fn index(&self, r: Range) -> &[T] { + unsafe { std::slice::from_raw_parts(self.inner.as_ptr().add(r.start), r.end - r.start) } + } +} +impl IndexMut> for UnsafeVec { + #[inline] + fn index_mut(&mut self, r: Range) -> &mut [T] { + unsafe { + std::slice::from_raw_parts_mut(self.inner.as_mut_ptr().add(r.start), r.end - r.start) + } + } +} + +impl Index> for UnsafeVec { + type Output = [T]; + #[inline] + fn index(&self, r: RangeFrom) -> &[T] { + let len = self.inner.len(); + unsafe { std::slice::from_raw_parts(self.inner.as_ptr().add(r.start), len - r.start) } + } +} +impl IndexMut> for UnsafeVec { + #[inline] + fn index_mut(&mut self, r: RangeFrom) -> &mut [T] { + let len = self.inner.len(); + unsafe { + std::slice::from_raw_parts_mut(self.inner.as_mut_ptr().add(r.start), len - r.start) + } + } +} + +impl Index> for UnsafeVec { + type Output = [T]; + #[inline] + fn index(&self, r: RangeTo) -> &[T] { + unsafe { std::slice::from_raw_parts(self.inner.as_ptr(), r.end) } + } +} +impl IndexMut> for UnsafeVec { + #[inline] + fn index_mut(&mut self, r: RangeTo) -> &mut [T] { + unsafe { std::slice::from_raw_parts_mut(self.inner.as_mut_ptr(), r.end) } + } +} + +impl Index for UnsafeVec { + type Output = [T]; + #[inline] + fn index(&self, _: RangeFull) -> &[T] { + &self.inner + } +} +impl IndexMut for UnsafeVec { + #[inline] + fn index_mut(&mut self, _: RangeFull) -> &mut [T] { + &mut self.inner + } +} + +impl Index> for UnsafeVec { + type Output = [T]; + #[inline] + fn index(&self, r: RangeInclusive) -> &[T] { + let start = *r.start(); + let end_incl = *r.end(); + unsafe { std::slice::from_raw_parts(self.inner.as_ptr().add(start), end_incl - start + 1) } + } +} +impl IndexMut> for UnsafeVec { + #[inline] + fn index_mut(&mut self, r: RangeInclusive) -> &mut [T] { + let start = *r.start(); + let end_incl = *r.end(); + unsafe { + std::slice::from_raw_parts_mut(self.inner.as_mut_ptr().add(start), end_incl - start + 1) + } + } +} + +impl Index> for UnsafeVec { + type Output = [T]; + #[inline] + fn index(&self, r: RangeToInclusive) -> &[T] { + unsafe { std::slice::from_raw_parts(self.inner.as_ptr(), r.end + 1) } + } +} +impl IndexMut> for UnsafeVec { + #[inline] + fn index_mut(&mut self, r: RangeToInclusive) -> &mut [T] { + unsafe { std::slice::from_raw_parts_mut(self.inner.as_mut_ptr(), r.end + 1) } + } +} + +/// Iteration support: `for x in &v`, `for x in &mut v`, `for x in v` +impl<'a, T> IntoIterator for &'a UnsafeVec { + type Item = &'a T; + type IntoIter = std::slice::Iter<'a, T>; + #[inline] + fn into_iter(self) -> Self::IntoIter { + self.inner.iter() + } +} +impl<'a, T> IntoIterator for &'a mut UnsafeVec { + type Item = &'a mut T; + type IntoIter = std::slice::IterMut<'a, T>; + #[inline] + fn into_iter(self) -> Self::IntoIter { + self.inner.iter_mut() + } +} +impl IntoIterator for UnsafeVec { + type Item = T; + type IntoIter = std::vec::IntoIter; + #[inline] + fn into_iter(self) -> Self::IntoIter { + self.inner.into_iter() + } +} diff --git a/src/lib.rs b/src/lib.rs index f45f702e..2f685f69 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,7 +5,7 @@ A complete [harfbuzz](https://github.com/harfbuzz/harfbuzz) shaping algorithm po #![cfg_attr(not(feature = "std"), no_std)] // Forbidding unsafe code only applies to the lib // examples continue to use it, so this cannot be placed into Cargo.toml -#![forbid(unsafe_code)] +//#![forbid(unsafe_code)] #![warn(missing_docs)] extern crate alloc; From fb91e2296b024e3ee0631dd8d3e5bc5b18456878 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Mon, 12 Jan 2026 17:38:27 -0700 Subject: [PATCH 2/3] [unsafe_vec] Make range accesses safe again --- src/hb/unsafe_vec.rs | 34 +++++++++++----------------------- 1 file changed, 11 insertions(+), 23 deletions(-) diff --git a/src/hb/unsafe_vec.rs b/src/hb/unsafe_vec.rs index 77fa7d56..fa01b666 100644 --- a/src/hb/unsafe_vec.rs +++ b/src/hb/unsafe_vec.rs @@ -101,20 +101,18 @@ impl IndexMut for UnsafeVec { } } -/// Unchecked range indexing (UB if invalid range) +/// Safe range indexing impl Index> for UnsafeVec { type Output = [T]; #[inline] fn index(&self, r: Range) -> &[T] { - unsafe { std::slice::from_raw_parts(self.inner.as_ptr().add(r.start), r.end - r.start) } + &self.inner[r] } } impl IndexMut> for UnsafeVec { #[inline] fn index_mut(&mut self, r: Range) -> &mut [T] { - unsafe { - std::slice::from_raw_parts_mut(self.inner.as_mut_ptr().add(r.start), r.end - r.start) - } + &mut self.inner[r] } } @@ -122,17 +120,13 @@ impl Index> for UnsafeVec { type Output = [T]; #[inline] fn index(&self, r: RangeFrom) -> &[T] { - let len = self.inner.len(); - unsafe { std::slice::from_raw_parts(self.inner.as_ptr().add(r.start), len - r.start) } + &self.inner[r] } } impl IndexMut> for UnsafeVec { #[inline] fn index_mut(&mut self, r: RangeFrom) -> &mut [T] { - let len = self.inner.len(); - unsafe { - std::slice::from_raw_parts_mut(self.inner.as_mut_ptr().add(r.start), len - r.start) - } + &mut self.inner[r] } } @@ -140,13 +134,13 @@ impl Index> for UnsafeVec { type Output = [T]; #[inline] fn index(&self, r: RangeTo) -> &[T] { - unsafe { std::slice::from_raw_parts(self.inner.as_ptr(), r.end) } + &self.inner[r] } } impl IndexMut> for UnsafeVec { #[inline] fn index_mut(&mut self, r: RangeTo) -> &mut [T] { - unsafe { std::slice::from_raw_parts_mut(self.inner.as_mut_ptr(), r.end) } + &mut self.inner[r] } } @@ -168,19 +162,13 @@ impl Index> for UnsafeVec { type Output = [T]; #[inline] fn index(&self, r: RangeInclusive) -> &[T] { - let start = *r.start(); - let end_incl = *r.end(); - unsafe { std::slice::from_raw_parts(self.inner.as_ptr().add(start), end_incl - start + 1) } + &self.inner[r] } } impl IndexMut> for UnsafeVec { #[inline] fn index_mut(&mut self, r: RangeInclusive) -> &mut [T] { - let start = *r.start(); - let end_incl = *r.end(); - unsafe { - std::slice::from_raw_parts_mut(self.inner.as_mut_ptr().add(start), end_incl - start + 1) - } + &mut self.inner[r] } } @@ -188,13 +176,13 @@ impl Index> for UnsafeVec { type Output = [T]; #[inline] fn index(&self, r: RangeToInclusive) -> &[T] { - unsafe { std::slice::from_raw_parts(self.inner.as_ptr(), r.end + 1) } + &self.inner[r] } } impl IndexMut> for UnsafeVec { #[inline] fn index_mut(&mut self, r: RangeToInclusive) -> &mut [T] { - unsafe { std::slice::from_raw_parts_mut(self.inner.as_mut_ptr(), r.end + 1) } + &mut self.inner[r] } } From bab6bddff0960c5bd2c766f8b9b5b5297c160b77 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Mon, 12 Jan 2026 17:57:30 -0700 Subject: [PATCH 3/3] [unsafe_vec] Remove into_vec / from_vec Seems unused. --- src/hb/unsafe_vec.rs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/hb/unsafe_vec.rs b/src/hb/unsafe_vec.rs index fa01b666..59357a0b 100644 --- a/src/hb/unsafe_vec.rs +++ b/src/hb/unsafe_vec.rs @@ -46,16 +46,6 @@ impl UnsafeVec { pub fn as_mut_ptr(&mut self) -> *mut T { self.inner.as_mut_ptr() } - - #[inline] - pub fn into_vec(self) -> Vec { - self.inner - } - - #[inline] - pub fn from_vec(v: Vec) -> Self { - Self { inner: v } - } } impl Default for UnsafeVec {