artichoke_backend/convert/
bytes.rs1use core::mem;
2use std::borrow::Cow;
3use std::ffi::{OsStr, OsString};
4
5use scolapasta_path::{os_str_to_bytes, os_string_to_bytes};
6use spinoso_string::String;
7
8use crate::Artichoke;
9use crate::convert::BoxUnboxVmValue;
10use crate::core::TryConvertMut;
11use crate::error::Error;
12use crate::value::Value;
13
14impl TryConvertMut<Vec<u8>, Value> for Artichoke {
15 type Error = Error;
16
17 fn try_convert_mut(&mut self, value: Vec<u8>) -> Result<Value, Self::Error> {
18 let s = String::utf8(value);
19 let value = String::alloc_value(s, self)?;
20 Ok(self.protect(value))
21 }
22}
23
24impl TryConvertMut<&[u8], Value> for Artichoke {
25 type Error = Error;
26
27 fn try_convert_mut(&mut self, value: &[u8]) -> Result<Value, Self::Error> {
28 self.try_convert_mut(value.to_vec())
29 }
30}
31
32impl<'a> TryConvertMut<Cow<'a, [u8]>, Value> for Artichoke {
33 type Error = Error;
34
35 fn try_convert_mut(&mut self, value: Cow<'a, [u8]>) -> Result<Value, Self::Error> {
36 match value {
37 Cow::Borrowed(bytes) => self.try_convert_mut(bytes),
38 Cow::Owned(bytes) => self.try_convert_mut(bytes),
39 }
40 }
41}
42
43impl TryConvertMut<OsString, Value> for Artichoke {
44 type Error = Error;
45
46 fn try_convert_mut(&mut self, value: OsString) -> Result<Value, Self::Error> {
47 let bytes = os_string_to_bytes(value)?;
48 self.try_convert_mut(bytes)
49 }
50}
51
52impl TryConvertMut<&OsStr, Value> for Artichoke {
53 type Error = Error;
54
55 fn try_convert_mut(&mut self, value: &OsStr) -> Result<Value, Self::Error> {
56 let bytes = os_str_to_bytes(value)?;
57 self.try_convert_mut(bytes)
58 }
59}
60
61impl<'a> TryConvertMut<Cow<'a, OsStr>, Value> for Artichoke {
62 type Error = Error;
63
64 fn try_convert_mut(&mut self, value: Cow<'a, OsStr>) -> Result<Value, Self::Error> {
65 match value {
66 Cow::Borrowed(value) => {
67 let bytes = os_str_to_bytes(value)?;
68 self.try_convert_mut(bytes)
69 }
70 Cow::Owned(value) => {
71 let bytes = os_string_to_bytes(value)?;
72 self.try_convert_mut(bytes)
73 }
74 }
75 }
76}
77
78impl TryConvertMut<Value, Vec<u8>> for Artichoke {
79 type Error = Error;
80
81 fn try_convert_mut(&mut self, mut value: Value) -> Result<Vec<u8>, Self::Error> {
82 let s = unsafe { String::unbox_from_value(&mut value, self)? };
83 Ok(s.clone().into_vec())
84 }
85}
86
87impl<'a> TryConvertMut<Value, &'a [u8]> for Artichoke {
88 type Error = Error;
89
90 fn try_convert_mut(&mut self, mut value: Value) -> Result<&'a [u8], Self::Error> {
91 self.protect(value);
92 let s = unsafe { String::unbox_from_value(&mut value, self)? };
93 let slice = unsafe { mem::transmute::<&'_ [u8], &'a [u8]>(s.as_slice()) };
102 Ok(slice)
103 }
104}
105
106#[cfg(test)]
107mod tests {
108 use bstr::ByteSlice;
109
110 use crate::test::prelude::*;
111
112 #[test]
113 fn fail_convert() {
114 let mut interp = interpreter();
115 let value = interp.eval(b"Object.new").unwrap();
117 let result = value.try_convert_into_mut::<Vec<u8>>(&mut interp);
118 assert!(result.is_err());
119 }
120
121 #[test]
122 fn convert_with_trailing_nul() {
123 let mut interp = interpreter();
124 let bytes: &[u8] = &[0];
125 let value = interp.try_convert_mut(bytes).unwrap();
126 let retrieved_bytes = value.try_convert_into_mut::<&[u8]>(&mut interp).unwrap();
127 assert_eq!(bytes.as_bstr(), retrieved_bytes.as_bstr());
128
129 let len = value.funcall(&mut interp, "bytesize", &[], None).unwrap();
130 let len = len.try_convert_into::<usize>(&interp).unwrap();
131 assert_eq!(len, 1);
132
133 let empty = value.funcall(&mut interp, "empty?", &[], None).unwrap();
134 let empty = empty.try_convert_into::<bool>(&interp).unwrap();
135 assert!(!empty);
136
137 let zero = interp.convert(0);
138 let one = interp.convert(1);
139
140 let str_bytes = value.funcall(&mut interp, "bytes", &[], None).unwrap();
141 let first = str_bytes.funcall(&mut interp, "[]", &[zero], None).unwrap();
142 let first = first.try_convert_into::<i64>(&interp).unwrap();
143 assert_eq!(first, 0_i64);
144
145 let slice = value.funcall(&mut interp, "byteslice", &[zero, one], None).unwrap();
146 let slice = slice.try_convert_into_mut::<Option<&[u8]>>(&mut interp).unwrap();
147 let expected: Option<&[u8]> = Some(&[0]);
148 assert_eq!(slice, expected);
149 }
150
151 #[test]
152 fn prop_convert_to_vec() {
153 let mut interp = interpreter();
154 run_arbitrary::<Vec<u8>>(|bytes| {
155 let value = interp.try_convert_mut(bytes).unwrap();
156 assert_eq!(value.ruby_type(), Ruby::String);
157 });
158 }
159
160 #[test]
161 fn prop_byte_string_borrowed() {
162 let mut interp = interpreter();
163 run_arbitrary::<Vec<u8>>(|bytes| {
164 let value = interp.try_convert_mut(bytes.clone()).unwrap();
166 let len = value.funcall(&mut interp, "bytesize", &[], None).unwrap();
167 let len = len.try_convert_into::<usize>(&interp).unwrap();
168 assert_eq!(len, bytes.len());
169
170 let empty = value.funcall(&mut interp, "empty?", &[], None).unwrap();
171 let empty = empty.try_convert_into::<bool>(&interp).unwrap();
172 assert_eq!(empty, bytes.is_empty());
173
174 let zero = interp.convert(0);
175 let one = interp.convert(1);
176
177 let str_bytes = value.funcall(&mut interp, "bytes", &[], None).unwrap();
178 let first = str_bytes.funcall(&mut interp, "[]", &[zero], None).unwrap();
179 let first = first.try_convert_into::<Option<i64>>(&interp).unwrap();
180 assert_eq!(first, bytes.first().copied().map(i64::from));
181
182 let slice = value.funcall(&mut interp, "byteslice", &[zero, one], None).unwrap();
183 let slice = slice.try_convert_into_mut::<Option<&[u8]>>(&mut interp).unwrap();
184 assert_eq!(slice.unwrap_or_default(), bytes.get(0..1).unwrap_or_default());
185
186 let recovered: Vec<u8> = interp.try_convert_mut(value).unwrap();
187 assert_eq!(recovered, bytes);
188 });
189 }
190
191 #[test]
192 fn prop_byte_string_owned() {
193 let mut interp = interpreter();
194 run_arbitrary::<Vec<u8>>(|bytes| {
195 let value = interp.try_convert_mut(bytes.clone()).unwrap();
197 let len = value.funcall(&mut interp, "bytesize", &[], None).unwrap();
198 let len = len.try_convert_into::<usize>(&interp).unwrap();
199 assert_eq!(len, bytes.len());
200
201 let empty = value.funcall(&mut interp, "empty?", &[], None).unwrap();
202 let empty = empty.try_convert_into::<bool>(&interp).unwrap();
203 assert_eq!(empty, bytes.is_empty());
204
205 let zero = interp.convert(0);
206 let one = interp.convert(1);
207
208 let str_bytes = value.funcall(&mut interp, "bytes", &[], None).unwrap();
209 let first = str_bytes.funcall(&mut interp, "[]", &[zero], None).unwrap();
210 let first = first.try_convert_into::<Option<i64>>(&interp).unwrap();
211 assert_eq!(first, bytes.first().copied().map(i64::from));
212
213 let slice = value.funcall(&mut interp, "byteslice", &[zero, one], None).unwrap();
214 let slice = slice.try_convert_into_mut::<Option<&[u8]>>(&mut interp).unwrap();
215 assert_eq!(slice.unwrap_or_default(), bytes.get(0..1).unwrap_or_default());
216
217 let recovered: Vec<u8> = interp.try_convert_mut(value).unwrap();
218 assert_eq!(recovered, bytes);
219 });
220 }
221
222 #[test]
223 fn prop_roundtrip() {
224 let mut interp = interpreter();
225 run_arbitrary::<Vec<u8>>(|bytes| {
226 let value = interp.try_convert_mut(bytes.as_slice()).unwrap();
227 let value = value.try_convert_into_mut::<Vec<u8>>(&mut interp).unwrap();
228 assert_eq!(value, bytes);
229 });
230 }
231
232 #[test]
233 fn prop_roundtrip_err() {
234 let mut interp = interpreter();
235 for b in [true, false] {
236 let value = interp.convert(b);
237 let value = value.try_convert_into_mut::<Vec<u8>>(&mut interp);
238 assert!(value.is_err());
239 }
240 }
241}