diff --git a/Cargo.toml b/Cargo.toml index cf10eef4..e46b89f8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,8 +14,8 @@ exclude = ["benches/", "tests/"] bitflags = "2.9" bytemuck = { version = "1.22", features = ["extern_crate_alloc"] } core_maths = "0.1" # only for no_std builds -read-fonts = { version = "0.36.0", default-features = false, features = ["libm"] } -# read-fonts = { git = "https://github.com/googlefonts/fontations", default-features = false, features = ["libm"] } +# read-fonts = { version = "0.36.0", default-features = false, features = ["libm"] } +read-fonts = { git = "https://github.com/googlefonts/fontations", branch = "unchecked-read", default-features = false, features = ["libm"] } smallvec = "1.14" [features] 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/ot/gpos/pair.rs b/src/hb/ot/gpos/pair.rs index 4f5ad18c..c25855af 100644 --- a/src/hb/ot/gpos/pair.rs +++ b/src/hb/ot/gpos/pair.rs @@ -234,7 +234,7 @@ impl Apply for PairPosFormat2<'_> { // Compute an offset into the 2D array of positioning records let record_offset = (class1 as usize * record_size * self.class2_count() as usize) + (class2 as usize * record_size) - + self.shape().class1_records_byte_range().start; + + self.class1_records_byte_range().start; let has_record2 = !format2.is_empty(); let worked1 = !format1.is_empty() && super::apply_value(ctx, ctx.buffer.idx, &data, record_offset, format1) == Some(true); diff --git a/src/hb/ot/gpos/single.rs b/src/hb/ot/gpos/single.rs index a5b3ffb3..de383fdb 100644 --- a/src/hb/ot/gpos/single.rs +++ b/src/hb/ot/gpos/single.rs @@ -7,7 +7,7 @@ impl Apply for SinglePosFormat1<'_> { let glyph = ctx.buffer.cur(0).as_glyph(); self.coverage().ok()?.get(glyph)?; let format = self.value_format(); - let offset = self.shape().value_record_byte_range().start; + let offset = self.value_record_byte_range().start; super::apply_value(ctx, ctx.buffer.idx, &self.offset_data(), offset, format); ctx.buffer.idx += 1; Some(()) @@ -19,8 +19,7 @@ impl Apply for SinglePosFormat2<'_> { let glyph = ctx.buffer.cur(0).as_glyph(); let index = self.coverage().ok()?.get(glyph)? as usize; let format = self.value_format(); - let offset = - self.shape().value_records_byte_range().start + (format.record_byte_len() * index); + let offset = self.value_records_byte_range().start + (format.record_byte_len() * index); super::apply_value(ctx, ctx.buffer.idx, &self.offset_data(), offset, format); ctx.buffer.idx += 1; Some(()) 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;