Skip to content

Commit 488dc72

Browse files
stepanchegfacebook-github-bot
authored andcommitted
StringValue type
Summary: `StringValue` is a wrapper to `Value` which asserts that contained value is `StarlarkStr`. (Similar to `FrozenStringValue` which is logically a wrapper for `FrozenValue` which asserts that contained value is `StarlarkStr`). Used in the following diff D30884359. In theory `StringValue` can be used for faster operations on strings (e. g. obtaining a string from `StringValue` is cheaper than obtaining a string from `Value`), but there's no obvious applications for that at the moment. Reviewed By: ndmitchell Differential Revision: D30884361 fbshipit-source-id: 21b632f2a6a4362eb05dda35b0e2a12162397faf
1 parent 70a8171 commit 488dc72

File tree

5 files changed

+114
-6
lines changed

5 files changed

+114
-6
lines changed

starlark/src/values/alloc_value.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
//! This mod defines utilities to easily create Rust values as Starlark values.
1919
20-
use crate::values::{layout::Value, FrozenHeap, FrozenValue, Heap};
20+
use crate::values::{layout::Value, FrozenHeap, FrozenStringValue, FrozenValue, Heap, StringValue};
2121

2222
/// Trait for things that can be created on a [`Heap`] producing a [`Value`].
2323
///
@@ -69,11 +69,21 @@ impl FrozenHeap {
6969
pub fn alloc<T: AllocFrozenValue>(&self, val: T) -> FrozenValue {
7070
val.alloc_frozen_value(self)
7171
}
72+
73+
#[allow(dead_code)] // TODO: remove in the following diff
74+
pub(crate) fn alloc_string_value(&self, s: &str) -> FrozenStringValue {
75+
unsafe { FrozenStringValue::new_unchecked(self.alloc_str(s)) }
76+
}
7277
}
7378

7479
impl Heap {
7580
/// Allocate a new value on a [`Heap`].
7681
pub fn alloc<'v, T: AllocValue<'v>>(&'v self, x: T) -> Value<'v> {
7782
x.alloc_value(self)
7883
}
84+
85+
#[allow(dead_code)] // TODO: remove in the following diff
86+
pub(crate) fn alloc_string_value<'v>(&'v self, s: &str) -> StringValue<'v> {
87+
unsafe { StringValue::new_unchecked(self.alloc_str(s)) }
88+
}
7989
}

starlark/src/values/layout/arena.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -319,8 +319,8 @@ impl AValueHeader {
319319

320320
pub(crate) fn unpack<'v>(&'v self) -> &'v dyn AValue<'v> {
321321
unsafe {
322-
let self_repr = self as *const AValueHeader as *const AValueRepr<()>;
323-
let res = &*(from_raw_parts(&(*self_repr).payload, self.0));
322+
let self_repr = self.as_repr::<()>();
323+
let res = &*(from_raw_parts(&self_repr.payload, self.0));
324324
mem::transmute::<&'v dyn AValue<'static>, &'v dyn AValue<'v>>(res)
325325
}
326326
}
@@ -362,6 +362,11 @@ impl AValueHeader {
362362
let p = self as *const AValueHeader as *mut u8;
363363
p.add(mem::size_of::<AValueHeader>() + n)
364364
}
365+
366+
/// Cast header pointer to repr pointer.
367+
pub(crate) unsafe fn as_repr<T>(&self) -> &AValueRepr<T> {
368+
&*(self as *const AValueHeader as *const AValueRepr<T>)
369+
}
365370
}
366371

