Skip to content

Commit cf24b20

Browse files
committed
Add GcString as a garbage collected 'str'
1 parent c249e2b commit cf24b20

File tree

4 files changed

+125
-4
lines changed

4 files changed

+125
-4
lines changed

src/array.rs

Lines changed: 115 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,85 @@
11
//! Defines the interface to garbage collected arrays.
22
use core::ops::Deref;
33
use core::ptr::NonNull;
4+
use core::cmp::Ordering;
5+
use core::str;
6+
use core::fmt::{self, Formatter, Debug, Display};
7+
use core::hash::{Hash, Hasher};
48

59
use crate::{CollectorId, GcSafe, GcRebrand};
6-
use zerogc_derive::unsafe_gc_impl;
10+
use zerogc_derive::{Trace, unsafe_gc_impl};
711

812
use self::repr::GcArrayRepr;
913

1014
pub mod repr;
1115

16+
/// A garbage collected string.
17+
///
18+
/// This is a transparent wrapper around `GcArray<u8>`,
19+
/// with the additional invariant that it's utf8 encoded.
20+
///
21+
/// ## Safety
22+
/// The bytes can be assumed to be UTF8 encoded,
23+
/// just like with a `str`.
24+
///
25+
/// Assuming the bytes are utf8 encoded,
26+
/// this can be transmuted back and forth from `GcArray<u8, Id>`
27+
#[repr(transparent)]
28+
#[derive(Trace, Eq, PartialEq, Hash, Clone, Copy)]
29+
#[zerogc(copy, collector_ids(Id))]
30+
pub struct GcString<'gc, Id: CollectorId> {
31+
bytes: GcArray<'gc, u8, Id>
32+
}
33+
impl<'gc, Id: CollectorId> GcString<'gc, Id> {
34+
/// Convert an array of UTF8 bytes into a string.
35+
///
36+
/// Returns an error if the bytes aren't valid UTF8,
37+
/// just like [core::str::from_utf8].
38+
#[inline]
39+
pub fn from_utf8(bytes: GcArray<'gc, u8, Id>) -> Result<Self, core::str::Utf8Error> {
40+
core::str::from_utf8(bytes.as_slice())?;
41+
// SAFETY: Validated with from_utf8 call
42+
Ok(unsafe { Self::from_utf8_unchecked(bytes) })
43+
}
44+
/// Convert an array of UTF8 bytes into a string,
45+
/// without checking for validity.
46+
///
47+
/// ## Safety
48+
/// Undefined behavior if the bytes aren't valid
49+
/// UTF8, just like with [core::str::from_utf8_unchecked]
50+
#[inline]
51+
pub unsafe fn from_utf8_unchecked(bytes: GcArray<'gc, u8, Id>) -> Self {
52+
GcString { bytes }
53+
}
54+
/// Retrieve this string as a raw array of bytes
55+
#[inline]
56+
pub fn as_bytes(&self) -> GcArray<'gc, u8, Id> {
57+
self.bytes
58+
}
59+
/// Convert this string into a slice of bytes
60+
#[inline]
61+
pub fn as_str(&self) -> &'gc str {
62+
unsafe { str::from_utf8_unchecked(self.as_bytes().as_slice()) }
63+
}
64+
}
65+
impl<'gc, Id: CollectorId> Deref for GcString<'gc, Id> {
66+
type Target = str;
67+
#[inline]
68+
fn deref(&self) -> &'_ str {
69+
self.as_str()
70+
}
71+
}
72+
impl<'gc, Id: CollectorId> Debug for GcString<'gc, Id> {
73+
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
74+
Debug::fmt(self.as_str(), f)
75+
}
76+
}
77+
impl<'gc, Id: CollectorId> Display for GcString<'gc, Id> {
78+
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
79+
Display::fmt(self.as_str(), f)
80+
}
81+
}
82+
1283
/// A garbage collected array.
1384
///
1485
/// The length is immutable and cannot change
@@ -65,7 +136,7 @@ impl<'gc, T, Id: CollectorId> GcArray<'gc, T, Id> {
65136
&self.repr
66137
}
67138
}
68-
impl<'gc, T: GcSafe<'gc, Id>, Id: CollectorId> Deref for GcArray<'gc, T, Id> {
139+
impl<'gc, T, Id: CollectorId> Deref for GcArray<'gc, T, Id> {
69140
type Target = [T];
70141

71142
#[inline]
@@ -80,6 +151,48 @@ impl<'gc, T, Id: CollectorId> Clone for GcArray<'gc, T, Id> {
80151
*self
81152
}
82153
}
154+
impl<'gc, T: Debug, Id: CollectorId> Debug for GcArray<'gc, T, Id> {
155+
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
156+
f.debug_list().entries(self.iter()).finish()
157+
}
158+
}
159+
impl<'gc, T: PartialEq, Id: CollectorId> PartialEq for GcArray<'gc, T, Id> {
160+
#[inline]
161+
fn eq(&self, other: &Self) -> bool {
162+
self.as_slice() == other.as_slice()
163+
}
164+
}
165+
impl<'gc, T: PartialEq, Id: CollectorId> PartialEq<[T]> for GcArray<'gc, T, Id> {
166+
#[inline]
167+
fn eq(&self, other: &[T]) -> bool {
168+
self.as_slice() == other
169+
}
170+
}
171+
impl<'gc, T: PartialOrd, Id: CollectorId> PartialOrd for GcArray<'gc, T, Id> {
172+
#[inline]
173+
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
174+
self.as_slice().partial_cmp(other.as_slice())
175+
}
176+
}
177+
impl<'gc, T: PartialOrd, Id: CollectorId> PartialOrd<[T]> for GcArray<'gc, T, Id> {
178+
#[inline]
179+
fn partial_cmp(&self, other: &[T]) -> Option<Ordering> {
180+
self.as_slice().partial_cmp(other)
181+
}
182+
}
183+
impl<'gc, T: Ord, Id: CollectorId> Ord for GcArray<'gc, T, Id> {
184+
#[inline]
185+
fn cmp(&self, other: &Self) -> Ordering {
186+
self.as_slice().cmp(other)
187+
}
188+
}
189+
impl<'gc, T: Eq, Id: CollectorId> Eq for GcArray<'gc, T, Id> {}
190+
impl<'gc, T: Hash, Id: CollectorId> Hash for GcArray<'gc, T, Id> {
191+
#[inline]
192+
fn hash<H: Hasher>(&self, hasher: &mut H) {
193+
T::hash_slice(self.as_slice(), hasher)
194+
}
195+
}
83196
// Need to implement by hand, because [T] is not GcRebrand
84197
unsafe_gc_impl!(
85198
target => GcArray<'gc, T, Id>,

src/epsilon.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use std::rc::Rc;
1818
use std::cell::Cell;
1919
use std::lazy::OnceCell;
2020

21-
use self::{alloc::{EpsilonAlloc}, layout::TypeInfo};
21+
use self::alloc::{EpsilonAlloc};
2222

2323
/// Coerce a reference into a [Gc] pointer.
2424
///

src/lib.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,14 @@ pub unsafe trait GcSimpleAlloc: GcContext {
474474
Gc::from_raw(NonNull::new_unchecked(ptr))
475475
}
476476
}
477+
/// Allocate a [GcString], copied from the specified source
478+
#[inline]
479+
fn alloc_str<'gc>(&'gc self, src: &str) -> array::GcString<'gc, Self::Id> {
480+
let bytes = self.alloc_slice_copy(src.as_bytes());
481+
// SAFETY: Guaranteed by the original `src`
482+
unsafe { array::GcString::from_utf8_unchecked(bytes) }
483+
}
484+
477485
/// Allocate a slice with the specified length,
478486
/// whose memory is uninitialized
479487
///

src/vec/repr.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ pub struct Unsupported<'gc, Id: CollectorId> {
7878
pub marker: PhantomData<(Id, &'gc ())>,
7979
/// indicates this type should never exist at runtime
8080
// TODO: Replace with `!` once stabilized
81-
pub never: std::convert::Infallible
81+
pub never: core::convert::Infallible
8282
}
8383
unsafe_gc_impl! {
8484
target => Unsupported<'gc, Id>,

0 commit comments

Comments
 (0)