Initial Commit
+ Basic implementation working with just unsigned ints + Can fetch data from arbitrary bit positions in a byte string + TOML config implementation for describing data + Simple CLIpull/1/head
commit
b85ba31f91
|
@ -0,0 +1 @@
|
|||
/target
|
|
@ -0,0 +1,263 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "ansi_term"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "2.33.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"atty",
|
||||
"bitflags",
|
||||
"strsim",
|
||||
"textwrap",
|
||||
"unicode-width",
|
||||
"vec_map",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "formaty"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"serde",
|
||||
"structopt",
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
|
||||
dependencies = [
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.101"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3cb00336871be5ed2c8ed44b60ae9959dc5b9f08539422ed43f09e34ecaeba21"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9f5105d4fdaab20335ca9565e106a5d9b82b6219b5ba735731124ac6711d23d"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.130"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.130"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
||||
|
||||
[[package]]
|
||||
name = "structopt"
|
||||
version = "0.3.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf9d950ef167e25e0bdb073cf1d68e9ad2795ac826f2f3f59647817cf23c0bfa"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"lazy_static",
|
||||
"structopt-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "structopt-derive"
|
||||
version = "0.4.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "134d838a2c9943ac3125cf6df165eda53493451b719f3255b2a26b85f772d0ba"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.75"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7f58f7e8eaa0009c5fec437aabf511bd9933e4b2d7407bd05273c01a8906ea7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
|
||||
dependencies = [
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.5.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||
|
||||
[[package]]
|
||||
name = "vec_map"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
|
@ -0,0 +1,12 @@
|
|||
[package]
|
||||
name = "formaty"
|
||||
version = "0.1.0"
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
structopt = "0.3.23"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
toml = "0.5.8"
|
||||
byteorder = "1.4.3"
|
|
@ -0,0 +1,136 @@
|
|||
#[derive(Clone, Debug)]
|
||||
pub enum ByteStreamError {
|
||||
OutOfRange,
|
||||
}
|
||||
|
||||
const fn bit_mask(mask: u8) -> u8 {
|
||||
match mask {
|
||||
0 => 0x00,
|
||||
1 => 0x01,
|
||||
2 => 0x03,
|
||||
3 => 0x07,
|
||||
4 => 0x0f,
|
||||
5 => 0x1f,
|
||||
6 => 0x3f,
|
||||
7 => 0x7f,
|
||||
_ => 0xff,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Clone, Debug)]
|
||||
pub struct ByteStream {
|
||||
data: Vec<u8>,
|
||||
}
|
||||
|
||||
impl ByteStream {
|
||||
pub fn get_bytes(&self, bit_ndx: usize, bit_count: usize) -> Result<Vec<u8>, ByteStreamError> {
|
||||
let byte_ndx = bit_ndx / 8;
|
||||
let bits_before = (bit_ndx % 8) as u8;
|
||||
let bits_in_last_byte = ((bit_ndx + bit_count - bits_before as usize) % 8) as u8;
|
||||
let byte_count = if bit_count >= 8 { bit_count / 8 } else { 1 };
|
||||
let bytes_needed = if bits_before != 0 {
|
||||
byte_count + 1
|
||||
} else {
|
||||
byte_count
|
||||
};
|
||||
|
||||
if bytes_needed > self.data.len() || bytes_needed + byte_ndx > self.data.len() {
|
||||
return Err(ByteStreamError::OutOfRange);
|
||||
}
|
||||
|
||||
let mut byte_stream = self.data[byte_ndx..byte_ndx + bytes_needed].to_vec();
|
||||
|
||||
if bits_before != 0 {
|
||||
let mut carry: u8 = 0;
|
||||
for _ in 0..bits_before {
|
||||
for k in (0..bytes_needed).rev() {
|
||||
let next: u8 = (byte_stream[k] & 0x01) << 7;
|
||||
byte_stream[k] = (byte_stream[k] >> 1) | carry;
|
||||
|
||||
carry = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if bytes_needed > byte_count {
|
||||
byte_stream.pop();
|
||||
}
|
||||
|
||||
if bits_in_last_byte != 0 {
|
||||
*byte_stream.last_mut().unwrap() &= bit_mask(bits_in_last_byte);
|
||||
}
|
||||
|
||||
Ok(byte_stream)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&[u8]> for ByteStream {
|
||||
fn from(slice: &[u8]) -> Self {
|
||||
ByteStream::from(slice.to_vec())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<u8>> for ByteStream {
|
||||
fn from(vec: Vec<u8>) -> Self {
|
||||
ByteStream { data: vec }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::ByteStream;
|
||||
|
||||
#[test]
|
||||
fn test_get_bytes_no_shift() {
|
||||
let bytes: Vec<u8> = vec![0xff, 0x00, 0x55];
|
||||
let bit_stream = ByteStream::from(bytes.clone());
|
||||
|
||||
let new_bytes = bit_stream.get_bytes(0, bytes.len() * 8).unwrap();
|
||||
assert_eq!(bytes, new_bytes);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_bytes_with_shift_in_byte() {
|
||||
let bytes: Vec<u8> = vec![0xff, 0x00, 0x55];
|
||||
let bit_stream = ByteStream::from(bytes.clone());
|
||||
|
||||
let new_bytes = bit_stream.get_bytes(4, 4).unwrap();
|
||||
assert_eq!(vec![0x0f], new_bytes);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_bytes_with_shift_across_bytes() {
|
||||
let bytes: Vec<u8> = vec![0xff, 0x55];
|
||||
let bit_stream = ByteStream::from(bytes.clone());
|
||||
|
||||
let new_bytes = bit_stream.get_bytes(4, 8).unwrap();
|
||||
assert_eq!(vec![0x5f], new_bytes);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_bytes_with_shift_across_bytes_odd() {
|
||||
let bytes: Vec<u8> = vec![0xff, 0x55];
|
||||
let bit_stream = ByteStream::from(bytes.clone());
|
||||
|
||||
let new_bytes = bit_stream.get_bytes(7, 2).unwrap();
|
||||
assert_eq!(vec![0x03], new_bytes);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_bytes_one_byte() {
|
||||
let bytes: Vec<u8> = vec![0xff, 0x55];
|
||||
let bit_stream = ByteStream::from(bytes.clone());
|
||||
|
||||
let new_bytes = bit_stream.get_bytes(8, 8).unwrap();
|
||||
assert_eq!(vec![0x55], new_bytes);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_bytes_3_bits() {
|
||||
let bytes: Vec<u8> = vec![0x55];
|
||||
let bit_stream = ByteStream::from(bytes.clone());
|
||||
|
||||
let new_bytes = bit_stream.get_bytes(0, 3).unwrap();
|
||||
assert_eq!(vec![0x05], new_bytes);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
use crate::byte_stream::ByteStream;
|
||||
use serde::Deserialize;
|
||||
use std::fmt::Write;
|
||||
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum FieldType {
|
||||
/// Unsigned Int
|
||||
UInt { bit_width: usize },
|
||||
/// Unsigned Int
|
||||
Int { bit_width: usize },
|
||||
/// Null Terminated String Field
|
||||
String { max_len: usize },
|
||||
/// Fixed Byte Length Field
|
||||
Bytes { max_len: usize },
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
pub struct Field {
|
||||
/// Field Name
|
||||
pub name: String,
|
||||
/// Field Type
|
||||
pub field_type: FieldType,
|
||||
}
|
||||
|
||||
impl Field {
|
||||
fn format_data(&self, byte_stream: &ByteStream, bit_ndx: usize) -> (String, usize) {
|
||||
match self.field_type {
|
||||
FieldType::UInt { bit_width } => {
|
||||
let bytes = byte_stream.get_bytes(bit_ndx, bit_width).unwrap();
|
||||
let mut string = String::with_capacity(bytes.len() * 2);
|
||||
|
||||
for byte in bytes.iter().rev() {
|
||||
string.push_str(&format!("{:x}", byte))
|
||||
}
|
||||
|
||||
(string, bit_width)
|
||||
}
|
||||
_ => ("".to_string(), 0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
pub struct Format {
|
||||
/// Format Name
|
||||
pub name: String,
|
||||
/// Elements of the format
|
||||
pub fields: Vec<Field>,
|
||||
}
|
||||
|
||||
impl Format {
|
||||
pub fn format_data(&self, data: &[u8]) -> String {
|
||||
let mut s = String::new();
|
||||
let byte_stream = ByteStream::from(data);
|
||||
let mut bit_ndx: usize = 0;
|
||||
|
||||
for field in &self.fields {
|
||||
let (data_str, bit_width) = field.format_data(&byte_stream, bit_ndx);
|
||||
bit_ndx += bit_width;
|
||||
writeln!(s, "{}: {}", field.name, data_str).unwrap();
|
||||
}
|
||||
|
||||
s
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
pub mod format;
|
||||
|
||||
use crate::formatter::format::Format;
|
||||
use serde::Deserialize;
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
use std::path::Path;
|
||||
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
pub struct FormatConfig {
|
||||
pub formats: Vec<Format>,
|
||||
}
|
||||
|
||||
impl FormatConfig {
|
||||
pub fn new(config_path: &Path) -> Result<Self, std::io::Error> {
|
||||
let mut config = File::open(config_path)?;
|
||||
|
||||
let mut contents = String::new();
|
||||
|
||||
config.read_to_string(&mut contents)?;
|
||||
|
||||
Ok(toml::from_str(&contents).unwrap())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
use crate::parser::parse_bytes_from_input_arg;
|
||||
use formatter::FormatConfig;
|
||||
use std::path::PathBuf;
|
||||
use structopt::StructOpt;
|
||||
|
||||
mod byte_stream;
|
||||
mod formatter;
|
||||
mod parser;
|
||||
|
||||
#[derive(Debug, StructOpt)]
|
||||
#[structopt(name = "Formaty", about = "Arbitrary Binary Data Formatting")]
|
||||
pub struct Args {
|
||||
#[structopt(parse(from_os_str))]
|
||||
config: PathBuf,
|
||||
|
||||
format: String,
|
||||
|
||||
data: Vec<String>,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args: Args = Args::from_args();
|
||||
|
||||
let config = FormatConfig::new(&args.config).unwrap();
|
||||
|
||||
let format = match config.formats.iter().find(|f| f.name == args.format) {
|
||||
None => {
|
||||
println!("Format not found in config file");
|
||||
return;
|
||||
}
|
||||
Some(format) => format,
|
||||
};
|
||||
|
||||
let data = parse_bytes_from_input_arg(args.data).unwrap();
|
||||
|
||||
println!("{}", format.format_data(&data));
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
use std::num::ParseIntError;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ByteArrayParseErr {
|
||||
EmptySrcArray,
|
||||
ParseIntError(ParseIntError),
|
||||
}
|
||||
|
||||
impl From<ParseIntError> for ByteArrayParseErr {
|
||||
fn from(e: ParseIntError) -> Self {
|
||||
ByteArrayParseErr::ParseIntError(e)
|
||||
}
|
||||
}
|
||||
|
||||
fn bytes_from_str_array(src: Vec<String>) -> Result<Vec<u8>, ByteArrayParseErr> {
|
||||
src.iter()
|
||||
.map(|element| {
|
||||
if element.starts_with("0x") || element.starts_with("0X") {
|
||||
u8::from_str_radix(&element[2..], 16)
|
||||
} else if element.starts_with("0b") || element.starts_with("0B") {
|
||||
u8::from_str_radix(&element[2..], 1)
|
||||
} else if element.starts_with('h') || element.starts_with('h') {
|
||||
u8::from_str_radix(&element[1..], 16)
|
||||
} else if let Some(value) = element.strip_prefix('0') {
|
||||
u8::from_str_radix(value, 8)
|
||||
} else {
|
||||
str::parse(element)
|
||||
}
|
||||
})
|
||||
.map(|e| e.map_err( ByteArrayParseErr::from ))
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn parse_bytes_from_input_arg(src: Vec<String>) -> Result<Vec<u8>, ByteArrayParseErr> {
|
||||
if src.is_empty() {
|
||||
return Err(ByteArrayParseErr::EmptySrcArray);
|
||||
}
|
||||
|
||||
let str_arr = if src.len() == 1 {
|
||||
src[0]
|
||||
.replace(",", " ")
|
||||
.replace("[", "")
|
||||
.replace("]", "")
|
||||
.split_whitespace()
|
||||
.map(|s| s.to_string())
|
||||
.collect()
|
||||
} else {
|
||||
src
|
||||
};
|
||||
|
||||
bytes_from_str_array(str_arr)
|
||||
}
|
Loading…
Reference in New Issue