mirror of
https://github.com/saymrwulf/xous-core.git
synced 2026-11-04 04:27:34 +00:00
not doing a reformat of the entire project to match nightly formatting changes, but capturing the published crates so we can maybe save a version bump later on to get the project clean.
282 lines
9.9 KiB
Rust
282 lines
9.9 KiB
Rust
use crate::{
|
|
CID, Error, MemoryMessage, MemoryRange, MemorySize, Message, Result, map_memory, send_message,
|
|
unmap_memory,
|
|
};
|
|
|
|
/// A buffered String suitable for sending across as a message
|
|
pub struct StringBuffer<'a> {
|
|
/// The backing store for this string, as a mutable pointer to
|
|
/// page-aligned memory.
|
|
bytes: Option<&'a mut [u8]>,
|
|
|
|
// Current length of the string, in bytes
|
|
length: u32,
|
|
|
|
/// `true` if the backing should be freed when the struct is dropped,
|
|
/// `false` when it shouldn't. It should not be freed when it is `Sent`,
|
|
/// because the kernel will simply move the backing to the target process.
|
|
should_free: bool,
|
|
|
|
/// When this object is reconstituted from a MemoryMessage, that object
|
|
/// is placed here. That way, when the object is returned, we can update
|
|
/// the MemoryMessage `valid` field with the new length.
|
|
memory_message: Option<&'a mut MemoryMessage>,
|
|
}
|
|
|
|
impl<'a> StringBuffer<'a> {
|
|
/// Create a new StringBuffer with no backing. This will get
|
|
/// resized as soon as data is written to it.
|
|
pub fn new() -> Self { StringBuffer { bytes: None, length: 0, should_free: true, memory_message: None } }
|
|
|
|
/// Create a new StringBuffer with enough space to hold
|
|
/// `usize` characters
|
|
pub fn with_capacity(capacity: usize) -> Self {
|
|
let remainder =
|
|
if ((capacity & 0xFFF) == 0) && (capacity > 0) { 0 } else { 0x1000 - (capacity & 0xFFF) };
|
|
|
|
let flags = crate::MemoryFlags::R | crate::MemoryFlags::W;
|
|
|
|
// Allocate enough memory to hold the requested data
|
|
let new_mem = map_memory(
|
|
None,
|
|
None,
|
|
// Ensure our byte size is a multiple of 4096
|
|
capacity + remainder,
|
|
flags,
|
|
)
|
|
.expect("Buffer: error in new()/map_memory");
|
|
|
|
StringBuffer {
|
|
bytes: Some(unsafe {
|
|
core::slice::from_raw_parts_mut(new_mem.as_mut_ptr(), capacity + remainder)
|
|
}),
|
|
length: 0,
|
|
should_free: true,
|
|
memory_message: None,
|
|
}
|
|
}
|
|
|
|
fn resize(&mut self, new_capacity: usize) {
|
|
// It is an error to resize a loaned StringBuffer
|
|
if self.memory_message.is_some() {
|
|
return;
|
|
}
|
|
|
|
let remainder = if ((new_capacity & 0xFFF) == 0) && (new_capacity > 0) {
|
|
0
|
|
} else {
|
|
0x1000 - (new_capacity & 0xFFF)
|
|
};
|
|
|
|
// If the new size is the same as the current size, don't do anything.
|
|
let rounded_new_capacity = new_capacity + remainder;
|
|
if rounded_new_capacity == self.bytes.as_ref().map(|b| b.len()).unwrap_or(0) {
|
|
return;
|
|
}
|
|
|
|
let flags = crate::MemoryFlags::R | crate::MemoryFlags::W;
|
|
|
|
// Allocate enough memory to hold the new requested data
|
|
let new_slice = if rounded_new_capacity > 0 {
|
|
let new_mem = map_memory(None, None, rounded_new_capacity, flags)
|
|
.expect("Buffer: error in new()/map_memory");
|
|
let new_slice =
|
|
unsafe { core::slice::from_raw_parts_mut(new_mem.as_mut_ptr(), rounded_new_capacity) };
|
|
// Copy the existing string to the new slice
|
|
for (dest_byte, src_byte) in new_slice.iter_mut().zip(self.as_bytes()[0..self.len()].iter()) {
|
|
*dest_byte = *src_byte;
|
|
}
|
|
Some(new_slice)
|
|
} else {
|
|
None
|
|
};
|
|
|
|
if let Some(old_slice) = self.bytes.take() {
|
|
let old_addr = old_slice.as_ptr();
|
|
let old_length = old_slice.len();
|
|
unmap_memory(unsafe { MemoryRange::new(old_addr as usize, old_length).unwrap() }).unwrap();
|
|
}
|
|
self.bytes = new_slice;
|
|
|
|
// If the string has shrunk, truncate the string.
|
|
if new_capacity < self.len() {
|
|
self.length = new_capacity as u32;
|
|
}
|
|
}
|
|
|
|
pub fn as_bytes(&self) -> &[u8] { if let Some(bytes) = &self.bytes { bytes } else { &[] } }
|
|
|
|
pub fn as_bytes_mut(&mut self) -> &mut [u8] {
|
|
if let Some(bytes) = self.bytes.as_mut() { bytes } else { &mut [] }
|
|
}
|
|
|
|
pub fn as_str(&self) -> core::result::Result<&str, core::str::Utf8Error> {
|
|
if let Some(bytes) = &self.bytes { core::str::from_utf8(&bytes[0..self.len()]) } else { Ok("") }
|
|
}
|
|
|
|
pub fn len(&self) -> usize { self.length as usize }
|
|
|
|
pub fn is_empty(&self) -> bool { self.length == 0 }
|
|
|
|
/// Clear the contents of this String and set the length to 0
|
|
pub fn clear(&mut self) {
|
|
self.length = 0;
|
|
if let Some(mm) = self.memory_message.as_mut() {
|
|
mm.valid = None;
|
|
}
|
|
}
|
|
|
|
pub fn to_str(&self) -> &str {
|
|
if let Some(bytes) = &self.bytes {
|
|
unsafe { core::str::from_utf8_unchecked(&bytes[0..self.len()]) }
|
|
} else {
|
|
""
|
|
}
|
|
}
|
|
|
|
fn create_memory_message(&self, id: u32) -> MemoryMessage {
|
|
if let Some(bytes) = &self.bytes {
|
|
let backing_store = unsafe { MemoryRange::new(bytes.as_ptr() as _, bytes.len()).unwrap() };
|
|
MemoryMessage {
|
|
id: id as usize,
|
|
buf: backing_store,
|
|
offset: None,
|
|
valid: MemorySize::new(self.len()),
|
|
}
|
|
} else {
|
|
panic!("Tried to create a memory message with no string!");
|
|
}
|
|
}
|
|
|
|
/// # Safety
|
|
///
|
|
/// This can turn any `MemoryMessage` into a `StringBuffer`, so ensure
|
|
/// that this function is only called when the contents are a `StringBuffer`
|
|
pub unsafe fn from_memory_message(mem: &'a MemoryMessage) -> Self {
|
|
StringBuffer {
|
|
// range: mem.buf,
|
|
bytes: Some(core::slice::from_raw_parts_mut(mem.buf.as_mut_ptr(), mem.buf.len())),
|
|
length: mem.valid.map(|v| v.get()).unwrap_or(0) as u32,
|
|
// offset: mem.offset,
|
|
should_free: false,
|
|
memory_message: None,
|
|
}
|
|
}
|
|
|
|
/// # Safety
|
|
///
|
|
/// This can turn any `MemoryMessage` into a `StringBuffer`, so ensure
|
|
/// that this function is only called when the contents are a `StringBuffer`
|
|
pub unsafe fn from_memory_message_mut(mem: &'a mut MemoryMessage) -> Self {
|
|
StringBuffer {
|
|
// range: mem.buf,
|
|
bytes: Some(core::slice::from_raw_parts_mut(mem.buf.as_mut_ptr(), mem.buf.len())),
|
|
length: mem.valid.map(|v| v.get()).unwrap_or(0) as u32,
|
|
// valid: mem.buf,
|
|
// offset: mem.offset,
|
|
should_free: false,
|
|
memory_message: Some(mem),
|
|
}
|
|
}
|
|
|
|
/// Perform a mutable lend of this String to the server.
|
|
pub fn lend_mut(&mut self, connection: CID, id: u32) -> core::result::Result<Result, Error> {
|
|
let msg = self.create_memory_message(id);
|
|
|
|
// Update the offset pointer if the server modified it.
|
|
let result = send_message(connection, Message::MutableBorrow(msg));
|
|
if let Ok(Result::MemoryReturned(_offset, valid)) = result {
|
|
self.length = valid.map(|v| v.get()).unwrap_or(0) as u32;
|
|
}
|
|
|
|
result
|
|
}
|
|
|
|
pub fn lend(&self, connection: CID, id: u32) -> core::result::Result<Result, Error> {
|
|
let msg = self.create_memory_message(id);
|
|
send_message(connection, Message::Borrow(msg))
|
|
}
|
|
|
|
pub fn send(mut self, connection: CID, id: u32) -> core::result::Result<Result, Error> {
|
|
let msg = self.create_memory_message(id);
|
|
let result = send_message(connection, Message::Move(msg))?;
|
|
|
|
// prevents it from being Dropped.
|
|
self.should_free = false;
|
|
Ok(result)
|
|
}
|
|
}
|
|
|
|
impl<'a> core::str::FromStr for StringBuffer<'a> {
|
|
type Err = &'static str;
|
|
|
|
fn from_str(src: &str) -> core::result::Result<StringBuffer<'a>, &'static str> {
|
|
let mut s = Self::with_capacity(src.len());
|
|
// Copy the string into our backing store.
|
|
for (dest_byte, src_byte) in s.bytes.as_mut().unwrap().iter_mut().zip(src.as_bytes()) {
|
|
*dest_byte = *src_byte;
|
|
}
|
|
// Set the string length to the length of the passed-in String,
|
|
// or the maximum possible length. Which ever is smaller.
|
|
s.length = s.as_bytes().len().min(src.as_bytes().len()) as u32;
|
|
|
|
// If the string is not valid, set its length to 0.
|
|
if s.as_str().is_err() {
|
|
s.length = 0;
|
|
}
|
|
|
|
Ok(s)
|
|
}
|
|
}
|
|
|
|
impl<'a> Default for StringBuffer<'a> {
|
|
fn default() -> Self { Self::new() }
|
|
}
|
|
|
|
impl<'a> core::fmt::Display for StringBuffer<'a> {
|
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!(f, "{}", self.to_str()) }
|
|
}
|
|
|
|
impl<'a> core::fmt::Write for StringBuffer<'a> {
|
|
fn write_str(&mut self, s: &str) -> core::result::Result<(), core::fmt::Error> {
|
|
// Ensure the string can hold the new data
|
|
self.resize(self.len() + s.len());
|
|
|
|
// Copy the data over
|
|
let length = self.len();
|
|
for (dest, src) in self.as_bytes_mut()[length..].iter_mut().zip(s.as_bytes()) {
|
|
*dest = *src;
|
|
}
|
|
self.length += s.len() as u32;
|
|
if let Some(mm) = self.memory_message.as_mut() {
|
|
mm.valid = MemorySize::new(self.length as _).or(None);
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl<'a> core::fmt::Debug for StringBuffer<'a> {
|
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!(f, "{}", self.to_str()) }
|
|
}
|
|
|
|
impl<'a> core::convert::AsRef<str> for StringBuffer<'a> {
|
|
fn as_ref(&self) -> &str { self.to_str() }
|
|
}
|
|
|
|
impl<'a> PartialEq for StringBuffer<'a> {
|
|
fn eq(&self, other: &Self) -> bool {
|
|
self.length == other.length && self.as_bytes()[..self.len()] == other.as_bytes()[..other.len()]
|
|
}
|
|
}
|
|
|
|
impl<'a> Eq for StringBuffer<'a> {}
|
|
|
|
impl<'a> Drop for StringBuffer<'a> {
|
|
fn drop(&mut self) {
|
|
if self.should_free && !self.as_bytes().is_empty() {
|
|
let range =
|
|
unsafe { MemoryRange::new(self.as_bytes().as_ptr() as _, self.as_bytes().len()).unwrap() };
|
|
unmap_memory(range).expect("Buffer: failed to drop memory");
|
|
}
|
|
}
|
|
}
|