forked from M-Labs/zynq-rs
add libconfig, libcoreio, szl from artiq-zynq a277e89b3ad; update dependencies
parent
82794d3abd
commit
7360984efb
@ -1,15 +1,24 @@
|
||||
[workspace]
|
||||
members = [
|
||||
"libregister", "libcortex_a9",
|
||||
"libboard_zynq", "libsupport_zynq",
|
||||
"libregister",
|
||||
"libcortex_a9",
|
||||
"libboard_zynq",
|
||||
"libsupport_zynq",
|
||||
"libasync",
|
||||
"libconfig",
|
||||
"libcoreio",
|
||||
"experiments",
|
||||
"szl",
|
||||
]
|
||||
|
||||
[profile.release]
|
||||
panic = "abort"
|
||||
debug = true
|
||||
# Link-Time Optimization:
|
||||
# turn off if you get unusable debug symbols.
|
||||
codegen-units = 1
|
||||
opt-level = 'z'
|
||||
lto = true
|
||||
opt-level = 'z' # Optimize for size.
|
||||
debug-assertions = false
|
||||
overflow-checks = false
|
||||
|
||||
[patch.crates-io]
|
||||
core_io = { path = "./libcoreio" }
|
||||
|
@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "libconfig"
|
||||
version = "0.1.0"
|
||||
authors = ["M-Labs"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
libboard_zynq = { path = "../libboard_zynq" }
|
||||
core_io = { version = "0.1", features = ["collections"] }
|
||||
fatfs = { version = "0.3", features = ["core_io"], default-features = false }
|
||||
log = "0.4"
|
||||
|
||||
[features]
|
||||
ipv6 = []
|
||||
|
@ -0,0 +1,181 @@
|
||||
use alloc::vec::Vec;
|
||||
use core_io::{Error, Read, Seek, SeekFrom};
|
||||
use libboard_zynq::devc;
|
||||
use log::debug;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum BootgenLoadingError {
|
||||
InvalidBootImageHeader,
|
||||
MissingPartition,
|
||||
EncryptedBitstream,
|
||||
IoError(Error),
|
||||
DevcError(devc::DevcError),
|
||||
}
|
||||
|
||||
impl From<Error> for BootgenLoadingError {
|
||||
fn from(error: Error) -> Self {
|
||||
BootgenLoadingError::IoError(error)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<devc::DevcError> for BootgenLoadingError {
|
||||
fn from(error: devc::DevcError) -> Self {
|
||||
BootgenLoadingError::DevcError(error)
|
||||
}
|
||||
}
|
||||
|
||||
impl core::fmt::Display for BootgenLoadingError {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
use BootgenLoadingError::*;
|
||||
match self {
|
||||
InvalidBootImageHeader => write!(
|
||||
f,
|
||||
"Invalid boot image header. Check if the file is correct."
|
||||
),
|
||||
MissingPartition => write!(f, "Partition not found. Check your compile configuration."),
|
||||
EncryptedBitstream => write!(f, "Encrypted bitstream is not supported."),
|
||||
IoError(e) => write!(f, "Error while reading: {}", e),
|
||||
DevcError(e) => write!(f, "PCAP interface error: {}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct PartitionHeader {
|
||||
pub encrypted_length: u32,
|
||||
pub unencrypted_length: u32,
|
||||
pub word_length: u32,
|
||||
pub dest_load_addr: u32,
|
||||
pub dest_exec_addr: u32,
|
||||
pub data_offset: u32,
|
||||
pub attribute_bits: u32,
|
||||
pub section_count: u32,
|
||||
pub checksum_offset: u32,
|
||||
pub header_offset: u32,
|
||||
pub cert_offset: u32,
|
||||
pub reserved: [u32; 4],
|
||||
pub checksum: u32,
|
||||
}
|
||||
|
||||
/// Read a u32 word from the reader.
|
||||
fn read_u32<Reader: Read>(reader: &mut Reader) -> Result<u32, BootgenLoadingError> {
|
||||
let mut buffer: [u8; 4] = [0; 4];
|
||||
reader.read_exact(&mut buffer)?;
|
||||
let mut result: u32 = 0;
|
||||
for i in 0..4 {
|
||||
result |= (buffer[i] as u32) << (i * 8);
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Load PL partition header.
|
||||
fn load_pl_header<File: Read + Seek>(
|
||||
file: &mut File,
|
||||
) -> Result<Option<PartitionHeader>, BootgenLoadingError> {
|
||||
let mut buffer: [u8; 0x40] = [0; 0x40];
|
||||
file.read_exact(&mut buffer)?;
|
||||
let header = unsafe { core::mem::transmute::<_, PartitionHeader>(buffer) };
|
||||
if header.attribute_bits & (2 << 4) != 0 {
|
||||
Ok(Some(header))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
fn load_ps_header<File: Read + Seek>(
|
||||
file: &mut File,
|
||||
) -> Result<Option<PartitionHeader>, BootgenLoadingError> {
|
||||
let mut buffer: [u8; 0x40] = [0; 0x40];
|
||||
file.read_exact(&mut buffer)?;
|
||||
let header = unsafe { core::mem::transmute::<_, PartitionHeader>(buffer) };
|
||||
if header.attribute_bits & (1 << 4) != 0 {
|
||||
Ok(Some(header))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
/// Locate the partition from the image, and return the size (in bytes) of the partition if successful.
|
||||
/// This function would seek the file to the location of the partition.
|
||||
fn locate<
|
||||
File: Read + Seek,
|
||||
F: Fn(&mut File) -> Result<Option<PartitionHeader>, BootgenLoadingError>,
|
||||
>(
|
||||
file: &mut File,
|
||||
f: F,
|
||||
) -> Result<usize, BootgenLoadingError> {
|
||||
file.seek(SeekFrom::Start(0))?;
|
||||
const BOOT_HEADER_SIGN: u32 = 0x584C4E58;
|
||||
// read boot header signature
|
||||
file.seek(SeekFrom::Start(0x24))?;
|
||||
if read_u32(file)? != BOOT_HEADER_SIGN {
|
||||
return Err(BootgenLoadingError::InvalidBootImageHeader);
|
||||
}
|
||||
// find fsbl offset
|
||||
file.seek(SeekFrom::Start(0x30))?;
|
||||
// the length is in bytes, we have to convert it to words to compare with the partition offset
|
||||
// later
|
||||
let fsbl = read_u32(file)? / 4;
|
||||
// read partition header offset
|
||||
file.seek(SeekFrom::Start(0x9C))?;
|
||||
let ptr = read_u32(file)?;
|
||||
debug!("Partition header pointer = {:0X}", ptr);
|
||||
file.seek(SeekFrom::Start(ptr as u64))?;
|
||||
|
||||
// at most 3 partition headers
|
||||
for _ in 0..3 {
|
||||
if let Some(header) = f(file)? {
|
||||
let encrypted_length = header.encrypted_length;
|
||||
let unencrypted_length = header.unencrypted_length;
|
||||
debug!("Unencrypted length = {:0X}", unencrypted_length);
|
||||
if encrypted_length != unencrypted_length {
|
||||
return Err(BootgenLoadingError::EncryptedBitstream);
|
||||
}
|
||||
|
||||
let start_addr = header.data_offset;
|
||||
// skip fsbl
|
||||
if start_addr == fsbl {
|
||||
continue;
|
||||
}
|
||||
debug!("Partition start address: {:0X}", start_addr);
|
||||
file.seek(SeekFrom::Start(start_addr as u64 * 4))?;
|
||||
|
||||
return Ok(unencrypted_length as usize * 4);
|
||||
}
|
||||
}
|
||||
Err(BootgenLoadingError::MissingPartition)
|
||||
}
|
||||
|
||||
/// Load bitstream from bootgen file.
|
||||
/// This function parses the file, locate the bitstream and load it through the PCAP driver.
|
||||
/// It requires a large buffer, please enable the DDR RAM before using it.
|
||||
pub fn load_bitstream<File: Read + Seek>(file: &mut File) -> Result<(), BootgenLoadingError> {
|
||||
let size = locate(file, load_pl_header)?;
|
||||
unsafe {
|
||||
// align to 64 bytes
|
||||
let ptr = alloc::alloc::alloc(alloc::alloc::Layout::from_size_align(size, 64).unwrap());
|
||||
let buffer = core::slice::from_raw_parts_mut(ptr, size);
|
||||
file.read_exact(buffer).map_err(|e| {
|
||||
core::ptr::drop_in_place(ptr);
|
||||
e
|
||||
})?;
|
||||
let mut devcfg = devc::DevC::new();
|
||||
devcfg.enable();
|
||||
devcfg.program(&buffer).map_err(|e| {
|
||||
core::ptr::drop_in_place(ptr);
|
||||
e
|
||||
})?;
|
||||
core::ptr::drop_in_place(ptr);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_runtime<File: Read + Seek>(file: &mut File) -> Result<Vec<u8>, BootgenLoadingError> {
|
||||
let size = locate(file, load_ps_header)?;
|
||||
let mut buffer = Vec::with_capacity(size);
|
||||
unsafe {
|
||||
buffer.set_len(size);
|
||||
}
|
||||
file.read_exact(&mut buffer)?;
|
||||
Ok(buffer)
|
||||
}
|
@ -0,0 +1,115 @@
|
||||
#![no_std]
|
||||
extern crate alloc;
|
||||
|
||||
use core::fmt;
|
||||
use alloc::{string::FromUtf8Error, string::String, vec::Vec, rc::Rc};
|
||||
use core_io::{self as io, BufRead, BufReader, Read};
|
||||
use libboard_zynq::sdio;
|
||||
|
||||
pub mod sd_reader;
|
||||
pub mod net_settings;
|
||||
pub mod bootgen;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error<'a> {
|
||||
SdError(sdio::sd_card::CardInitializationError),
|
||||
IoError(io::Error),
|
||||
Utf8Error(FromUtf8Error),
|
||||
KeyNotFoundError(&'a str),
|
||||
NoConfig,
|
||||
}
|
||||
|
||||
pub type Result<'a, T> = core::result::Result<T, Error<'a>>;
|
||||
|
||||
impl<'a> fmt::Display for Error<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Error::SdError(error) => write!(f, "SD error: {}", error),
|
||||
Error::IoError(error) => write!(f, "I/O error: {}", error),
|
||||
Error::Utf8Error(error) => write!(f, "UTF-8 error: {}", error),
|
||||
Error::KeyNotFoundError(name) => write!(f, "Configuration key `{}` not found", name),
|
||||
Error::NoConfig => write!(f, "Configuration not present"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<sdio::sd_card::CardInitializationError> for Error<'a> {
|
||||
fn from(error: sdio::sd_card::CardInitializationError) -> Self {
|
||||
Error::SdError(error)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<io::Error> for Error<'a> {
|
||||
fn from(error: io::Error) -> Self {
|
||||
Error::IoError(error)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<FromUtf8Error> for Error<'a> {
|
||||
fn from(error: FromUtf8Error) -> Self {
|
||||
Error::Utf8Error(error)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_config<'a>(
|
||||
key: &'a str,
|
||||
buffer: &mut Vec<u8>,
|
||||
file: fatfs::File<sd_reader::SdReader>,
|
||||
) -> Result<'a, ()> {
|
||||
let prefix = [key, "="].concat();
|
||||
for line in BufReader::new(file).lines() {
|
||||
let line = line?;
|
||||
if line.starts_with(&prefix) {
|
||||
buffer.extend(line[prefix.len()..].as_bytes());
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
Err(Error::KeyNotFoundError(key))
|
||||
}
|
||||
|
||||
pub struct Config {
|
||||
fs: Option<Rc<fatfs::FileSystem<sd_reader::SdReader>>>,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn new() -> Result<'static, Self> {
|
||||
let sdio = sdio::Sdio::sdio0(true);
|
||||
if !sdio.is_card_inserted() {
|
||||
Err(sdio::sd_card::CardInitializationError::NoCardInserted)?;
|
||||
}
|
||||
let sd = sdio::sd_card::SdCard::from_sdio(sdio)?;
|
||||
let reader = sd_reader::SdReader::new(sd);
|
||||
|
||||
let fs = reader.mount_fatfs(sd_reader::PartitionEntry::Entry1)?;
|
||||
Ok(Config { fs: Some(Rc::new(fs)) })
|
||||
}
|
||||
|
||||
pub fn from_fs(fs: Option<Rc<fatfs::FileSystem<sd_reader::SdReader>>>) -> Self {
|
||||
Config { fs }
|
||||
}
|
||||
|
||||
pub fn new_dummy() -> Self {
|
||||
Config { fs: None }
|
||||
}
|
||||
|
||||
pub fn read<'b>(&self, key: &'b str) -> Result<'b, Vec<u8>> {
|
||||
if let Some(fs) = &self.fs {
|
||||
let root_dir = fs.root_dir();
|
||||
let mut buffer: Vec<u8> = Vec::new();
|
||||
match root_dir.open_file(&["/CONFIG/", key, ".BIN"].concat()) {
|
||||
Ok(mut f) => f.read_to_end(&mut buffer).map(|_| ())?,
|
||||
Err(_) => match root_dir.open_file("/CONFIG.TXT") {
|
||||
Ok(f) => parse_config(key, &mut buffer, f)?,
|
||||
Err(_) => return Err(Error::KeyNotFoundError(key)),
|
||||
},
|
||||
};
|
||||
Ok(buffer)
|
||||
} else {
|
||||
Err(Error::NoConfig)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_str<'b>(&self, key: &'b str) -> Result<'b, String> {
|
||||
Ok(String::from_utf8(self.read(key)?)?)
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
use core::fmt;
|
||||
|
||||
use libboard_zynq::smoltcp::wire::{EthernetAddress, IpAddress};
|
||||
|
||||
use super::Config;
|
||||
|
||||
pub struct NetAddresses {
|
||||
pub hardware_addr: EthernetAddress,
|
||||
pub ipv4_addr: IpAddress,
|
||||
#[cfg(feature = "ipv6")]
|
||||
pub ipv6_ll_addr: IpAddress,
|
||||
#[cfg(feature = "ipv6")]
|
||||
pub ipv6_addr: Option<IpAddress>
|
||||
}
|
||||
|
||||
impl fmt::Display for NetAddresses {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "MAC={} IPv4={} ",
|
||||
self.hardware_addr, self.ipv4_addr)?;
|
||||
|
||||
#[cfg(feature = "ipv6")]
|
||||
{
|
||||
write!(f, "IPv6-LL={}", self.ipv6_ll_addr)?;
|
||||
match self.ipv6_addr {
|
||||
Some(addr) => write!(f, " {}", addr)?,
|
||||
None => write!(f, " IPv6: no configured address")?
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_adresses(cfg: &Config) -> NetAddresses {
|
||||
let mut hardware_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x52]);
|
||||
let mut ipv4_addr = IpAddress::v4(192, 168, 1, 52);
|
||||
|
||||
if let Ok(Ok(addr)) = cfg.read_str("mac").map(|s| s.parse()) {
|
||||
hardware_addr = addr;
|
||||
}
|
||||
if let Ok(Ok(addr)) = cfg.read_str("ip").map(|s| s.parse()) {
|
||||
ipv4_addr = addr;
|
||||
}
|
||||
#[cfg(feature = "ipv6")]
|
||||
let ipv6_addr = cfg.read_str("ipv6").ok().and_then(|s| s.parse().ok());
|
||||
|
||||
#[cfg(feature = "ipv6")]
|
||||
let ipv6_ll_addr = IpAddress::v6(
|
||||
0xfe80, 0x0000, 0x0000, 0x0000,
|
||||
(((hardware_addr.0[0] ^ 0x02) as u16) << 8) | (hardware_addr.0[1] as u16),
|
||||
((hardware_addr.0[2] as u16) << 8) | 0x00ff,
|
||||
0xfe00 | (hardware_addr.0[3] as u16),
|
||||
((hardware_addr.0[4] as u16) << 8) | (hardware_addr.0[5] as u16));
|
||||
|
||||
NetAddresses {
|
||||
hardware_addr,
|
||||
ipv4_addr,
|
||||
#[cfg(feature = "ipv6")]
|
||||
ipv6_ll_addr,
|
||||
#[cfg(feature = "ipv6")]
|
||||
ipv6_addr
|
||||
}
|
||||
}
|
@ -0,0 +1,303 @@
|
||||
use core_io::{BufRead, Error, ErrorKind, Read, Result as IoResult, Seek, SeekFrom, Write};
|
||||
use fatfs;
|
||||
use libboard_zynq::sdio::{sd_card::SdCard, CmdTransferError};
|
||||
use log::debug;
|
||||
use alloc::vec::Vec;
|
||||
|
||||
const MBR_SIGNATURE: [u8; 2] = [0x55, 0xAA];
|
||||
const PARTID_FAT12: u8 = 0x01;
|
||||
const PARTID_FAT16_LESS32M: u8 = 0x04;
|
||||
const PARTID_FAT16: u8 = 0x06;
|
||||
const PARTID_FAT32: u8 = 0x0B;
|
||||
const PARTID_FAT32_LBA: u8 = 0x0C;
|
||||
|
||||
fn cmd_error_to_io_error(_: CmdTransferError) -> Error {
|
||||
Error::new(ErrorKind::Other, "Command transfer error")
|
||||
}
|
||||
|
||||
const BLOCK_SIZE: usize = 512;
|
||||
|
||||
/// SdReader struct implementing `Read + BufRead + Write + Seek` traits for `core_io`.
|
||||
/// Used as an adaptor for fatfs crate, but could be used directly for raw data access.
|
||||
///
|
||||
/// Implementation: all read/writes would be split into unaligned and block-aligned parts,
|
||||
/// unaligned read/writes would do a buffered read/write using a block-sized internal buffer,
|
||||
/// while aligned transactions would be sent to the SD card directly for performance reason.
|
||||
pub struct SdReader {
|
||||
/// Internal SdCard handle.
|
||||
sd: SdCard,
|
||||
/// Read buffer with the size of 1 block.
|
||||
buffer: Vec<u8>,
|
||||
/// Address for the next byte.
|
||||
byte_addr: u32,
|
||||
/// Internal index for the next byte.
|
||||
/// Normally in range `[0, BLOCK_SIZE - 1]`.
|
||||
///
|
||||
/// `index = BLOCK_SIZE` means that the `buffer` is invalid for the current `byte_addr`,
|
||||
/// the next `fill_buf` call would fill the buffer.
|
||||
index: usize,
|
||||
/// Dirty flag indicating the content has to be flushed.
|
||||
dirty: bool,
|
||||
/// Base offset for translation from logical address to physical address.
|
||||
offset: u32,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
#[allow(unused)]
|
||||
// Partition entry enum, normally we would use entry1.
|
||||
pub enum PartitionEntry {
|
||||
Entry1 = 0x1BE,
|
||||
Entry2 = 0x1CE,
|
||||
Entry3 = 0x1DE,
|
||||
Entry4 = 0x1EE,
|
||||
}
|
||||
|
||||
impl SdReader {
|
||||
/// Create SdReader from SdCard
|
||||
pub fn new(sd: SdCard) -> SdReader {
|
||||
let mut vec: Vec<u8> = Vec::with_capacity(BLOCK_SIZE);
|
||||
unsafe {
|
||||
vec.set_len(vec.capacity());
|
||||
}
|
||||
SdReader {
|
||||
sd,
|
||||
buffer: vec,
|
||||
byte_addr: 0,
|
||||
index: BLOCK_SIZE,
|
||||
dirty: false,
|
||||
offset: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Internal read function for unaligned read.
|
||||
/// The read must not cross block boundary.
|
||||
fn read_unaligned(&mut self, buf: &mut [u8]) -> IoResult<usize> {
|
||||
if buf.len() == 0 {
|
||||
return Ok(0);
|
||||
}
|
||||
let filled_buffer = self.fill_buf()?;
|
||||
for (dest, src) in buf.iter_mut().zip(filled_buffer.iter()) {
|
||||
*dest = *src;
|
||||
}
|
||||
self.consume(buf.len());
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
/// Internal write function for unaligned write.
|
||||
/// The write must not cross block boundary.
|
||||
fn write_unaligned(&mut self, buf: &[u8]) -> IoResult<usize> {
|
||||
if buf.len() == 0 {
|
||||
return Ok(0);
|
||||
}
|
||||
// update buffer if needed, as we will flush the entire block later.
|
||||
self.fill_buf()?;
|
||||
self.dirty = true;
|
||||
let dest_buffer = &mut self.buffer[self.index..];
|
||||
for (src, dest) in buf.iter().zip(dest_buffer.iter_mut()) {
|
||||
*dest = *src;
|
||||
}
|
||||
self.consume(buf.len());
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
/// Split the slice into three segments, with the middle block-aligned.
|
||||
/// Alignment depends on the current `self.byte_addr` instead of the slice pointer address
|
||||
fn block_align<'b>(&self, buf: &'b [u8]) -> (&'b [u8], &'b [u8], &'b [u8]) {
|
||||
let head_len = BLOCK_SIZE - (self.byte_addr as usize % BLOCK_SIZE);
|
||||
if head_len > buf.len() {
|
||||
(buf, &[], &[])
|
||||
} else {
|
||||
let remaining_length = buf.len() - head_len;
|
||||
let mid_length = remaining_length - remaining_length % BLOCK_SIZE;
|
||||
let (head, remaining) = buf.split_at(head_len);
|
||||
let (mid, tail) = remaining.split_at(mid_length);
|
||||
(head, mid, tail)
|
||||
}
|
||||
}
|
||||
|
||||
/// Split the mutable slice into three segments, with the middle block-aligned.
|
||||
/// Alignment depends on the current `self.byte_addr` instead of the slice pointer address
|
||||
fn block_align_mut<'b>(&self, buf: &'b mut [u8]) -> (&'b mut [u8], &'b mut [u8], &'b mut [u8]) {
|
||||
let head_len = BLOCK_SIZE - (self.byte_addr as usize % BLOCK_SIZE);
|
||||
if head_len > buf.len() {
|
||||
(buf, &mut [], &mut [])
|
||||
} else {
|
||||
let remaining_length = buf.len() - head_len;
|
||||
let mid_length = remaining_length - remaining_length % BLOCK_SIZE;
|
||||
let (head, remaining) = buf.split_at_mut(head_len);
|
||||
let (mid, tail) = remaining.split_at_mut(mid_length);
|
||||
(head, mid, tail)
|
||||
}
|
||||
}
|
||||
|
||||
/// Invalidate the buffer, so later unaligned read/write would reload the buffer from SD card.
|
||||
fn invalidate_buffer(&mut self) {
|
||||
self.index = BLOCK_SIZE;
|
||||
}
|
||||
|
||||
/// Set the base offset of the SD card, to transform from physical address to logical address.
|
||||
fn set_base_offset(&mut self, offset: u32) -> IoResult<u64> {
|
||||
self.offset = offset;
|
||||
self.seek(SeekFrom::Start(0))
|
||||
}
|
||||
|
||||
/// Mount fatfs from partition entry, and return the fatfs object if success.
|
||||
/// This takes the ownership of self, so currently there is no way to recover from an error,
|
||||
/// except creating a new SD card instance.
|
||||
pub fn mount_fatfs(mut self, entry: PartitionEntry) -> IoResult<fatfs::FileSystem<Self>> {
|
||||
let mut buffer: [u8; 4] = [0; 4];
|
||||
self.seek(SeekFrom::Start(0x1FE))?;
|
||||
self.read_exact(&mut buffer[..2])?;
|
||||
// check MBR signature
|
||||
if buffer[..2] != MBR_SIGNATURE {
|
||||
return Err(Error::new(
|
||||
ErrorKind::InvalidData,
|
||||
"Incorrect signature for MBR sector.",
|
||||
));
|
||||
}
|
||||
// Read partition ID.
|
||||
self.seek(SeekFrom::Start(entry as u64 + 0x4))?;
|
||||
self.read_exact(&mut buffer[..1])?;
|
||||
debug!("Partition ID: {:0X}", buffer[0]);
|
||||
match buffer[0] {
|
||||
PARTID_FAT12 | PARTID_FAT16_LESS32M | PARTID_FAT16 |
|
||||
PARTID_FAT32 | PARTID_FAT32_LBA => {}
|
||||
_ => {
|
||||
return Err(Error::new(
|
||||
ErrorKind::InvalidData,
|
||||
"No FAT partition found for the specified entry.",
|
||||
));
|
||||
}
|
||||
}
|
||||
// Read LBA
|
||||
self.seek(SeekFrom::Current(0x3))?;
|
||||
self.read_exact(&mut buffer)?;
|
||||
let mut lba: u32 = 0;
|
||||
// Little endian
|
||||
for i in 0..4 {
|
||||
lba |= (buffer[i] as u32) << (i * 8);
|
||||
}
|
||||
// Set to logical address
|
||||
self.set_base_offset(lba * BLOCK_SIZE as u32)?;
|
||||
// setup fatfs
|
||||
fatfs::FileSystem::new(self, fatfs::FsOptions::new())
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for SdReader {
|
||||
fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
|
||||
let total_length = buf.len();
|
||||
let (a, b, c) = self.block_align_mut(buf);
|
||||
self.read_unaligned(a)?;
|
||||
if b.len() > 0 {
|
||||
// invalidate internal buffer
|
||||
self.invalidate_buffer();
|
||||
if let Err(_) = self.sd.read_block(
|
||||
self.byte_addr / BLOCK_SIZE as u32,
|
||||
(b.len() / BLOCK_SIZE) as u16,
|
||||
b,
|
||||
) {
|
||||
// we have to allow partial read, as per the trait required
|
||||
return Ok(a.len());
|
||||
}
|
||||
self.byte_addr += b.len() as u32;
|
||||
}
|
||||
if let Err(_) = self.read_unaligned(c) {
|
||||
// we have to allow partial read, as per the trait required
|
||||
return Ok(a.len() + b.len());
|
||||
}
|
||||
Ok(total_length)
|
||||
}
|
||||
}
|
||||
|
||||
impl BufRead for SdReader {
|
||||
fn fill_buf(&mut self) -> IoResult<&[u8]> {
|
||||
if self.index == BLOCK_SIZE {
|
||||
// flush the buffer if it is dirty before overwriting it with new data
|
||||
if self.dirty {
|
||||
self.flush()?;
|
||||
}
|
||||
// reload buffer
|
||||
self.sd
|
||||
.read_block(self.byte_addr / (BLOCK_SIZE as u32), 1, &mut self.buffer)
|
||||
.map_err(cmd_error_to_io_error)?;
|
||||
self.index = (self.byte_addr as usize) % BLOCK_SIZE;
|
||||
}
|
||||
Ok(&self.buffer[self.index..])
|
||||
}
|
||||
|
||||
fn consume(&mut self, amt: usize) {
|
||||
self.index += amt;
|
||||
self.byte_addr += amt as u32;
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for SdReader {
|
||||
fn write(&mut self, buf: &[u8]) -> IoResult<usize> {
|
||||
let (a, b, c) = self.block_align(buf);
|
||||
self.write_unaligned(a)?;
|
||||
if b.len() > 0 {
|
||||
self.flush()?;
|
||||
self.invalidate_buffer();
|
||||
if let Err(_) = self.sd.write_block(
|
||||
self.byte_addr / BLOCK_SIZE as u32,
|
||||
(b.len() / BLOCK_SIZE) as u16,
|
||||
b,
|
||||
) {
|
||||
return Ok(a.len());
|
||||
}
|
||||
self.byte_addr += b.len() as u32;
|
||||
}
|
||||
if let Err(_) = self.write_unaligned(c) {
|
||||
return Ok(a.len() + b.len());
|
||||
}
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> IoResult<()> {
|
||||
if self.dirty {
|
||||
let block_addr = (self.byte_addr - self.index as u32) / (BLOCK_SIZE as u32);
|
||||
self.sd
|
||||
.write_block(block_addr, 1, &self.buffer)
|
||||
.map_err(cmd_error_to_io_error)?;
|
||||
self.dirty = false;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Seek for SdReader {
|
||||
fn seek(&mut self, pos: SeekFrom) -> IoResult<u64> {
|
||||
let raw_target = match pos {
|
||||
SeekFrom::Start(x) => self.offset as i64 + x as i64,
|
||||
SeekFrom::Current(x) => self.byte_addr as i64 + x,
|
||||
SeekFrom::End(_) => panic!("SD card does not support seek from end"),
|
||||
};
|
||||
if raw_target < self.offset as i64 || raw_target > core::u32::MAX as i64 {
|
||||
return Err(Error::new(ErrorKind::InvalidInput, "Invalid address"));
|
||||
}
|
||||
let target_byte_addr = raw_target as u32;
|
||||
let address_same_block =
|
||||
self.byte_addr / (BLOCK_SIZE as u32) == target_byte_addr / (BLOCK_SIZE as u32);
|
||||
// if the buffer was invalidated, we consider seek as different block
|
||||
let same_block = address_same_block && self.index != BLOCK_SIZE;
|
||||
if !same_block {
|
||||
self.flush()?;
|
||||
}
|
||||
self.byte_addr = target_byte_addr;
|
||||
self.index = if same_block {
|
||||
target_byte_addr as usize % BLOCK_SIZE
|
||||
} else {
|
||||
// invalidate the buffer as we moved to a different block
|
||||
BLOCK_SIZE
|
||||
};
|
||||
Ok((self.byte_addr - self.offset) as u64)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for SdReader {
|
||||
fn drop(&mut self) {
|
||||
// just try to flush it, ignore error if any
|
||||
self.flush().unwrap_or(());
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
[package]
|
||||
authors = ["M-Labs"]
|
||||
name = "core_io"
|
||||
version = "0.1.20200410"
|
||||
|
||||
[lib]
|
||||
name = "core_io"
|
||||
|
||||
[dependencies]
|
||||
memchr = { version = "2", default-features = false, optional = true }
|
||||
|
||||
[features]
|
||||
alloc = []
|
||||
collections = ["alloc", "memchr"]
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,896 @@
|
||||
use crate::io::prelude::*;
|
||||
|
||||
use core::cmp;
|
||||
use crate::io::{self, Error, ErrorKind, Initializer, SeekFrom};
|
||||
|
||||
#[cfg(feature = "collections")]
|
||||
use core::convert::TryInto;
|
||||
|
||||
#[cfg(feature="collections")]
|
||||
use collections::vec::Vec;
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::boxed::Box;
|
||||
|
||||
/// A `Cursor` wraps an in-memory buffer and provides it with a
|
||||
/// [`Seek`] implementation.
|
||||
///
|
||||
/// `Cursor`s are used with in-memory buffers, anything implementing
|
||||
/// `AsRef<[u8]>`, to allow them to implement [`Read`] and/or [`Write`],
|
||||
/// allowing these buffers to be used anywhere you might use a reader or writer
|
||||
/// that does actual I/O.
|
||||
///
|
||||
/// The standard library implements some I/O traits on various types which
|
||||
/// are commonly used as a buffer, like `Cursor<`[`Vec`]`<u8>>` and
|
||||
/// `Cursor<`[`&[u8]`][bytes]`>`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// We may want to write bytes to a [`File`] in our production
|
||||
/// code, but use an in-memory buffer in our tests. We can do this with
|
||||
/// `Cursor`:
|
||||
///
|
||||
/// [`Seek`]: trait.Seek.html
|
||||
/// [`Read`]: ../../std/io/trait.Read.html
|
||||
/// [`Write`]: ../../std/io/trait.Write.html
|
||||
/// [`Vec`]: ../../std/vec/struct.Vec.html
|
||||
/// [bytes]: ../../std/primitive.slice.html
|
||||
/// [`File`]: ../fs/struct.File.html
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::prelude::*;
|
||||
/// use std::io::{self, SeekFrom};
|
||||
/// use std::fs::File;
|
||||
///
|
||||
/// // a library function we've written
|
||||
/// fn write_ten_bytes_at_end<W: Write + Seek>(writer: &mut W) -> io::Result<()> {
|
||||
/// writer.seek(SeekFrom::End(-10))?;
|
||||
///
|
||||
/// for i in 0..10 {
|
||||
/// writer.write(&[i])?;
|
||||
/// }
|
||||
///
|
||||
/// // all went well
|
||||
/// Ok(())
|
||||
/// }
|
||||
///
|
||||
/// # fn foo() -> io::Result<()> {
|
||||
/// // Here's some code that uses this library function.
|
||||
/// //
|
||||
/// // We might want to use a BufReader here for efficiency, but let's
|
||||
/// // keep this example focused.
|
||||
/// let mut file = File::create("foo.txt")?;
|
||||
///
|
||||
/// write_ten_bytes_at_end(&mut file)?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
///
|
||||
/// // now let's write a test
|
||||
/// #[test]
|
||||
/// fn test_writes_bytes() {
|
||||
/// // setting up a real File is much slower than an in-memory buffer,
|
||||
/// // let's use a cursor instead
|
||||
/// use std::io::Cursor;
|
||||
/// let mut buff = Cursor::new(vec![0; 15]);
|
||||
///
|
||||
/// write_ten_bytes_at_end(&mut buff).unwrap();
|
||||
///
|
||||
/// assert_eq!(&buff.get_ref()[5..15], &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||
pub struct Cursor<T> {
|
||||
inner: T,
|
||||
pos: u64,
|
||||
}
|
||||
|
||||
impl<T> Cursor<T> {
|
||||
/// Creates a new cursor wrapping the provided underlying in-memory buffer.
|
||||
///
|
||||
/// Cursor initial position is `0` even if underlying buffer (e.g., `Vec`)
|
||||
/// is not empty. So writing to cursor starts with overwriting `Vec`
|
||||
/// content, not with appending to it.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::io::Cursor;
|
||||
///
|
||||
/// let buff = Cursor::new(Vec::new());
|
||||
/// # fn force_inference(_: &Cursor<Vec<u8>>) {}
|
||||
/// # force_inference(&buff);
|
||||
/// ```
|
||||
pub fn new(inner: T) -> Cursor<T> {
|
||||
Cursor { pos: 0, inner }
|
||||
}
|
||||
|
||||
/// Consumes this cursor, returning the underlying value.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::io::Cursor;
|
||||
///
|
||||
/// let buff = Cursor::new(Vec::new());
|
||||
/// # fn force_inference(_: &Cursor<Vec<u8>>) {}
|
||||
/// # force_inference(&buff);
|
||||
///
|
||||
/// let vec = buff.into_inner();
|
||||
/// ```
|
||||
pub fn into_inner(self) -> T {
|
||||
self.inner
|
||||
}
|
||||
|
||||
/// Gets a reference to the underlying value in this cursor.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::io::Cursor;
|
||||
///
|
||||
/// let buff = Cursor::new(Vec::new());
|
||||
/// # fn force_inference(_: &Cursor<Vec<u8>>) {}
|
||||
/// # force_inference(&buff);
|
||||
///
|
||||
/// let reference = buff.get_ref();
|
||||
/// ```
|
||||
pub fn get_ref(&self) -> &T {
|
||||
&self.inner
|
||||
}
|
||||
|
||||
/// Gets a mutable reference to the underlying value in this cursor.
|
||||
///
|
||||
/// Care should be taken to avoid modifying the internal I/O state of the
|
||||
/// underlying value as it may corrupt this cursor's position.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::io::Cursor;
|
||||
///
|
||||
/// let mut buff = Cursor::new(Vec::new());
|
||||
/// # fn force_inference(_: &Cursor<Vec<u8>>) {}
|
||||
/// # force_inference(&buff);
|
||||
///
|
||||
/// let reference = buff.get_mut();
|
||||
/// ```
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
&mut self.inner
|
||||
}
|
||||
|
||||
/// Returns the current position of this cursor.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::io::Cursor;
|
||||
/// use std::io::prelude::*;
|
||||
/// use std::io::SeekFrom;
|
||||
///
|
||||
/// let mut buff = Cursor::new(vec![1, 2, 3, 4, 5]);
|
||||
///
|
||||
/// assert_eq!(buff.position(), 0);
|
||||
///
|
||||
/// buff.seek(SeekFrom::Current(2)).unwrap();
|
||||
/// assert_eq!(buff.position(), 2);
|
||||
///
|
||||
/// buff.seek(SeekFrom::Current(-1)).unwrap();
|
||||
/// assert_eq!(buff.position(), 1);
|
||||
/// ```
|
||||
pub fn position(&self) -> u64 {
|
||||
self.pos
|
||||
}
|
||||
|
||||
/// Sets the position of this cursor.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::io::Cursor;
|
||||
///
|
||||
/// let mut buff = Cursor::new(vec![1, 2, 3, 4, 5]);
|
||||
///
|
||||
/// assert_eq!(buff.position(), 0);
|
||||
///
|
||||
/// buff.set_position(2);
|
||||
/// assert_eq!(buff.position(), 2);
|
||||
///
|
||||
/// buff.set_position(4);
|
||||
/// assert_eq!(buff.position(), 4);
|
||||
/// ```
|
||||
pub fn set_position(&mut self, pos: u64) {
|
||||
self.pos = pos;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> io::Seek for Cursor<T>
|
||||
where
|
||||
T: AsRef<[u8]>,
|
||||
{
|
||||
fn seek(&mut self, style: SeekFrom) -> io::Result<u64> {
|
||||
let (base_pos, offset) = match style {
|
||||
SeekFrom::Start(n) => {
|
||||
self.pos = n;
|
||||
return Ok(n);
|
||||
}
|
||||
SeekFrom::End(n) => (self.inner.as_ref().len() as u64, n),
|
||||
SeekFrom::Current(n) => (self.pos, n),
|
||||
};
|
||||
let new_pos = if offset >= 0 {
|
||||
base_pos.checked_add(offset as u64)
|
||||
} else {
|
||||
base_pos.checked_sub((offset.wrapping_neg()) as u64)
|
||||
};
|
||||
match new_pos {
|
||||
Some(n) => {
|
||||
self.pos = n;
|
||||
Ok(self.pos)
|
||||
}
|
||||
None => Err(Error::new(
|
||||
ErrorKind::InvalidInput,
|
||||
"invalid seek to a negative or overflowing position",
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
fn stream_len(&mut self) -> io::Result<u64> {
|
||||
Ok(self.inner.as_ref().len() as u64)
|
||||
}
|
||||
|
||||
fn stream_position(&mut self) -> io::Result<u64> {
|
||||
Ok(self.pos)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Read for Cursor<T>
|
||||
where
|
||||
T: AsRef<[u8]>,
|
||||
{
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
let n = Read::read(&mut self.get_ref().as_ref(), buf)?;
|
||||
self.pos += n as u64;
|
||||
Ok(n)
|
||||
}
|
||||
|
||||
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
|
||||
let n = buf.len();
|
||||
Read::read_exact(&mut self.get_ref().as_ref(), buf)?;
|
||||
self.pos += n as u64;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn initializer(&self) -> Initializer {
|
||||
Initializer::nop()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "collections")]
|
||||
impl<T> BufRead for Cursor<T>
|
||||
where
|
||||
T: AsRef<[u8]>,
|
||||
{
|
||||
fn fill_buf(&mut self) -> io::Result<&[u8]> {
|
||||
let amt = cmp::min(self.pos, self.inner.as_ref().len() as u64);
|
||||
Ok(&self.inner.as_ref()[(amt as usize)..])
|
||||
}
|
||||
fn consume(&mut self, amt: usize) {
|
||||
self.pos += amt as u64;
|
||||
}
|
||||
}
|
||||
|
||||
// Non-resizing write implementation
|
||||
#[inline]
|
||||
fn slice_write(pos_mut: &mut u64, slice: &mut [u8], buf: &[u8]) -> io::Result<usize> {
|
||||
let pos = cmp::min(*pos_mut, slice.len() as u64);
|
||||
let amt = (&mut slice[(pos as usize)..]).write(buf)?;
|
||||
*pos_mut += amt as u64;
|
||||
Ok(amt)
|
||||
}
|
||||
|
||||
// Resizing write implementation
|
||||
#[cfg(feature = "collections")]
|
||||
fn vec_write(pos_mut: &mut u64, vec: &mut Vec<u8>, buf: &[u8]) -> io::Result<usize> {
|
||||
let pos: usize = (*pos_mut).try_into().map_err(|_| {
|
||||
Error::new(
|
||||
ErrorKind::InvalidInput,
|
||||
"cursor position exceeds maximum possible vector length",
|
||||
)
|
||||
})?;
|
||||
// Make sure the internal buffer is as least as big as where we
|
||||
// currently are
|
||||
let len = vec.len();
|
||||
if len < pos {
|
||||
// use `resize` so that the zero filling is as efficient as possible
|
||||
vec.resize(pos, 0);
|
||||
}
|
||||
// Figure out what bytes will be used to overwrite what's currently
|
||||
// there (left), and what will be appended on the end (right)
|
||||
{
|
||||
let space = vec.len() - pos;
|
||||
let (left, right) = buf.split_at(cmp::min(space, buf.len()));
|
||||
vec[pos..pos + left.len()].copy_from_slice(left);
|
||||
vec.extend_from_slice(right);
|
||||
}
|
||||
|
||||
// Bump us forward
|
||||
*pos_mut = (pos + buf.len()) as u64;
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
impl Write for Cursor<&mut [u8]> {
|
||||
#[inline]
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
slice_write(&mut self.pos, self.inner, buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "collections")]
|
||||
impl Write for Cursor<&mut Vec<u8>> {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
vec_write(&mut self.pos, self.inner, buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "collections")]
|
||||
impl Write for Cursor<Vec<u8>> {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
vec_write(&mut self.pos, &mut self.inner, buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
impl Write for Cursor<Box<[u8]>> {
|
||||
#[inline]
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
slice_write(&mut self.pos, &mut self.inner, buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::io::prelude::*;
|
||||
use crate::io::{Cursor, IoSlice, IoSliceMut, SeekFrom};
|
||||
|
||||
#[test]
|
||||
fn test_vec_writer() {
|
||||
let mut writer = Vec::new();
|
||||
assert_eq!(writer.write(&[0]).unwrap(), 1);
|
||||
assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3);
|
||||
assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4);
|
||||
assert_eq!(
|
||||
writer
|
||||
.write_vectored(&[IoSlice::new(&[]), IoSlice::new(&[8, 9]), IoSlice::new(&[10])],)
|
||||
.unwrap(),
|
||||
3
|
||||
);
|
||||
let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
||||
assert_eq!(writer, b);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mem_writer() {
|
||||
let mut writer = Cursor::new(Vec::new());
|
||||
assert_eq!(writer.write(&[0]).unwrap(), 1);
|
||||
assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3);
|
||||
assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4);
|
||||
assert_eq!(
|
||||
writer
|
||||
.write_vectored(&[IoSlice::new(&[]), IoSlice::new(&[8, 9]), IoSlice::new(&[10])],)
|
||||
.unwrap(),
|
||||
3
|
||||
);
|
||||
let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
||||
assert_eq!(&writer.get_ref()[..], b);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mem_mut_writer() {
|
||||
let mut vec = Vec::new();
|
||||
let mut writer = Cursor::new(&mut vec);
|
||||
assert_eq!(writer.write(&[0]).unwrap(), 1);
|
||||
assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3);
|
||||
assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4);
|
||||
assert_eq!(
|
||||
writer
|
||||
.write_vectored(&[IoSlice::new(&[]), IoSlice::new(&[8, 9]), IoSlice::new(&[10])],)
|
||||
.unwrap(),
|
||||
3
|
||||
);
|
||||
let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
||||
assert_eq!(&writer.get_ref()[..], b);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_box_slice_writer() {
|
||||
let mut writer = Cursor::new(vec![0u8; 9].into_boxed_slice());
|
||||
assert_eq!(writer.position(), 0);
|
||||
assert_eq!(writer.write(&[0]).unwrap(), 1);
|
||||
assert_eq!(writer.position(), 1);
|
||||
assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3);
|
||||
assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4);
|
||||
assert_eq!(writer.position(), 8);
|
||||
assert_eq!(writer.write(&[]).unwrap(), 0);
|
||||
assert_eq!(writer.position(), 8);
|
||||
|
||||
assert_eq!(writer.write(&[8, 9]).unwrap(), 1);
|
||||
assert_eq!(writer.write(&[10]).unwrap(), 0);
|
||||
let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8];
|
||||
assert_eq!(&**writer.get_ref(), b);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_box_slice_writer_vectored() {
|
||||
let mut writer = Cursor::new(vec![0u8; 9].into_boxed_slice());
|
||||
assert_eq!(writer.position(), 0);
|
||||
assert_eq!(writer.write_vectored(&[IoSlice::new(&[0])]).unwrap(), 1);
|
||||
assert_eq!(writer.position(), 1);
|
||||
assert_eq!(
|
||||
writer
|
||||
.write_vectored(&[IoSlice::new(&[1, 2, 3]), IoSlice::new(&[4, 5, 6, 7]),])
|
||||
.unwrap(),
|
||||