Skip to content

Commit

Permalink
feat(vm): Add a from_ut8 method for converting Array Byte into `Str…
Browse files Browse the repository at this point in the history
…ing`

The bulk of these changes are to replace the use of `GcPtr<Str>` with a type backed by the normal array type. This makes it possible to convert `Array Byte` directly into `String` without copying any data.
  • Loading branch information
Marwes committed Jan 7, 2017
1 parent ba7f316 commit a336a49
Show file tree
Hide file tree
Showing 10 changed files with 205 additions and 117 deletions.
1 change: 1 addition & 0 deletions std/string.glu
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,6 @@ let show : Show String = {
slice = string_prim.slice,
starts_with = string_prim.starts_with,
ends_with = string_prim.ends_with,
from_utf8 = string_prim.from_utf8,
monoid
}
12 changes: 11 additions & 1 deletion tests/pass/string.glu
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ let string = import "std/string.glu"

let assert_oieq = assert_eq (prelude.show_Option prelude.show_Int) (prelude.eq_Option prelude.eq_Int)
let assert_beq = assert_eq prelude.show_Bool prelude.eq_Bool
let assert_req =
assert_eq (prelude.show_Result prelude.show_Unit string.show)
(prelude.eq_Result prelude.eq_Unit string.eq)

let slice_tests =
assert_seq (string.slice "ab" 0 1) "a"
Expand Down Expand Up @@ -46,7 +49,14 @@ let trim_tests =
*> assert_seq (string.trim_left " ab ") "ab "
*> assert_seq (string.trim_right " ab ") " ab"

let from_utf8_tests =
assert_req (string.from_utf8 []) (Ok "") *>
assert_req (string.from_utf8 [32b]) (Ok " ") *>
assert_req (string.from_utf8 [195b, 165b, 195b, 164b, 195b, 182b]) (Ok "åäö") *>
assert_req (string.from_utf8 [195b, 165b, 195b, 164b, 195b]) (Err ()) *>
assert_req (string.from_utf8 [195b, 165b, 195b, 195b, 182b]) (Err ())

let tests =
slice_tests *> append_tests *> find_tests
slice_tests *> append_tests *> find_tests *> from_utf8_tests

run tests
51 changes: 48 additions & 3 deletions vm/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use gc::{DataDef, Gc, Traverseable, Move};
use base::symbol::Symbol;
use stack::{State, StackFrame};
use vm::{self, Thread, Status, RootStr, RootedValue, Root};
use value::{ArrayRepr, Cloner, DataStruct, ExternFunction, Value, ValueArray, Def};
use value::{ArrayRepr, Cloner, DataStruct, ExternFunction, GcStr, Value, ValueArray, Def};
use thread::{self, Context, RootedThread};
use thread::ThreadInternal;
use base::types::{self, ArcType, Type};
Expand Down Expand Up @@ -318,7 +318,9 @@ pub trait Pushable<'vm> {
match self.push(vm, context) {
Ok(()) => Status::Ok,
Err(err) => {
let msg = context.alloc_ignore_limit(&format!("{}", err)[..]);
let msg = unsafe {
GcStr::from_utf8_unchecked(context.alloc_ignore_limit(format!("{}", err).as_bytes()))
};
context.stack.push(Value::String(msg));
Status::Error
}
Expand Down Expand Up @@ -612,7 +614,7 @@ impl<'vm, 's> Pushable<'vm> for &'s String {
}
impl<'vm, 's> Pushable<'vm> for &'s str {
fn push(self, thread: &'vm Thread, context: &mut Context) -> Result<()> {
let s = context.alloc_with(thread, self)?;
let s = unsafe { GcStr::from_utf8_unchecked(context.alloc_with(thread, self.as_bytes())?) };
context.stack.push(Value::String(s));
Ok(())
}
Expand Down Expand Up @@ -703,6 +705,10 @@ impl<T> VmType for Vec<T>
T::Type: Sized,
{
type Type = Vec<T::Type>;

fn make_type(thread: &Thread) -> ArcType {
Array::<T>::make_type(thread)
}
}

impl<'vm, T> Pushable<'vm> for Vec<T>
Expand Down Expand Up @@ -846,6 +852,15 @@ pub enum RuntimeResult<T, E> {
Panic(E),
}

impl<T, E> From<StdResult<T, E>> for RuntimeResult<T, E> {
fn from(result: StdResult<T, E>) -> RuntimeResult<T, E> {
match result {
Ok(ok) => RuntimeResult::Return(ok),
Err(err) => RuntimeResult::Panic(err),
}
}
}

impl<T: VmType, E> VmType for RuntimeResult<T, E> {
type Type = T::Type;
fn make_type(vm: &Thread) -> ArcType {
Expand Down Expand Up @@ -1045,6 +1060,36 @@ impl<'vm> Getable<'vm> for RootStr<'vm> {
}
}

/// NewType which can be used to push types implementating `AsRef`
pub struct PushAsRef<T, R>(T, PhantomData<R>);

impl<T, R> PushAsRef<T, R> {
pub fn new(value: T) -> PushAsRef<T, R> {
PushAsRef(value, PhantomData)
}
}

impl<T, R> VmType for PushAsRef<T, R>
where T: AsRef<R>,
R: 'static,
&'static R: VmType,
{
type Type = <&'static R as VmType>::Type;

fn make_type(thread: &Thread) -> ArcType {
<&'static R as VmType>::make_type(thread)
}
}

impl<'vm, T, R> Pushable<'vm> for PushAsRef<T, R>
where T: AsRef<R>,
for<'a> &'a R: Pushable<'vm>,
{
fn push(self, thread: &'vm Thread, context: &mut Context) -> Result<()> {
self.0.as_ref().push(thread, context)
}
}

macro_rules! define_tuple {
($($id: ident)+) => {
impl<$($id),+> VmType for ($($id),+)
Expand Down
59 changes: 1 addition & 58 deletions vm/src/array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::mem;
use std::ops::{Deref, DerefMut};
use std::slice;

use gc::{DataDef, Gc, Traverseable, WriteOnly};
use gc::{Gc, Traverseable};

enum Void {}

Expand Down Expand Up @@ -121,60 +121,3 @@ impl<'a, T: Copy + 'a> IntoIterator for &'a mut Array<T> {
(&mut **self).into_iter()
}
}

#[derive(Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Str(Array<u8>);

impl Str {
pub fn size_of(len: usize) -> usize {
Array::<u8>::size_of(len)
}
pub fn as_mut_array(s: &mut Str) -> &mut Array<u8> {
&mut s.0
}
}

impl fmt::Debug for Str {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", &**self)
}
}

impl fmt::Display for Str {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", &**self)
}
}

