From 6454f994af3a715f723f5716771fdb7ee7dab330 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Wed, 3 Jun 2020 11:15:49 +0800 Subject: [PATCH] add libio from ARTIQ --- src/Cargo.lock | 8 ++ src/Cargo.toml | 1 + src/Makefile | 2 +- src/libio/Cargo.toml | 13 ++++ src/libio/src/cursor.rs | 86 +++++++++++++++++++++ src/libio/src/lib.rs | 137 +++++++++++++++++++++++++++++++++ src/libio/src/proto.rs | 162 +++++++++++++++++++++++++++++++++++++++ src/runtime/Cargo.toml | 1 + src/runtime/src/proto.rs | 1 + 9 files changed, 410 insertions(+), 1 deletion(-) create mode 100644 src/libio/Cargo.toml create mode 100644 src/libio/src/cursor.rs create mode 100644 src/libio/src/lib.rs create mode 100644 src/libio/src/proto.rs diff --git a/src/Cargo.lock b/src/Cargo.lock index cf387773..e3235030 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -151,6 +151,13 @@ dependencies = [ "proc-macro-nested", ] +[[package]] +name = "io" +version = "0.1.0" +dependencies = [ + "byteorder", +] + [[package]] name = "libasync" version = "0.0.0" @@ -333,6 +340,7 @@ dependencies = [ "cslice", "dyld", "futures", + "io", "libasync", "libboard_zynq", "libcortex_a9", diff --git a/src/Cargo.toml b/src/Cargo.toml index db8f74b3..90f73446 100644 --- a/src/Cargo.toml +++ b/src/Cargo.toml @@ -1,6 +1,7 @@ [workspace] members = [ "libdyld", + "libio", "runtime", "szl" ] diff --git a/src/Makefile b/src/Makefile index 92ce17e7..21d147ea 100644 --- a/src/Makefile +++ b/src/Makefile @@ -9,7 +9,7 @@ all: ../build/firmware/armv7-none-eabihf/release/szl mkdir -p ../build python zc706.py -r ../build/pl.rs -V $(VARIANT) -../build/firmware/armv7-none-eabihf/release/runtime: .cargo/* armv7-none-eabihf.json Cargo.lock Cargo.toml libdyld/* libdyld/src/* runtime/* runtime/src/* ../build/pl.rs +../build/firmware/armv7-none-eabihf/release/runtime: .cargo/* armv7-none-eabihf.json Cargo.lock Cargo.toml libdyld/* libdyld/src/* libio/* libio/src/* runtime/* runtime/src/* ../build/pl.rs XBUILD_SYSROOT_PATH=`pwd`/../build/sysroot cargo xbuild --release -p runtime --target-dir ../build/firmware ../build/szl-payload.bin.lzma: ../build/firmware/armv7-none-eabihf/release/runtime diff --git a/src/libio/Cargo.toml b/src/libio/Cargo.toml new file mode 100644 index 00000000..94149e34 --- /dev/null +++ b/src/libio/Cargo.toml @@ -0,0 +1,13 @@ +[package] +authors = ["M-Labs"] +name = "io" +version = "0.1.0" + +[lib] +name = "io" + +[dependencies] +byteorder = { version = "1.3", default-features = false } + +[features] +alloc = [] diff --git a/src/libio/src/cursor.rs b/src/libio/src/cursor.rs new file mode 100644 index 00000000..85ff478f --- /dev/null +++ b/src/libio/src/cursor.rs @@ -0,0 +1,86 @@ +use {Read, Write}; + +#[derive(Debug, Clone)] +pub struct Cursor { + inner: T, + pos: usize +} + +impl Cursor { + #[inline] + pub fn new(inner: T) -> Cursor { + Cursor { inner, pos: 0 } + } + + #[inline] + pub fn into_inner(self) -> T { + self.inner + } + + #[inline] + pub fn get_ref(&self) -> &T { + &self.inner + } + + #[inline] + pub fn get_mut(&mut self) -> &mut T { + &mut self.inner + } + + #[inline] + pub fn position(&self) -> usize { + self.pos + } + + #[inline] + pub fn set_position(&mut self, pos: usize) { + self.pos = pos + } +} + +impl> Read for Cursor { + type ReadError = !; + + fn read(&mut self, buf: &mut [u8]) -> Result { + let data = &self.inner.as_ref()[self.pos..]; + let len = buf.len().min(data.len()); + buf[..len].copy_from_slice(&data[..len]); + self.pos += len; + Ok(len) + } +} + +impl<'a> Write for Cursor<&'a mut [u8]> { + type WriteError = !; + type FlushError = !; + + fn write(&mut self, buf: &[u8]) -> Result { + let data = &mut self.inner[self.pos..]; + let len = buf.len().min(data.len()); + data[..len].copy_from_slice(&buf[..len]); + self.pos += len; + Ok(len) + } + + #[inline] + fn flush(&mut self) -> Result<(), Self::FlushError> { + Ok(()) + } +} + +#[cfg(feature = "alloc")] +impl Write for Cursor<::alloc::vec::Vec> { + type WriteError = !; + type FlushError = !; + + #[inline] + fn write(&mut self, buf: &[u8]) -> Result { + self.inner.extend_from_slice(buf); + Ok(buf.len()) + } + + #[inline] + fn flush(&mut self) -> Result<(), Self::FlushError> { + Ok(()) + } +} diff --git a/src/libio/src/lib.rs b/src/libio/src/lib.rs new file mode 100644 index 00000000..f7b2305d --- /dev/null +++ b/src/libio/src/lib.rs @@ -0,0 +1,137 @@ +#![no_std] +#![feature(never_type)] + +#[cfg(feature = "alloc")] +extern crate alloc; +extern crate byteorder; + +mod cursor; +mod proto; + +pub use cursor::Cursor; +pub use proto::{ProtoRead, ProtoWrite}; +#[cfg(feature = "alloc")] +pub use proto::ReadStringError; + +#[derive(Debug, Clone, PartialEq)] +pub enum Error { + UnexpectedEnd, + Other(T) +} + +impl From for Error { + fn from(value: T) -> Error { + Error::Other(value) + } +} + +pub trait Read { + type ReadError; + + /// Pull some bytes from this source into the specified buffer, returning + /// how many bytes were read. + fn read(&mut self, buf: &mut [u8]) -> Result; + + /// Read the exact number of bytes required to fill `buf`. + fn read_exact(&mut self, mut buf: &mut [u8]) -> Result<(), Error> { + while !buf.is_empty() { + let read_bytes = self.read(buf)?; + if read_bytes == 0 { + return Err(Error::UnexpectedEnd) + } + + buf = &mut { buf }[read_bytes..]; + } + + Ok(()) + } +} + +impl<'a, T: Read> Read for &'a mut T { + type ReadError = T::ReadError; + + fn read(&mut self, buf: &mut [u8]) -> Result { + T::read(self, buf) + } +} + +pub trait Write { + type WriteError; + type FlushError; + + /// Write a buffer into this object, returning how many bytes were written. + fn write(&mut self, buf: &[u8]) -> Result; + + /// Flush this output stream, ensuring that all intermediately buffered contents + /// reach their destination. + fn flush(&mut self) -> Result<(), Self::FlushError>; + + /// Attempts to write an entire buffer into `self`. + fn write_all(&mut self, mut buf: &[u8]) -> Result<(), Error> { + while buf.len() > 0 { + let written_bytes = self.write(buf)?; + if written_bytes == 0 { + return Err(Error::UnexpectedEnd) + } + + buf = &buf[written_bytes..]; + } + + Ok(()) + } + + /// Hints the writer how much bytes will be written after call to this function. + /// + /// At least `min` bytes should be written after the call to this function and + /// if `max` is `Some(x)` than at most `x` bytes should be written. + fn size_hint(&mut self, _min: usize, _max: Option) {} +} + +impl<'a, T: Write> Write for &'a mut T { + type WriteError = T::WriteError; + type FlushError = T::FlushError; + + fn write(&mut self, buf: &[u8]) -> Result { + T::write(self, buf) + } + + fn flush(&mut self) -> Result<(), Self::FlushError> { + T::flush(self) + } + + fn size_hint(&mut self, min: usize, max: Option) { + T::size_hint(self, min, max) + } +} + +impl<'a> Write for &'a mut [u8] { + type WriteError = !; + type FlushError = !; + + fn write(&mut self, buf: &[u8]) -> Result { + let len = buf.len().min(self.len()); + self[..len].copy_from_slice(&buf[..len]); + Ok(len) + } + + #[inline] + fn flush(&mut self) -> Result<(), Self::FlushError> { + Ok(()) + } +} + +#[cfg(feature = "alloc")] +impl<'a> Write for alloc::vec::Vec { + type WriteError = !; + type FlushError = !; + + fn write(&mut self, buf: &[u8]) -> Result { + self.extend_from_slice(buf); + Ok(buf.len()) + } + + #[inline] + fn flush(&mut self) -> Result<(), Self::FlushError> { + Ok(()) + } +} diff --git a/src/libio/src/proto.rs b/src/libio/src/proto.rs new file mode 100644 index 00000000..5b62a5c5 --- /dev/null +++ b/src/libio/src/proto.rs @@ -0,0 +1,162 @@ +#[cfg(feature = "alloc")] +use core::str::Utf8Error; +use byteorder::{ByteOrder, NetworkEndian}; +#[cfg(feature = "alloc")] +use alloc::vec; +use alloc::string::String; + +use ::{Read, Write, Error as IoError}; + +#[cfg(feature = "alloc")] +#[derive(Debug, Clone, PartialEq)] +pub enum ReadStringError { + Utf8(Utf8Error), + Other(T) +} + +pub trait ProtoRead { + type ReadError; + + fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), Self::ReadError>; + + #[inline] + fn read_u8(&mut self) -> Result { + let mut bytes = [0; 1]; + self.read_exact(&mut bytes)?; + Ok(bytes[0]) + } + + #[inline] + fn read_u16(&mut self) -> Result { + let mut bytes = [0; 2]; + self.read_exact(&mut bytes)?; + Ok(NetworkEndian::read_u16(&bytes)) + } + + #[inline] + fn read_u32(&mut self) -> Result { + let mut bytes = [0; 4]; + self.read_exact(&mut bytes)?; + Ok(NetworkEndian::read_u32(&bytes)) + } + + #[inline] + fn read_u64(&mut self) -> Result { + let mut bytes = [0; 8]; + self.read_exact(&mut bytes)?; + Ok(NetworkEndian::read_u64(&bytes)) + } + + #[inline] + fn read_bool(&mut self) -> Result { + Ok(self.read_u8()? != 0) + } + + #[cfg(feature = "alloc")] + #[inline] + fn read_bytes(&mut self) -> Result<::alloc::vec::Vec, Self::ReadError> { + let length = self.read_u32()?; + let mut value = vec![0; length as usize]; + self.read_exact(&mut value)?; + Ok(value) + } + + #[cfg(feature = "alloc")] + #[inline] + fn read_string(&mut self) -> Result<::alloc::string::String, ReadStringError> { + let bytes = self.read_bytes().map_err(ReadStringError::Other)?; + String::from_utf8(bytes).map_err(|err| ReadStringError::Utf8(err.utf8_error())) + } +} + +pub trait ProtoWrite { + type WriteError; + + fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::WriteError>; + + #[inline] + fn write_u8(&mut self, value: u8) -> Result<(), Self::WriteError> { + let bytes = [value; 1]; + self.write_all(&bytes) + } + + #[inline] + fn write_i8(&mut self, value: i8) -> Result<(), Self::WriteError> { + let bytes = [value as u8; 1]; + self.write_all(&bytes) + } + + #[inline] + fn write_u16(&mut self, value: u16) -> Result<(), Self::WriteError> { + let mut bytes = [0; 2]; + NetworkEndian::write_u16(&mut bytes, value); + self.write_all(&bytes) + } + + #[inline] + fn write_i16(&mut self, value: i16) -> Result<(), Self::WriteError> { + let mut bytes = [0; 2]; + NetworkEndian::write_i16(&mut bytes, value); + self.write_all(&bytes) + } + + #[inline] + fn write_u32(&mut self, value: u32) -> Result<(), Self::WriteError> { + let mut bytes = [0; 4]; + NetworkEndian::write_u32(&mut bytes, value); + self.write_all(&bytes) + } + + #[inline] + fn write_i32(&mut self, value: i32) -> Result<(), Self::WriteError> { + let mut bytes = [0; 4]; + NetworkEndian::write_i32(&mut bytes, value); + self.write_all(&bytes) + } + + #[inline] + fn write_u64(&mut self, value: u64) -> Result<(), Self::WriteError> { + let mut bytes = [0; 8]; + NetworkEndian::write_u64(&mut bytes, value); + self.write_all(&bytes) + } + + #[inline] + fn write_i64(&mut self, value: i64) -> Result<(), Self::WriteError> { + let mut bytes = [0; 8]; + NetworkEndian::write_i64(&mut bytes, value); + self.write_all(&bytes) + } + + #[inline] + fn write_bool(&mut self, value: bool) -> Result<(), Self::WriteError> { + self.write_u8(value as u8) + } + + #[inline] + fn write_bytes(&mut self, value: &[u8]) -> Result<(), Self::WriteError> { + self.write_u32(value.len() as u32)?; + self.write_all(value) + } + + #[inline] + fn write_string(&mut self, value: &str) -> Result<(), Self::WriteError> { + self.write_bytes(value.as_bytes()) + } +} + +impl ProtoRead for T where T: Read + ?Sized { + type ReadError = IoError; + + fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), Self::ReadError> { + T::read_exact(self, buf) + } +} + +impl ProtoWrite for T where T: Write + ?Sized { + type WriteError = IoError; + + fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::WriteError> { + T::write_all(self, buf) + } +} diff --git a/src/runtime/Cargo.toml b/src/runtime/Cargo.toml index 20c381af..d8e6f5c9 100644 --- a/src/runtime/Cargo.toml +++ b/src/runtime/Cargo.toml @@ -21,3 +21,4 @@ libsupport_zynq = { git = "https://git.m-labs.hk/M-Labs/zc706.git" } libcortex_a9 = { git = "https://git.m-labs.hk/M-Labs/zc706.git" } libasync = { git = "https://git.m-labs.hk/M-Labs/zc706.git" } dyld = { path = "../libdyld" } +io = { path = "../libio", features = ["alloc"] } diff --git a/src/runtime/src/proto.rs b/src/runtime/src/proto.rs index f82a9a0a..bd7fa4f4 100644 --- a/src/runtime/src/proto.rs +++ b/src/runtime/src/proto.rs @@ -5,6 +5,7 @@ use core::cell::RefCell; use libboard_zynq::smoltcp; use libasync::smoltcp::TcpStream; +// TODO: use byteorder, make it more like libio pub type Result = core::result::Result;