use crate::seqstring::global_string;
use crate::stack::{Stack, pop, push};
use crate::value::Value;
use base64::prelude::*;
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_base64_encode(stack: Stack) -> Stack {
assert!(!stack.is_null(), "base64-encode: stack is empty");
let (stack, value) = unsafe { pop(stack) };
match value {
Value::String(s) => {
let encoded = BASE64_STANDARD.encode(s.as_str().as_bytes());
unsafe { push(stack, Value::String(global_string(encoded))) }
}
_ => panic!("base64-encode: expected String on stack, got {:?}", value),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_base64_decode(stack: Stack) -> Stack {
assert!(!stack.is_null(), "base64-decode: stack is empty");
let (stack, value) = unsafe { pop(stack) };
match value {
Value::String(s) => match BASE64_STANDARD.decode(s.as_str().as_bytes()) {
Ok(bytes) => match String::from_utf8(bytes) {
Ok(decoded) => {
let stack = unsafe { push(stack, Value::String(global_string(decoded))) };
unsafe { push(stack, Value::Bool(true)) }
}
Err(_) => {
let stack = unsafe { push(stack, Value::String(global_string(String::new()))) };
unsafe { push(stack, Value::Bool(false)) }
}
},
Err(_) => {
let stack = unsafe { push(stack, Value::String(global_string(String::new()))) };
unsafe { push(stack, Value::Bool(false)) }
}
},
_ => panic!("base64-decode: expected String on stack, got {:?}", value),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_base64url_encode(stack: Stack) -> Stack {
assert!(!stack.is_null(), "base64url-encode: stack is empty");
let (stack, value) = unsafe { pop(stack) };
match value {
Value::String(s) => {
let encoded = BASE64_URL_SAFE_NO_PAD.encode(s.as_str().as_bytes());
unsafe { push(stack, Value::String(global_string(encoded))) }
}
_ => panic!(
"base64url-encode: expected String on stack, got {:?}",
value
),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_base64url_decode(stack: Stack) -> Stack {
assert!(!stack.is_null(), "base64url-decode: stack is empty");
let (stack, value) = unsafe { pop(stack) };
match value {
Value::String(s) => match BASE64_URL_SAFE_NO_PAD.decode(s.as_str().as_bytes()) {
Ok(bytes) => match String::from_utf8(bytes) {
Ok(decoded) => {
let stack = unsafe { push(stack, Value::String(global_string(decoded))) };
unsafe { push(stack, Value::Bool(true)) }
}
Err(_) => {
let stack = unsafe { push(stack, Value::String(global_string(String::new()))) };
unsafe { push(stack, Value::Bool(false)) }
}
},
Err(_) => {
let stack = unsafe { push(stack, Value::String(global_string(String::new()))) };
unsafe { push(stack, Value::Bool(false)) }
}
},
_ => panic!(
"base64url-decode: expected String on stack, got {:?}",
value
),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_hex_encode(stack: Stack) -> Stack {
assert!(!stack.is_null(), "hex-encode: stack is empty");
let (stack, value) = unsafe { pop(stack) };
match value {
Value::String(s) => {
let encoded = hex::encode(s.as_str().as_bytes());
unsafe { push(stack, Value::String(global_string(encoded))) }
}
_ => panic!("hex-encode: expected String on stack, got {:?}", value),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_hex_decode(stack: Stack) -> Stack {
assert!(!stack.is_null(), "hex-decode: stack is empty");
let (stack, value) = unsafe { pop(stack) };
match value {
Value::String(s) => match hex::decode(s.as_str()) {
Ok(bytes) => match String::from_utf8(bytes) {
Ok(decoded) => {
let stack = unsafe { push(stack, Value::String(global_string(decoded))) };
unsafe { push(stack, Value::Bool(true)) }
}
Err(_) => {
let stack = unsafe { push(stack, Value::String(global_string(String::new()))) };
unsafe { push(stack, Value::Bool(false)) }
}
},
Err(_) => {
let stack = unsafe { push(stack, Value::String(global_string(String::new()))) };
unsafe { push(stack, Value::Bool(false)) }
}
},
_ => panic!("hex-decode: expected String on stack, got {:?}", value),
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::stack::pop;
#[test]
fn test_base64_encode() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, Value::String(global_string("hello".to_string())));
let stack = patch_seq_base64_encode(stack);
let (_, value) = pop(stack);
match value {
Value::String(s) => assert_eq!(s.as_str(), "aGVsbG8="),
_ => panic!("Expected String"),
}
}
}
#[test]
fn test_base64_decode_success() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, Value::String(global_string("aGVsbG8=".to_string())));
let stack = patch_seq_base64_decode(stack);
let (stack, success) = pop(stack);
let (_, decoded) = pop(stack);
match (decoded, success) {
(Value::String(s), Value::Bool(true)) => assert_eq!(s.as_str(), "hello"),
_ => panic!("Expected (String, true)"),
}
}
}
#[test]
fn test_base64_decode_failure() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push(
stack,
Value::String(global_string("not valid base64!!!".to_string())),
);
let stack = patch_seq_base64_decode(stack);
let (stack, success) = pop(stack);
let (_, decoded) = pop(stack);
match (decoded, success) {
(Value::String(s), Value::Bool(false)) => assert_eq!(s.as_str(), ""),
_ => panic!("Expected (empty String, false)"),
}
}
}
#[test]
fn test_base64url_encode() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, Value::String(global_string("hello??".to_string())));
let stack = patch_seq_base64url_encode(stack);
let (_, value) = pop(stack);
match value {
Value::String(s) => {
assert!(!s.as_str().contains('+'));
assert!(!s.as_str().contains('/'));
assert!(!s.as_str().contains('='));
}
_ => panic!("Expected String"),
}
}
}
#[test]
fn test_base64url_roundtrip() {
unsafe {
let original = "hello world! 123";
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, Value::String(global_string(original.to_string())));
let stack = patch_seq_base64url_encode(stack);
let stack = patch_seq_base64url_decode(stack);
let (stack, success) = pop(stack);
let (_, decoded) = pop(stack);
match (decoded, success) {
(Value::String(s), Value::Bool(true)) => assert_eq!(s.as_str(), original),
_ => panic!("Expected (String, true)"),
}
}
}
#[test]
fn test_hex_encode() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, Value::String(global_string("hello".to_string())));
let stack = patch_seq_hex_encode(stack);
let (_, value) = pop(stack);
match value {
Value::String(s) => assert_eq!(s.as_str(), "68656c6c6f"),
_ => panic!("Expected String"),
}
}
}
#[test]
fn test_hex_decode_success() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push(
stack,
Value::String(global_string("68656c6c6f".to_string())),
);
let stack = patch_seq_hex_decode(stack);
let (stack, success) = pop(stack);
let (_, decoded) = pop(stack);
match (decoded, success) {
(Value::String(s), Value::Bool(true)) => assert_eq!(s.as_str(), "hello"),
_ => panic!("Expected (String, true)"),
}
}
}
#[test]
fn test_hex_decode_uppercase() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push(
stack,
Value::String(global_string("68656C6C6F".to_string())),
);
let stack = patch_seq_hex_decode(stack);
let (stack, success) = pop(stack);
let (_, decoded) = pop(stack);
match (decoded, success) {
(Value::String(s), Value::Bool(true)) => assert_eq!(s.as_str(), "hello"),
_ => panic!("Expected (String, true)"),
}
}
}
#[test]
fn test_hex_decode_failure() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, Value::String(global_string("not hex!".to_string())));
let stack = patch_seq_hex_decode(stack);
let (stack, success) = pop(stack);
let (_, decoded) = pop(stack);
match (decoded, success) {
(Value::String(s), Value::Bool(false)) => assert_eq!(s.as_str(), ""),
_ => panic!("Expected (empty String, false)"),
}
}
}
#[test]
fn test_hex_roundtrip() {
unsafe {
let original = "Hello, World! 123";
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, Value::String(global_string(original.to_string())));
let stack = patch_seq_hex_encode(stack);
let stack = patch_seq_hex_decode(stack);
let (stack, success) = pop(stack);
let (_, decoded) = pop(stack);
match (decoded, success) {
(Value::String(s), Value::Bool(true)) => assert_eq!(s.as_str(), original),
_ => panic!("Expected (String, true)"),
}
}
}
}