impl Traverseable for Str {
fn traverse(&self, _: &mut Gc) {}
}

impl Deref for Str {
type Target = str;
fn deref(&self) -> &str {
unsafe { ::std::str::from_utf8_unchecked(&self.0) }
}
}

impl DerefMut for Str {
fn deref_mut(&mut self) -> &mut str {
unsafe { mem::transmute::<&mut [u8], &mut str>(&mut self.0) }
}
}

unsafe impl<'a> DataDef for &'a str {
type Value = Str;
fn size(&self) -> usize {
use std::mem::size_of;
size_of::<usize>() + self.len()
}
fn initialize<'w>(self, mut result: WriteOnly<'w, Str>) -> &'w mut Str {
unsafe {
let result = &mut *result.as_mut_ptr();
result.0.len = self.len();
result.0.clone_from_slice(self.as_bytes());
result
}
}
}
6 changes: 4 additions & 2 deletions vm/src/channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use api::generic::A;
use gc::{Traverseable, Gc, GcPtr};
use vm::{Thread, RootedThread, Status};
use thread::ThreadInternal;
use value::{Userdata, Value};
use value::{GcStr, Userdata, Value};
use stack::{State, StackFrame};

pub struct Sender<T> {
Expand Down Expand Up @@ -143,7 +143,9 @@ extern "C" fn resume(vm: &Thread) -> Status {
}
Err(err) => {
let fmt = format!("{}", err);
let result = Value::String(context.alloc_ignore_limit(&fmt[..]));
let result = unsafe {
Value::String(GcStr::from_utf8_unchecked(context.alloc_ignore_limit(fmt.as_bytes())))
};
context.stack.push(result);
Status::Error
}
Expand Down
10 changes: 5 additions & 5 deletions vm/src/interner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ use std::ops::Deref;
use Result;
use base::fnv::FnvMap;

use gc::{GcPtr, Gc, Traverseable};
use array::Str;
use gc::{Gc, Traverseable};
use value::GcStr;

/// Interned strings which allow for fast equality checks and hashing
#[derive(Copy, Clone, Eq)]
pub struct InternedStr(GcPtr<Str>);
pub struct InternedStr(GcStr);

impl PartialEq<InternedStr> for InternedStr {
fn eq(&self, other: &InternedStr) -> bool {
Expand Down Expand Up @@ -60,7 +60,7 @@ impl AsRef<str> for InternedStr {
}

impl InternedStr {
pub fn inner(&self) -> GcPtr<Str> {
pub fn inner(&self) -> GcStr {
self.0
}
}
Expand Down Expand Up @@ -91,7 +91,7 @@ impl Interner {
Some(interned_str) => return Ok(*interned_str),
None => (),
}
let gc_str = InternedStr(gc.alloc(s)?);
let gc_str = unsafe { InternedStr(GcStr::from_utf8_unchecked(gc.alloc(s.as_bytes())?)) };
// The key will live as long as the value it refers to and the static str never escapes
// outside interner so this is safe
let key: &'static str = unsafe { ::std::mem::transmute::<&str, &'static str>(&gc_str) };
Expand Down
14 changes: 5 additions & 9 deletions vm/src/lazy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ use std::sync::Mutex;
use base::types;
use base::types::{Type, ArcType};
use gc::{Gc, GcPtr, Move, Traverseable};
use api::{OpaqueValue, Userdata, VmType};
use api::Generic;
use api::{Pushable, Generic, OpaqueValue, Userdata, VmType, RuntimeResult};
use api::generic::A;
use vm::{Status, Thread};
use {Error, Result};
Expand Down Expand Up @@ -84,9 +83,8 @@ extern "C" fn force(vm: &Thread) -> Status {
let value = *lazy.value.lock().unwrap();
match value {
Lazy_::Blackhole => {
let result = Value::String(context.alloc_ignore_limit("<<loop>>"));
context.stack.push(result);
Status::Error
let result: RuntimeResult<(), _> = RuntimeResult::Panic("<<loop>>");
result.status_push(vm, &mut context)
}
Lazy_::Thunk(value) => {
context.stack.push(value);
Expand All @@ -107,10 +105,8 @@ extern "C" fn force(vm: &Thread) -> Status {
}
Err(err) => {
let mut context = vm.context();
let err = format!("{}", err);
let result = Value::String(context.alloc_ignore_limit(&err[..]));
context.stack.push(result);
Status::Error
let result: RuntimeResult<(), _> = RuntimeResult::Panic(err);
result.status_push(vm, &mut context)
}
}
}
Expand Down
Loading

0 comments on commit a336a49

Please sign in to comment.