use f2rust_std::{CharArray, CharArrayMut};
use std::ops::{Index, IndexMut};
const LBCELL: i32 = -5;
const METADATA: usize = (1 - LBCELL) as usize;
const MD_SIZE: usize = 4; const MD_CARD: usize = 5;
mod internal {
use std::fmt::Debug;
pub trait CellValue: Clone + Debug {
fn default() -> Self;
fn get_usize(&self) -> usize;
fn set_usize(&mut self, value: usize);
}
impl CellValue for i32 {
fn default() -> Self {
0
}
fn get_usize(&self) -> usize {
*self as usize
}
fn set_usize(&mut self, value: usize) {
*self = value as i32;
}
}
impl CellValue for f64 {
fn default() -> Self {
0.0
}
fn get_usize(&self) -> usize {
*self as usize
}
fn set_usize(&mut self, value: usize) {
*self = value as f64;
}
}
}
#[derive(Clone, Debug)]
pub struct Cell<T> {
data: Vec<T>,
}
impl<T> Cell<T>
where
T: internal::CellValue,
{
pub fn with_capacity(capacity: usize) -> Self {
let mut data = vec![T::default(); METADATA + capacity];
for i in 0..MD_SIZE {
data[i].set_usize(0);
}
data[MD_SIZE].set_usize(capacity);
data[MD_CARD].set_usize(0);
Self { data }
}
pub fn len(&self) -> usize {
self.data[MD_CARD].get_usize()
}
pub fn capacity(&self) -> usize {
self.data[MD_SIZE].get_usize()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn push(&mut self, value: T) {
let len = self.data[MD_CARD].get_usize();
self.reserve(1);
self.data[METADATA + len] = value;
self.data[MD_CARD].set_usize(len + 1);
self.validate();
}
pub fn reserve(&mut self, additional: usize) {
let len = self.len();
if len + additional >= self.capacity() {
self.data.resize(METADATA + len + additional, T::default());
self.data[MD_SIZE].set_usize(len + additional);
}
self.validate();
}
pub fn resize(&mut self, new_len: usize, value: T) {
if new_len > self.capacity() {
self.reserve(new_len - self.capacity());
}
for i in self.len()..new_len {
self[i] = value.clone();
}
self.data[MD_CARD].set_usize(new_len);
self.validate();
}
pub fn as_raw_slice(&self) -> &[T] {
self.validate();
&self.data
}
pub fn as_raw_mut_slice(&mut self) -> &mut [T] {
self.validate();
&mut self.data
}
pub(crate) fn validate(&self) {
assert!(self.len() <= self.capacity());
assert_eq!(self.data.len(), METADATA + self.capacity());
}
pub fn iter(&self) -> impl Iterator<Item = &T> {
let len = self.len();
self.data.iter().skip(METADATA).take(len)
}
pub fn into_iter(self) -> impl Iterator<Item = T> {
let len = self.len();
self.data.into_iter().skip(METADATA).take(len)
}
}
impl<T> Index<usize> for Cell<T>
where
T: internal::CellValue,
{
type Output = T;
fn index(&self, index: usize) -> &Self::Output {
&self.data[METADATA + index]
}
}
impl<T> IndexMut<usize> for Cell<T>
where
T: internal::CellValue,
{
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
&mut self.data[METADATA + index]
}
}
#[derive(Clone, Debug)]
pub struct CharCell {
data: Vec<u8>,
element_length: usize,
}
impl CharCell {
fn set_usize(&mut self, i: usize, value: usize) {
let s = (value as u64).to_be_bytes();
self.data[i * self.element_length..i * self.element_length + 5].copy_from_slice(&s[3..8]);
}
fn get_usize(&self, i: usize) -> usize {
let mut s = [0; 8];
s[3..8].copy_from_slice(&self.data[i * self.element_length..i * self.element_length + 5]);
u64::from_be_bytes(s) as usize
}
pub fn with_capacity_and_length(capacity: usize, element_length: usize) -> Self {
assert!(
element_length >= 5,
"CharCell element length must exceed MINLEN (5)"
);
let data = vec![b' '; (METADATA + capacity) * element_length];
let mut cell = Self {
data,
element_length,
};
for i in 0..MD_SIZE {
cell.set_usize(i, 0);
}
cell.set_usize(MD_SIZE, capacity);
cell.set_usize(MD_CARD, 0);
cell
}
pub fn element_length(&self) -> usize {
self.element_length
}
pub fn len(&self) -> usize {
self.get_usize(MD_CARD)
}
pub fn capacity(&self) -> usize {
self.get_usize(MD_SIZE)
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn push(&mut self, value: &str) {
let len = self.get_usize(MD_CARD);
self.reserve(1);
self.set(len, value);
self.set_usize(MD_CARD, len + 1);
self.validate();
}
pub fn reserve(&mut self, additional: usize) {
let len = self.len();
if len + additional >= self.capacity() {
self.data
.resize((METADATA + len + additional) * self.element_length, b' ');
self.set_usize(MD_SIZE, len + additional);
}
self.validate();
}
pub fn resize(&mut self, new_len: usize, value: &str) {
if new_len > self.capacity() {
self.reserve(new_len - self.capacity());
}
for i in self.len()..new_len {
self.set(i, value);
}
self.set_usize(MD_CARD, new_len);
self.validate();
}
pub fn as_arg(&self) -> CharArray {
self.validate();
CharArray::new(&self.data, self.element_length)
}
pub fn as_arg_mut(&mut self) -> CharArrayMut {
self.validate();
CharArrayMut::new(&mut self.data, self.element_length)
}
pub(crate) fn validate(&self) {
assert!(self.len() <= self.capacity());
assert_eq!(
self.data.len(),
(METADATA + self.capacity()) * self.element_length
);
}
pub fn get(&self, index: usize) -> &str {
let b = &self.data[(METADATA + index) * self.element_length
..(METADATA + index + 1) * self.element_length];
let b = if let Some(end) = b.iter().rposition(|c| *c != b' ') {
&b[..=end]
} else {
b""
};
std::str::from_utf8(b).expect("CharCell contains invalid UTF-8 bytes")
}
pub fn set(&mut self, index: usize, value: &str) {
let b = &mut self.data[(METADATA + index) * self.element_length
..(METADATA + index + 1) * self.element_length];
f2rust_std::fstr::assign(b, value.as_bytes());
}
pub fn iter(&self) -> impl Iterator<Item = &str> {
(0..self.len()).map(|i| self.get(i))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{Result, SpiceContext};
#[test]
fn test_i() {
let mut cell = Cell::with_capacity(20);
cell.validate();
assert_eq!(cell.capacity(), 20);
assert_eq!(cell.len(), 0);
assert_eq!(cell[0], 0);
cell[0] += 100;
assert_eq!(cell[0], 100);
cell.push(200);
assert_eq!(cell[0], 200);
assert_eq!(cell.len(), 1);
cell.resize(19, 300);
assert_eq!(cell.capacity(), 20);
assert_eq!(cell.len(), 19);
assert_eq!(cell[18], 300);
cell.push(400);
assert_eq!(cell.capacity(), 20);
assert_eq!(cell.len(), 20);
assert_eq!(cell[19], 400);
cell.push(500);
assert_eq!(cell[20], 500);
assert_eq!(cell.capacity(), 21);
assert_eq!(cell.len(), 21);
cell.resize(10, 0);
assert_eq!(cell.capacity(), 21);
assert_eq!(cell.len(), 10);
assert_eq!(
cell.into_iter().collect::<Vec<_>>(),
[200, 300, 300, 300, 300, 300, 300, 300, 300, 300]
);
}
#[test]
fn test_d() {
let mut cell = Cell::with_capacity(20);
assert_eq!(cell.capacity(), 20);
assert_eq!(cell.len(), 0);
assert_eq!(cell[0], 0.0);
cell[0] += 100.0;
assert_eq!(cell[0], 100.0);
cell.push(200.0);
assert_eq!(cell[0], 200.0);
assert_eq!(cell.len(), 1);
assert_eq!(cell.into_iter().collect::<Vec<_>>(), [200.0]);
}
#[test]
fn test_c() {
let mut cell = CharCell::with_capacity_and_length(20, 15);
assert_eq!(cell.capacity(), 20);
assert_eq!(cell.len(), 0);
assert_eq!(cell.element_length(), 15);
assert_eq!(cell.get(0), "");
cell.set(0, "aaa");
assert_eq!(cell.get(0), "aaa");
cell.push("b");
assert_eq!(cell.get(0), "b");
assert_eq!(cell.len(), 1);
cell.resize(19, "c");
assert_eq!(cell.capacity(), 20);
assert_eq!(cell.len(), 19);
assert_eq!(cell.get(18), "c");
cell.push("d");
assert_eq!(cell.capacity(), 20);
assert_eq!(cell.len(), 20);
assert_eq!(cell.get(19), "d");
cell.push("e");
assert_eq!(cell.get(20), "e");
assert_eq!(cell.capacity(), 21);
assert_eq!(cell.len(), 21);
cell.resize(10, "");
assert_eq!(cell.capacity(), 21);
assert_eq!(cell.len(), 10);
assert_eq!(
cell.iter().collect::<Vec<_>>(),
["b", "c", "c", "c", "c", "c", "c", "c", "c", "c"]
);
}
#[test]
fn test_interop_i() -> Result<()> {
let mut spice = SpiceContext::new();
let mut cell = Cell::with_capacity(20);
assert_eq!(spice.cardi(&cell)?, 0);
assert_eq!(spice.sizei(&cell)?, 20);
spice.appndi(100, &mut cell)?;
assert_eq!(cell.len(), 1);
assert_eq!(cell[0], 100);
Ok(())
}
#[test]
fn test_interop_d() -> Result<()> {
let mut spice = SpiceContext::new();
let mut cell = Cell::with_capacity(20);
assert_eq!(spice.cardd(&cell)?, 0);
assert_eq!(spice.sized(&cell)?, 20);
spice.appndd(100.0, &mut cell)?;
assert_eq!(cell.len(), 1);
assert_eq!(cell[0], 100.0);
Ok(())
}
#[test]
fn test_interop_c() -> Result<()> {
let mut spice = SpiceContext::new();
let mut cell = CharCell::with_capacity_and_length(20, 15);
assert_eq!(cell.capacity(), 20);
assert_eq!(cell.len(), 0);
assert_eq!(spice.cardc(&cell)?, 0);
assert_eq!(spice.sizec(&cell)?, 20);
spice.appndc("Hello world", &mut cell)?;
assert_eq!(cell.len(), 1);
assert_eq!(cell.get(0), "Hello world");
cell.set(0, "Test");
assert_eq!(cell.get(0), "Test");
spice.ssizec(0x12345678, &mut cell)?;
assert_eq!(cell.capacity(), 0x12345678);
Ok(())
}
}