use crate::{self as libradicl, constants};
use anyhow::{self, Context, bail};
use bio_types::strand::Strand;
use libradicl::{tag_value_try_into_int, u8_to_vec_of, u8_to_vec_of_bool, write_tag_value_array};
use num::cast::AsPrimitive;
use scroll::Pread;
use std::io::Read;
use std::io::Write;
use std::mem;
const U8ID: u8 = 1_u8;
const U16ID: u8 = 2_u8;
const U32ID: u8 = 3_u8;
const U64ID: u8 = 4_u8;
const U128ID: u8 = 9_u8;
const I8ID: u8 = 10_u8;
const I16ID: u8 = 11_u8;
const I32ID: u8 = 12_u8;
const I64ID: u8 = 13_u8;
const I128ID: u8 = 14_u8;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TagDesc {
pub name: String,
pub typeid: RadType,
}
impl TagDesc {
pub fn write<W: Write>(&self, writer: &mut W) -> anyhow::Result<()> {
let name_len: u16 = self
.name
.len()
.try_into()
.context("TagDesc name must have size < 65536")?;
writer
.write_all(&name_len.to_le_bytes())
.context("could not write name length to writer")?;
writer
.write_all(self.name.as_bytes())
.context("could not write name to writer")?;
let type_enc: u8 =
encode_type_tag(self.typeid).context("RadType tag doesn't have valid encoding")?;
writer
.write_all(&type_enc.to_le_bytes())
.context("could not write name length to writer")?;
if let RadType::Array(len_t, val_t) = self.typeid {
let len_id: u8 = len_t.into();
let val_id: u8 = val_t.into();
writer
.write_all(&len_id.to_le_bytes())
.context("could not write Array length type")?;
writer
.write_all(&val_id.to_le_bytes())
.context("could not write Array value type")?;
};
Ok(())
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum TagSectionLabel {
FileTags,
ReadTags,
AlignmentTags,
Unlabeled,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TagSection {
pub label: TagSectionLabel,
pub tags: Vec<TagDesc>,
}
impl TagSection {
pub fn new_with_label(label: TagSectionLabel) -> Self {
TagSection {
label,
tags: vec![],
}
}
pub fn add_tag_desc(&mut self, desc: TagDesc) {
self.tags.push(desc);
}
pub fn write<W: Write>(&self, writer: &mut W) -> anyhow::Result<()> {
let num_tags: u16 = self
.tags
.len()
.try_into()
.unwrap_or_else(|_| panic!("should have < {} tags", u16::MAX));
writer
.write_all(&num_tags.to_le_bytes())
.context("couldn't write number of tags to writer")?;
for tag in &self.tags {
tag.write(writer)?;
}
Ok(())
}
pub fn iter_desc(&self) -> impl std::iter::ExactSizeIterator<Item = &TagDesc> + use<'_> {
self.tags.iter()
}
pub fn from_tag_values(label: TagSectionLabel, entries: &[(&str, TagValue)]) -> (Self, TagMap) {
let mut section = Self::new_with_label(label);
for (name, value) in entries {
section.add_tag_desc(TagDesc {
name: name.to_string(),
typeid: value.rad_type(),
});
}
let mut tag_map = TagMap::with_keyset(§ion.tags);
for (_, value) in entries {
tag_map.add(value.clone());
}
(section, tag_map)
}
pub fn add_map_tags(&mut self, name: &str, key_type: RadIntId, val_type: RadIntId) {
self.add_tag_desc(TagDesc {
name: format!("{name}.keys"),
typeid: RadType::Array(RadIntId::U32, RadAtomicId::Int(key_type)),
});
self.add_tag_desc(TagDesc {
name: format!("{name}.values"),
typeid: RadType::Array(RadIntId::U32, RadAtomicId::Int(val_type)),
});
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum RadIntId {
U8,
U16,
U32,
U64,
U128,
I8,
I16,
I32,
I64,
I128,
}
impl RadIntId {
#[inline]
pub fn size_of(&self) -> usize {
match self {
Self::U8 => mem::size_of::<u8>(),
Self::U16 => mem::size_of::<u16>(),
Self::U32 => mem::size_of::<u32>(),
Self::U64 => mem::size_of::<u64>(),
Self::U128 => mem::size_of::<u128>(),
Self::I8 => mem::size_of::<i8>(),
Self::I16 => mem::size_of::<i16>(),
Self::I32 => mem::size_of::<i32>(),
Self::I64 => mem::size_of::<i64>(),
Self::I128 => mem::size_of::<i128>(),
}
}
#[inline]
pub fn read_value_into_u64<R: Read>(&self, reader: &mut R) -> u64 {
let mut rbuf = [0u8; 8];
let v: u64 = match self {
RadIntId::U8 => {
reader.read_exact(&mut rbuf[0..1]).unwrap();
rbuf.pread::<u8>(0).unwrap() as u64
}
RadIntId::U16 => {
reader.read_exact(&mut rbuf[0..2]).unwrap();
rbuf.pread::<u16>(0).unwrap() as u64
}
RadIntId::U32 => {
reader.read_exact(&mut rbuf[0..4]).unwrap();
rbuf.pread::<u32>(0).unwrap() as u64
}
RadIntId::U64 => {
reader.read_exact(&mut rbuf[0..8]).unwrap();
rbuf.pread::<u64>(0).unwrap()
}
RadIntId::U128 => {
panic!("cannot read a u128 into a u64");
}
_ => {
panic!("cannot read signed RadIntId into a u64")
}
};
v
}
#[inline]
pub fn read_value_into_i64<R: Read>(&self, reader: &mut R) -> i64 {
let mut rbuf = [0u8; 8];
let v: i64 = match self {
RadIntId::I8 => {
reader.read_exact(&mut rbuf[0..1]).unwrap();
rbuf.pread::<i8>(0).unwrap() as i64
}
RadIntId::I16 => {
reader.read_exact(&mut rbuf[0..2]).unwrap();
rbuf.pread::<i16>(0).unwrap() as i64
}
RadIntId::I32 => {
reader.read_exact(&mut rbuf[0..4]).unwrap();
rbuf.pread::<i32>(0).unwrap() as i64
}
RadIntId::I64 => {
reader.read_exact(&mut rbuf[0..8]).unwrap();
rbuf.pread::<i64>(0).unwrap()
}
RadIntId::I128 => {
panic!("cannot read a i128 into a i64");
}
_ => {
panic!("cannot read an unsigned RadIntId into a i64")
}
};
v
}
#[inline]
pub fn read_value_into_u128<R: Read>(&self, reader: &mut R) -> u128 {
let mut rbuf = [0u8; 16];
let v: u128 = match self {
RadIntId::U8 => {
reader.read_exact(&mut rbuf[0..1]).unwrap();
rbuf.pread::<u8>(0).unwrap() as u128
}
RadIntId::U16 => {
reader.read_exact(&mut rbuf[0..2]).unwrap();
rbuf.pread::<u16>(0).unwrap() as u128
}
RadIntId::U32 => {
reader.read_exact(&mut rbuf[0..4]).unwrap();
rbuf.pread::<u32>(0).unwrap() as u128
}
RadIntId::U64 => {
reader.read_exact(&mut rbuf[0..8]).unwrap();
rbuf.pread::<u64>(0).unwrap() as u128
}
RadIntId::U128 => {
reader.read_exact(&mut rbuf[0..16]).unwrap();
rbuf.pread::<u128>(0).unwrap()
}
_ => {
panic!("cannot read a signed RadIntId into a u128")
}
};
v
}
#[inline]
pub fn read_value_into_i128<R: Read>(&self, reader: &mut R) -> i128 {
let mut rbuf = [0u8; 16];
let v: i128 = match self {
RadIntId::I8 => {
reader.read_exact(&mut rbuf[0..1]).unwrap();
rbuf.pread::<i8>(0).unwrap() as i128
}
RadIntId::I16 => {
reader.read_exact(&mut rbuf[0..2]).unwrap();
rbuf.pread::<i16>(0).unwrap() as i128
}
RadIntId::I32 => {
reader.read_exact(&mut rbuf[0..4]).unwrap();
rbuf.pread::<i32>(0).unwrap() as i128
}
RadIntId::I64 => {
reader.read_exact(&mut rbuf[0..8]).unwrap();
rbuf.pread::<i64>(0).unwrap() as i128
}
RadIntId::I128 => {
reader.read_exact(&mut rbuf[0..16]).unwrap();
rbuf.pread::<i128>(0).unwrap()
}
_ => {
panic!("cannot read an unsigned RadIntId into a i128")
}
};
v
}
}
impl From<RadIntId> for u8 {
fn from(r: RadIntId) -> Self {
match r {
RadIntId::U8 => U8ID,
RadIntId::U16 => U16ID,
RadIntId::U32 => U32ID,
RadIntId::U64 => U64ID,
RadIntId::U128 => U128ID,
RadIntId::I8 => I8ID,
RadIntId::I16 => I16ID,
RadIntId::I32 => I32ID,
RadIntId::I64 => I64ID,
RadIntId::I128 => I128ID,
}
}
}
impl From<u8> for RadIntId {
fn from(x: u8) -> Self {
match x {
U8ID => Self::U8,
U16ID => Self::U16,
U32ID => Self::U32,
U64ID => Self::U64,
U128ID => Self::U128,
I8ID => Self::I8,
I16ID => Self::I16,
I32ID => Self::I32,
I64ID => Self::I64,
I128ID => Self::I128,
_ => panic!("Should not happen"),
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum RadFloatId {
F32,
F64,
}
impl RadFloatId {
#[inline]
pub fn size_of(&self) -> usize {
match self {
Self::F32 => mem::size_of::<f32>(),
Self::F64 => mem::size_of::<f64>(),
}
}
}
impl From<u8> for RadFloatId {
#[inline]
fn from(x: u8) -> Self {
match x {
5 => Self::F32,
6 => Self::F64,
_ => panic!("Should not happen"),
}
}
}
pub trait PrimitiveInteger:
AsPrimitive<u8>
+ AsPrimitive<u16>
+ AsPrimitive<u32>
+ AsPrimitive<u64>
+ AsPrimitive<u128>
+ AsPrimitive<usize>
+ AsPrimitive<i8>
+ AsPrimitive<i16>
+ AsPrimitive<i32>
+ AsPrimitive<i64>
+ AsPrimitive<i128>
+ AsPrimitive<isize>
{
}
impl<
T: AsPrimitive<u8>
+ AsPrimitive<u16>
+ AsPrimitive<u32>
+ AsPrimitive<u64>
+ AsPrimitive<u128>
+ AsPrimitive<usize>
+ AsPrimitive<i8>
+ AsPrimitive<i16>
+ AsPrimitive<i32>
+ AsPrimitive<i64>
+ AsPrimitive<i128>
+ AsPrimitive<isize>,
> PrimitiveInteger for T
{
}
pub trait PrimitiveUnsignedInteger: PrimitiveInteger + num::Unsigned {}
impl<T: PrimitiveInteger + num::Unsigned> PrimitiveUnsignedInteger for T {}
pub trait PrimitiveSignedInteger: PrimitiveInteger + num::Signed {}
impl<T: PrimitiveInteger + num::Signed> PrimitiveSignedInteger for T {}
impl RadIntId {
#[inline]
pub fn bytes_for_type(&self) -> usize {
self.size_of()
}
pub fn write_to<T: PrimitiveInteger, U: Write>(
&self,
v: T,
owriter: &mut U,
) -> std::io::Result<()> {
match self {
Self::U8 => {
let vo: u8 = v.as_();
owriter.write_all(&vo.to_le_bytes())
}
Self::U16 => {
let vo: u16 = v.as_();
owriter.write_all(&vo.to_le_bytes())
}
Self::U32 => {
let vo: u32 = v.as_();
owriter.write_all(&vo.to_le_bytes())
}
Self::U64 => {
let vo: u64 = v.as_();
owriter.write_all(&vo.to_le_bytes())
}
Self::U128 => {
let vo: u128 = v.as_();
owriter.write_all(&vo.to_le_bytes())
}
Self::I8 => {
let vo: i8 = v.as_();
owriter.write_all(&vo.to_le_bytes())
}
Self::I16 => {
let vo: i16 = v.as_();
owriter.write_all(&vo.to_le_bytes())
}
Self::I32 => {
let vo: i32 = v.as_();
owriter.write_all(&vo.to_le_bytes())
}
Self::I64 => {
let vo: i64 = v.as_();
owriter.write_all(&vo.to_le_bytes())
}
Self::I128 => {
let vo: i128 = v.as_();
owriter.write_all(&vo.to_le_bytes())
}
}
}
#[inline]
pub fn read_value_into_usize(&self, buf: &[u8]) -> usize {
match self {
Self::U8 => buf.pread::<u8>(0).unwrap() as usize,
Self::U16 => buf.pread::<u16>(0).unwrap() as usize,
Self::U32 => buf.pread::<u32>(0).unwrap() as usize,
Self::U64 => buf.pread::<u64>(0).unwrap() as usize,
Self::U128 => {
panic!("cannot read u128 into usize!")
}
Self::I8 => buf.pread::<i8>(0).unwrap() as usize,
Self::I16 => buf.pread::<i16>(0).unwrap() as usize,
Self::I32 => buf.pread::<i32>(0).unwrap() as usize,
Self::I64 => buf.pread::<i64>(0).unwrap() as usize,
Self::I128 => {
panic!("cannot read u128 into usize!")
}
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum RadAtomicId {
Int(RadIntId),
Float(RadFloatId),
Bool,
String,
}
impl RadAtomicId {
#[inline]
pub fn size_of(&self) -> usize {
match self {
Self::Int(x) => x.size_of(),
Self::Float(x) => x.size_of(),
Self::Bool => std::mem::size_of::<bool>(),
Self::String => panic!("RadAtomicId::String does not have a fixed type"),
}
}
}
impl From<RadAtomicId> for u8 {
fn from(x: RadAtomicId) -> Self {
match x {
RadAtomicId::Bool => 0,
RadAtomicId::Int(RadIntId::U8) => U8ID,
RadAtomicId::Int(RadIntId::U16) => U16ID,
RadAtomicId::Int(RadIntId::U32) => U32ID,
RadAtomicId::Int(RadIntId::U64) => U64ID,
RadAtomicId::Int(RadIntId::U128) => U128ID,
RadAtomicId::Int(RadIntId::I8) => I8ID,
RadAtomicId::Int(RadIntId::I16) => I16ID,
RadAtomicId::Int(RadIntId::I32) => I32ID,
RadAtomicId::Int(RadIntId::I64) => I64ID,
RadAtomicId::Int(RadIntId::I128) => I128ID,
RadAtomicId::Float(RadFloatId::F32) => 5,
RadAtomicId::Float(RadFloatId::F64) => 6,
RadAtomicId::String => 8,
}
}
}
impl From<u8> for RadAtomicId {
fn from(x: u8) -> Self {
match x {
0 => Self::Bool,
U8ID => Self::Int(RadIntId::U8),
U16ID => Self::Int(RadIntId::U16),
U32ID => Self::Int(RadIntId::U32),
U64ID => Self::Int(RadIntId::U64),
U128ID => Self::Int(RadIntId::U128),
I8ID => Self::Int(RadIntId::I8),
I16ID => Self::Int(RadIntId::I16),
I32ID => Self::Int(RadIntId::I32),
I64ID => Self::Int(RadIntId::I64),
I128ID => Self::Int(RadIntId::I128),
5 => Self::Float(RadFloatId::F32),
6 => Self::Float(RadFloatId::F64),
8 => Self::String,
x => panic!("Should not happen, num is {x}"),
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum RadType {
Bool,
Int(RadIntId),
Float(RadFloatId),
Array(RadIntId, RadAtomicId),
String,
}
impl RadType {
#[inline]
pub fn is_int_type(&self) -> bool {
matches!(self, Self::Int(_))
}
pub fn from_bytes<R: Read>(r: &mut R) -> Self {
let mut type_num = 0_u8;
r.read_exact(std::slice::from_mut(&mut type_num))
.expect("cannot read RadType id from file");
if type_num == 7 {
let mut len_id = 0_u8;
let mut member_id = 0_u8;
r.read_exact(std::slice::from_mut(&mut len_id))
.expect("cannot read Array length type from file");
r.read_exact(std::slice::from_mut(&mut member_id))
.expect("cannot read Array value type from file");
let len_type: RadIntId = len_id.into();
let member_type: RadAtomicId = member_id.into();
RadType::Array(len_type, member_type)
} else {
type_num.into()
}
}
pub fn try_from_bytes<R: Read>(r: &mut R) -> anyhow::Result<Self> {
let mut type_num = 0_u8;
r.read_exact(std::slice::from_mut(&mut type_num))
.context("cannot read RadType id from file")?;
if type_num == 7 {
let mut len_id = 0_u8;
let mut member_id = 0_u8;
r.read_exact(std::slice::from_mut(&mut len_id))
.context("cannot read Array length type from file")?;
r.read_exact(std::slice::from_mut(&mut member_id))
.context("cannot read Array value type from file")?;
let len_type: RadIntId = len_id.into();
let member_type: RadAtomicId = member_id.into();
Ok(RadType::Array(len_type, member_type))
} else {
Ok(type_num.into())
}
}
}
pub fn encode_type_tag(type_tag: RadType) -> Option<u8> {
match type_tag {
RadType::Bool => Some(0),
RadType::Int(RadIntId::U8) => Some(U8ID),
RadType::Int(RadIntId::U16) => Some(U16ID),
RadType::Int(RadIntId::U32) => Some(U32ID),
RadType::Int(RadIntId::U64) => Some(U64ID),
RadType::Int(RadIntId::U128) => Some(U128ID),
RadType::Int(RadIntId::I8) => Some(I8ID),
RadType::Int(RadIntId::I16) => Some(I16ID),
RadType::Int(RadIntId::I32) => Some(I32ID),
RadType::Int(RadIntId::I64) => Some(I64ID),
RadType::Int(RadIntId::I128) => Some(I128ID),
RadType::Float(RadFloatId::F32) => Some(5),
RadType::Float(RadFloatId::F64) => Some(6),
RadType::Array(_, _) => Some(7),
RadType::String => Some(8), }
}
pub fn decode_int_type_tag(type_id: u8) -> Option<RadIntId> {
match type_id {
U8ID => Some(RadIntId::U8),
U16ID => Some(RadIntId::U16),
U32ID => Some(RadIntId::U32),
U64ID => Some(RadIntId::U64),
U128ID => Some(RadIntId::U128),
I8ID => Some(RadIntId::I8),
I16ID => Some(RadIntId::I16),
I32ID => Some(RadIntId::I32),
I64ID => Some(RadIntId::I64),
I128ID => Some(RadIntId::I128),
_ => None,
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum MappingType {
Unmapped,
SingleMapped,
MappedFirstOrphan,
MappedSecondOrphan,
MappedPair,
}
impl MappingType {
#[inline]
pub fn from_u8(t: u8) -> Self {
match t {
0 => MappingType::Unmapped,
1 => MappingType::SingleMapped,
2 => MappingType::MappedFirstOrphan,
3 => MappingType::MappedSecondOrphan,
4 => MappingType::MappedPair,
_ => MappingType::Unmapped,
}
}
#[inline]
pub fn get_mask(&self) -> u32 {
match &self {
MappingType::Unmapped => 0b00,
MappingType::MappedPair => 0b11,
_ => 0b10,
}
}
#[inline]
pub fn is_orphan(&self) -> bool {
matches!(
&self,
MappingType::MappedFirstOrphan | MappingType::MappedSecondOrphan
)
}
}
#[derive(Debug, Copy, Clone)]
pub enum MappedFragmentOrientation {
Reverse,
Forward,
ReverseReverse,
ReverseForward,
ForwardReverse,
ForwardForward,
Unknown,
}
impl From<&Strand> for MappedFragmentOrientation {
fn from(v: &Strand) -> Self {
match v {
Strand::Forward => MappedFragmentOrientation::Forward,
Strand::Reverse => MappedFragmentOrientation::Reverse,
Strand::Unknown => MappedFragmentOrientation::Unknown,
}
}
}
impl From<Strand> for MappedFragmentOrientation {
fn from(v: Strand) -> Self {
match v {
Strand::Forward => MappedFragmentOrientation::Forward,
Strand::Reverse => MappedFragmentOrientation::Reverse,
Strand::Unknown => MappedFragmentOrientation::Unknown,
}
}
}
impl From<&MappedFragmentOrientation> for &Strand {
fn from(v: &MappedFragmentOrientation) -> Self {
match v {
MappedFragmentOrientation::Forward => &Strand::Forward,
MappedFragmentOrientation::Reverse => &Strand::Reverse,
MappedFragmentOrientation::Unknown => &Strand::Unknown,
_ => &Strand::Unknown,
}
}
}
impl MappedFragmentOrientation {
#[inline]
pub fn is_unknown(&self) -> bool {
matches!(*self, MappedFragmentOrientation::Unknown)
}
#[inline]
pub fn same(&self, s1: &Self) -> bool {
matches!(
(*self, *s1),
(
MappedFragmentOrientation::Forward,
MappedFragmentOrientation::Forward
) | (
MappedFragmentOrientation::Reverse,
MappedFragmentOrientation::Reverse
) | (
MappedFragmentOrientation::ForwardForward,
MappedFragmentOrientation::ForwardForward
) | (
MappedFragmentOrientation::ReverseReverse,
MappedFragmentOrientation::ReverseReverse
) | (
MappedFragmentOrientation::ForwardReverse,
MappedFragmentOrientation::ForwardReverse
) | (
MappedFragmentOrientation::ReverseForward,
MappedFragmentOrientation::ReverseForward
) | (
MappedFragmentOrientation::Unknown,
MappedFragmentOrientation::Unknown
)
)
}
#[inline]
pub fn from_u32_paired_status(n: u32, m: MappingType) -> Self {
if matches!(
m,
MappingType::SingleMapped
| MappingType::MappedFirstOrphan
| MappingType::MappedSecondOrphan
) {
if (n & 0b10) == 2 {
MappedFragmentOrientation::Forward
} else {
MappedFragmentOrientation::Reverse
}
} else {
match n {
0 => MappedFragmentOrientation::ReverseReverse,
1 => MappedFragmentOrientation::ReverseForward,
2 => MappedFragmentOrientation::ForwardReverse,
3 => MappedFragmentOrientation::ForwardForward,
_ => MappedFragmentOrientation::Unknown,
}
}
}
}
impl From<MappedFragmentOrientation> for u32 {
fn from(item: MappedFragmentOrientation) -> Self {
match item {
MappedFragmentOrientation::ForwardReverse => 0b011,
MappedFragmentOrientation::ForwardForward => 0b101,
MappedFragmentOrientation::ReverseReverse => 0b110,
MappedFragmentOrientation::ReverseForward => 0b100,
MappedFragmentOrientation::Forward => 0b1,
MappedFragmentOrientation::Reverse => 0b10,
MappedFragmentOrientation::Unknown => 0b0,
}
}
}
impl From<u32> for MappedFragmentOrientation {
fn from(item: u32) -> Self {
match item {
0b011 => MappedFragmentOrientation::ForwardReverse,
0b101 => MappedFragmentOrientation::ForwardForward,
0b110 => MappedFragmentOrientation::ReverseReverse,
0b100 => MappedFragmentOrientation::ReverseForward,
0b1 => MappedFragmentOrientation::Forward,
0b10 => MappedFragmentOrientation::Reverse,
_ => MappedFragmentOrientation::Unknown,
}
}
}
impl From<u8> for RadType {
fn from(x: u8) -> Self {
match x {
0 => RadType::Bool,
U8ID => RadType::Int(RadIntId::U8),
U16ID => RadType::Int(RadIntId::U16),
U32ID => RadType::Int(RadIntId::U32),
U64ID => RadType::Int(RadIntId::U64),
U128ID => RadType::Int(RadIntId::U128),
I8ID => RadType::Int(RadIntId::I8),
I16ID => RadType::Int(RadIntId::I16),
I32ID => RadType::Int(RadIntId::I32),
I64ID => RadType::Int(RadIntId::I64),
I128ID => RadType::Int(RadIntId::I128),
5 => RadType::Float(RadFloatId::F32),
6 => RadType::Float(RadFloatId::F64),
7 => panic!("Should not happen"),
8 => RadType::String,
_ => panic!("Should not happen"),
}
}
}
#[derive(Clone, Debug)]
pub enum TagValue {
Bool(bool),
U8(u8),
U16(u16),
U32(u32),
U64(u64),
U128(u128),
I8(i8),
I16(i16),
I32(i32),
I64(i64),
I128(i128),
F32(f32),
F64(f64),
ArrayBool(Vec<bool>),
ArrayU8(Vec<u8>),
ArrayU16(Vec<u16>),
ArrayU32(Vec<u32>),
ArrayU64(Vec<u64>),
ArrayU128(Vec<u128>),
ArrayI8(Vec<i8>),
ArrayI16(Vec<i16>),
ArrayI32(Vec<i32>),
ArrayI64(Vec<i64>),
ArrayI128(Vec<i128>),
ArrayF32(Vec<f32>),
ArrayF64(Vec<f64>),
ArrayString(Vec<String>),
String(String),
}
impl std::cmp::PartialEq for TagValue {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::F32(s), Self::F32(o)) => (s - o).abs() < f32::EPSILON,
(Self::F64(s), Self::F64(o)) => (s - o).abs() < f64::EPSILON,
(Self::ArrayF32(a), Self::ArrayF32(b)) => {
a.len() == b.len()
&& a.iter()
.zip(b.iter())
.all(|(x, y)| (x - y).abs() < f32::EPSILON)
}
(Self::ArrayF64(a), Self::ArrayF64(b)) => {
a.len() == b.len()
&& a.iter()
.zip(b.iter())
.all(|(x, y)| (x - y).abs() < f64::EPSILON)
}
(Self::Bool(a), Self::Bool(b)) => a == b,
(Self::U8(a), Self::U8(b)) => a == b,
(Self::U16(a), Self::U16(b)) => a == b,
(Self::U32(a), Self::U32(b)) => a == b,
(Self::U64(a), Self::U64(b)) => a == b,
(Self::U128(a), Self::U128(b)) => a == b,
(Self::I8(a), Self::I8(b)) => a == b,
(Self::I16(a), Self::I16(b)) => a == b,
(Self::I32(a), Self::I32(b)) => a == b,
(Self::I64(a), Self::I64(b)) => a == b,
(Self::I128(a), Self::I128(b)) => a == b,
(Self::ArrayBool(a), Self::ArrayBool(b)) => a == b,
(Self::ArrayU8(a), Self::ArrayU8(b)) => a == b,
(Self::ArrayU16(a), Self::ArrayU16(b)) => a == b,
(Self::ArrayU32(a), Self::ArrayU32(b)) => a == b,
(Self::ArrayU64(a), Self::ArrayU64(b)) => a == b,
(Self::ArrayU128(a), Self::ArrayU128(b)) => a == b,
(Self::ArrayI8(a), Self::ArrayI8(b)) => a == b,
(Self::ArrayI16(a), Self::ArrayI16(b)) => a == b,
(Self::ArrayI32(a), Self::ArrayI32(b)) => a == b,
(Self::ArrayI64(a), Self::ArrayI64(b)) => a == b,
(Self::ArrayI128(a), Self::ArrayI128(b)) => a == b,
(Self::ArrayString(a), Self::ArrayString(b)) => a == b,
(Self::String(a), Self::String(b)) => a == b,
_ => false,
}
}
}
impl std::cmp::Eq for TagValue {}
tag_value_try_into_int!(u8);
tag_value_try_into_int!(u16);
tag_value_try_into_int!(u32);
tag_value_try_into_int!(u64);
tag_value_try_into_int!(i8);
tag_value_try_into_int!(i16);
tag_value_try_into_int!(i32);
tag_value_try_into_int!(i64);
impl TagValue {
pub fn rad_type(&self) -> RadType {
match self {
Self::Bool(_) => RadType::Bool,
Self::U8(_) => RadType::Int(RadIntId::U8),
Self::U16(_) => RadType::Int(RadIntId::U16),
Self::U32(_) => RadType::Int(RadIntId::U32),
Self::U64(_) => RadType::Int(RadIntId::U64),
Self::U128(_) => RadType::Int(RadIntId::U128),
Self::I8(_) => RadType::Int(RadIntId::I8),
Self::I16(_) => RadType::Int(RadIntId::I16),
Self::I32(_) => RadType::Int(RadIntId::I32),
Self::I64(_) => RadType::Int(RadIntId::I64),
Self::I128(_) => RadType::Int(RadIntId::I128),
Self::F32(_) => RadType::Float(RadFloatId::F32),
Self::F64(_) => RadType::Float(RadFloatId::F64),
Self::ArrayBool(_) => RadType::Array(RadIntId::U32, RadAtomicId::Bool),
Self::ArrayU8(_) => RadType::Array(RadIntId::U32, RadAtomicId::Int(RadIntId::U8)),
Self::ArrayU16(_) => RadType::Array(RadIntId::U32, RadAtomicId::Int(RadIntId::U16)),
Self::ArrayU32(_) => RadType::Array(RadIntId::U32, RadAtomicId::Int(RadIntId::U32)),
Self::ArrayU64(_) => RadType::Array(RadIntId::U32, RadAtomicId::Int(RadIntId::U64)),
Self::ArrayU128(_) => RadType::Array(RadIntId::U32, RadAtomicId::Int(RadIntId::U128)),
Self::ArrayI8(_) => RadType::Array(RadIntId::U32, RadAtomicId::Int(RadIntId::I8)),
Self::ArrayI16(_) => RadType::Array(RadIntId::U32, RadAtomicId::Int(RadIntId::I16)),
Self::ArrayI32(_) => RadType::Array(RadIntId::U32, RadAtomicId::Int(RadIntId::I32)),
Self::ArrayI64(_) => RadType::Array(RadIntId::U32, RadAtomicId::Int(RadIntId::I64)),
Self::ArrayI128(_) => RadType::Array(RadIntId::U32, RadAtomicId::Int(RadIntId::I128)),
Self::ArrayF32(_) => RadType::Array(RadIntId::U32, RadAtomicId::Float(RadFloatId::F32)),
Self::ArrayF64(_) => RadType::Array(RadIntId::U32, RadAtomicId::Float(RadFloatId::F64)),
Self::ArrayString(_) => RadType::Array(RadIntId::U32, RadAtomicId::String),
Self::String(_) => RadType::String,
}
}
#[inline]
pub fn write_with_type<W: Write>(
&self,
tag_type: &RadType,
writer: &mut W,
) -> anyhow::Result<()> {
match self {
Self::Bool(b) => {
let b = if *b { 1_u8 } else { 0_u8 };
writer
.write_all(&b.to_le_bytes())
.context("couldn't write Bool tag value")?;
}
Self::U8(v) => {
writer
.write_all(&v.to_le_bytes())
.context("couldn't write U8 tag value")?;
}
Self::U16(v) => {
writer
.write_all(&v.to_le_bytes())
.context("couldn't write U16 tag value")?;
}
Self::U32(v) => {
writer
.write_all(&v.to_le_bytes())
.context("couldn't write U32 tag value")?;
}
Self::U64(v) => {
writer
.write_all(&v.to_le_bytes())
.context("couldn't write U64 tag value")?;
}
Self::U128(v) => {
writer
.write_all(&v.to_le_bytes())
.context("couldn't write U128 tag value")?;
}
Self::I8(v) => {
writer
.write_all(&v.to_le_bytes())
.context("couldn't write I8 tag value")?;
}
Self::I16(v) => {
writer
.write_all(&v.to_le_bytes())
.context("couldn't write I16 tag value")?;
}
Self::I32(v) => {
writer
.write_all(&v.to_le_bytes())
.context("couldn't write I32 tag value")?;
}
Self::I64(v) => {
writer
.write_all(&v.to_le_bytes())
.context("couldn't write I64 tag value")?;
}
Self::I128(v) => {
writer
.write_all(&v.to_le_bytes())
.context("couldn't write I128 tag value")?;
}
Self::F32(v) => {
writer
.write_all(&v.to_le_bytes())
.context("couldn't write U8 tag value")?;
}
Self::F64(v) => {
writer
.write_all(&v.to_le_bytes())
.context("couldn't write U8 tag value")?;
}
Self::ArrayBool(vb) => {
if let RadType::Array(len_t, _) = tag_type {
write_tag_value_array!(vb, len_t, bool, x, writer);
} else {
bail!("Array TagValue didn't correspond to an Array RadType");
}
}
Self::ArrayU8(vb) => {
if let RadType::Array(len_t, _) = tag_type {
write_tag_value_array!(vb, len_t, u8, x, writer);
} else {
bail!("Array TagValue didn't correspond to an Array RadType");
}
}
Self::ArrayU16(vb) => {
if let RadType::Array(len_t, _) = tag_type {
write_tag_value_array!(vb, len_t, u16, x, writer);
} else {
bail!("Array TagValue didn't correspond to an Array RadType");
}
}
Self::ArrayU32(vb) => {
if let RadType::Array(len_t, _) = tag_type {
write_tag_value_array!(vb, len_t, u32, x, writer);
} else {
bail!("Array TagValue didn't correspond to an Array RadType");
}
}
Self::ArrayU64(vb) => {
if let RadType::Array(len_t, _) = tag_type {
write_tag_value_array!(vb, len_t, u64, x, writer);
} else {
bail!("Array TagValue didn't correspond to an Array RadType");
}
}
Self::ArrayU128(vb) => {
if let RadType::Array(len_t, _) = tag_type {
write_tag_value_array!(vb, len_t, u128, x, writer);
} else {
bail!("Array TagValue didn't correspond to an Array RadType");
}
}
Self::ArrayI8(vb) => {
if let RadType::Array(len_t, _) = tag_type {
write_tag_value_array!(vb, len_t, i8, x, writer);
} else {
bail!("Array TagValue didn't correspond to an Array RadType");
}
}
Self::ArrayI16(vb) => {
if let RadType::Array(len_t, _) = tag_type {
write_tag_value_array!(vb, len_t, i16, x, writer);
} else {
bail!("Array TagValue didn't correspond to an Array RadType");
}
}
Self::ArrayI32(vb) => {
if let RadType::Array(len_t, _) = tag_type {
write_tag_value_array!(vb, len_t, i32, x, writer);
} else {
bail!("Array TagValue didn't correspond to an Array RadType");
}
}
Self::ArrayI64(vb) => {
if let RadType::Array(len_t, _) = tag_type {
write_tag_value_array!(vb, len_t, i64, x, writer);
} else {
bail!("Array TagValue didn't correspond to an Array RadType");
}
}
Self::ArrayI128(vb) => {
if let RadType::Array(len_t, _) = tag_type {
write_tag_value_array!(vb, len_t, i128, x, writer);
} else {
bail!("Array TagValue didn't correspond to an Array RadType");
}
}
Self::ArrayF32(vb) => {
if let RadType::Array(len_t, _) = tag_type {
write_tag_value_array!(vb, len_t, f32, x, writer);
} else {
bail!("Array TagValue didn't correspond to an Array RadType");
}
}
Self::ArrayF64(vb) => {
if let RadType::Array(len_t, _) = tag_type {
write_tag_value_array!(vb, len_t, f64, x, writer);
} else {
bail!("Array TagValue didn't correspond to an Array RadType");
}
}
Self::ArrayString(_vb) => {
todo!("Not yet implemented")
}
Self::String(s) => {
let slen: u16 = s.len() as u16;
writer
.write_all(&slen.to_le_bytes())
.context("couldn't write String tag value's length")?;
writer
.write_all(s.as_bytes())
.context("couldn't write String tag value's content")?;
}
}
Ok(())
}
}
impl TagDesc {
pub fn from_bytes<T: Read>(reader: &mut T) -> anyhow::Result<TagDesc> {
let mut buf = [0u8; constants::MAX_REF_NAME_LEN];
reader
.read_exact(&mut buf[0..2])
.context("failed to read the string length for the Tag Description.")?;
let str_len = buf.pread::<u16>(0)? as usize;
reader
.read_exact(&mut buf[0..str_len + 1])
.context("failed to read string name or type-id from Tag Description.")?;
let name = std::str::from_utf8(&buf[0..str_len])
.context("failed to convert string name to a valid string.")?
.to_string();
let typeid = buf
.pread(str_len)
.context("failed to read RadType id from the buffer.")?;
let rad_t = match typeid {
0..=6 | 8..=14 => typeid.into(),
7 => {
reader.read_exact(&mut buf[0..2]).context("failed to read aggregate type parameters (array length and element types) from the reader.")?;
let t1: RadIntId = buf
.pread::<u8>(0)
.context("failed to parse the array length type")?
.into();
let t2: RadAtomicId = buf
.pread::<u8>(1)
.context("failed to parse the array value type")?
.into();
RadType::Array(t1, t2)
}
_ => {
bail!("{typeid} is an unrecognized RAD type id")
}
};
Ok(TagDesc {
name,
typeid: rad_t,
})
}
pub fn value_from_bytes<T: Read>(&self, reader: &mut T) -> TagValue {
let mut small_buf = [0u8; 16];
match self.typeid {
RadType::Bool => {
let _ = reader.read_exact(&mut small_buf[0..std::mem::size_of::<u8>()]);
TagValue::Bool(small_buf[0] > 1)
}
RadType::Int(RadIntId::U8) => {
let _ = reader.read_exact(&mut small_buf[0..std::mem::size_of::<u8>()]);
TagValue::U8(small_buf[0])
}
RadType::Int(RadIntId::U16) => {
let _ = reader.read_exact(&mut small_buf[0..std::mem::size_of::<u16>()]);
TagValue::U16(small_buf.pread::<u16>(0).unwrap())
}
RadType::Int(RadIntId::U32) => {
let _ = reader.read_exact(&mut small_buf[0..std::mem::size_of::<u32>()]);
TagValue::U32(small_buf.pread::<u32>(0).unwrap())
}
RadType::Int(RadIntId::U64) => {
let _ = reader.read_exact(&mut small_buf[0..std::mem::size_of::<u64>()]);
TagValue::U64(small_buf.pread::<u64>(0).unwrap())
}
RadType::Int(RadIntId::U128) => {
let _ = reader.read_exact(&mut small_buf[0..std::mem::size_of::<u128>()]);
TagValue::U128(small_buf.pread::<u128>(0).unwrap())
}
RadType::Int(RadIntId::I8) => {
let _ = reader.read_exact(&mut small_buf[0..std::mem::size_of::<i8>()]);
TagValue::I8(small_buf[0] as i8)
}
RadType::Int(RadIntId::I16) => {
let _ = reader.read_exact(&mut small_buf[0..std::mem::size_of::<i16>()]);
TagValue::I16(small_buf.pread::<i16>(0).unwrap())
}
RadType::Int(RadIntId::I32) => {
let _ = reader.read_exact(&mut small_buf[0..std::mem::size_of::<i32>()]);
TagValue::I32(small_buf.pread::<i32>(0).unwrap())
}
RadType::Int(RadIntId::I64) => {
let _ = reader.read_exact(&mut small_buf[0..std::mem::size_of::<i64>()]);
TagValue::I64(small_buf.pread::<i64>(0).unwrap())
}
RadType::Int(RadIntId::I128) => {
let _ = reader.read_exact(&mut small_buf[0..std::mem::size_of::<i128>()]);
TagValue::I128(small_buf.pread::<i128>(0).unwrap())
}
RadType::Float(RadFloatId::F32) => {
let _ = reader.read_exact(&mut small_buf[0..std::mem::size_of::<f32>()]);
TagValue::F32(small_buf.pread::<f32>(0).unwrap())
}
RadType::Float(RadFloatId::F64) => {
let _ = reader.read_exact(&mut small_buf[0..std::mem::size_of::<f64>()]);
TagValue::F64(small_buf.pread::<f64>(0).unwrap())
}
RadType::Array(len_t, val_t) => {
let _ = reader.read_exact(&mut small_buf[0..len_t.size_of()]);
let vec_len = len_t.read_value_into_usize(&small_buf);
if val_t == RadAtomicId::String {
let mut strings = Vec::with_capacity(vec_len);
let sl: u16 = 0;
let mut buf = [0u8; 65536];
for _ in 0..vec_len {
let _ = reader.read_exact(&mut sl.to_ne_bytes());
let l = sl as usize;
let _ = reader.read_exact(&mut buf[0..l]);
unsafe {
strings.push(std::str::from_utf8_unchecked(&buf[0..l]).to_string());
}
}
TagValue::ArrayString(strings)
} else {
let num_bytes = val_t.size_of() * vec_len;
let mut data = vec![0u8; num_bytes];
let _ = reader.read_exact(data.as_mut_slice());
match val_t {
RadAtomicId::Bool => TagValue::ArrayBool(u8_to_vec_of_bool!(data)),
RadAtomicId::Int(RadIntId::U8) => TagValue::ArrayU8(data),
RadAtomicId::Int(RadIntId::U16) => {
TagValue::ArrayU16(u8_to_vec_of!(data, u16))
}
RadAtomicId::Int(RadIntId::U32) => {
TagValue::ArrayU32(u8_to_vec_of!(data, u32))
}
RadAtomicId::Int(RadIntId::U64) => {
TagValue::ArrayU64(u8_to_vec_of!(data, u64))
}
RadAtomicId::Int(RadIntId::U128) => {
TagValue::ArrayU128(u8_to_vec_of!(data, u128))
}
RadAtomicId::Int(RadIntId::I8) => TagValue::ArrayI8(
bytemuck::try_cast_vec::<u8, i8>(data)
.expect("should be valid to cast from Vec<u8> to Vec<i8>"),
),
RadAtomicId::Int(RadIntId::I16) => {
TagValue::ArrayI16(u8_to_vec_of!(data, i16))
}
RadAtomicId::Int(RadIntId::I32) => {
TagValue::ArrayI32(u8_to_vec_of!(data, i32))
}
RadAtomicId::Int(RadIntId::I64) => {
TagValue::ArrayI64(u8_to_vec_of!(data, i64))
}
RadAtomicId::Int(RadIntId::I128) => {
TagValue::ArrayI128(u8_to_vec_of!(data, i128))
}
RadAtomicId::Float(RadFloatId::F32) => {
TagValue::ArrayF32(u8_to_vec_of!(data, f32))
}
RadAtomicId::Float(RadFloatId::F64) => {
TagValue::ArrayF64(u8_to_vec_of!(data, f64))
}
RadAtomicId::String => {
unimplemented!("match of RadAtomicId should not occur in this branch")
}
}
}
}
RadType::String => {
let _ = reader.read_exact(&mut small_buf[0..std::mem::size_of::<u16>()]);
let slen = small_buf.pread::<u16>(0).unwrap();
let mut dat: Vec<u8> = vec![0_u8; slen as usize];
let _ = reader.read_exact(dat.as_mut_slice());
let s = unsafe { String::from_utf8_unchecked(dat) };
TagValue::String(s)
}
}
}
#[inline]
pub fn matches_value_type(&self, o: &TagValue) -> bool {
match (&self.typeid, o) {
(RadType::Bool, TagValue::Bool(_)) => true,
(RadType::Int(RadIntId::U8), TagValue::U8(_)) => true,
(RadType::Int(RadIntId::U16), TagValue::U16(_)) => true,
(RadType::Int(RadIntId::U32), TagValue::U32(_)) => true,
(RadType::Int(RadIntId::U64), TagValue::U64(_)) => true,
(RadType::Int(RadIntId::U128), TagValue::U128(_)) => true,
(RadType::Int(RadIntId::I8), TagValue::I8(_)) => true,
(RadType::Int(RadIntId::I16), TagValue::I16(_)) => true,
(RadType::Int(RadIntId::I32), TagValue::I32(_)) => true,
(RadType::Int(RadIntId::I64), TagValue::I64(_)) => true,
(RadType::Int(RadIntId::I128), TagValue::I128(_)) => true,
(RadType::Float(RadFloatId::F32), TagValue::F32(_)) => true,
(RadType::Float(RadFloatId::F64), TagValue::F64(_)) => true,
(RadType::Array(_, RadAtomicId::Bool), TagValue::ArrayBool(_)) => true,
(RadType::Array(_, RadAtomicId::Int(RadIntId::U8)), TagValue::ArrayU8(_)) => true,
(RadType::Array(_, RadAtomicId::Int(RadIntId::U16)), TagValue::ArrayU16(_)) => true,
(RadType::Array(_, RadAtomicId::Int(RadIntId::U32)), TagValue::ArrayU32(_)) => true,
(RadType::Array(_, RadAtomicId::Int(RadIntId::U64)), TagValue::ArrayU64(_)) => true,
(RadType::Array(_, RadAtomicId::Int(RadIntId::U128)), TagValue::ArrayU128(_)) => true,
(RadType::Array(_, RadAtomicId::Int(RadIntId::I8)), TagValue::ArrayI8(_)) => true,
(RadType::Array(_, RadAtomicId::Int(RadIntId::I16)), TagValue::ArrayI16(_)) => true,
(RadType::Array(_, RadAtomicId::Int(RadIntId::I32)), TagValue::ArrayI32(_)) => true,
(RadType::Array(_, RadAtomicId::Int(RadIntId::I64)), TagValue::ArrayI64(_)) => true,
(RadType::Array(_, RadAtomicId::Int(RadIntId::I128)), TagValue::ArrayI128(_)) => true,
(RadType::Array(_, RadAtomicId::Float(RadFloatId::F32)), TagValue::ArrayF32(_)) => true,
(RadType::Array(_, RadAtomicId::Float(RadFloatId::F64)), TagValue::ArrayF64(_)) => true,
(RadType::Array(_, RadAtomicId::String), TagValue::ArrayString(_)) => true,
(RadType::String, TagValue::String(_)) => true,
(_, _) => false,
}
}
}
#[derive(Debug, PartialEq)]
pub struct TagViewMap<'a> {
keys: &'a [TagDesc],
dat: Vec<TagValue>,
}
#[inline(always)]
fn try_add(dat: &mut Vec<TagValue>, keys: &[TagDesc], val: TagValue) -> anyhow::Result<()> {
let next_idx = dat.len();
anyhow::ensure!(
next_idx < keys.len(),
"Attempted to add a TagVal {val:?} at index {next_idx}, but there are only {} keys in the keyset",
keys.len()
);
anyhow::ensure!(
keys[next_idx].matches_value_type(&val),
"The TagValue that was attempted to be added {val:?} didn't match the next TagDesc {:?}",
keys[next_idx]
);
dat.push(val);
Ok(())
}
#[inline(always)]
pub fn get_tag_by_name<'a>(
key: &str,
dat: &'a [TagValue],
keys: &[TagDesc],
) -> Option<&'a TagValue> {
for (k, val) in keys.iter().zip(dat.iter()) {
if k.name == key {
return Some(val);
}
}
None
}
#[inline(always)]
fn write_tag_map_values<W: Write>(
dat: &[TagValue],
keys: &[TagDesc],
writer: &mut W,
) -> anyhow::Result<()> {
for (n, v) in keys.iter().zip(dat.iter()) {
v.write_with_type(&n.typeid, writer)
.with_context(|| format!("couldn't write tag value for tag {}", n.name))?;
}
Ok(())
}
impl<'a> TagViewMap<'a> {
pub fn with_keyset(keyset: &'a [TagDesc]) -> Self {
Self {
keys: keyset,
dat: Vec::with_capacity(keyset.len()),
}
}
pub fn try_add(&mut self, val: TagValue) -> anyhow::Result<()> {
try_add(&mut self.dat, self.keys, val)
}
pub fn add(&mut self, val: TagValue) {
self.dat.push(val);
}
pub fn get(&self, key: &str) -> Option<&TagValue> {
get_tag_by_name(key, &self.dat, self.keys)
}
pub fn get_at_index(&self, idx: usize) -> Option<&TagValue> {
self.dat.get(idx)
}
pub fn write_values<W: Write>(&self, writer: &mut W) -> anyhow::Result<()> {
write_tag_map_values(&self.dat, self.keys, writer)
}
}
impl<'a> std::ops::Index<usize> for TagViewMap<'a> {
type Output = TagValue;
#[inline]
fn index(&self, index: usize) -> &Self::Output {
&self.dat[index]
}
}
#[derive(Debug, PartialEq)]
pub struct TagMap {
keys: Vec<TagDesc>,
dat: Vec<TagValue>,
}
impl TagMap {
pub fn with_keyset(keyset: &[TagDesc]) -> Self {
Self {
keys: Vec::from(keyset),
dat: Vec::with_capacity(keyset.len()),
}
}
pub fn try_add(&mut self, val: TagValue) -> anyhow::Result<()> {
try_add(&mut self.dat, &self.keys, val)
}
pub fn add(&mut self, val: TagValue) {
self.dat.push(val);
}
pub fn get(&self, key: &str) -> Option<&TagValue> {
get_tag_by_name(key, &self.dat, &self.keys)
}
pub fn get_at_index(&self, idx: usize) -> Option<&TagValue> {
self.dat.get(idx)
}
pub fn write_values<W: Write>(&self, writer: &mut W) -> anyhow::Result<()> {
write_tag_map_values(&self.dat, &self.keys, writer)
}
pub fn iter_keys(&self) -> impl std::iter::ExactSizeIterator + use<'_> {
self.keys.iter()
}
pub fn entries(
&self,
) -> impl std::iter::ExactSizeIterator<Item = (&TagDesc, &TagValue)> + use<'_> {
self.keys.iter().zip(self.dat.iter())
}
pub fn insert_map_tags(
&mut self,
name: &str,
keys: TagValue,
vals: TagValue,
) -> anyhow::Result<()> {
let keys_tag_name = format!("{name}.keys");
let vals_tag_name = format!("{name}.values");
let next_idx = self.dat.len();
anyhow::ensure!(
next_idx < self.keys.len() && self.keys[next_idx].name == keys_tag_name,
"insert_map_tags: expected next tag to be '{}' but found '{}'",
keys_tag_name,
self.keys
.get(next_idx)
.map(|k| k.name.as_str())
.unwrap_or("<none>")
);
self.try_add(keys)?;
let next_idx = self.dat.len();
anyhow::ensure!(
next_idx < self.keys.len() && self.keys[next_idx].name == vals_tag_name,
"insert_map_tags: expected next tag to be '{}' but found '{}'",
vals_tag_name,
self.keys
.get(next_idx)
.map(|k| k.name.as_str())
.unwrap_or("<none>")
);
self.try_add(vals)?;
Ok(())
}
pub fn get_map_tag_arrays(&self, name: &str) -> anyhow::Result<(&TagValue, &TagValue)> {
let keys_tag_name = format!("{name}.keys");
let vals_tag_name = format!("{name}.values");
let keys = self
.get(&keys_tag_name)
.ok_or_else(|| anyhow::anyhow!("no map tag '{keys_tag_name}' found in TagMap"))?;
let vals = self
.get(&vals_tag_name)
.ok_or_else(|| anyhow::anyhow!("no map tag '{vals_tag_name}' found in TagMap"))?;
Ok((keys, vals))
}
}
impl std::ops::Index<usize> for TagMap {
type Output = TagValue;
#[inline]
fn index(&self, index: usize) -> &Self::Output {
&self.dat[index]
}
}
impl TagSection {
pub fn from_bytes<T: Read>(reader: &mut T) -> anyhow::Result<Self> {
Self::from_bytes_with_label(reader, TagSectionLabel::Unlabeled)
}
pub fn from_bytes_with_label<T: Read>(
reader: &mut T,
label: TagSectionLabel,
) -> anyhow::Result<Self> {
let mut buf = [0u8; 2];
reader.read_exact(&mut buf)?;
let num_tags = buf.pread::<u16>(0)? as usize;
let mut ts = Self {
label,
tags: Vec::with_capacity(num_tags),
};
for _ in 0..num_tags {
ts.tags.push(TagDesc::from_bytes(reader)?);
}
Ok(ts)
}
pub fn parse_tags_from_bytes<T: Read>(&self, reader: &mut T) -> anyhow::Result<TagMap> {
let mut tm = TagMap::with_keyset(&self.tags);
for tag_desc in &self.tags {
tm.add(tag_desc.value_from_bytes(reader));
}
Ok(tm)
}
pub fn parse_tags_view_from_bytes<T: Read>(
&self,
reader: &mut T,
) -> anyhow::Result<TagViewMap<'_>> {
let mut tm = TagViewMap::with_keyset(&self.tags);
for tag_desc in &self.tags {
tm.add(tag_desc.value_from_bytes(reader));
}
Ok(tm)
}
pub fn try_parse_tags_from_bytes<T: Read>(&self, reader: &mut T) -> anyhow::Result<TagMap> {
let mut tm = TagMap::with_keyset(&self.tags);
for tag_desc in &self.tags {
tm.try_add(tag_desc.value_from_bytes(reader))?;
}
Ok(tm)
}
pub fn try_parse_tags_view_from_bytes<T: Read>(
&self,
reader: &mut T,
) -> anyhow::Result<TagViewMap<'_>> {
let mut tm = TagViewMap::with_keyset(&self.tags);
for tag_desc in &self.tags {
tm.try_add(tag_desc.value_from_bytes(reader))?;
}
Ok(tm)
}
pub fn get_tag_type(&self, name: &str) -> Option<RadType> {
for td in &self.tags {
if name == td.name {
return Some(td.typeid);
}
}
None
}
pub fn has_tag(&self, name: &str) -> bool {
for td in &self.tags {
if name == td.name {
return true;
}
}
false
}
}
#[cfg(test)]
mod tests {
use crate::rad_types::RadType;
use crate::rad_types::{RadAtomicId, RadIntId, TagSection, TagSectionLabel, TagValue};
use std::io::Write;
use super::TagDesc;
#[test]
fn tag_value_rad_type_roundtrip() {
assert_eq!(TagValue::U32(42).rad_type(), RadType::Int(RadIntId::U32));
assert_eq!(TagValue::U64(99).rad_type(), RadType::Int(RadIntId::U64));
assert_eq!(TagValue::I32(-1).rad_type(), RadType::Int(RadIntId::I32));
assert_eq!(TagValue::String("hi".into()).rad_type(), RadType::String);
assert_eq!(
TagValue::ArrayU32(vec![1, 2]).rad_type(),
RadType::Array(RadIntId::U32, RadAtomicId::Int(RadIntId::U32))
);
}
#[test]
fn from_tag_values_roundtrip() {
let (section, tag_map) = TagSection::from_tag_values(
TagSectionLabel::FileTags,
&[
("bc_len", TagValue::U16(16)),
("umi_len", TagValue::U16(12)),
("rad_type", TagValue::String("sc_rna".into())),
],
);
assert_eq!(section.tags.len(), 3);
assert_eq!(tag_map.get("bc_len"), Some(&TagValue::U16(16)));
assert_eq!(tag_map.get("umi_len"), Some(&TagValue::U16(12)));
assert_eq!(
tag_map.get("rad_type"),
Some(&TagValue::String("sc_rna".into()))
);
let mut buf = Vec::<u8>::new();
section.write(&mut buf).unwrap();
tag_map.write_values(&mut buf).unwrap();
let section_keys_bytes = {
let mut c = std::io::Cursor::new(&buf);
TagSection::from_bytes_with_label(&mut c, TagSectionLabel::FileTags).unwrap()
};
let read_map = section_keys_bytes
.parse_tags_from_bytes(&mut std::io::Cursor::new(
&buf[{
let mut tmp = Vec::<u8>::new();
section.write(&mut tmp).unwrap();
tmp.len()
}..],
))
.unwrap();
assert_eq!(read_map.get("bc_len"), Some(&TagValue::U16(16)));
assert_eq!(
read_map.get("rad_type"),
Some(&TagValue::String("sc_rna".into()))
);
}
#[test]
fn map_tags_roundtrip() {
use crate::rad_types::TagMap;
let mut section = TagSection::new_with_label(TagSectionLabel::FileTags);
section.add_map_tags("ref_lengths", RadIntId::U32, RadIntId::U32);
let keys_vec: Vec<u32> = vec![0, 1, 2];
let vals_vec: Vec<u32> = vec![1000, 2000, 3000];
let mut tag_map = TagMap::with_keyset(§ion.tags);
tag_map
.insert_map_tags(
"ref_lengths",
TagValue::ArrayU32(keys_vec.clone()),
TagValue::ArrayU32(vals_vec.clone()),
)
.unwrap();
let (k, v) = tag_map.get_map_tag_arrays("ref_lengths").unwrap();
assert_eq!(k, &TagValue::ArrayU32(keys_vec.clone()));
assert_eq!(v, &TagValue::ArrayU32(vals_vec.clone()));
if let (TagValue::ArrayU32(ks), TagValue::ArrayU32(vs)) = (k, v) {
let hmap: std::collections::HashMap<u32, u32> =
ks.iter().copied().zip(vs.iter().copied()).collect();
assert_eq!(hmap[&0], 1000);
assert_eq!(hmap[&1], 2000);
assert_eq!(hmap[&2], 3000);
} else {
panic!("unexpected TagValue variants");
}
}
#[test]
fn can_parse_simple_tag_desc() {
let mut buf = Vec::<u8>::new();
let tag_name = b"mytag";
let _ = buf.write_all(&5_u16.to_ne_bytes());
let _ = buf.write_all(tag_name);
let tag_type = 4_u8;
let _ = buf.write_all(&tag_type.to_ne_bytes());
let desc = TagDesc::from_bytes(&mut buf.as_slice()).unwrap();
assert_eq!(desc.name, "mytag");
assert_eq!(desc.typeid, RadType::Int(RadIntId::U64));
}
#[test]
fn can_parse_u128_tag_desc() {
let mut buf = Vec::<u8>::new();
let tag_name = b"mytag";
let _ = buf.write_all(&5_u16.to_ne_bytes());
let _ = buf.write_all(tag_name);
let tag_type = 9_u8;
let _ = buf.write_all(&tag_type.to_ne_bytes());
let desc = TagDesc::from_bytes(&mut buf.as_slice()).unwrap();
assert_eq!(desc.name, "mytag");
assert_eq!(desc.typeid, RadType::Int(RadIntId::U128));
}
#[test]
fn can_parse_array_tag_desc() {
let mut buf = Vec::<u8>::new();
let tag_name = b"mytag";
let _ = buf.write_all(&5_u16.to_ne_bytes());
let _ = buf.write_all(tag_name);
let tag_type = 7_u8;
let _ = buf.write_all(&tag_type.to_ne_bytes());
let _ = buf.write_all(&1_u8.to_ne_bytes());
let _ = buf.write_all(&2_u8.to_ne_bytes());
let desc = TagDesc::from_bytes(&mut buf.as_slice()).unwrap();
assert_eq!(desc.name, "mytag");
assert_eq!(
desc.typeid,
RadType::Array(RadIntId::U8, RadAtomicId::Int(RadIntId::U16))
);
}
#[test]
fn can_parse_tag_values_from_section() {
let mut buf = Vec::<u8>::new();
let tag_name = b"mytag";
let _ = buf.write_all(&5_u16.to_ne_bytes());
let _ = buf.write_all(tag_name);
let tag_type = 7_u8;
let _ = buf.write_all(&tag_type.to_ne_bytes());
let _ = buf.write_all(&1_u8.to_ne_bytes());
let _ = buf.write_all(&2_u8.to_ne_bytes());
let desc = TagDesc::from_bytes(&mut buf.as_slice()).unwrap();
assert_eq!(desc.name, "mytag");
assert_eq!(
desc.typeid,
RadType::Array(RadIntId::U8, RadAtomicId::Int(RadIntId::U16))
);
buf.clear();
let tag_name = b"stringtag";
let _ = buf.write_all(&9_u16.to_ne_bytes());
let _ = buf.write_all(tag_name);
let _ = buf.write_all(&8_u8.to_ne_bytes());
let desc_str = TagDesc::from_bytes(&mut buf.as_slice()).unwrap();
let tag_sec = TagSection {
label: TagSectionLabel::FileTags,
tags: vec![desc, desc_str],
};
buf.clear();
let _ = buf.write_all(&3_u8.to_ne_bytes());
let _ = buf.write_all(&1_u16.to_ne_bytes());
let _ = buf.write_all(&2_u16.to_ne_bytes());
let _ = buf.write_all(&3_u16.to_ne_bytes());
let _ = buf.write_all(&6_u16.to_ne_bytes());
let _ = buf.write_all(b"hi_rad");
let map = tag_sec.parse_tags_from_bytes(&mut buf.as_slice()).unwrap();
assert_eq!(
map.get("mytag").unwrap(),
&TagValue::ArrayU16(vec![1, 2, 3])
);
assert_eq!(
map.get("stringtag").unwrap(),
&TagValue::String(String::from("hi_rad"))
);
assert_eq!(
map.get_at_index(0).unwrap(),
&TagValue::ArrayU16(vec![1, 2, 3])
);
assert_eq!(
map.get_at_index(1).unwrap(),
&TagValue::String(String::from("hi_rad"))
);
let map = tag_sec
.try_parse_tags_from_bytes(&mut buf.as_slice())
.unwrap();
assert_eq!(
map.get("mytag").unwrap(),
&TagValue::ArrayU16(vec![1, 2, 3])
);
assert_eq!(
map.get("stringtag").unwrap(),
&TagValue::String(String::from("hi_rad"))
);
assert_eq!(
map.get_at_index(0).unwrap(),
&TagValue::ArrayU16(vec![1, 2, 3])
);
assert_eq!(
map.get_at_index(1).unwrap(),
&TagValue::String(String::from("hi_rad"))
);
assert_eq!(map[0], TagValue::ArrayU16(vec![1, 2, 3]));
assert_eq!(map[1], TagValue::String(String::from("hi_rad")));
}
}