use crate::Result;
use crate::error::SlowError;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AuxType {
Int8,
UInt8,
Int16,
UInt16,
Int32,
UInt32,
Int64,
UInt64,
Float,
Double,
String,
Int8Array,
UInt8Array,
Int16Array,
UInt16Array,
Int32Array,
UInt32Array,
Int64Array,
UInt64Array,
FloatArray,
DoubleArray,
Enum(Vec<std::string::String>),
}
#[derive(Debug, Clone, PartialEq)]
pub enum AuxValue {
Int8(i8),
UInt8(u8),
Int16(i16),
UInt16(u16),
Int32(i32),
UInt32(u32),
Int64(i64),
UInt64(u64),
Float(f32),
Double(f64),
String(std::string::String),
Int8Array(Vec<i8>),
UInt8Array(Vec<u8>),
Int16Array(Vec<i16>),
UInt16Array(Vec<u16>),
Int32Array(Vec<i32>),
UInt32Array(Vec<u32>),
Int64Array(Vec<i64>),
UInt64Array(Vec<u64>),
FloatArray(Vec<f32>),
DoubleArray(Vec<f64>),
Enum(u8),
Missing,
}
#[derive(Debug, Clone, Default)]
pub struct AuxMeta {
pub names: Vec<std::string::String>,
pub types: Vec<AuxType>,
}
impl AuxMeta {
pub fn is_empty(&self) -> bool {
self.names.is_empty()
}
pub fn len(&self) -> usize {
self.names.len()
}
}
const MISSING_I8: i8 = i8::MIN;
const MISSING_I16: i16 = i16::MIN;
const MISSING_I32: i32 = i32::MIN;
const MISSING_I64: i64 = i64::MIN;
const MISSING_U8: u8 = u8::MAX;
const MISSING_U16: u16 = u16::MAX;
const MISSING_U32: u32 = u32::MAX;
const MISSING_U64: u64 = u64::MAX;
const MISSING_ENUM: u8 = u8::MAX;
pub(crate) fn parse_aux_type(s: &str) -> Result<AuxType> {
match s {
"int8_t" => Ok(AuxType::Int8),
"uint8_t" => Ok(AuxType::UInt8),
"int16_t" => Ok(AuxType::Int16),
"uint16_t" => Ok(AuxType::UInt16),
"int32_t" => Ok(AuxType::Int32),
"uint32_t" => Ok(AuxType::UInt32),
"int64_t" => Ok(AuxType::Int64),
"uint64_t" => Ok(AuxType::UInt64),
"float" => Ok(AuxType::Float),
"double" => Ok(AuxType::Double),
"char*" => Ok(AuxType::String),
"int8_t*" => Ok(AuxType::Int8Array),
"uint8_t*" => Ok(AuxType::UInt8Array),
"int16_t*" => Ok(AuxType::Int16Array),
"uint16_t*" => Ok(AuxType::UInt16Array),
"int32_t*" => Ok(AuxType::Int32Array),
"uint32_t*" => Ok(AuxType::UInt32Array),
"int64_t*" => Ok(AuxType::Int64Array),
"uint64_t*" => Ok(AuxType::UInt64Array),
"float*" => Ok(AuxType::FloatArray),
"double*" => Ok(AuxType::DoubleArray),
s if s.starts_with("enum{") && s.ends_with('}') => {
let inner = &s[5..s.len() - 1];
let variants = inner.split(',').map(|v| v.to_string()).collect::<Vec<_>>();
Ok(AuxType::Enum(variants))
}
other => Err(SlowError::InvalidFormat(format!(
"unknown aux field type: '{other}'"
))),
}
}
pub(crate) fn aux_type_str(typ: &AuxType) -> std::string::String {
match typ {
AuxType::Int8 => "int8_t".into(),
AuxType::UInt8 => "uint8_t".into(),
AuxType::Int16 => "int16_t".into(),
AuxType::UInt16 => "uint16_t".into(),
AuxType::Int32 => "int32_t".into(),
AuxType::UInt32 => "uint32_t".into(),
AuxType::Int64 => "int64_t".into(),
AuxType::UInt64 => "uint64_t".into(),
AuxType::Float => "float".into(),
AuxType::Double => "double".into(),
AuxType::String => "char*".into(),
AuxType::Int8Array => "int8_t*".into(),
AuxType::UInt8Array => "uint8_t*".into(),
AuxType::Int16Array => "int16_t*".into(),
AuxType::UInt16Array => "uint16_t*".into(),
AuxType::Int32Array => "int32_t*".into(),
AuxType::UInt32Array => "uint32_t*".into(),
AuxType::Int64Array => "int64_t*".into(),
AuxType::UInt64Array => "uint64_t*".into(),
AuxType::FloatArray => "float*".into(),
AuxType::DoubleArray => "double*".into(),
AuxType::Enum(variants) => format!("enum{{{}}}", variants.join(",")),
}
}
pub(crate) fn parse_aux_meta_from_lines(type_line: &str, name_line: &str) -> Result<AuxMeta> {
let type_fields: Vec<&str> = type_line.trim_start_matches('#').split('\t').collect();
let name_fields: Vec<&str> = name_line.trim_start_matches('#').split('\t').collect();
let aux_types: Result<Vec<AuxType>> = type_fields
.iter()
.skip(8)
.map(|&t| parse_aux_type(t))
.collect();
let aux_names: Vec<std::string::String> =
name_fields.iter().skip(8).map(|&n| n.to_string()).collect();
Ok(AuxMeta {
names: aux_names,
types: aux_types?,
})
}
fn read_bytes_at<'a>(buf: &'a [u8], pos: &mut usize, n: usize) -> Result<&'a [u8]> {
let end = *pos + n;
if end > buf.len() {
return Err(SlowError::InvalidFormat(format!(
"aux binary decode: need {n} bytes at offset {pos}, have {}",
buf.len() - *pos
)));
}
let s = &buf[*pos..end];
*pos = end;
Ok(s)
}
fn read_u32_at(buf: &[u8], pos: &mut usize) -> Result<u32> {
let b = read_bytes_at(buf, pos, 4)?;
Ok(u32::from_le_bytes([b[0], b[1], b[2], b[3]]))
}
fn read_u64_at(buf: &[u8], pos: &mut usize) -> Result<u64> {
let b = read_bytes_at(buf, pos, 8)?;
Ok(u64::from_le_bytes([
b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7],
]))
}
pub(crate) fn decode_binary(buf: &[u8], pos: &mut usize, typ: &AuxType) -> Result<AuxValue> {
match typ {
AuxType::Int8 => {
let b = read_bytes_at(buf, pos, 1)?;
let v = i8::from_le_bytes([b[0]]);
if v == MISSING_I8 {
Ok(AuxValue::Missing)
} else {
Ok(AuxValue::Int8(v))
}
}
AuxType::UInt8 => {
let b = read_bytes_at(buf, pos, 1)?;
let v = b[0];
if v == MISSING_U8 {
Ok(AuxValue::Missing)
} else {
Ok(AuxValue::UInt8(v))
}
}
AuxType::Int16 => {
let b = read_bytes_at(buf, pos, 2)?;
let v = i16::from_le_bytes([b[0], b[1]]);
if v == MISSING_I16 {
Ok(AuxValue::Missing)
} else {
Ok(AuxValue::Int16(v))
}
}
AuxType::UInt16 => {
let b = read_bytes_at(buf, pos, 2)?;
let v = u16::from_le_bytes([b[0], b[1]]);
if v == MISSING_U16 {
Ok(AuxValue::Missing)
} else {
Ok(AuxValue::UInt16(v))
}
}
AuxType::Int32 => {
let b = read_bytes_at(buf, pos, 4)?;
let v = i32::from_le_bytes([b[0], b[1], b[2], b[3]]);
if v == MISSING_I32 {
Ok(AuxValue::Missing)
} else {
Ok(AuxValue::Int32(v))
}
}
AuxType::UInt32 => {
let b = read_bytes_at(buf, pos, 4)?;
let v = u32::from_le_bytes([b[0], b[1], b[2], b[3]]);
if v == MISSING_U32 {
Ok(AuxValue::Missing)
} else {
Ok(AuxValue::UInt32(v))
}
}
AuxType::Int64 => {
let b = read_bytes_at(buf, pos, 8)?;
let v = i64::from_le_bytes([b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]]);
if v == MISSING_I64 {
Ok(AuxValue::Missing)
} else {
Ok(AuxValue::Int64(v))
}
}
AuxType::UInt64 => {
let b = read_bytes_at(buf, pos, 8)?;
let v = u64::from_le_bytes([b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]]);
if v == MISSING_U64 {
Ok(AuxValue::Missing)
} else {
Ok(AuxValue::UInt64(v))
}
}
AuxType::Float => {
let b = read_bytes_at(buf, pos, 4)?;
let v = f32::from_le_bytes([b[0], b[1], b[2], b[3]]);
if v.is_nan() {
Ok(AuxValue::Missing)
} else {
Ok(AuxValue::Float(v))
}
}
AuxType::Double => {
let b = read_bytes_at(buf, pos, 8)?;
let v = f64::from_le_bytes([b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]]);
if v.is_nan() {
Ok(AuxValue::Missing)
} else {
Ok(AuxValue::Double(v))
}
}
AuxType::String => {
let len = read_u64_at(buf, pos)? as usize;
if len == 0 {
return Ok(AuxValue::Missing);
}
let sb = read_bytes_at(buf, pos, len)?;
if len == 1 && sb[0] == b'.' {
Ok(AuxValue::Missing)
} else {
let s = std::str::from_utf8(sb).map_err(|e| {
SlowError::InvalidFormat(format!("aux char* field: invalid UTF-8: {e}"))
})?;
Ok(AuxValue::String(s.to_string()))
}
}
AuxType::Enum(_) => {
let b = read_bytes_at(buf, pos, 1)?;
let v = b[0];
if v == MISSING_ENUM {
Ok(AuxValue::Missing)
} else {
Ok(AuxValue::Enum(v))
}
}
AuxType::Int8Array => {
let count = read_u32_at(buf, pos)? as usize;
if count == 0 {
return Ok(AuxValue::Missing);
}
let data = read_bytes_at(buf, pos, count)?;
Ok(AuxValue::Int8Array(data.iter().map(|&b| b as i8).collect()))
}
AuxType::UInt8Array => {
let count = read_u32_at(buf, pos)? as usize;
if count == 0 {
return Ok(AuxValue::Missing);
}
let data = read_bytes_at(buf, pos, count)?;
Ok(AuxValue::UInt8Array(data.to_vec()))
}
AuxType::Int16Array => {
let count = read_u32_at(buf, pos)? as usize;
if count == 0 {
return Ok(AuxValue::Missing);
}
let data = read_bytes_at(buf, pos, count * 2)?;
Ok(AuxValue::Int16Array(
data.chunks_exact(2)
.map(|c| i16::from_le_bytes([c[0], c[1]]))
.collect(),
))
}
AuxType::UInt16Array => {
let count = read_u32_at(buf, pos)? as usize;
if count == 0 {
return Ok(AuxValue::Missing);
}
let data = read_bytes_at(buf, pos, count * 2)?;
Ok(AuxValue::UInt16Array(
data.chunks_exact(2)
.map(|c| u16::from_le_bytes([c[0], c[1]]))
.collect(),
))
}
AuxType::Int32Array => {
let count = read_u32_at(buf, pos)? as usize;
if count == 0 {
return Ok(AuxValue::Missing);
}
let data = read_bytes_at(buf, pos, count * 4)?;
Ok(AuxValue::Int32Array(
data.chunks_exact(4)
.map(|c| i32::from_le_bytes([c[0], c[1], c[2], c[3]]))
.collect(),
))
}
AuxType::UInt32Array => {
let count = read_u32_at(buf, pos)? as usize;
if count == 0 {
return Ok(AuxValue::Missing);
}
let data = read_bytes_at(buf, pos, count * 4)?;
Ok(AuxValue::UInt32Array(
data.chunks_exact(4)
.map(|c| u32::from_le_bytes([c[0], c[1], c[2], c[3]]))
.collect(),
))
}
AuxType::Int64Array => {
let count = read_u32_at(buf, pos)? as usize;
if count == 0 {
return Ok(AuxValue::Missing);
}
let data = read_bytes_at(buf, pos, count * 8)?;
Ok(AuxValue::Int64Array(
data.chunks_exact(8)
.map(|c| i64::from_le_bytes([c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7]]))
.collect(),
))
}
AuxType::UInt64Array => {
let count = read_u32_at(buf, pos)? as usize;
if count == 0 {
return Ok(AuxValue::Missing);
}
let data = read_bytes_at(buf, pos, count * 8)?;
Ok(AuxValue::UInt64Array(
data.chunks_exact(8)
.map(|c| u64::from_le_bytes([c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7]]))
.collect(),
))
}
AuxType::FloatArray => {
let count = read_u32_at(buf, pos)? as usize;
if count == 0 {
return Ok(AuxValue::Missing);
}
let data = read_bytes_at(buf, pos, count * 4)?;
Ok(AuxValue::FloatArray(
data.chunks_exact(4)
.map(|c| f32::from_le_bytes([c[0], c[1], c[2], c[3]]))
.collect(),
))
}
AuxType::DoubleArray => {
let count = read_u32_at(buf, pos)? as usize;
if count == 0 {
return Ok(AuxValue::Missing);
}
let data = read_bytes_at(buf, pos, count * 8)?;
Ok(AuxValue::DoubleArray(
data.chunks_exact(8)
.map(|c| f64::from_le_bytes([c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7]]))
.collect(),
))
}
}
}
pub(crate) fn encode_binary(val: &AuxValue, typ: &AuxType, out: &mut Vec<u8>) {
match (val, typ) {
(AuxValue::Missing, AuxType::Int8) => out.push(MISSING_I8 as u8),
(AuxValue::Int8(v), _) => out.push(*v as u8),
(AuxValue::Missing, AuxType::UInt8) => out.push(MISSING_U8),
(AuxValue::UInt8(v), _) => out.push(*v),
(AuxValue::Missing, AuxType::Int16) => out.extend_from_slice(&MISSING_I16.to_le_bytes()),
(AuxValue::Int16(v), _) => out.extend_from_slice(&v.to_le_bytes()),
(AuxValue::Missing, AuxType::UInt16) => out.extend_from_slice(&MISSING_U16.to_le_bytes()),
(AuxValue::UInt16(v), _) => out.extend_from_slice(&v.to_le_bytes()),
(AuxValue::Missing, AuxType::Int32) => out.extend_from_slice(&MISSING_I32.to_le_bytes()),
(AuxValue::Int32(v), _) => out.extend_from_slice(&v.to_le_bytes()),
(AuxValue::Missing, AuxType::UInt32) => out.extend_from_slice(&MISSING_U32.to_le_bytes()),
(AuxValue::UInt32(v), _) => out.extend_from_slice(&v.to_le_bytes()),
(AuxValue::Missing, AuxType::Int64) => out.extend_from_slice(&MISSING_I64.to_le_bytes()),
(AuxValue::Int64(v), _) => out.extend_from_slice(&v.to_le_bytes()),
(AuxValue::Missing, AuxType::UInt64) => out.extend_from_slice(&MISSING_U64.to_le_bytes()),
(AuxValue::UInt64(v), _) => out.extend_from_slice(&v.to_le_bytes()),
(AuxValue::Missing, AuxType::Float) => out.extend_from_slice(&f32::NAN.to_le_bytes()),
(AuxValue::Float(v), _) => out.extend_from_slice(&v.to_le_bytes()),
(AuxValue::Missing, AuxType::Double) => out.extend_from_slice(&f64::NAN.to_le_bytes()),
(AuxValue::Double(v), _) => out.extend_from_slice(&v.to_le_bytes()),
(AuxValue::Missing, AuxType::String) => {
out.extend_from_slice(&1u64.to_le_bytes());
out.push(b'.');
}
(AuxValue::String(s), _) => {
let bytes = s.as_bytes();
out.extend_from_slice(&(bytes.len() as u64).to_le_bytes());
out.extend_from_slice(bytes);
}
(AuxValue::Missing, AuxType::Enum(_)) => out.push(MISSING_ENUM),
(AuxValue::Enum(v), _) => out.push(*v),
(AuxValue::Missing, AuxType::Int8Array)
| (AuxValue::Missing, AuxType::UInt8Array)
| (AuxValue::Missing, AuxType::Int16Array)
| (AuxValue::Missing, AuxType::UInt16Array)
| (AuxValue::Missing, AuxType::Int32Array)
| (AuxValue::Missing, AuxType::UInt32Array)
| (AuxValue::Missing, AuxType::Int64Array)
| (AuxValue::Missing, AuxType::UInt64Array)
| (AuxValue::Missing, AuxType::FloatArray)
| (AuxValue::Missing, AuxType::DoubleArray) => out.extend_from_slice(&0u32.to_le_bytes()),
(AuxValue::Int8Array(v), _) => {
out.extend_from_slice(&(v.len() as u32).to_le_bytes());
out.extend(v.iter().map(|&x| x as u8));
}
(AuxValue::UInt8Array(v), _) => {
out.extend_from_slice(&(v.len() as u32).to_le_bytes());
out.extend_from_slice(v);
}
(AuxValue::Int16Array(v), _) => {
out.extend_from_slice(&(v.len() as u32).to_le_bytes());
for &x in v {
out.extend_from_slice(&x.to_le_bytes());
}
}
(AuxValue::UInt16Array(v), _) => {
out.extend_from_slice(&(v.len() as u32).to_le_bytes());
for &x in v {
out.extend_from_slice(&x.to_le_bytes());
}
}
(AuxValue::Int32Array(v), _) => {
out.extend_from_slice(&(v.len() as u32).to_le_bytes());
for &x in v {
out.extend_from_slice(&x.to_le_bytes());
}
}
(AuxValue::UInt32Array(v), _) => {
out.extend_from_slice(&(v.len() as u32).to_le_bytes());
for &x in v {
out.extend_from_slice(&x.to_le_bytes());
}
}
(AuxValue::Int64Array(v), _) => {
out.extend_from_slice(&(v.len() as u32).to_le_bytes());
for &x in v {
out.extend_from_slice(&x.to_le_bytes());
}
}
(AuxValue::UInt64Array(v), _) => {
out.extend_from_slice(&(v.len() as u32).to_le_bytes());
for &x in v {
out.extend_from_slice(&x.to_le_bytes());
}
}
(AuxValue::FloatArray(v), _) => {
out.extend_from_slice(&(v.len() as u32).to_le_bytes());
for &x in v {
out.extend_from_slice(&x.to_le_bytes());
}
}
(AuxValue::DoubleArray(v), _) => {
out.extend_from_slice(&(v.len() as u32).to_le_bytes());
for &x in v {
out.extend_from_slice(&x.to_le_bytes());
}
}
}
}
pub(crate) fn decode_text(s: &str, typ: &AuxType) -> Result<AuxValue> {
if s == "." {
return Ok(AuxValue::Missing);
}
let parse_err = |what: &str, e: &dyn std::fmt::Display| {
SlowError::InvalidFormat(format!("aux {what} field: cannot parse '{s}': {e}"))
};
match typ {
AuxType::Int8 => s
.parse::<i8>()
.map(AuxValue::Int8)
.map_err(|e| parse_err("int8_t", &e)),
AuxType::UInt8 => s
.parse::<u8>()
.map(AuxValue::UInt8)
.map_err(|e| parse_err("uint8_t", &e)),
AuxType::Int16 => s
.parse::<i16>()
.map(AuxValue::Int16)
.map_err(|e| parse_err("int16_t", &e)),
AuxType::UInt16 => s
.parse::<u16>()
.map(AuxValue::UInt16)
.map_err(|e| parse_err("uint16_t", &e)),
AuxType::Int32 => s
.parse::<i32>()
.map(AuxValue::Int32)
.map_err(|e| parse_err("int32_t", &e)),
AuxType::UInt32 => s
.parse::<u32>()
.map(AuxValue::UInt32)
.map_err(|e| parse_err("uint32_t", &e)),
AuxType::Int64 => s
.parse::<i64>()
.map(AuxValue::Int64)
.map_err(|e| parse_err("int64_t", &e)),
AuxType::UInt64 => s
.parse::<u64>()
.map(AuxValue::UInt64)
.map_err(|e| parse_err("uint64_t", &e)),
AuxType::Float => s
.parse::<f32>()
.map(AuxValue::Float)
.map_err(|e| parse_err("float", &e)),
AuxType::Double => s
.parse::<f64>()
.map(AuxValue::Double)
.map_err(|e| parse_err("double", &e)),
AuxType::String => Ok(AuxValue::String(s.to_string())),
AuxType::Enum(_) => s
.parse::<u8>()
.map(AuxValue::Enum)
.map_err(|e| parse_err("enum", &e)),
AuxType::Int8Array => {
let v: Result<Vec<i8>> = s
.split(',')
.map(|x| x.parse::<i8>().map_err(|e| parse_err("int8_t*", &e)))
.collect();
v.map(AuxValue::Int8Array)
}
AuxType::UInt8Array => {
let v: Result<Vec<u8>> = s
.split(',')
.map(|x| x.parse::<u8>().map_err(|e| parse_err("uint8_t*", &e)))
.collect();
v.map(AuxValue::UInt8Array)
}
AuxType::Int16Array => {
let v: Result<Vec<i16>> = s
.split(',')
.map(|x| x.parse::<i16>().map_err(|e| parse_err("int16_t*", &e)))
.collect();
v.map(AuxValue::Int16Array)
}
AuxType::UInt16Array => {
let v: Result<Vec<u16>> = s
.split(',')
.map(|x| x.parse::<u16>().map_err(|e| parse_err("uint16_t*", &e)))
.collect();
v.map(AuxValue::UInt16Array)
}
AuxType::Int32Array => {
let v: Result<Vec<i32>> = s
.split(',')
.map(|x| x.parse::<i32>().map_err(|e| parse_err("int32_t*", &e)))
.collect();
v.map(AuxValue::Int32Array)
}
AuxType::UInt32Array => {
let v: Result<Vec<u32>> = s
.split(',')
.map(|x| x.parse::<u32>().map_err(|e| parse_err("uint32_t*", &e)))
.collect();
v.map(AuxValue::UInt32Array)
}
AuxType::Int64Array => {
let v: Result<Vec<i64>> = s
.split(',')
.map(|x| x.parse::<i64>().map_err(|e| parse_err("int64_t*", &e)))
.collect();
v.map(AuxValue::Int64Array)
}
AuxType::UInt64Array => {
let v: Result<Vec<u64>> = s
.split(',')
.map(|x| x.parse::<u64>().map_err(|e| parse_err("uint64_t*", &e)))
.collect();
v.map(AuxValue::UInt64Array)
}
AuxType::FloatArray => {
let v: Result<Vec<f32>> = s
.split(',')
.map(|x| x.parse::<f32>().map_err(|e| parse_err("float*", &e)))
.collect();
v.map(AuxValue::FloatArray)
}
AuxType::DoubleArray => {
let v: Result<Vec<f64>> = s
.split(',')
.map(|x| x.parse::<f64>().map_err(|e| parse_err("double*", &e)))
.collect();
v.map(AuxValue::DoubleArray)
}
}
}
pub(crate) fn encode_text(val: &AuxValue) -> std::string::String {
match val {
AuxValue::Missing => ".".into(),
AuxValue::Int8(v) => v.to_string(),
AuxValue::UInt8(v) => v.to_string(),
AuxValue::Int16(v) => v.to_string(),
AuxValue::UInt16(v) => v.to_string(),
AuxValue::Int32(v) => v.to_string(),
AuxValue::UInt32(v) => v.to_string(),
AuxValue::Int64(v) => v.to_string(),
AuxValue::UInt64(v) => v.to_string(),
AuxValue::Float(v) => v.to_string(),
AuxValue::Double(v) => v.to_string(),
AuxValue::String(s) => s.clone(),
AuxValue::Enum(idx) => idx.to_string(),
AuxValue::Int8Array(v) => v
.iter()
.map(|x| x.to_string())
.collect::<Vec<_>>()
.join(","),
AuxValue::UInt8Array(v) => v
.iter()
.map(|x| x.to_string())
.collect::<Vec<_>>()
.join(","),
AuxValue::Int16Array(v) => v
.iter()
.map(|x| x.to_string())
.collect::<Vec<_>>()
.join(","),
AuxValue::UInt16Array(v) => v
.iter()
.map(|x| x.to_string())
.collect::<Vec<_>>()
.join(","),
AuxValue::Int32Array(v) => v
.iter()
.map(|x| x.to_string())
.collect::<Vec<_>>()
.join(","),
AuxValue::UInt32Array(v) => v
.iter()
.map(|x| x.to_string())
.collect::<Vec<_>>()
.join(","),
AuxValue::Int64Array(v) => v
.iter()
.map(|x| x.to_string())
.collect::<Vec<_>>()
.join(","),
AuxValue::UInt64Array(v) => v
.iter()
.map(|x| x.to_string())
.collect::<Vec<_>>()
.join(","),
AuxValue::FloatArray(v) => v
.iter()
.map(|x| x.to_string())
.collect::<Vec<_>>()
.join(","),
AuxValue::DoubleArray(v) => v
.iter()
.map(|x| x.to_string())
.collect::<Vec<_>>()
.join(","),
}
}