1use crate::seqstring::global_string;
22use crate::stack::{Stack, pop, push};
23use crate::value::Value;
24
25use base64::prelude::*;
26
27#[unsafe(no_mangle)]
34pub unsafe extern "C" fn patch_seq_base64_encode(stack: Stack) -> Stack {
35 assert!(!stack.is_null(), "base64-encode: stack is empty");
36
37 let (stack, value) = unsafe { pop(stack) };
38
39 match value {
40 Value::String(s) => {
41 let encoded = BASE64_STANDARD.encode(s.as_str().as_bytes());
42 unsafe { push(stack, Value::String(global_string(encoded))) }
43 }
44 _ => panic!("base64-encode: expected String on stack, got {:?}", value),
45 }
46}
47
48#[unsafe(no_mangle)]
57pub unsafe extern "C" fn patch_seq_base64_decode(stack: Stack) -> Stack {
58 assert!(!stack.is_null(), "base64-decode: stack is empty");
59
60 let (stack, value) = unsafe { pop(stack) };
61
62 match value {
63 Value::String(s) => match BASE64_STANDARD.decode(s.as_str().as_bytes()) {
64 Ok(bytes) => match String::from_utf8(bytes) {
65 Ok(decoded) => {
66 let stack = unsafe { push(stack, Value::String(global_string(decoded))) };
67 unsafe { push(stack, Value::Bool(true)) }
68 }
69 Err(_) => {
70 let stack = unsafe { push(stack, Value::String(global_string(String::new()))) };
72 unsafe { push(stack, Value::Bool(false)) }
73 }
74 },
75 Err(_) => {
76 let stack = unsafe { push(stack, Value::String(global_string(String::new()))) };
78 unsafe { push(stack, Value::Bool(false)) }
79 }
80 },
81 _ => panic!("base64-decode: expected String on stack, got {:?}", value),
82 }
83}
84
85#[unsafe(no_mangle)]
95pub unsafe extern "C" fn patch_seq_base64url_encode(stack: Stack) -> Stack {
96 assert!(!stack.is_null(), "base64url-encode: stack is empty");
97
98 let (stack, value) = unsafe { pop(stack) };
99
100 match value {
101 Value::String(s) => {
102 let encoded = BASE64_URL_SAFE_NO_PAD.encode(s.as_str().as_bytes());
103 unsafe { push(stack, Value::String(global_string(encoded))) }
104 }
105 _ => panic!(
106 "base64url-encode: expected String on stack, got {:?}",
107 value
108 ),
109 }
110}
111
112#[unsafe(no_mangle)]
121pub unsafe extern "C" fn patch_seq_base64url_decode(stack: Stack) -> Stack {
122 assert!(!stack.is_null(), "base64url-decode: stack is empty");
123
124 let (stack, value) = unsafe { pop(stack) };
125
126 match value {
127 Value::String(s) => match BASE64_URL_SAFE_NO_PAD.decode(s.as_str().as_bytes()) {
128 Ok(bytes) => match String::from_utf8(bytes) {
129 Ok(decoded) => {
130 let stack = unsafe { push(stack, Value::String(global_string(decoded))) };
131 unsafe { push(stack, Value::Bool(true)) }
132 }
133 Err(_) => {
134 let stack = unsafe { push(stack, Value::String(global_string(String::new()))) };
135 unsafe { push(stack, Value::Bool(false)) }
136 }
137 },
138 Err(_) => {
139 let stack = unsafe { push(stack, Value::String(global_string(String::new()))) };
140 unsafe { push(stack, Value::Bool(false)) }
141 }
142 },
143 _ => panic!(
144 "base64url-decode: expected String on stack, got {:?}",
145 value
146 ),
147 }
148}
149
150#[unsafe(no_mangle)]
159pub unsafe extern "C" fn patch_seq_hex_encode(stack: Stack) -> Stack {
160 assert!(!stack.is_null(), "hex-encode: stack is empty");
161
162 let (stack, value) = unsafe { pop(stack) };
163
164 match value {
165 Value::String(s) => {
166 let encoded = hex::encode(s.as_str().as_bytes());
167 unsafe { push(stack, Value::String(global_string(encoded))) }
168 }
169 _ => panic!("hex-encode: expected String on stack, got {:?}", value),
170 }
171}
172
173#[unsafe(no_mangle)]
183pub unsafe extern "C" fn patch_seq_hex_decode(stack: Stack) -> Stack {
184 assert!(!stack.is_null(), "hex-decode: stack is empty");
185
186 let (stack, value) = unsafe { pop(stack) };
187
188 match value {
189 Value::String(s) => match hex::decode(s.as_str()) {
190 Ok(bytes) => match String::from_utf8(bytes) {
191 Ok(decoded) => {
192 let stack = unsafe { push(stack, Value::String(global_string(decoded))) };
193 unsafe { push(stack, Value::Bool(true)) }
194 }
195 Err(_) => {
196 let stack = unsafe { push(stack, Value::String(global_string(String::new()))) };
198 unsafe { push(stack, Value::Bool(false)) }
199 }
200 },
201 Err(_) => {
202 let stack = unsafe { push(stack, Value::String(global_string(String::new()))) };
204 unsafe { push(stack, Value::Bool(false)) }
205 }
206 },
207 _ => panic!("hex-decode: expected String on stack, got {:?}", value),
208 }
209}
210
211#[cfg(test)]
212mod tests {
213 use super::*;
214 use crate::stack::pop;
215
216 #[test]
217 fn test_base64_encode() {
218 unsafe {
219 let stack = crate::stack::alloc_test_stack();
220 let stack = push(stack, Value::String(global_string("hello".to_string())));
221 let stack = patch_seq_base64_encode(stack);
222 let (_, value) = pop(stack);
223
224 match value {
225 Value::String(s) => assert_eq!(s.as_str(), "aGVsbG8="),
226 _ => panic!("Expected String"),
227 }
228 }
229 }
230
231 #[test]
232 fn test_base64_decode_success() {
233 unsafe {
234 let stack = crate::stack::alloc_test_stack();
235 let stack = push(stack, Value::String(global_string("aGVsbG8=".to_string())));
236 let stack = patch_seq_base64_decode(stack);
237
238 let (stack, success) = pop(stack);
239 let (_, decoded) = pop(stack);
240
241 match (decoded, success) {
242 (Value::String(s), Value::Bool(true)) => assert_eq!(s.as_str(), "hello"),
243 _ => panic!("Expected (String, true)"),
244 }
245 }
246 }
247
248 #[test]
249 fn test_base64_decode_failure() {
250 unsafe {
251 let stack = crate::stack::alloc_test_stack();
252 let stack = push(
253 stack,
254 Value::String(global_string("not valid base64!!!".to_string())),
255 );
256 let stack = patch_seq_base64_decode(stack);
257
258 let (stack, success) = pop(stack);
259 let (_, decoded) = pop(stack);
260
261 match (decoded, success) {
262 (Value::String(s), Value::Bool(false)) => assert_eq!(s.as_str(), ""),
263 _ => panic!("Expected (empty String, false)"),
264 }
265 }
266 }
267
268 #[test]
269 fn test_base64url_encode() {
270 unsafe {
271 let stack = crate::stack::alloc_test_stack();
272 let stack = push(stack, Value::String(global_string("hello??".to_string())));
274 let stack = patch_seq_base64url_encode(stack);
275 let (_, value) = pop(stack);
276
277 match value {
278 Value::String(s) => {
279 assert!(!s.as_str().contains('+'));
281 assert!(!s.as_str().contains('/'));
282 assert!(!s.as_str().contains('='));
283 }
284 _ => panic!("Expected String"),
285 }
286 }
287 }
288
289 #[test]
290 fn test_base64url_roundtrip() {
291 unsafe {
292 let original = "hello world! 123";
293 let stack = crate::stack::alloc_test_stack();
294 let stack = push(stack, Value::String(global_string(original.to_string())));
295 let stack = patch_seq_base64url_encode(stack);
296 let stack = patch_seq_base64url_decode(stack);
297
298 let (stack, success) = pop(stack);
299 let (_, decoded) = pop(stack);
300
301 match (decoded, success) {
302 (Value::String(s), Value::Bool(true)) => assert_eq!(s.as_str(), original),
303 _ => panic!("Expected (String, true)"),
304 }
305 }
306 }
307
308 #[test]
309 fn test_hex_encode() {
310 unsafe {
311 let stack = crate::stack::alloc_test_stack();
312 let stack = push(stack, Value::String(global_string("hello".to_string())));
313 let stack = patch_seq_hex_encode(stack);
314 let (_, value) = pop(stack);
315
316 match value {
317 Value::String(s) => assert_eq!(s.as_str(), "68656c6c6f"),
318 _ => panic!("Expected String"),
319 }
320 }
321 }
322
323 #[test]
324 fn test_hex_decode_success() {
325 unsafe {
326 let stack = crate::stack::alloc_test_stack();
327 let stack = push(
328 stack,
329 Value::String(global_string("68656c6c6f".to_string())),
330 );
331 let stack = patch_seq_hex_decode(stack);
332
333 let (stack, success) = pop(stack);
334 let (_, decoded) = pop(stack);
335
336 match (decoded, success) {
337 (Value::String(s), Value::Bool(true)) => assert_eq!(s.as_str(), "hello"),
338 _ => panic!("Expected (String, true)"),
339 }
340 }
341 }
342
343 #[test]
344 fn test_hex_decode_uppercase() {
345 unsafe {
346 let stack = crate::stack::alloc_test_stack();
347 let stack = push(
348 stack,
349 Value::String(global_string("68656C6C6F".to_string())),
350 );
351 let stack = patch_seq_hex_decode(stack);
352
353 let (stack, success) = pop(stack);
354 let (_, decoded) = pop(stack);
355
356 match (decoded, success) {
357 (Value::String(s), Value::Bool(true)) => assert_eq!(s.as_str(), "hello"),
358 _ => panic!("Expected (String, true)"),
359 }
360 }
361 }
362
363 #[test]
364 fn test_hex_decode_failure() {
365 unsafe {
366 let stack = crate::stack::alloc_test_stack();
367 let stack = push(stack, Value::String(global_string("not hex!".to_string())));
368 let stack = patch_seq_hex_decode(stack);
369
370 let (stack, success) = pop(stack);
371 let (_, decoded) = pop(stack);
372
373 match (decoded, success) {
374 (Value::String(s), Value::Bool(false)) => assert_eq!(s.as_str(), ""),
375 _ => panic!("Expected (empty String, false)"),
376 }
377 }
378 }
379
380 #[test]
381 fn test_hex_roundtrip() {
382 unsafe {
383 let original = "Hello, World! 123";
384 let stack = crate::stack::alloc_test_stack();
385 let stack = push(stack, Value::String(global_string(original.to_string())));
386 let stack = patch_seq_hex_encode(stack);
387 let stack = patch_seq_hex_decode(stack);
388
389 let (stack, success) = pop(stack);
390 let (_, decoded) = pop(stack);
391
392 match (decoded, success) {
393 (Value::String(s), Value::Bool(true)) => assert_eq!(s.as_str(), original),
394 _ => panic!("Expected (String, true)"),
395 }
396 }
397 }
398}