1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
use libc::size_t;
use std::mem;

use constant::{FL_USHIFT, FL_USER_1, FL_USER_2, FL_USER_3, FL_USER_4, FL_USER_5, FL_USER_6};
use types::{c_char, c_long, InternalValue, RBasic, Value};

extern "C" {
    pub fn rb_str_new_cstr(str: *const c_char) -> Value;
    pub fn rb_string_value_cstr(str: *const Value) -> *const c_char;
    pub fn rb_string_value_ptr(str: *const Value) -> *const c_char;
}

#[repr(C)]
enum RStringEmbed {
    NoEmbed = FL_USER_1,
    LenMask = FL_USER_2 | FL_USER_3 | FL_USER_4 | FL_USER_5 | FL_USER_6,
    LenShift = FL_USHIFT + 2,
}

#[repr(C)]
struct RStringAs {
    heap: RStringHeap,
}

#[repr(C)]
struct RStringHeap {
    len: c_long,
    // Really, this is a union but value is the largest item.
    value: InternalValue,
    ptr: InternalValue,
}

#[repr(C)]
struct RString {
    basic: RBasic,
    as_: RStringAs,
}

pub unsafe fn rb_str_len(value: Value) -> c_long {
    let rstring: *const RString = mem::transmute(value.value);
    let flags = (*rstring).basic.flags;

    if flags & (RStringEmbed::NoEmbed as size_t) == 0 {
        ((flags as i64 >> RStringEmbed::LenShift as i64) &
         (RStringEmbed::LenMask as i64 >> RStringEmbed::LenShift as i64)) as c_long
    } else {
        (*rstring).as_.heap.len
    }
}