rutie/rubysys/
string.rs

1use std::mem;
2
3use crate::rubysys::{
4    constant::{
5        FL_USER_1, FL_USER_17, FL_USER_2, FL_USER_3, FL_USER_4, FL_USER_5, FL_USER_6, FL_USER_7,
6        FL_USHIFT,
7    },
8    libc::size_t,
9    types::{c_char, c_long, CallbackPtr, EncodingType, InternalValue, RBasic, Value},
10};
11
12pub const STR_TMPLOCK: isize = FL_USER_7;
13
14extern "C" {
15    // VALUE
16    // rb_str_new(const char *ptr, long len)
17    pub fn rb_str_new(str: *const c_char, len: c_long) -> Value;
18    // VALUE
19    // rb_str_new_cstr(const char *ptr)
20    pub fn rb_str_new_cstr(str: *const c_char) -> Value;
21    // VALUE
22    // rb_utf8_str_new(const char *ptr, long len)
23    pub fn rb_utf8_str_new(str: *const c_char, len: c_long) -> Value;
24    // VALUE
25    // rb_utf8_str_new_cstr(const char *ptr)
26    pub fn rb_utf8_str_new_cstr(str: *const c_char) -> Value;
27    // char *
28    // rb_string_value_cstr(volatile VALUE *ptr)
29    pub fn rb_string_value_cstr(str: *const Value) -> *const c_char;
30    // char *
31    // rb_string_value_ptr(volatile VALUE *ptr)
32    pub fn rb_string_value_ptr(str: *const Value) -> *const c_char;
33    // long
34    // rb_str_strlen(VALUE str)
35    pub fn rb_str_strlen(str: Value) -> c_long;
36    // int
37    // rb_enc_str_asciionly_p(VALUE str)
38    pub fn rb_enc_str_asciionly_p(str: Value) -> bool;
39    // VALUE
40    // rb_enc_str_new(const char *ptr, long len, rb_encoding *enc)
41    pub fn rb_enc_str_new(str: *const c_char, len: c_long, enc: EncodingType) -> Value;
42    // VALUE
43    // rb_str_export_locale(VALUE str)
44    pub fn rb_str_export_locale(str: Value) -> Value;
45    // static VALUE
46    // rb_str_valid_encoding_p(VALUE str)
47    pub fn rb_str_valid_encoding_p(str: Value) -> bool;
48    // VALUE
49    // rb_str_cat(VALUE str, const char *ptr, long len)
50    pub fn rb_str_cat(str: Value, ptr: *const c_char, len: c_long) -> Value;
51    // VALUE
52    // rb_check_string_type(VALUE str)
53    pub fn rb_check_string_type(str: Value) -> Value;
54    //-------------------------------------------------------------
55    // LINKER CANNOT FIND
56    // //
57    // //  call-seq:
58    // //     str.force_encoding(encoding)   -> str
59    // //
60    // //  Changes the encoding to +encoding+ and returns self.
61    // //
62    // // static VALUE
63    // // rb_str_force_encoding(VALUE str, VALUE enc)
64    // pub fn rb_str_force_encoding(s: Value, enc: Value) -> Value;
65    //-------------------------------------------------------------
66    // VALUE
67    // rb_str_locktmp(VALUE str)
68    pub fn rb_str_locktmp(str: Value) -> Value;
69    // VALUE
70    // rb_str_unlocktmp(VALUE str)
71    pub fn rb_str_unlocktmp(str: Value) -> Value;
72    // VALUE
73    // rb_str_new_frozen(VALUE orig)
74    pub fn rb_str_new_frozen(orig: Value) -> Value;
75}
76
77// #[link_name = "ruby_rstring_flags"]
78#[derive(Debug, PartialEq)]
79#[repr(C)]
80enum RStringEmbed {
81    NoEmbed = FL_USER_1,
82    LenMask = FL_USER_2 | FL_USER_3 | FL_USER_4 | FL_USER_5 | FL_USER_6,
83    LenShift = FL_USHIFT + 2,
84    LenMax = (mem::size_of::<Value>() as isize * 3) / mem::size_of::<c_char>() as isize - 1,
85    Fstr = FL_USER_17,
86}
87
88#[derive(Copy, Clone)]
89#[repr(C)]
90union RStringAs {
91    heap: RStringHeap,
92    ary: [c_char; RStringEmbed::LenMax as usize + 1],
93}
94
95#[derive(Copy, Clone)]
96#[repr(C)]
97union RStringAux {
98    capa: c_long,
99    value: InternalValue,
100}
101
102#[derive(Copy, Clone)]
103#[repr(C)]
104struct RStringHeap {
105    len: c_long,
106    ptr: *const c_char,
107    aux: RStringAux,
108}
109
110#[repr(C)]
111struct RString {
112    basic: RBasic,
113    as_: RStringAs,
114}
115
116unsafe fn rstring_and_flags(value: Value) -> (*const RString, InternalValue) {
117    let rstring: *const RString = mem::transmute(value.value);
118    let flags = (*rstring).basic.flags;
119
120    (rstring, flags)
121}
122
123unsafe fn embed_check(flags: InternalValue) -> bool {
124    flags & (RStringEmbed::NoEmbed as size_t) == 0
125}
126
127pub unsafe fn rstring_embed_len(value: Value) -> c_long {
128    let (_rstring, flags) = rstring_and_flags(value);
129
130    ((flags as i64 >> RStringEmbed::LenShift as i64)
131        & (RStringEmbed::LenMask as i64 >> RStringEmbed::LenShift as i64)) as c_long
132}
133
134pub unsafe fn rstring_len(value: Value) -> c_long {
135    let (rstring, flags) = rstring_and_flags(value);
136
137    if embed_check(flags) {
138        rstring_embed_len(value)
139    } else {
140        (*rstring).as_.heap.len
141    }
142}
143
144pub unsafe fn rstring_ptr(value: Value) -> *const c_char {
145    let (rstring, flags) = rstring_and_flags(value);
146
147    if embed_check(flags) {
148        (*rstring).as_.ary.as_ptr()
149    } else {
150        (*rstring).as_.heap.ptr
151    }
152}
153
154pub unsafe fn rstring_end(value: Value) -> *const c_char {
155    let (rstring, flags) = rstring_and_flags(value);
156
157    if embed_check(flags) {
158        (*rstring)
159            .as_
160            .ary
161            .as_ptr()
162            .add(rstring_embed_len(value) as usize)
163    } else {
164        (*rstring)
165            .as_
166            .heap
167            .ptr
168            .add((*rstring).as_.heap.len as usize)
169    }
170}
171
172// ```
173// use rutie::VM;
174// # VM::init();
175//
176// use rutie::binding::string::*; // binding not public
177//
178// let word = new_utf8("word");
179// unsafe {
180//     assert!(!is_locktmp(word), "word should not be locktmp but is");
181//     locktmp(word);
182//     assert!(is_locktmp(word), "word should be locktmp but is not");
183//     unlocktmp(word);
184//     assert!(!is_locktmp(word), "word should not be locktmp but is");
185// }
186// ```
187pub unsafe fn is_lockedtmp(value: Value) -> bool {
188    let (_rstring, flags) = rstring_and_flags(value);
189
190    flags & STR_TMPLOCK as size_t != 0
191}