use core::fmt;
#[cfg(feature = "std")]
use std::error;
use crate::phys::{BinaryDecodeError, BinaryDecoder, BinaryEncodeError, BinaryEncoder};
#[derive(Debug)]
pub struct SpaceMapHeader {
pub obj: u64,
pub length_bytes: u64,
pub allocated_bytes: u64,
pub histogram: Option<[u64; SpaceMapHeader::HISTOGRAM_BUCKETS_COUNT]>,
}
impl SpaceMapHeader {
pub const SIZE_EXT_NONE: usize = 24;
pub const SIZE_EXT_HISTOGRAM: usize = 280;
pub const SIZE_EXT_HISTOGRAM_PADDING: usize = 40;
const HISTOGRAM_BUCKETS_COUNT: usize = 32;
pub fn from_decoder(
decoder: &mut dyn BinaryDecoder<'_>,
) -> Result<SpaceMapHeader, SpaceMapHeaderDecodeError> {
let obj = match decoder.get_u64()? {
0 => return Err(SpaceMapHeaderDecodeError::MissingObject {}),
v => v,
};
let length_bytes = decoder.get_u64()?;
let allocated_bytes = decoder.get_u64()?;
let mut histogram = None;
if !decoder.is_empty() {
decoder.skip_zeros(SpaceMapHeader::SIZE_EXT_HISTOGRAM_PADDING)?;
let mut counts: [u64; SpaceMapHeader::HISTOGRAM_BUCKETS_COUNT] = [0; 32];
for v in &mut counts {
*v = decoder.get_u64()?;
}
histogram = Some(counts);
}
Ok(SpaceMapHeader {
obj,
length_bytes,
allocated_bytes,
histogram,
})
}
pub fn to_encoder(
&self,
encoder: &mut dyn BinaryEncoder<'_>,
) -> Result<(), SpaceMapHeaderEncodeError> {
if self.obj == 0 {
return Err(SpaceMapHeaderEncodeError::MissingObject {});
}
encoder.put_u64(self.obj)?;
encoder.put_u64(self.length_bytes)?;
encoder.put_u64(self.allocated_bytes)?;
if let Some(histogram) = &self.histogram {
encoder.put_zeros(SpaceMapHeader::SIZE_EXT_HISTOGRAM_PADDING)?;
for v in histogram {
encoder.put_u64(*v)?;
}
}
Ok(())
}
}
#[derive(Debug)]
pub enum SpaceMapHeaderDecodeError {
Binary {
err: BinaryDecodeError,
},
MissingObject {},
NonZeroPadding {},
}
impl From<BinaryDecodeError> for SpaceMapHeaderDecodeError {
fn from(err: BinaryDecodeError) -> Self {
SpaceMapHeaderDecodeError::Binary { err }
}
}
impl fmt::Display for SpaceMapHeaderDecodeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
SpaceMapHeaderDecodeError::Binary { err } => {
write!(f, "SpaceMapHeader decode error | {err}")
}
SpaceMapHeaderDecodeError::MissingObject {} => {
write!(f, "SpaceMapHeader decode error, missing object number")
}
SpaceMapHeaderDecodeError::NonZeroPadding {} => {
write!(f, "SpaceMapHeader decode error, non-zero padding")
}
}
}
}
#[cfg(feature = "std")]
impl error::Error for SpaceMapHeaderDecodeError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
SpaceMapHeaderDecodeError::Binary { err } => Some(err),
_ => None,
}
}
}
#[derive(Debug)]
pub enum SpaceMapHeaderEncodeError {
Binary {
err: BinaryEncodeError,
},
MissingObject {},
}
impl From<BinaryEncodeError> for SpaceMapHeaderEncodeError {
fn from(err: BinaryEncodeError) -> Self {
SpaceMapHeaderEncodeError::Binary { err }
}
}
impl fmt::Display for SpaceMapHeaderEncodeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
SpaceMapHeaderEncodeError::Binary { err } => {
write!(f, "SpaceMapHeader encode error | {err}")
}
SpaceMapHeaderEncodeError::MissingObject {} => {
write!(f, "SpaceMapHeader encode error, missing object number")
}
}
}
}
#[cfg(feature = "std")]
impl error::Error for SpaceMapHeaderEncodeError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
SpaceMapHeaderEncodeError::Binary { err } => Some(err),
_ => None,
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(u8)]
pub enum SpaceMapAction {
Allocate = 0,
Free = 1,
}
impl From<SpaceMapAction> for u8 {
fn from(val: SpaceMapAction) -> u8 {
val as u8
}
}
impl TryFrom<u8> for SpaceMapAction {
type Error = SpaceMapActionError;
fn try_from(action: u8) -> Result<Self, Self::Error> {
match action {
0 => Ok(SpaceMapAction::Allocate),
1 => Ok(SpaceMapAction::Free),
_ => Err(SpaceMapActionError::Unknown { action }),
}
}
}
#[derive(Debug)]
pub enum SpaceMapActionError {
Unknown {
action: u8,
},
}
impl fmt::Display for SpaceMapActionError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
SpaceMapActionError::Unknown { action } => {
write!(f, "Unknown SpaceMapAction {action}")
}
}
}
}
#[cfg(feature = "std")]
impl error::Error for SpaceMapActionError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
None
}
}
#[derive(Debug)]
pub struct SpaceMapDebugEntry {
pub action: SpaceMapAction,
pub sync_pass: u16,
pub txg: u64,
}
impl SpaceMapDebugEntry {
pub const SYNC_PASS_MAX: u16 = SpaceMapDebugEntry::SYNC_PASS_MASK_DOWN_SHIFTED as u16;
const ACTION_SHIFT: usize = 60;
const ACTION_MASK_DOWN_SHIFTED: u64 = (1 << 2) - 1;
const SYNC_PASS_SHIFT: usize = 50;
const SYNC_PASS_MASK_DOWN_SHIFTED: u64 = (1 << 10) - 1;
const TXG_MASK: u64 = (1 << 50) - 1;
const PADDING_VALUE: u64 = (1 << 63);
}
#[derive(Debug)]
pub struct SpaceMapEntryV1 {
pub action: SpaceMapAction,
pub offset: u64,
pub run: u16,
}
impl SpaceMapEntryV1 {
pub const RUN_MAX: u16 = SpaceMapEntryV1::RUN_MASK as u16;
const ACTION_SHIFT: usize = 15;
const ACTION_MASK_DOWN_SHIFTED: u64 = 1;
const OFFSET_SHIFT: usize = 16;
const OFFSET_MASK_DOWN_SHIFTED: u64 = (1 << 47) - 1;
const RUN_MASK: u64 = (1 << 15) - 1;
}
#[derive(Debug)]
pub struct SpaceMapEntryV2 {
pub action: SpaceMapAction,
pub offset: u64,
pub run: u64,
pub vdev: u32,
}
impl SpaceMapEntryV2 {
pub const VDEV_MAX: u32 = SpaceMapEntryV2::VDEV_MASK as u32;
const ACTION_SHIFT: usize = 63;
const ACTION_MASK_DOWN_SHIFTED: u64 = 1;
const OFFSET_MASK: u64 = (1 << 63) - 1;
const VDEV_MASK: u64 = (1 << 24) - 1;
const RUN_SHIFT: usize = 24;
const RUN_MASK_DOWN_SHIFTED: u64 = (1 << 36) - 1;
const PADDING_MASK: u64 = (3 << 60);
}
#[derive(Debug)]
pub enum SpaceMapEntry {
Debug(SpaceMapDebugEntry),
V1(SpaceMapEntryV1),
V2(SpaceMapEntryV2),
Padding,
}
impl SpaceMapEntry {
pub fn from_decoder(
decoder: &mut dyn BinaryDecoder<'_>,
) -> Result<SpaceMapEntry, SpaceMapEntryDecodeError> {
let a = decoder.get_u64()?;
let entry_type_bits = (a >> 62) & 0x3;
match entry_type_bits {
0 | 1 => {
let action = ((a >> SpaceMapEntryV1::ACTION_SHIFT)
& SpaceMapEntryV1::ACTION_MASK_DOWN_SHIFTED) as u8;
let action = SpaceMapAction::try_from(action)?;
let offset = (a >> SpaceMapEntryV1::OFFSET_SHIFT)
& SpaceMapEntryV1::OFFSET_MASK_DOWN_SHIFTED;
let run = (a & SpaceMapEntryV1::RUN_MASK) as u16;
Ok(SpaceMapEntry::V1(SpaceMapEntryV1 {
action,
offset,
run,
}))
}
2 => {
if a == SpaceMapDebugEntry::PADDING_VALUE {
return Ok(SpaceMapEntry::Padding {});
}
let action = ((a >> SpaceMapDebugEntry::ACTION_SHIFT)
& SpaceMapDebugEntry::ACTION_MASK_DOWN_SHIFTED)
as u8;
let action = SpaceMapAction::try_from(action)?;
let sync_pass = ((a >> SpaceMapDebugEntry::SYNC_PASS_SHIFT)
& SpaceMapDebugEntry::SYNC_PASS_MASK_DOWN_SHIFTED)
as u16;
let txg = a & SpaceMapDebugEntry::TXG_MASK;
Ok(SpaceMapEntry::Debug(SpaceMapDebugEntry {
action,
sync_pass,
txg,
}))
}
3 => {
let b = decoder.get_u64()?;
let padding = a & SpaceMapEntryV2::PADDING_MASK;
if padding != 0 {
return Err(SpaceMapEntryDecodeError::Padding { padding });
}
let vdev = (a & SpaceMapEntryV2::VDEV_MASK) as u32;
let run =
(a >> SpaceMapEntryV2::RUN_SHIFT) & SpaceMapEntryV2::RUN_MASK_DOWN_SHIFTED;
let action = ((b >> SpaceMapEntryV2::ACTION_SHIFT)
& SpaceMapEntryV2::ACTION_MASK_DOWN_SHIFTED) as u8;
let action = SpaceMapAction::try_from(action)?;
let offset = b & SpaceMapEntryV2::OFFSET_MASK;
Ok(SpaceMapEntry::V2(SpaceMapEntryV2 {
action,
offset,
run,
vdev,
}))
}
_ => unreachable!(),
}
}
}
#[derive(Debug)]
pub enum SpaceMapEntryDecodeError {
Action {
err: SpaceMapActionError,
},
Binary {
err: BinaryDecodeError,
},
Padding {
padding: u64,
},
}
impl From<SpaceMapActionError> for SpaceMapEntryDecodeError {
fn from(err: SpaceMapActionError) -> Self {
SpaceMapEntryDecodeError::Action { err }
}
}
impl From<BinaryDecodeError> for SpaceMapEntryDecodeError {
fn from(err: BinaryDecodeError) -> Self {
SpaceMapEntryDecodeError::Binary { err }
}
}
impl fmt::Display for SpaceMapEntryDecodeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
SpaceMapEntryDecodeError::Action { err } => {
write!(f, "SpaceMapEntry decode error | {err}")
}
SpaceMapEntryDecodeError::Binary { err } => {
write!(f, "SpaceMapEntry decode error | {err}")
}
SpaceMapEntryDecodeError::Padding { padding } => {
write!(f, "SpaceMapEntry decode error, non-zero padding {padding}")
}
}
}
}
#[cfg(feature = "std")]
impl error::Error for SpaceMapEntryDecodeError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
SpaceMapEntryDecodeError::Action { err } => Some(err),
SpaceMapEntryDecodeError::Binary { err } => Some(err),
_ => None,
}
}
}
#[derive(Debug)]
pub enum SpaceMapEntryEncodeError {
Binary {
err: BinaryEncodeError,
},
}
impl From<BinaryEncodeError> for SpaceMapEntryEncodeError {
fn from(err: BinaryEncodeError) -> Self {
SpaceMapEntryEncodeError::Binary { err }
}
}
impl fmt::Display for SpaceMapEntryEncodeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
SpaceMapEntryEncodeError::Binary { err } => {
write!(f, "SpaceMapEntry encode error | {err}")
}
}
}
}
#[cfg(feature = "std")]
impl error::Error for SpaceMapEntryEncodeError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
SpaceMapEntryEncodeError::Binary { err } => Some(err),
}
}
}