use haiku_sys::*;
use std::mem;
use std::ffi::{CStr, CString};
use support::{ErrorKind, HaikuError, Result};
pub trait Flattenable<T> {
fn type_code() -> u32;
fn is_fixed_size() -> bool;
fn flattened_size(&self) -> usize;
fn flatten(&self) -> Vec<u8>;
fn unflatten(&[u8]) -> Result<T>;
}
impl Flattenable<bool> for bool {
fn type_code() -> u32 {
B_BOOL_TYPE
}
fn is_fixed_size() -> bool {
true
}
fn flattened_size(&self) -> usize {
1
}
fn flatten(&self) -> Vec<u8> {
if *self {
vec!(1 as u8)
} else {
vec!(0 as u8)
}
}
fn unflatten(buffer: &[u8]) -> Result<bool> {
if buffer.len() != 1 {
Err(HaikuError::from(ErrorKind::InvalidData))
} else if buffer[0] == 0 {
Ok(false)
} else {
Ok(true)
}
}
}
impl Flattenable<i8> for i8 {
fn type_code() -> u32 {
B_INT8_TYPE
}
fn is_fixed_size() -> bool {
true
}
fn flattened_size(&self) -> usize {
1
}
fn flatten(&self) -> Vec<u8> {
vec!(*self as u8)
}
fn unflatten(buffer: &[u8]) -> Result<i8> {
if buffer.len() != 1 {
Err(HaikuError::from(ErrorKind::InvalidData))
} else {
Ok(buffer[0] as i8)
}
}
}
impl Flattenable<i16> for i16 {
fn type_code() -> u32 {
B_INT16_TYPE
}
fn flattened_size(&self) -> usize {
2
}
fn is_fixed_size() -> bool {
true
}
fn flatten(&self) -> Vec<u8> {
let data = unsafe { mem::transmute::<i16, [u8; 2]>(*self) };
data.to_vec()
}
fn unflatten(buffer: &[u8]) -> Result<i16> {
if buffer.len() != 2 {
Err(HaikuError::from(ErrorKind::InvalidData))
} else {
Ok(buffer.iter().rev().fold(0, |acc, &b| (acc << 8) | b as i16))
}
}
}
impl Flattenable<i32> for i32 {
fn type_code() -> u32 {
B_INT32_TYPE
}
fn flattened_size(&self) -> usize {
4
}
fn is_fixed_size() -> bool {
true
}
fn flatten(&self) -> Vec<u8> {
let data = unsafe { mem::transmute::<i32, [u8; 4]>(*self) };
data.to_vec()
}
fn unflatten(buffer: &[u8]) -> Result<i32> {
if buffer.len() != 4 {
Err(HaikuError::from(ErrorKind::InvalidData))
} else {
Ok(buffer.iter().rev().fold(0, |acc, &b| (acc << 8) | b as i32))
}
}
}
impl Flattenable<i64> for i64 {
fn type_code() -> u32 {
B_INT64_TYPE
}
fn flattened_size(&self) -> usize {
8
}
fn is_fixed_size() -> bool {
true
}
fn flatten(&self) -> Vec<u8> {
let data = unsafe { mem::transmute::<i64, [u8; 8]>(*self) };
data.to_vec()
}
fn unflatten(buffer: &[u8]) -> Result<i64> {
if buffer.len() != 8 {
Err(HaikuError::from(ErrorKind::InvalidData))
} else {
Ok(buffer.iter().rev().fold(0, |acc, &b| (acc << 8) | b as i64))
}
}
}
impl Flattenable<u8> for u8 {
fn type_code() -> u32 {
B_UINT8_TYPE
}
fn is_fixed_size() -> bool {
true
}
fn flattened_size(&self) -> usize {
1
}
fn flatten(&self) -> Vec<u8> {
vec!(*self)
}
fn unflatten(buffer: &[u8]) -> Result<u8> {
if buffer.len() != 1 {
Err(HaikuError::from(ErrorKind::InvalidData))
} else {
Ok(buffer[0])
}
}
}
impl Flattenable<u16> for u16 {
fn type_code() -> u32 {
B_UINT16_TYPE
}
fn flattened_size(&self) -> usize {
2
}
fn is_fixed_size() -> bool {
true
}
fn flatten(&self) -> Vec<u8> {
let data = unsafe { mem::transmute::<u16, [u8; 2]>(*self) };
data.to_vec()
}
fn unflatten(buffer: &[u8]) -> Result<u16> {
if buffer.len() != 2 {
Err(HaikuError::from(ErrorKind::InvalidData))
} else {
Ok(buffer.iter().rev().fold(0, |acc, &b| (acc << 8) | b as u16))
}
}
}
impl Flattenable<u32> for u32 {
fn type_code() -> u32 {
B_UINT32_TYPE
}
fn flattened_size(&self) -> usize {
4
}
fn is_fixed_size() -> bool {
true
}
fn flatten(&self) -> Vec<u8> {
let data = unsafe { mem::transmute::<u32, [u8; 4]>(*self) };
data.to_vec()
}
fn unflatten(buffer: &[u8]) -> Result<u32> {
if buffer.len() != 4 {
Err(HaikuError::from(ErrorKind::InvalidData))
} else {
Ok(buffer.iter().rev().fold(0, |acc, &b| (acc << 8) | b as u32))
}
}
}
impl Flattenable<u64> for u64 {
fn type_code() -> u32 {
B_UINT64_TYPE
}
fn flattened_size(&self) -> usize {
8
}
fn is_fixed_size() -> bool {
true
}
fn flatten(&self) -> Vec<u8> {
let data = unsafe { mem::transmute::<u64, [u8; 8]>(*self) };
data.to_vec()
}
fn unflatten(buffer: &[u8]) -> Result<u64> {
if buffer.len() != 8 {
Err(HaikuError::from(ErrorKind::InvalidData))
} else {
Ok(buffer.iter().rev().fold(0, |acc, &b| (acc << 8) | b as u64))
}
}
}
impl Flattenable<f32> for f32 {
fn type_code() -> u32 {
B_FLOAT_TYPE
}
fn flattened_size(&self) -> usize {
4
}
fn is_fixed_size() -> bool {
true
}
fn flatten(&self) -> Vec<u8> {
let data = unsafe { mem::transmute::<f32, [u8; 4]>(*self) };
data.to_vec()
}
fn unflatten(buffer: &[u8]) -> Result<f32> {
if buffer.len() != 4 {
Err(HaikuError::from(ErrorKind::InvalidData))
} else {
let tmp: u32 = buffer.iter().rev().fold(0, |acc, &b| (acc << 8) | b as u32);
let tmp: f32 = unsafe { mem::transmute::<u32, f32>(tmp) };
Ok(tmp)
}
}
}
impl Flattenable<f64> for f64 {
fn type_code() -> u32 {
B_DOUBLE_TYPE
}
fn flattened_size(&self) -> usize {
8
}
fn is_fixed_size() -> bool {
true
}
fn flatten(&self) -> Vec<u8> {
let data = unsafe { mem::transmute::<f64, [u8; 8]>(*self) };
data.to_vec()
}
fn unflatten(buffer: &[u8]) -> Result<f64> {
if buffer.len() != 8 {
Err(HaikuError::from(ErrorKind::InvalidData))
} else {
let tmp: u64 = buffer.iter().rev().fold(0, |acc, &b| (acc << 8) | b as u64);
let tmp: f64 = unsafe { mem::transmute::<u64, f64>(tmp) };
Ok(tmp)
}
}
}
impl Flattenable<String> for String {
fn type_code() -> u32 {
B_STRING_TYPE
}
fn flattened_size(&self) -> usize {
self.as_bytes().len() + 1 }
fn is_fixed_size() -> bool {
false
}
fn flatten(&self) -> Vec<u8> {
let data = CString::new(self.clone()).unwrap();
data.into_bytes_with_nul()
}
fn unflatten(buffer: &[u8]) -> Result<String> {
let s = match CStr::from_bytes_with_nul(buffer) {
Ok(s) => s,
Err(e) => return Err(HaikuError::new(ErrorKind::InvalidData, format!("{}", e)))
};
let s_vec = s.to_bytes().to_vec();
match String::from_utf8(s_vec) {
Ok(s) => Ok(s),
Err(_) => Err(HaikuError::new(ErrorKind::InvalidData, "Invalid UTF8 characters"))
}
}
}
#[test]
fn test_flattenable_primitives() {
let value: u8 = 150;
let flattened_value = value.flatten();
assert_eq!(flattened_value.len(), value.flattened_size());
assert_eq!(value, flattened_value[0]);
let value: i64 = -3_223_372_036_854_775_807;
let flattened_value = value.flatten();
let unflattened_value = i64::unflatten(&flattened_value).unwrap();
assert_eq!(value, unflattened_value);
let value = "This is a test string".to_string();
let flattened_value = value.flatten();
let unflattened_value = String::unflatten(&flattened_value).unwrap();
assert_eq!(value, unflattened_value);
}