367372
impl<T> AValueRepr<T> {

starlark/src/values/layout/constant.rs

Lines changed: 85 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,17 @@
1818
use crate::values::{
1919
layout::{arena::AValueRepr, avalue::VALUE_STR_A_VALUE_PTR, value::FrozenValue},
2020
string::StarlarkStr,
21+
Freezer, Trace, Tracer, Value,
22+
};
23+
use gazebo::{
24+
coerce::{Coerce, CoerceKey},
25+
prelude::*,
26+
};
27+
use std::{
28+
fmt,
29+
fmt::{Debug, Formatter},
30+
intrinsics::copy_nonoverlapping,
2131
};
22-
use gazebo::prelude::*;
23-
use std::intrinsics::copy_nonoverlapping;
2432

2533
/// A constant string that can be converted to a [`FrozenValue`].
2634
#[repr(C)] // Must match this layout on the heap
@@ -71,13 +79,88 @@ impl<const N: usize> ConstFrozenStringN<N> {
7179
/// assert_eq!(Some("magic"), fv.to_value().unpack_str());
7280
/// ```
7381
#[derive(Copy, Clone, Dupe)]
82+
#[repr(C)]
7483
pub struct FrozenStringValue(&'static AValueRepr<StarlarkStr>);
7584

85+
impl Debug for FrozenStringValue {
86+
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
87+
f.debug_tuple("FrozenStringValue")
88+
.field(&self.unpack())
89+
.finish()
90+
}
91+
}
92+
93+
/// Wrapper for a [`Value`] which can only contain a [`StarlarkStr`].
94+
#[derive(Copy, Clone, Dupe, Debug)]
95+
#[repr(C)]
96+
pub struct StringValue<'v>(Value<'v>);
97+
98+
unsafe impl<'v> Coerce<StringValue<'v>> for FrozenStringValue {}
99+
unsafe impl<'v> CoerceKey<StringValue<'v>> for FrozenStringValue {}
100+
unsafe impl<'v> Coerce<StringValue<'v>> for StringValue<'v> {}
101+
unsafe impl<'v> CoerceKey<StringValue<'v>> for StringValue<'v> {}
102+
76103
impl FrozenStringValue {
77104
/// Obtain the [`FrozenValue`] for a [`FrozenStringValue`].
78105
pub fn unpack(self) -> FrozenValue {
79106
FrozenValue::new_repr(self.0)
80107
}
108+
109+
/// Construct without a check that the value contains a string.
110+
///
111+
/// If passed value does not contain a string, it may lead to memory corruption.
112+
pub(crate) unsafe fn new_unchecked(value: FrozenValue) -> FrozenStringValue {
113+
debug_assert!(value.unpack_str().is_some());
114+
FrozenStringValue(&*(value.0.ptr_value() as *const AValueRepr<StarlarkStr>))
115+
}
116+
}
117+
118+
#[allow(dead_code)] // TODO: remove in the following diff
119+
impl<'v> StringValue<'v> {
120+
/// Construct without a check that the value contains a string.
121+
///
122+
/// If passed value does not contain a string, it may lead to memory corruption.
123+
pub(crate) unsafe fn new_unchecked(value: Value<'v>) -> StringValue<'v> {
124+
debug_assert!(value.unpack_str().is_some());
125+
StringValue(value)
126+
}
127+
128+
/// Construct from a value. Returns [`None`] if a value does not contain a string.
129+
pub(crate) fn new(value: Value<'v>) -> Option<StringValue<'v>> {
130+
if value.unpack_str().is_some() {
131+
Some(StringValue(value))
132+
} else {
133+
None
134+
}
135+
}
136+
137+
pub(crate) fn unpack_starlark_str(self) -> &'v StarlarkStr {
138+
debug_assert!(self.0.unpack_str().is_some());
139+
unsafe { &self.0.0.unpack_ptr_no_int_unchecked().as_repr().payload }
140+
}
141+
142+
pub(crate) fn to_value(self) -> Value<'v> {
143+
self.0
144+
}
145+
146+
/// Convert a value to a [`FrozenValue`] using a supplied [`Freezer`].
147+
pub(crate) fn freeze(self, freezer: &Freezer) -> anyhow::Result<FrozenStringValue> {
148+
Ok(unsafe { FrozenStringValue::new_unchecked(freezer.freeze(self.0)?) })
149+
}
150+
}
151+
152+
/// Common type for [`StringValue`] and [`FrozenStringValue`].
153+
pub(crate) trait StringValueLike<'v>: Debug + Coerce<StringValue<'v>> {}
154+
155+
impl<'v> StringValueLike<'v> for StringValue<'v> {}
156+
157+
impl<'v> StringValueLike<'v> for FrozenStringValue {}
158+
159+
unsafe impl<'v> Trace<'v> for StringValue<'v> {
160+
fn trace(&mut self, tracer: &Tracer<'v>) {
161+
self.0.trace(tracer);
162+
debug_assert!(self.0.unpack_str().is_some());
163+
}
81164
}
82165

83166
/// Create a [`FrozenStringValue`].

starlark/src/values/layout/mod.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ mod pointer_i32;
2727
mod value;
2828
mod value_captured;
2929

30-
pub use constant::{ConstFrozenStringN, FrozenStringValue};
30+
#[allow(unused_imports)] // TODO: remove in the following diff
31+
pub(crate) use constant::StringValueLike;
32+
pub use constant::{ConstFrozenStringN, FrozenStringValue, StringValue};
3133
pub use heap::{Freezer, FrozenHeap, FrozenHeapRef, Heap, Tracer};
3234
pub(crate) use pointer_i32::PointerI32;
3335
pub use value::{FrozenValue, Value};

starlark/src/values/layout/pointer.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,14 @@ impl<'p, P> Pointer<'p, P> {
133133
}
134134
}
135135

136+
/// Unpack pointer when it is known to be not an integer.
137+
#[allow(dead_code)] // TODO: remove in the following diff
138+
pub(crate) unsafe fn unpack_ptr_no_int_unchecked(self) -> &'p P {
139+
let p = self.pointer.get();
140+
debug_assert!(p & TAG_INT == 0);
141+
untag_pointer(p)
142+
}
143+
136144
pub fn ptr_eq(self, other: Pointer<'_, P>) -> bool {
137145
self.pointer == other.pointer
138146
}

0 commit comments

Comments
 (0)