use std::fmt;
use std::ops::Deref;
use std::vec::Vec;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct NeoByteString {
data: Vec<u8>,
}
impl NeoByteString {
pub fn new(data: Vec<u8>) -> Self {
Self { data }
}
pub fn from_slice(slice: &[u8]) -> Self {
Self {
data: slice.to_vec(),
}
}
pub fn as_slice(&self) -> &[u8] {
&self.data
}
pub const MAX_SIZE: usize = 1024 * 1024;
pub fn max_size() -> usize {
Self::MAX_SIZE
}
pub fn len(&self) -> usize {
self.data.len()
}
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
pub fn try_push(&mut self, byte: u8) -> Result<(), ByteStringFullError> {
if self.data.len() >= Self::MAX_SIZE {
return Err(ByteStringFullError {
current_len: self.data.len(),
attempted: 1,
});
}
self.data.push(byte);
Ok(())
}
pub fn try_extend_from_slice(&mut self, slice: &[u8]) -> Result<(), ByteStringFullError> {
let new_len = self
.data
.len()
.checked_add(slice.len())
.ok_or(ByteStringFullError {
current_len: self.data.len(),
attempted: slice.len(),
})?;
if new_len > Self::MAX_SIZE {
return Err(ByteStringFullError {
current_len: self.data.len(),
attempted: slice.len(),
});
}
self.data.extend_from_slice(slice);
Ok(())
}
pub fn push(&mut self, byte: u8) {
self.data.push(byte);
}
pub fn extend_from_slice(&mut self, slice: &[u8]) {
self.data.extend_from_slice(slice);
}
}
impl fmt::Display for NeoByteString {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for byte in &self.data {
write!(f, "{:02x}", byte)?;
}
Ok(())
}
}
impl From<Vec<u8>> for NeoByteString {
fn from(data: Vec<u8>) -> Self {
Self { data }
}
}
impl From<&[u8]> for NeoByteString {
fn from(slice: &[u8]) -> Self {
Self::from_slice(slice)
}
}
impl AsRef<[u8]> for NeoByteString {
fn as_ref(&self) -> &[u8] {
&self.data
}
}
impl Deref for NeoByteString {
type Target = [u8];
fn deref(&self) -> &Self::Target {
&self.data
}
}
impl Extend<u8> for NeoByteString {
fn extend<I: IntoIterator<Item = u8>>(&mut self, iter: I) {
self.data.extend(iter);
}
}
impl FromIterator<u8> for NeoByteString {
fn from_iter<I: IntoIterator<Item = u8>>(iter: I) -> Self {
Self {
data: Vec::from_iter(iter),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ByteStringFullError {
pub current_len: usize,
pub attempted: usize,
}
impl core::fmt::Display for ByteStringFullError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(
f,
"NeoByteString is full (current_len = {}; attempted {} more; MAX_SIZE = {})",
self.current_len,
self.attempted,
NeoByteString::MAX_SIZE
)
}
}
impl std::error::Error for ByteStringFullError {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn deref_to_slice() {
let bs = NeoByteString::from_slice(&[1, 2, 3]);
assert_eq!(bs.len(), 3);
assert_eq!(&bs[..2], &[1, 2]);
let sum: u32 = bs.iter().map(|&b| b as u32).sum();
assert_eq!(sum, 6);
}
#[test]
fn max_size_constant_matches_csharp_limit() {
assert_eq!(NeoByteString::MAX_SIZE, 1024 * 1024);
}
#[test]
fn try_push_returns_error_at_limit() {
let mut bs = NeoByteString::from_slice(&[]);
bs.try_push(0x42).expect("tiny string fits");
assert_eq!(bs.len(), 1);
}
#[test]
fn try_extend_propagates_error() {
let mut bs = NeoByteString::from_slice(&[1, 2, 3]);
bs.try_extend_from_slice(&[]).expect("empty extend ok");
bs.try_extend_from_slice(&[4, 5]).expect("small extend ok");
assert_eq!(bs.as_slice(), &[1, 2, 3, 4, 5]);
}
}