use crate::{ codegen::types, patio::values::{PatioDataPtrValue, PatioDataValue, PatioIntValue}, }; pub(crate) fn data_value_to_bytes( value: &PatioDataValue, target_triple: &target_lexicon::Triple, ) -> Box<[u8]> { let mut bytes = Vec::with_capacity( types::data_layout_byte_size(&value.layout(), target_triple) .try_into() .unwrap(), ); write_data_value(value, &mut bytes, target_triple); bytes.into_boxed_slice() } fn write_data_value( value: &PatioDataValue, bytes: &mut Vec, target_triple: &target_lexicon::Triple, ) { match value { PatioDataValue::Array { element_layout: _, elements, } => { for element in elements { write_data_value(element, bytes, target_triple); } } PatioDataValue::Bool(value) => bytes.push(if *value { 1 } else { 0 }), PatioDataValue::Byte(value) => bytes.push(*value), PatioDataValue::DataPtr(PatioDataPtrValue::DataElement { name: _ }) => todo!(), PatioDataValue::DataPtr(PatioDataPtrValue::Null) => { write_patio_int(0, bytes, target_triple); } PatioDataValue::Int(int_value) => { let value = eval_int_value(int_value, target_triple); write_patio_int(value, bytes, target_triple); } PatioDataValue::Int32(int_value) => { let value: i32 = eval_int_value(int_value, target_triple); bytes.extend_from_slice(&value.to_le_bytes()); } PatioDataValue::Struct { elements } => { for element in elements { write_data_value(element, bytes, target_triple); } } } } fn write_patio_int(value: i32, bytes: &mut Vec, target_triple: &target_lexicon::Triple) { match target_triple.pointer_width() { Ok(target_lexicon::PointerWidth::U16) => { panic!("unsupported pointer width: 16 bits") } Ok(target_lexicon::PointerWidth::U32) => { let value_at_width: i32 = value; bytes.extend_from_slice(&value_at_width.to_le_bytes()); } Ok(target_lexicon::PointerWidth::U64) => { let value_at_width: i64 = value.into(); bytes.extend_from_slice(&value_at_width.to_le_bytes()); } Err(()) => panic!("pointer width unknown"), } } pub(crate) fn eval_int_value(value: &PatioIntValue, target_triple: &target_lexicon::Triple) -> i32 { match value { PatioIntValue::ByteOffsetOf { layout, element_offsets, } => types::data_layout_byte_offset(layout, element_offsets, target_triple) .try_into() .unwrap(), PatioIntValue::ByteSizeOf { layout } => types::data_layout_byte_size(layout, target_triple) .try_into() .unwrap(), PatioIntValue::Literal { value } => *value, } } #[cfg(test)] mod data_value_to_bytes_test { use std::str::FromStr; use crate::patio::{ types::PatioDataLayout, values::{PatioDataPtrValue, PatioDataValue, PatioIntValue}, }; fn data_value_to_bytes(data_value: PatioDataValue) -> Box<[u8]> { super::data_value_to_bytes( &data_value, &target_lexicon::Triple::from_str("x86_64-unknown-linux-gnu").unwrap(), ) } #[test] fn array_is_written_as_elements() { let data_value = PatioDataValue::Array { element_layout: PatioDataLayout::Int32, elements: vec![ PatioDataValue::Int32(PatioIntValue::Literal { value: 0x12345678 }), PatioDataValue::Int32(PatioIntValue::Literal { value: 0x23456789 }), ], }; let bytes = data_value_to_bytes(data_value); assert_eq!( bytes.as_ref(), &[0x78, 0x56, 0x34, 0x12, 0x89, 0x67, 0x45, 0x23] ); } #[test] fn false_is_single_byte_0() { let data_value = PatioDataValue::Bool(false); let bytes = data_value_to_bytes(data_value); assert_eq!(bytes.as_ref(), &[0]); } #[test] fn true_is_single_byte_1() { let data_value = PatioDataValue::Bool(true); let bytes = data_value_to_bytes(data_value); assert_eq!(bytes.as_ref(), &[1]); } #[test] fn byte_is_written_as_single_byte() { let data_value = PatioDataValue::Byte(42); let bytes = data_value_to_bytes(data_value); assert_eq!(bytes.as_ref(), &[42]); } #[test] fn given_pointer_is_64_bits_wide_then_null_data_ptr_is_written_as_64_bit_0() { let target_triple = target_lexicon::Triple::from_str("x86_64-unknown-linux-gnu").unwrap(); let data_value = PatioDataValue::DataPtr(PatioDataPtrValue::Null); let bytes = super::data_value_to_bytes(&data_value, &target_triple); assert_eq!(bytes.as_ref(), &[0, 0, 0, 0, 0, 0, 0, 0]); } #[test] fn given_pointer_is_32_bits_wide_then_null_data_ptr_is_written_as_32_bit_0() { let target_triple = target_lexicon::Triple::from_str("i686-unknown-linux-gnu").unwrap(); let data_value = PatioDataValue::DataPtr(PatioDataPtrValue::Null); let bytes = super::data_value_to_bytes(&data_value, &target_triple); assert_eq!(bytes.as_ref(), &[0, 0, 0, 0]); } #[test] fn given_pointer_is_64_bits_wide_then_int_is_written_with_pointer_width() { let target_triple = target_lexicon::Triple::from_str("x86_64-unknown-linux-gnu").unwrap(); let data_value = PatioDataValue::Int(PatioIntValue::Literal { value: 0x12345678 }); let bytes = super::data_value_to_bytes(&data_value, &target_triple); assert_eq!(bytes.as_ref(), &[0x78, 0x56, 0x34, 0x12, 0, 0, 0, 0]); } #[test] fn given_pointer_is_32_bits_wide_then_int_is_written_with_pointer_width() { let target_triple = target_lexicon::Triple::from_str("i686-unknown-linux-gnu").unwrap(); let data_value = PatioDataValue::Int(PatioIntValue::Literal { value: 0x12345678 }); let bytes = super::data_value_to_bytes(&data_value, &target_triple); assert_eq!(bytes.as_ref(), &[0x78, 0x56, 0x34, 0x12]); } #[test] fn int_32_is_written_as_four_bytes() { let data_value = PatioDataValue::Int32(PatioIntValue::Literal { value: 0x12345678 }); let bytes = data_value_to_bytes(data_value); assert_eq!(bytes.as_ref(), &[0x78, 0x56, 0x34, 0x12]); } #[test] fn struct_is_written_as_elements() { let data_value = PatioDataValue::Struct { elements: vec![ PatioDataValue::Int32(PatioIntValue::Literal { value: 0x12345678 }), PatioDataValue::Int32(PatioIntValue::Literal { value: 0x23456789 }), ], }; let bytes = data_value_to_bytes(data_value); assert_eq!( bytes.as_ref(), &[0x78, 0x56, 0x34, 0x12, 0x89, 0x67, 0x45, 0x23] ); } } #[cfg(test)] mod eval_int_value_test { use std::str::FromStr; use crate::patio::{types::PatioDataLayout, values::PatioIntValue}; fn eval_int_value(int_value: PatioIntValue) -> i32 { super::eval_int_value( &int_value, &target_lexicon::Triple::from_str("x86_64-unknown-linux-gnu").unwrap(), ) } #[test] fn int_value_literal_is_used_verbatim() { let int_value = PatioIntValue::Literal { value: 0x12345678 }; let value = eval_int_value(int_value); assert_eq!(value, 0x12345678); } #[test] fn int_can_have_size_of_layout() { let int_value = PatioIntValue::ByteSizeOf { layout: PatioDataLayout::Int32, }; let value = eval_int_value(int_value); assert_eq!(value, 4); } #[test] fn int_can_have_byte_offset() { let int_value = PatioIntValue::ByteOffsetOf { layout: PatioDataLayout::array(PatioDataLayout::Int32, 7), element_offsets: vec![5], }; let value = eval_int_value(int_value); assert_eq!(value, 20); } }