2017-11-23 00:02:24 -05:00
|
|
|
use spellbook::components::DynamicArray;
|
|
|
|
|
2016-01-06 23:37:18 -05:00
|
|
|
use ::byte_sized::ByteSized;
|
2017-02-15 17:51:13 -05:00
|
|
|
use ::byte_sized::U64_BYTES;
|
2016-01-05 17:46:31 -05:00
|
|
|
use ::converter::Converter;
|
2016-11-03 15:04:29 -04:00
|
|
|
use ::transmutable::Transmutable;
|
2016-01-05 17:46:31 -05:00
|
|
|
|
2015-12-30 17:06:48 -05:00
|
|
|
|
|
|
|
|
|
|
|
/// Defines the current platforms endianess.
|
|
|
|
/// This is can only be big endian or little endian.
|
|
|
|
/// This library does not support a mixed endian setting.
|
|
|
|
#[cfg(target_endian="big")]
|
|
|
|
pub type PlatformEndian = BigEndian;
|
|
|
|
|
|
|
|
/// Defines the current platforms endianess.
|
|
|
|
/// This is can only be BigEndian or LittleEndian.
|
|
|
|
/// This library does not support a mixed endian setting.
|
|
|
|
///
|
|
|
|
/// Defaults to LittleEndian.
|
|
|
|
//#[cfg(target_endian="little")]
|
|
|
|
#[cfg(not(target_endian="big"))]
|
|
|
|
pub type PlatformEndian = LittleEndian;
|
|
|
|
|
2016-06-27 18:12:43 -04:00
|
|
|
/// Defines the endianess to use when communicating
|
|
|
|
/// over a network. This could be either
|
2016-06-17 04:34:36 -04:00
|
|
|
/// of the available endians as long as it is consistent
|
|
|
|
/// across the network. However, RFC1700 decided network
|
|
|
|
/// byte order would be BigEndian. As such, it is now the
|
|
|
|
/// common endian style for networking.
|
|
|
|
pub type NetworkEndian = BigEndian;
|
|
|
|
|
2016-11-03 15:04:29 -04:00
|
|
|
|
|
|
|
|
2016-01-19 12:02:53 -05:00
|
|
|
/// Handles serialization where the most
|
|
|
|
/// significant byte is stored at the lowest address.
|
|
|
|
pub enum BigEndian
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Handles serialization where the most
|
|
|
|
/// significant byte is stored at the lowest address.
|
|
|
|
pub enum LittleEndian
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2016-01-05 17:46:31 -05:00
|
|
|
/// Create an enumeration of the different
|
|
|
|
/// available endianesses.
|
2017-04-06 18:37:23 -04:00
|
|
|
#[derive(Copy, Clone, Eq, Ord, PartialEq, PartialOrd)]
|
2016-01-05 17:46:31 -05:00
|
|
|
pub enum Endianess
|
|
|
|
{
|
|
|
|
/// Referes to BigEndian.
|
2016-04-14 15:08:31 -04:00
|
|
|
Big,
|
2016-01-05 17:46:31 -05:00
|
|
|
|
|
|
|
/// Referes to LittleEndian.
|
2016-04-14 15:08:31 -04:00
|
|
|
Little,
|
2016-01-05 17:46:31 -05:00
|
|
|
|
|
|
|
/// Referes to PlatformEndian. This can be anyone
|
|
|
|
/// of the other available endians depending on
|
|
|
|
/// the platform you are on.
|
2016-06-17 04:34:36 -04:00
|
|
|
Platform,
|
|
|
|
|
|
|
|
/// Referes to NetworkEndian. This could be either
|
|
|
|
/// of the available endians as long as it is consistent
|
|
|
|
/// across the network. However, RFC1700 decided network
|
|
|
|
/// byte order would be BigEndian. As such, it is now the
|
|
|
|
/// common endian style for networking.
|
|
|
|
Network
|
2016-01-05 17:46:31 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-12-30 17:06:48 -05:00
|
|
|
|
|
|
|
/// Handles reading bytes from a given buffer
|
|
|
|
/// and turning them into the requested type.
|
|
|
|
macro_rules! read_bytes
|
|
|
|
{
|
2016-01-02 14:54:03 -05:00
|
|
|
($buffer: expr, $returnType: ident, $convertFunc: ident) =>
|
2015-12-30 17:06:48 -05:00
|
|
|
({
|
2016-01-03 03:22:21 -05:00
|
|
|
// Make sure that there is enough space to read
|
|
|
|
// a value from the buffer.
|
|
|
|
assert!($buffer.len() == $returnType::BYTES);
|
|
|
|
|
2015-12-30 17:06:48 -05:00
|
|
|
unsafe
|
|
|
|
{
|
2016-01-02 14:54:03 -05:00
|
|
|
(*($buffer.as_ptr() as *const $returnType)).$convertFunc()
|
2015-12-30 17:06:48 -05:00
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Handles turning a given number into bytes
|
|
|
|
/// and writing them to a buffer.
|
|
|
|
macro_rules! write_bytes
|
|
|
|
{
|
2016-04-13 23:57:40 -04:00
|
|
|
($buffer: expr, $valueType: ident, $numBytes: expr,
|
|
|
|
$num: expr, $convertFunc: ident) =>
|
2015-12-30 17:06:48 -05:00
|
|
|
({
|
|
|
|
unsafe
|
|
|
|
{
|
2016-01-06 23:37:18 -05:00
|
|
|
let bytes: [u8; $numBytes];
|
2015-12-30 17:06:48 -05:00
|
|
|
|
|
|
|
bytes =
|
2016-01-06 23:37:18 -05:00
|
|
|
mem::transmute::<_, [u8; $numBytes]>($num.$convertFunc());
|
2016-04-14 13:33:07 -04:00
|
|
|
$buffer.extend_from_slice(&bytes);
|
2015-12-30 17:06:48 -05:00
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-01-05 17:46:31 -05:00
|
|
|
impl Converter for BigEndian
|
2015-12-30 17:06:48 -05:00
|
|
|
{
|
|
|
|
fn bytes_to_u16(buffer: &[u8]) -> u16
|
|
|
|
{
|
2017-02-15 17:51:13 -05:00
|
|
|
// Make sure that there is enough space to read
|
|
|
|
// a value from the buffer.
|
|
|
|
assert!(buffer.len() == u16::BYTES);
|
|
|
|
|
|
|
|
pack_big_endian!(buffer, u16, u16::BYTES)
|
2015-12-30 17:06:48 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
fn bytes_to_u32(buffer: &[u8]) -> u32
|
|
|
|
{
|
2017-02-15 17:51:13 -05:00
|
|
|
// Make sure that there is enough space to read
|
|
|
|
// a value from the buffer.
|
|
|
|
assert!(buffer.len() == u32::BYTES);
|
|
|
|
|
|
|
|
pack_big_endian!(buffer, u32, u32::BYTES)
|
2015-12-30 17:06:48 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
fn bytes_to_u64(buffer: &[u8]) -> u64
|
|
|
|
{
|
2017-02-15 17:51:13 -05:00
|
|
|
// Make sure that there is enough space to read
|
|
|
|
// a value from the buffer.
|
|
|
|
assert!(buffer.len() == u64::BYTES);
|
|
|
|
|
|
|
|
pack_big_endian!(buffer, u64, u64::BYTES)
|
2015-12-30 17:06:48 -05:00
|
|
|
}
|
|
|
|
|
2016-01-02 14:54:03 -05:00
|
|
|
fn bytes_to_usize(buffer: &[u8]) -> usize
|
2015-12-30 17:06:48 -05:00
|
|
|
{
|
2017-02-15 17:51:13 -05:00
|
|
|
// Make sure that there is enough space to read
|
|
|
|
// a value from the buffer.
|
|
|
|
assert!(buffer.len() == usize::BYTES);
|
2015-12-30 17:06:48 -05:00
|
|
|
|
2017-02-15 17:51:13 -05:00
|
|
|
pack_big_endian!(buffer, usize, usize::BYTES)
|
2015-12-30 17:06:48 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-11-23 00:02:24 -05:00
|
|
|
fn u16_to_bytes(num: u16) -> DynamicArray<u8>
|
2015-12-30 17:06:48 -05:00
|
|
|
{
|
2017-02-15 17:51:13 -05:00
|
|
|
// Unpack the value into it's byte form.
|
|
|
|
unpack_big_endian!(num, u16::BYTES)
|
2015-12-30 17:06:48 -05:00
|
|
|
}
|
|
|
|
|
2017-11-23 00:02:24 -05:00
|
|
|
fn u32_to_bytes(num: u32) -> DynamicArray<u8>
|
2015-12-30 17:06:48 -05:00
|
|
|
{
|
2017-02-15 17:51:13 -05:00
|
|
|
// Unpack the value into it's byte form.
|
|
|
|
unpack_big_endian!(num, u32::BYTES)
|
2015-12-30 17:06:48 -05:00
|
|
|
}
|
|
|
|
|
2017-11-23 00:02:24 -05:00
|
|
|
fn u64_to_bytes(num: u64) -> DynamicArray<u8>
|
2015-12-30 17:06:48 -05:00
|
|
|
{
|
2017-02-15 17:51:13 -05:00
|
|
|
// Unpack the value into it's byte form.
|
|
|
|
unpack_big_endian!(num, u64::BYTES)
|
2015-12-30 17:06:48 -05:00
|
|
|
}
|
|
|
|
|
2017-11-23 00:02:24 -05:00
|
|
|
fn usize_to_bytes(num: usize) -> DynamicArray<u8>
|
2015-12-30 17:06:48 -05:00
|
|
|
{
|
2017-02-15 17:51:13 -05:00
|
|
|
// Unpack the value into it's byte form.
|
|
|
|
unpack_big_endian!(num, usize::BYTES)
|
2015-12-30 17:06:48 -05:00
|
|
|
}
|
2016-04-13 23:57:40 -04:00
|
|
|
|
|
|
|
fn bytes_to_string(buffer: &[u8]) -> String
|
|
|
|
{
|
|
|
|
let byte_count: u64;
|
|
|
|
let new_string: String;
|
|
|
|
|
|
|
|
// A string array should have atleast a u64 size byte count.
|
|
|
|
assert!(buffer.len() >= U64_BYTES);
|
|
|
|
|
|
|
|
// Strings start with the size of bytes to read as
|
|
|
|
// a u64. So read that in and then we know how many
|
|
|
|
// bytes make up the string.
|
2016-12-18 03:30:25 -05:00
|
|
|
byte_count = BigEndian::bytes_to_u64(&buffer[0..U64_BYTES]);
|
2016-04-13 23:57:40 -04:00
|
|
|
|
|
|
|
if byte_count > 0
|
|
|
|
{
|
|
|
|
match String::from_utf8(buffer[U64_BYTES..(buffer.len()-1)].to_vec())
|
|
|
|
{
|
|
|
|
Ok(string) =>
|
|
|
|
{
|
|
|
|
new_string = string;
|
|
|
|
}
|
|
|
|
|
|
|
|
Err(error) =>
|
|
|
|
{
|
|
|
|
error!("{}", error);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
new_string = String::new();
|
|
|
|
}
|
|
|
|
|
|
|
|
new_string
|
|
|
|
}
|
|
|
|
|
2017-11-23 00:02:24 -05:00
|
|
|
fn string_to_bytes(string: String) -> DynamicArray<u8>
|
2016-04-13 23:57:40 -04:00
|
|
|
{
|
|
|
|
let bytes: &[u8];
|
|
|
|
let byte_count: u64;
|
2017-11-23 00:02:24 -05:00
|
|
|
let mut buffer: DynamicArray<u8>;
|
2016-04-13 23:57:40 -04:00
|
|
|
|
|
|
|
// Turn the string into a byte array.
|
|
|
|
bytes = string.as_bytes();
|
|
|
|
|
|
|
|
// Determine how many bytes will be written
|
|
|
|
// for this string.
|
|
|
|
byte_count = bytes.len() as u64;
|
|
|
|
|
|
|
|
// Make sure the buffer has enough space for this string.
|
2017-11-23 00:02:24 -05:00
|
|
|
buffer = DynamicArray::with_capacity(bytes.len() + U64_BYTES);
|
2016-04-13 23:57:40 -04:00
|
|
|
|
|
|
|
// Add the count to the buffer.
|
2016-04-14 13:33:07 -04:00
|
|
|
buffer.append(&mut BigEndian::u64_to_bytes(byte_count));
|
2016-04-13 23:57:40 -04:00
|
|
|
|
|
|
|
// Add each byte of the string to the buffer.
|
2016-04-14 13:33:07 -04:00
|
|
|
buffer.extend_from_slice(bytes);
|
|
|
|
|
|
|
|
// Return the byte buffer.
|
|
|
|
buffer
|
2016-04-13 23:57:40 -04:00
|
|
|
}
|
2015-12-30 17:06:48 -05:00
|
|
|
}
|
|
|
|
|
2016-11-03 15:04:29 -04:00
|
|
|
|
|
|
|
|
2016-01-05 17:46:31 -05:00
|
|
|
impl Converter for LittleEndian
|
2015-12-30 17:06:48 -05:00
|
|
|
{
|
|
|
|
fn bytes_to_u16(buffer: &[u8]) -> u16
|
|
|
|
{
|
2017-02-15 17:51:13 -05:00
|
|
|
// Make sure that there is enough space to read
|
|
|
|
// a value from the buffer.
|
|
|
|
assert!(buffer.len() == u16::BYTES);
|
|
|
|
|
|
|
|
pack_little_endian!(buffer, u16, u16::BYTES)
|
2015-12-30 17:06:48 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
fn bytes_to_u32(buffer: &[u8]) -> u32
|
|
|
|
{
|
2017-02-15 17:51:13 -05:00
|
|
|
// Make sure that there is enough space to read
|
|
|
|
// a value from the buffer.
|
|
|
|
assert!(buffer.len() == u32::BYTES);
|
|
|
|
|
|
|
|
pack_little_endian!(buffer, u32, u32::BYTES)
|
2015-12-30 17:06:48 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
fn bytes_to_u64(buffer: &[u8]) -> u64
|
|
|
|
{
|
2017-02-15 17:51:13 -05:00
|
|
|
// Make sure that there is enough space to read
|
|
|
|
// a value from the buffer.
|
|
|
|
assert!(buffer.len() == u64::BYTES);
|
|
|
|
|
|
|
|
pack_little_endian!(buffer, u64, u64::BYTES)
|
2015-12-30 17:06:48 -05:00
|
|
|
}
|
|
|
|
|
2016-01-02 14:54:03 -05:00
|
|
|
fn bytes_to_usize(buffer: &[u8]) -> usize
|
2015-12-30 17:06:48 -05:00
|
|
|
{
|
2017-02-15 17:51:13 -05:00
|
|
|
// Make sure that there is enough space to read
|
|
|
|
// a value from the buffer.
|
|
|
|
assert!(buffer.len() == usize::BYTES);
|
2015-12-30 17:06:48 -05:00
|
|
|
|
2017-02-15 17:51:13 -05:00
|
|
|
pack_little_endian!(buffer, usize, usize::BYTES)
|
2015-12-30 17:06:48 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-11-23 00:02:24 -05:00
|
|
|
fn u16_to_bytes(num: u16) -> DynamicArray<u8>
|
2015-12-30 17:06:48 -05:00
|
|
|
{
|
2017-02-15 17:51:13 -05:00
|
|
|
// Unpack the value into it's byte form.
|
|
|
|
unpack_little_endian!(num, u16::BYTES)
|
2015-12-30 17:06:48 -05:00
|
|
|
}
|
|
|
|
|
2017-11-23 00:02:24 -05:00
|
|
|
fn u32_to_bytes(num: u32) -> DynamicArray<u8>
|
2015-12-30 17:06:48 -05:00
|
|
|
{
|
2017-02-15 17:51:13 -05:00
|
|
|
// Unpack the value into it's byte form.
|
|
|
|
unpack_little_endian!(num, u32::BYTES)
|
2015-12-30 17:06:48 -05:00
|
|
|
}
|
|
|
|
|
2017-11-23 00:02:24 -05:00
|
|
|
fn u64_to_bytes(num: u64) -> DynamicArray<u8>
|
2015-12-30 17:06:48 -05:00
|
|
|
{
|
2017-02-15 17:51:13 -05:00
|
|
|
// Unpack the value into it's byte form.
|
|
|
|
unpack_little_endian!(num, u64::BYTES)
|
2015-12-30 17:06:48 -05:00
|
|
|
}
|
|
|
|
|
2017-11-23 00:02:24 -05:00
|
|
|
fn usize_to_bytes(num: usize) -> DynamicArray<u8>
|
2015-12-30 17:06:48 -05:00
|
|
|
{
|
2017-02-15 17:51:13 -05:00
|
|
|
// Unpack the value into it's byte form.
|
|
|
|
unpack_little_endian!(num, usize::BYTES)
|
2015-12-30 17:06:48 -05:00
|
|
|
}
|
2016-04-13 23:57:40 -04:00
|
|
|
|
|
|
|
fn bytes_to_string(buffer: &[u8]) -> String
|
|
|
|
{
|
|
|
|
let byte_count: u64;
|
|
|
|
let new_string: String;
|
|
|
|
|
|
|
|
// A string array should have atleast a u64 size byte count.
|
|
|
|
assert!(buffer.len() >= U64_BYTES);
|
|
|
|
|
|
|
|
// Strings start with the size of bytes to read as
|
|
|
|
// a u64. So read that in and then we know how many
|
|
|
|
// bytes make up the string.
|
2017-02-15 17:51:13 -05:00
|
|
|
byte_count = LittleEndian::bytes_to_u64(&buffer[0..U64_BYTES]);
|
2016-04-13 23:57:40 -04:00
|
|
|
|
|
|
|
if byte_count > 0
|
|
|
|
{
|
|
|
|
match String::from_utf8(buffer[U64_BYTES..(buffer.len()-1)].to_vec())
|
|
|
|
{
|
|
|
|
Ok(string) =>
|
|
|
|
{
|
|
|
|
new_string = string;
|
|
|
|
}
|
|
|
|
|
|
|
|
Err(error) =>
|
|
|
|
{
|
|
|
|
error!("{}", error);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
new_string = String::new();
|
|
|
|
}
|
|
|
|
|
|
|
|
new_string
|
|
|
|
}
|
|
|
|
|
2017-11-23 00:02:24 -05:00
|
|
|
fn string_to_bytes(string: String) -> DynamicArray<u8>
|
2016-04-13 23:57:40 -04:00
|
|
|
{
|
|
|
|
let bytes: &[u8];
|
|
|
|
let byte_count: u64;
|
2017-11-23 00:02:24 -05:00
|
|
|
let mut buffer: DynamicArray<u8>;
|
2016-04-13 23:57:40 -04:00
|
|
|
|
|
|
|
// Turn the string into a byte array.
|
|
|
|
bytes = string.as_bytes();
|
|
|
|
|
|
|
|
// Determine how many bytes will be written
|
|
|
|
// for this string.
|
2016-04-14 13:33:07 -04:00
|
|
|
byte_count = bytes.len() as u64;
|
2016-04-13 23:57:40 -04:00
|
|
|
|
|
|
|
// Make sure the buffer has enough space for this string.
|
2017-11-23 00:02:24 -05:00
|
|
|
buffer = DynamicArray::with_capacity(bytes.len() + U64_BYTES);
|
2016-04-13 23:57:40 -04:00
|
|
|
|
|
|
|
// Add the count to the buffer.
|
2016-04-14 13:33:07 -04:00
|
|
|
buffer.append(&mut LittleEndian::u64_to_bytes(byte_count));
|
2016-04-13 23:57:40 -04:00
|
|
|
|
|
|
|
// Add each byte of the string to the buffer.
|
2016-04-14 13:33:07 -04:00
|
|
|
buffer.extend_from_slice(bytes);
|
|
|
|
|
|
|
|
// Return the byte buffer.
|
|
|
|
buffer
|
2016-04-13 23:57:40 -04:00
|
|
|
}
|
2015-12-30 17:06:48 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-12-18 03:20:31 -05:00
|
|
|
impl ::std::fmt::Debug for Endianess
|
|
|
|
{
|
|
|
|
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result
|
|
|
|
{
|
|
|
|
::std::fmt::Display::fmt(self, f)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ::std::fmt::Display for Endianess
|
|
|
|
{
|
|
|
|
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result
|
|
|
|
{
|
|
|
|
match *self
|
|
|
|
{
|
|
|
|
Endianess::Big =>
|
|
|
|
{
|
|
|
|
write!(f, "Big Endian")
|
|
|
|
}
|
|
|
|
|
|
|
|
Endianess::Little =>
|
|
|
|
{
|
|
|
|
write!(f, "Little Endian")
|
|
|
|
}
|
|
|
|
|
|
|
|
Endianess::Network =>
|
|
|
|
{
|
|
|
|
write!(f, "Network Endian")
|
|
|
|
}
|
|
|
|
|
|
|
|
Endianess::Platform =>
|
|
|
|
{
|
|
|
|
write!(f, "Big Endian")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Turns a Network order value to a Platform order value.
|
|
|
|
pub fn network_to_platform_order<T>(val: T) -> T
|
|
|
|
where T: Transmutable
|
|
|
|
{
|
|
|
|
// Determine what endianess the Platform is using.
|
|
|
|
if cfg!(target_endian="big")
|
|
|
|
{
|
|
|
|
// Network endianess is Big endian, so they are the same.
|
|
|
|
// Just return the value.
|
|
|
|
val
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-07-01 20:18:00 -04:00
|
|
|
T::from_endian_bytes(val.as_bytes().as_slice(), Endianess::Network)
|
2016-12-18 03:20:31 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Turns a Platform order value to a Network order value.
|
|
|
|
pub fn platform_to_network_order<T>(val: T) -> T
|
|
|
|
where T: Transmutable
|
|
|
|
{
|
|
|
|
// Determine what endianess the Platform is using.
|
|
|
|
if cfg!(target_endian="big")
|
|
|
|
{
|
|
|
|
// Network endianess is Big endian, so they are the same.
|
|
|
|
// Just return the value.
|
|
|
|
val
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-07-01 20:18:00 -04:00
|
|
|
T::from_endian_bytes(val.as_bytes().as_slice(), Endianess::Network)
|
2016-12-18 03:20:31 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the Endianess used for network transmission.
|
|
|
|
pub fn get_network_endianess() -> Endianess
|
|
|
|
{
|
|
|
|
Endianess::Big
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the Endianess of the current platform.
|
|
|
|
pub fn get_platform_endianess() -> Endianess
|
|
|
|
{
|
|
|
|
if cfg!(target_endian="big")
|
|
|
|
{
|
|
|
|
Endianess::Big
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Endianess::Little
|
|
|
|
}
|
|
|
|
}
|