git.delta.rocks / jrsonnet / refs/commits / 45d6bc3e2a43

difftreelog

fix(interner) data should be in UnsafeCell

Yaroslav Bolyukin2023-01-23parent: #153383c.patch.diff
in: master

2 files changed

modifiedcrates/jrsonnet-interner/src/inner.rsdiffbeforeafterboth
1use std::{1use std::{
2 alloc::{self, Layout},2 alloc::{self, Layout},
3 borrow::Borrow,3 borrow::Borrow,
4 cell::UnsafeCell,
4 cmp,5 cmp,
5 hash::{Hash, Hasher},6 hash::{Hash, Hasher},
6 mem,7 mem,
45}46}
4647
47/// Similar to Rc<[u8]>, but stores all data (refcnt, size) inline, instead of being DST48/// Similar to Rc<[u8]>, but stores all data (refcnt, size) inline, instead of being DST
48pub struct Inner(NonNull<u8>);49pub struct Inner(UnsafeCell<NonNull<InnerHeader>>);
49impl Inner {50impl Inner {
50 /// # Safety51 /// # Safety
51 /// `is_utf8` should only be set if data is really checked to be utf852 /// `is_utf8` should only be set if data is really checked to be utf8
59 // - data is written right after allocation60 // - data is written right after allocation
60 // - new allocation can't overlap with passed slice61 // - new allocation can't overlap with passed slice
61 unsafe {62 unsafe {
62 let data = alloc::alloc(Layout::from_size_align_unchecked(63 let data: *mut InnerHeader = alloc::alloc(Layout::from_size_align_unchecked(
63 mem::size_of::<InnerHeader>() + bytes.len(),64 mem::size_of::<InnerHeader>() + bytes.len(),
64 mem::align_of::<InnerHeader>(),65 mem::align_of::<InnerHeader>(),
65 ));66 ))
67 .cast();
66 assert!(!data.is_null());68 assert!(!data.is_null());
67 *data.cast::<InnerHeader>() =69 *data = InnerHeader::new(bytes.len().try_into().expect("bytes > 4GB"), is_utf8);
68 InnerHeader::new(bytes.len().try_into().expect("bytes > 4GB"), is_utf8);
69 ptr::copy_nonoverlapping(70 ptr::copy_nonoverlapping(bytes.as_ptr(), data.offset(1).cast::<u8>(), bytes.len());
70 bytes.as_ptr(),
71 data.add(mem::size_of::<InnerHeader>()),
72 bytes.len(),
73 );
74 Self(NonNull::new_unchecked(data))71 Self(UnsafeCell::new(NonNull::new_unchecked(data)))
75 }72 }
76 }73 }
77 pub fn new_bytes(bytes: &[u8]) -> Self {74 pub fn new_bytes(bytes: &[u8]) -> Self {
93 // SAFETY: bytes after data is allocated to be exactly data.size in length90 // SAFETY: bytes after data is allocated to be exactly data.size in length
94 unsafe {91 unsafe {
95 slice::from_raw_parts(92 slice::from_raw_parts(
96 self.0.as_ptr().add(mem::size_of::<InnerHeader>()),93 (*self.0.get()).as_ptr().offset(1).cast::<u8>(),
97 size as usize,94 size as usize,
98 )95 )
99 }96 }
135 unsafe { (*header).set_is_utf8() }132 unsafe { (*header).set_is_utf8() }
136 }133 }
137134
138 const fn header(this: &Self) -> *const InnerHeader {135 fn header(this: &Self) -> *const InnerHeader {
139 // in `new`, we allocate with correct alignment136 // Safety: in `new`, we allocate with correct alignment
140 #![allow(clippy::cast_ptr_alignment)]
141 this.0.as_ptr() as *const InnerHeader137 unsafe { (*this.0.get()).as_ptr() }
142 }138 }
143 const fn header_mut(this: &Self) -> *mut InnerHeader {139 fn header_mut(this: &Self) -> *mut InnerHeader {
144 // in `new`, we allocate with correct alignment140 // Safety: in `new`, we allocate with correct alignment
145 #![allow(clippy::cast_ptr_alignment)]
146 this.0.as_ptr().cast::<InnerHeader>()141 unsafe { (*this.0.get()).as_mut() }
147 }142 }
148143
149 fn clone(this: &Self) -> Self {144 fn clone(this: &Self) -> Self {
150 let header = Self::header_mut(this);145 let header = Self::header_mut(this);
151 // SAFETY: header is initialized146 // SAFETY: header is initialized
152 unsafe {147 unsafe {
153 let refcnt = (*header).refcnt() + 1;148 let refcnt = (*header).refcnt() + 1;
154 (*header).set_refcnt(refcnt);149 (*header).set_refcnt(refcnt);
155 }
156 Self(this.0)150 Self(UnsafeCell::new(*this.0.get()))
151 }
157 }152 }
158153
159 pub fn ptr_eq(a: &Self, b: &Self) -> bool {154 pub fn ptr_eq(a: &Self, b: &Self) -> bool {
160 a.0 == b.0155 Self::as_ptr(a) == Self::as_ptr(b)
161 }156 }
162 pub const fn as_ptr(this: &Self) -> *const u8 {157 pub fn as_ptr(this: &Self) -> *const u8 {
163 // SAFETY: data is initialized158 // SAFETY: data is initialized
164 unsafe { this.0.as_ptr().add(mem::size_of::<InnerHeader>()) }159 unsafe { (*this.0.get()).as_ptr().offset(1).cast() }
165 }160 }
166161
167 pub const fn strong_count(this: &Self) -> u32 {162 pub fn strong_count(this: &Self) -> u32 {
168 let header = Self::header(this);163 let header = Self::header(this);
169 // SAFETY: header is initialized164 // SAFETY: header is initialized
170 unsafe { (*header).refcnt() }165 unsafe { (*header).refcnt() }
183 #[inline(never)]178 #[inline(never)]
184 fn dealloc(val: &Inner) {179 fn dealloc(val: &Inner) {
185 let header = Inner::header_mut(val);180 let header = Inner::header_mut(val);
186 // SAFETY: size is correct, layout is valid181 // Safety: Data is valid yet
182 let size = unsafe { (*header).size as usize };
183 // SAFETY: size is correct, layout is valid, data will not be used after this, as refcn == 0
187 unsafe {184 unsafe {
188 alloc::dealloc(185 alloc::dealloc(
189 val.0.as_ptr(),186 header.cast(),
190 Layout::from_size_align_unchecked(187 Layout::from_size_align_unchecked(
191 mem::size_of::<InnerHeader>() + (*header).size as usize,188 mem::size_of::<InnerHeader>() + size,
192 mem::align_of::<InnerHeader>(),189 mem::align_of::<InnerHeader>(),
193 ),190 ),
194 );191 );
209206
210impl PartialEq for Inner {207impl PartialEq for Inner {
211 fn eq(&self, other: &Self) -> bool {208 fn eq(&self, other: &Self) -> bool {
212 self.0 == other.0 || self.as_slice().eq(other.as_slice())209 Self::as_ptr(self) == Self::as_ptr(other) || self.as_slice().eq(other.as_slice())
213 }210 }
214}211}
215impl Hash for Inner {212impl Hash for Inner {
modifiedcrates/jrsonnet-interner/src/lib.rsdiffbeforeafterboth
174 }174 }
175 // First reference - current object, second - POOL175 // First reference - current object, second - POOL
176 if Inner::strong_count(&self.0) <= 2 {176 if Inner::strong_count(&self.0) <= 2 {
177 eprintln!("unpool");
177 unpool(&self.0);178 unpool(&self.0);
178 }179 }
179 }180 }
275 unsafe { intern_bytes(str.as_bytes()).cast_str_unchecked() }276 unsafe { intern_bytes(str.as_bytes()).cast_str_unchecked() }
276}277}
278
279#[cfg(test)]
280mod tests {
281 use crate::IStr;
282
283 #[test]
284 fn simple() {
285 let a = IStr::from("a");
286 let b = IStr::from("a");
287
288 assert_eq!(a.as_ptr(), b.as_ptr());
289 }
290}
277291