#[allow(unused_imports)] // right now just used in feature flag use core; use alloc::{SliceWrapper, Allocator, SliceWrapperMut}; pub use super::input_pair::{InputPair,InputReference, InputReferenceMut}; use super::histogram; #[derive(Debug,Copy,Clone,Default)] pub struct BlockSwitch(pub u8); // Commands that can instantiate as a no-op should implement this. pub trait Nop { fn nop() -> T; } impl BlockSwitch { #[inline(always)] pub fn new(block_type: u8) -> Self { BlockSwitch(block_type) } #[inline(always)] pub fn block_type(&self) -> u8 { self.0 } } #[derive(Debug,Copy,Clone,Default)] pub struct LiteralBlockSwitch(pub BlockSwitch, pub u8); impl LiteralBlockSwitch { pub fn new(block_type: u8, stride: u8) -> Self { LiteralBlockSwitch(BlockSwitch::new(block_type), stride) } #[inline(always)] pub fn block_type(&self) -> u8 { self.0.block_type() } #[inline(always)] pub fn stride(&self) -> u8 { self.1 } #[inline(always)] pub fn update_stride(&mut self, new_stride: u8) { self.1 = new_stride; } } pub const LITERAL_PREDICTION_MODE_SIGN: u8 = 3; pub const LITERAL_PREDICTION_MODE_UTF8: u8 = 2; pub const LITERAL_PREDICTION_MODE_MSB6: u8 = 1; pub const LITERAL_PREDICTION_MODE_LSB6: u8 = 0; #[derive(Default, Copy, Clone, Debug, PartialEq, Eq, Hash)] pub struct LiteralPredictionModeNibble(pub u8); impl LiteralPredictionModeNibble { #[inline(always)] pub fn new(prediction_mode: u8) -> Result { if prediction_mode < 16 { return Ok(LiteralPredictionModeNibble(prediction_mode)); } return Err(()); } #[inline(always)] pub fn prediction_mode(&self) -> u8 { self.0 } #[inline(always)] pub fn signed() -> Self { LiteralPredictionModeNibble(LITERAL_PREDICTION_MODE_SIGN) } #[inline(always)] pub fn utf8() -> Self { LiteralPredictionModeNibble(LITERAL_PREDICTION_MODE_UTF8) } #[inline(always)] pub fn msb6() -> Self { LiteralPredictionModeNibble(LITERAL_PREDICTION_MODE_MSB6) } #[inline(always)] pub fn lsb6() -> Self { LiteralPredictionModeNibble(LITERAL_PREDICTION_MODE_LSB6) } #[inline(always)] pub fn to_context_enum(&self) -> Result{ match self.0 { LITERAL_PREDICTION_MODE_LSB6 => Ok(histogram::ContextType::CONTEXT_LSB6), LITERAL_PREDICTION_MODE_MSB6 => Ok(histogram::ContextType::CONTEXT_MSB6), LITERAL_PREDICTION_MODE_UTF8 => Ok(histogram::ContextType::CONTEXT_UTF8), LITERAL_PREDICTION_MODE_SIGN => Ok(histogram::ContextType::CONTEXT_SIGNED), _ => Err(()), } } } pub const NUM_SPEED_VALUES: usize = 12; pub const NUM_MIXING_VALUES: usize = 16 * 256 + 16 * 256; pub const NUM_PREDMODE_SETUP_VALUES: usize = 4; pub const RESERVED_OFFSET: usize = 3; pub const ADV_CONTEXT_MAP_OFFSET: usize = 2; pub const MIXING_MATH_OFFSET: usize = 1; pub const PREDMODE_OFFSET: usize = 0; pub const MIXING_OFFSET:usize = NUM_PREDMODE_SETUP_VALUES + PREDMODE_OFFSET; pub const SPEED_OFFSET: usize = MIXING_OFFSET + NUM_MIXING_VALUES; pub const DISTANCE_CONTEXT_MAP_OFFSET: usize = SPEED_OFFSET + NUM_SPEED_VALUES; pub const MAX_PREDMODE_SPEED_AND_DISTANCE_CONTEXT_MAP_SIZE: usize = DISTANCE_CONTEXT_MAP_OFFSET + 256 * 4; pub const MAX_LITERAL_CONTEXT_MAP_SIZE: usize = 256 * 64; pub const MAX_ADV_LITERAL_CONTEXT_MAP_SIZE: usize = 256 * 64 * 2; #[derive(Debug)] pub struct PredictionModeContextMap> { pub literal_context_map: SliceType, pub predmode_speed_and_distance_context_map: SliceType, } impl+SliceWrapperMut> PredictionModeContextMap { #[inline] pub fn distance_context_map_mut(&mut self) -> &mut [u8] { self.predmode_speed_and_distance_context_map.slice_mut().split_at_mut(DISTANCE_CONTEXT_MAP_OFFSET).1 } #[inline] pub fn set_stride_context_speed(&mut self, speed_max: [(u16, u16);2]) { let cm_slice = self.predmode_speed_and_distance_context_map.slice_mut(); for high in 0..2 { cm_slice[Self::stride_context_speed_offset()+high] = Self::u16_to_f8(speed_max[high].0); cm_slice[Self::stride_context_speed_max_offset()+high] = Self::u16_to_f8(speed_max[high].1); } } #[inline] pub fn set_context_map_speed(&mut self, speed_max: [(u16, u16);2]) { let cm_slice = self.predmode_speed_and_distance_context_map.slice_mut(); for high in 0..2 { cm_slice[Self::context_map_speed_offset()+high] = Self::u16_to_f8(speed_max[high].0); cm_slice[Self::context_map_speed_max_offset()+high] = Self::u16_to_f8(speed_max[high].1); } } pub fn set_mixing_math(&mut self, math_enum: u8) { let cm_slice = self.predmode_speed_and_distance_context_map.slice_mut(); cm_slice[MIXING_MATH_OFFSET] = math_enum; } pub fn set_adv_context_map(&mut self, is_adv: u8) { let cm_slice = self.predmode_speed_and_distance_context_map.slice_mut(); cm_slice[ADV_CONTEXT_MAP_OFFSET] = is_adv; } #[inline] pub fn set_mixing_values(&mut self, mixing_mask: &[u8; NUM_MIXING_VALUES]) { let cm_slice = self.predmode_speed_and_distance_context_map.slice_mut(); cm_slice[MIXING_OFFSET..(MIXING_OFFSET + NUM_MIXING_VALUES)].clone_from_slice(&mixing_mask[..]); } #[inline] pub fn get_mixing_values_mut(&mut self) -> &mut [u8] { let cm_slice = self.predmode_speed_and_distance_context_map.slice_mut(); &mut cm_slice[MIXING_OFFSET..(MIXING_OFFSET + NUM_MIXING_VALUES)] } #[inline] pub fn set_combined_stride_context_speed(&mut self, speed_max: [(u16, u16);2]) { let cm_slice = self.predmode_speed_and_distance_context_map.slice_mut(); for high in 0..2 { cm_slice[Self::combined_stride_context_speed_offset()+high] = Self::u16_to_f8(speed_max[high].0); cm_slice[Self::combined_stride_context_speed_max_offset()+high] = Self::u16_to_f8(speed_max[high].1); } } pub fn set_literal_prediction_mode(&mut self, val: LiteralPredictionModeNibble) { let cm_slice = self.predmode_speed_and_distance_context_map.slice_mut(); cm_slice[PREDMODE_OFFSET] = val.0; } } impl> PredictionModeContextMap { #[inline] pub fn from_mut>(other: PredictionModeContextMap) -> PredictionModeContextMap where SliceType: From{ PredictionModeContextMap::{ literal_context_map:SliceType::from(other.literal_context_map), predmode_speed_and_distance_context_map: SliceType::from(other.predmode_speed_and_distance_context_map), } } #[inline] pub fn get_mixing_values(&self) -> &[u8] { let cm_slice = self.predmode_speed_and_distance_context_map.slice(); &cm_slice[MIXING_OFFSET..(MIXING_OFFSET + NUM_MIXING_VALUES)] } #[inline] pub fn get_mixing_math(&self) ->u8 { let cm_slice = self.predmode_speed_and_distance_context_map.slice(); if cm_slice.len() <= MIXING_MATH_OFFSET { return 1; } cm_slice[MIXING_MATH_OFFSET] } #[inline] pub fn get_is_adv_context_map(&self) -> u8 { let cm_slice = self.predmode_speed_and_distance_context_map.slice(); if cm_slice.len() <= ADV_CONTEXT_MAP_OFFSET { return 0; } cm_slice[ADV_CONTEXT_MAP_OFFSET] } #[inline] pub fn has_context_speeds(&self) -> bool { self.predmode_speed_and_distance_context_map.slice().len() >= DISTANCE_CONTEXT_MAP_OFFSET } #[inline] pub fn size_of_combined_array(distance_context_map_size: usize) -> usize { distance_context_map_size + DISTANCE_CONTEXT_MAP_OFFSET } #[inline] pub fn context_speeds_standard_len(&self) -> usize { NUM_SPEED_VALUES } #[inline] pub fn context_speeds_f8(&self) -> &[u8] { &self.predmode_speed_and_distance_context_map.slice()[SPEED_OFFSET..DISTANCE_CONTEXT_MAP_OFFSET] } #[inline] pub fn distance_context_map(&self) -> &[u8] { self.predmode_speed_and_distance_context_map.slice().split_at(DISTANCE_CONTEXT_MAP_OFFSET).1 } #[inline] pub fn f8_to_u16(data: u8) -> u16 { self::u8_to_speed(data) } #[inline] pub fn u16_to_f8(data: u16) -> u8 { self::speed_to_u8(data) } #[inline] pub fn stride_context_speed_offset() -> usize { SPEED_OFFSET } #[inline] pub fn stride_context_speed_max_offset() -> usize { SPEED_OFFSET + 2 } #[inline] pub fn context_map_speed_offset() -> usize { SPEED_OFFSET + 4 } #[inline] pub fn context_map_speed_max_offset() -> usize { SPEED_OFFSET + 6 } #[inline] pub fn combined_stride_context_speed_offset() -> usize { SPEED_OFFSET + 8 } #[inline] pub fn combined_stride_context_speed_max_offset() -> usize { SPEED_OFFSET + 10 } #[inline] pub fn literal_prediction_mode(&self) -> LiteralPredictionModeNibble { let cm_slice = self.predmode_speed_and_distance_context_map.slice(); if PREDMODE_OFFSET < cm_slice.len() { LiteralPredictionModeNibble(cm_slice[PREDMODE_OFFSET]) } else { LiteralPredictionModeNibble::default() } } pub fn stride_context_speed(&self) -> [(u16, u16);2] { let v = self.stride_context_speed_f8(); [(self::u8_to_speed(v[0].0), self::u8_to_speed(v[0].1)), (self::u8_to_speed(v[1].0), self::u8_to_speed(v[1].1))] } pub fn context_map_speed(&self) -> [(u16, u16);2] { let v = self.context_map_speed_f8(); [(self::u8_to_speed(v[0].0), self::u8_to_speed(v[0].1)), (self::u8_to_speed(v[1].0), self::u8_to_speed(v[1].1))] } pub fn combined_stride_context_speed(&self) -> [(u16, u16);2] { let v = self.combined_stride_context_speed_f8(); [(self::u8_to_speed(v[0].0), self::u8_to_speed(v[0].1)), (self::u8_to_speed(v[1].0), self::u8_to_speed(v[1].1))] } #[inline] pub fn stride_context_speed_f8(&self) -> [(u8, u8);2] { let cm_slice = self.predmode_speed_and_distance_context_map.slice(); let low_speed = cm_slice[Self::stride_context_speed_offset()]; let high_speed = cm_slice[Self::stride_context_speed_offset() + 1]; let low_max = cm_slice[Self::stride_context_speed_max_offset()]; let high_max = cm_slice[Self::stride_context_speed_max_offset() + 1]; [(low_speed, low_max), (high_speed, high_max)] } #[inline] pub fn combined_stride_context_speed_f8(&self) -> [(u8, u8);2] { let cm_slice = self.predmode_speed_and_distance_context_map.slice(); let low_speed = cm_slice[Self::combined_stride_context_speed_offset()]; let high_speed = cm_slice[Self::combined_stride_context_speed_offset() + 1]; let low_max = cm_slice[Self::combined_stride_context_speed_max_offset()]; let high_max = cm_slice[Self::combined_stride_context_speed_max_offset() + 1]; [(low_speed, low_max), (high_speed, high_max)] } #[inline] pub fn context_map_speed_f8(&self) -> [(u8, u8);2] { let cm_slice = self.predmode_speed_and_distance_context_map.slice(); let low_speed = cm_slice[Self::context_map_speed_offset()]; let high_speed = cm_slice[Self::context_map_speed_offset() + 1]; let low_max = cm_slice[Self::context_map_speed_max_offset()]; let high_max = cm_slice[Self::context_map_speed_max_offset() + 1]; [(low_speed, low_max), (high_speed, high_max)] } } impl+Clone> Clone for PredictionModeContextMap { #[inline(always)] fn clone(&self) -> Self { PredictionModeContextMap:: { literal_context_map:self.literal_context_map.clone(), predmode_speed_and_distance_context_map:self.predmode_speed_and_distance_context_map.clone(), } } } impl+Clone+Copy> Copy for PredictionModeContextMap { } #[derive(Debug,Clone,Copy)] pub struct CopyCommand { pub distance: u32, pub num_bytes: u32, } impl Nop for CopyCommand { #[inline(always)] fn nop() -> Self { CopyCommand { distance: 1, num_bytes: 0 } } } #[derive(Debug,Clone,Copy)] pub struct DictCommand { pub word_size: u8, pub transform: u8, pub final_size: u8, pub empty: u8, pub word_id: u32, } impl Nop for DictCommand { #[inline(always)] fn nop() -> Self { DictCommand { word_size: 0, transform: 0, final_size: 0, empty: 1, word_id: 0 } } } #[derive(Debug)] #[cfg(not(feature="external-literal-probability"))] pub struct FeatureFlagSliceType >(core::marker::PhantomData); #[cfg(not(feature="external-literal-probability"))] impl> SliceWrapper for FeatureFlagSliceType { fn slice(&self) -> &[u8] { &[] } } #[cfg(not(feature="external-literal-probability"))] impl+Default> Default for FeatureFlagSliceType { fn default() -> Self { FeatureFlagSliceType::(core::marker::PhantomData::::default()) } } #[derive(Debug)] #[cfg(feature="external-literal-probability")] pub struct FeatureFlagSliceType >(pub SliceType); #[cfg(feature="external-literal-probability")] impl> SliceWrapper for FeatureFlagSliceType { #[inline(always)] fn slice(&self) -> &[u8] { self.0.slice() } } #[cfg(feature="external-literal-probability")] impl+Default> Default for FeatureFlagSliceType { #[inline(always)] fn default() -> Self { FeatureFlagSliceType::(SliceType::default()) } } impl+Clone> Clone for FeatureFlagSliceType { #[inline(always)] fn clone(&self) -> Self { FeatureFlagSliceType::(self.0.clone()) } } impl+Clone+Copy> Copy for FeatureFlagSliceType { } #[derive(Debug)] pub struct LiteralCommand> { pub data: SliceType, pub prob: FeatureFlagSliceType, pub high_entropy: bool, // this block of bytes is high entropy with a few patterns never seen again; adapt slower } impl> SliceWrapper for LiteralCommand { #[inline(always)] fn slice(&self) -> &[u8] { self.data.slice() } } impl+SliceWrapperMut> SliceWrapperMut for LiteralCommand { #[inline(always)] fn slice_mut(&mut self) -> &mut [u8] { self.data.slice_mut() } } impl+Default> Nop> for LiteralCommand { #[inline(always)] fn nop() -> Self { LiteralCommand { data: SliceType::default(), prob: FeatureFlagSliceType::::default(), high_entropy: false, } } } impl+Clone> Clone for LiteralCommand { #[inline(always)] fn clone(&self) -> LiteralCommand{ LiteralCommand::{data:self.data.clone(), prob:self.prob.clone(),high_entropy: self.high_entropy.clone(),} } } impl+Clone+Copy> Copy for LiteralCommand { } #[derive(Debug)] pub enum Command > { Copy(CopyCommand), Dict(DictCommand), Literal(LiteralCommand), BlockSwitchCommand(BlockSwitch), BlockSwitchLiteral(LiteralBlockSwitch), BlockSwitchDistance(BlockSwitch), PredictionMode(PredictionModeContextMap), } impl+Default> Command { #[inline] pub fn free_array(&mut self, apply_func: &mut F) where F: FnMut(SliceType) { match self { &mut Command::Literal(ref mut lit) => { apply_func(core::mem::replace(&mut lit.data, SliceType::default())) }, &mut Command::PredictionMode(ref mut pm) => { apply_func(core::mem::replace(&mut pm.literal_context_map, SliceType::default())); apply_func(core::mem::replace(&mut pm.predmode_speed_and_distance_context_map, SliceType::default())); }, _ => {}, } } } impl> Default for Command { #[inline(always)] fn default() -> Self { Command::::nop() } } impl> Nop> for Command { #[inline(always)] fn nop() -> Command { Command::Copy(CopyCommand::nop()) } } impl+Clone> Clone for Command { #[inline] fn clone(&self) -> Command{ match self { &Command::Copy(ref copy) => Command::Copy(copy.clone()), &Command::Dict(ref dict) => Command::Dict(dict.clone()), &Command::Literal(ref literal) => Command::Literal(literal.clone()), &Command::BlockSwitchCommand(ref switch) => Command::BlockSwitchCommand(switch.clone()), &Command::BlockSwitchLiteral(ref switch) => Command::BlockSwitchLiteral(switch.clone()), &Command::BlockSwitchDistance(ref switch) => Command::BlockSwitchDistance(switch.clone()), &Command::PredictionMode(ref pm) => Command::PredictionMode(pm.clone()), } } } impl+Clone+Copy> Copy for Command { } #[inline(always)] pub fn free_cmd_inline> (xself: &mut Command, m8: &mut SliceTypeAllocator) { match *xself { Command::Literal(ref mut lit) => { m8.free_cell(core::mem::replace(&mut lit.data, SliceTypeAllocator::AllocatedMemory::default())) }, Command::PredictionMode(ref mut pm) => { m8.free_cell(core::mem::replace(&mut pm.literal_context_map, SliceTypeAllocator::AllocatedMemory::default())); m8.free_cell(core::mem::replace(&mut pm.predmode_speed_and_distance_context_map, SliceTypeAllocator::AllocatedMemory::default())); }, Command::Dict(_) | Command::Copy(_) | Command::BlockSwitchCommand(_) | Command::BlockSwitchLiteral(_) | Command::BlockSwitchDistance(_) => {}, } } #[inline(never)] pub fn free_cmd> (xself: &mut Command, m8: &mut SliceTypeAllocator) { free_cmd_inline(xself, m8) } #[derive(Clone, Copy, Default, Debug)] pub struct SliceOffset(pub usize, pub u32); impl SliceWrapper for SliceOffset { fn slice(&self) -> &[u8] { // not perfect--shouldn't be calling this without thawing the wrapper &[] } } pub trait Freezable { fn freeze(&self) -> SliceOffset; } pub trait Unfreezable { fn thaw<'a>(&self, data: &'a [u8]) -> InputReference<'a>; fn thaw_mut<'a>(&self, data: &'a mut [u8]) -> InputReferenceMut<'a>; fn thaw_pair<'a>(&self, pair: &InputPair<'a>) -> Result, ()>; } impl<'a> From> for SliceOffset { fn from(f: InputReference<'a>) -> Self { debug_assert!(f.data.len() <= 0xffffffff); SliceOffset(f.orig_offset, f.data.len() as u32) } } impl Unfreezable for SliceOffset { fn thaw<'a>(&self, data: &'a [u8]) -> InputReference<'a> { InputReference{ data: data.split_at(self.0).1.split_at(self.1 as usize).0, orig_offset: self.0, } } fn thaw_mut<'a>(&self, data: &'a mut [u8]) -> InputReferenceMut<'a> { InputReferenceMut{ data: data.split_at_mut(self.0).1.split_at_mut(self.1 as usize).0, orig_offset: self.0, } } fn thaw_pair<'a>(&self, pair: &InputPair<'a>) -> Result, ()> { if self.0 >= pair.1.orig_offset { return Ok(InputReference{ data: pair.1.data.split_at(self.0 - pair.1.orig_offset).1.split_at(self.1 as usize).0, orig_offset: self.0, }); } let offset = self.0 - pair.0.orig_offset; if offset + self.1 as usize <= pair.0.data.len() { // overlap Ok(InputReference{ data: pair.0.data.split_at(offset).1.split_at(self.1 as usize).0, orig_offset: self.0, }) } else { Err(()) } } } impl SliceOffset { pub fn offset(&self) -> usize { self.0 } pub fn len(&self) -> usize { self.1 as usize } pub fn len32(&self) -> u32 { self.1 } } pub type StaticCommand = Command; pub trait CommandProcessor<'a> { fn push(&mut self, val: Command >); fn push_literals(&mut self, data:&InputPair<'a>) { if data.0.len() != 0 { self.push(Command::Literal(LiteralCommand{ data: data.0, prob:FeatureFlagSliceType::::default(), high_entropy: false, })); } if data.1.len() != 0 { self.push(Command::Literal(LiteralCommand{ data: data.1, prob:FeatureFlagSliceType::::default(), high_entropy: false, })); } } fn push_rand_literals(&mut self, data:&InputPair<'a>) { if data.0.len() != 0 { self.push(Command::Literal(LiteralCommand{ data:data.0, prob:FeatureFlagSliceType::::default(), high_entropy: true, })); } if data.1.len() != 0 { self.push(Command::Literal(LiteralCommand{ data:data.1, prob:FeatureFlagSliceType::::default(), high_entropy: true, })); } } fn push_block_switch_literal(&mut self, block_type: u8) { self.push(Command::BlockSwitchLiteral(LiteralBlockSwitch::new(block_type, 0))) } } pub fn thaw_pair<'a, SliceType: Unfreezable + SliceWrapper>(xself: &Command, data: &InputPair<'a>) -> Command> { match *xself { Command::Literal(ref lit) => { Command::Literal(LiteralCommand{ data:lit.data.thaw_pair(data).unwrap(), prob:FeatureFlagSliceType::default(), high_entropy: lit.high_entropy, }) }, Command::PredictionMode(ref pm) => { Command::PredictionMode(PredictionModeContextMap{ literal_context_map:pm.literal_context_map.thaw_pair(data).unwrap(), predmode_speed_and_distance_context_map:pm.predmode_speed_and_distance_context_map.thaw_pair(data).unwrap(), }) }, Command::Dict(ref d) => { Command::Dict(d.clone()) }, Command::Copy(ref c) => { Command::Copy(c.clone()) }, Command::BlockSwitchCommand(ref c) => { Command::BlockSwitchCommand(c.clone()) }, Command::BlockSwitchLiteral(ref c) => { Command::BlockSwitchLiteral(c.clone()) }, Command::BlockSwitchDistance(ref c) => { Command::BlockSwitchDistance(c.clone()) }, } } pub fn thaw<'a, SliceType: Unfreezable + SliceWrapper>(xself: &Command, data: &'a[u8]) -> Command> { match *xself { Command::Literal(ref lit) => { Command::Literal(LiteralCommand{ data:lit.data.thaw(data), prob:FeatureFlagSliceType::default(), high_entropy: lit.high_entropy, }) }, Command::PredictionMode(ref pm) => { Command::PredictionMode(PredictionModeContextMap{ literal_context_map:pm.literal_context_map.thaw(data), predmode_speed_and_distance_context_map:pm.predmode_speed_and_distance_context_map.thaw(data), }) }, Command::Dict(ref d) => { Command::Dict(d.clone()) }, Command::Copy(ref c) => { Command::Copy(c.clone()) }, Command::BlockSwitchCommand(ref c) => { Command::BlockSwitchCommand(c.clone()) }, Command::BlockSwitchLiteral(ref c) => { Command::BlockSwitchLiteral(c.clone()) }, Command::BlockSwitchDistance(ref c) => { Command::BlockSwitchDistance(c.clone()) }, } } impl +Freezable> Command { pub fn freeze(&self) -> Command { match *self { Command::Literal(ref lit) => { Command::Literal(LiteralCommand{ data:lit.data.freeze(), prob:FeatureFlagSliceType::default(), high_entropy: lit.high_entropy, }) }, Command::PredictionMode(ref pm) => { Command::PredictionMode(PredictionModeContextMap{ literal_context_map:pm.literal_context_map.freeze(), predmode_speed_and_distance_context_map:pm.predmode_speed_and_distance_context_map.freeze(), }) }, Command::Dict(ref d) => { Command::Dict(d.clone()) }, Command::Copy(ref c) => { Command::Copy(c.clone()) }, Command::BlockSwitchCommand(ref c) => { Command::BlockSwitchCommand(c.clone()) }, Command::BlockSwitchLiteral(ref c) => { Command::BlockSwitchLiteral(c.clone()) }, Command::BlockSwitchDistance(ref c) => { Command::BlockSwitchDistance(c.clone()) }, } } } #[inline(always)] pub fn speed_to_u8(data: u16) -> u8 { let length = 16 - data.leading_zeros() as u8; let mantissa = if data != 0 { let rem = data - (1 << (length - 1)); (rem << 3) >> (length - 1) } else { 0 }; (length << 3) | mantissa as u8 } #[inline(always)] pub fn u8_to_speed(data: u8) -> u16 { if data < 8 { 0 } else { let log_val = (data >> 3) - 1; let rem = (u16::from(data) & 0x7) << log_val; (1u16 << log_val) | (rem >> 3) } } #[cfg(test)] mod test { use super::speed_to_u8; use super::u8_to_speed; fn tst_u8_to_speed(data: u16) { assert_eq!(u8_to_speed(speed_to_u8(data)), data); } #[test] fn test_u8_to_speed() { tst_u8_to_speed(0); tst_u8_to_speed(1); tst_u8_to_speed(2); tst_u8_to_speed(3); tst_u8_to_speed(4); tst_u8_to_speed(5); tst_u8_to_speed(6); tst_u8_to_speed(7); tst_u8_to_speed(8); tst_u8_to_speed(10); tst_u8_to_speed(12); tst_u8_to_speed(16); tst_u8_to_speed(24); tst_u8_to_speed(32); tst_u8_to_speed(48); tst_u8_to_speed(64); tst_u8_to_speed(96); tst_u8_to_speed(768); tst_u8_to_speed(1280); tst_u8_to_speed(1536); tst_u8_to_speed(1664); } }