dimension_shiftable_buffer/
lib.rs

1//! [![github]](https://github.com/usagi/dimension_shiftable_buffer)&ensp;[![crates-io]](https://crates.io/crates/dimension_shiftable_buffer)&ensp;[![docs-rs]](https://docs.rs/dimension_shiftable_buffer)<br>
2//! [![Build Status](https://travis-ci.org/usagi/dimension_shiftable_buffer.svg?branch=master)](https://travis-ci.org/usagi/dimension_shiftable_buffer)
3//!
4//! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github
5//! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust
6//! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logoColor=white&logo=data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K
7//!
8//! # Dimension shiftable buffer
9//!
10//! The `DimensionShiftableBuffer` by this crate presents a dimension shiftable buffer feature.
11//!
12//! ## What's the "dimension shiftable buffer"?
13//!
14//! - First, a dimension "un-shiftable" buffer is a common buffer types such as:
15//!   - `[T;3]` = One datum, 3-fixed-dimension, buffer is allocated a single heap, elements placed on a sequential memory addresses.
16//!   - `[[T;3]]` = 0..* data, 3-fixed-dimension, buffer is allocated a single heap, elements placed on a sequential memory addresses.
17//!   - `Vec<T>` = 0..* data, 1-fiexed-dimension, buffer is allocated a single heap, elements placed on a sequential memory addresses.
18//!   - `Vec<[T;3]>` = 0..* data, 3-fixed-dimension, buffer is allocated a single heap, elements placed on a sequential memory addresses.
19//!   - `Vec<Vec<T>>` = 0..* data, 1..*-any-dimension, buffer is allocated a multiple heap, elements placed on a dispersed memory addresses.
20//! - Then, a dimension "shiftable" buffer is:
21//!   - `DimensionShiftableBuffer<T>` = 0..* data, N-shiftable-dimension, buffer is allocated a single heap, elements places on a sequential memory addresses.
22//!     - Has one buffer heap.
23//!     - Can access as N=1..* dimension view such as like a `[&[T;3]]`, `[&mut [T;3]]`, `[&[T;127]]`.
24//!     - With only safe implementation.
25//!
26//! ## Example
27//!
28//! ```toml
29//! [dependencies]
30//! dimension_shiftable_buffer = "*"
31//! ```
32//!
33//! ### Example-1
34//!
35//! A simple usages.
36//!
37//! ```rust,ignore
38//! // make a 2d-empty DimensionShiftableBuffer
39//! let mut dsb = DimensionShiftableBuffer::<u8>::new(vec![], 2).unwrap();
40//! // push a 2d-datum
41//! dsb.push(&[0u8, 1]).unwrap();
42//! // push a 2d-datum
43//! dsb.push(&[2u8, 3]).unwrap();
44//! // append a 2d-datum sequence
45//! dsb.append(&[4u8, 5, 6, 7, 8, 9, 10, 11]).unwrap();
46//! for index in 0..dsb.len().unwrap()
47//! {
48//!  // get a 2d slice
49//!  assert_eq!(dsb.get(index).unwrap(), &[index as u8 * 2, index as u8 * 2 + 1]);
50//! }
51//! // shift dimension to 3 from 2
52//! dsb.shift_dimension(3).unwrap();
53//! // push a 3d-datum
54//! dsb.push(&[12u8, 13, 14]).unwrap();
55//! // get a 3d-datum
56//! assert_eq!(dsb.get(0).unwrap(), &[0u8, 1, 2]);
57//! assert_eq!(dsb.get(1).unwrap(), &[3u8, 4, 5]);
58//! assert_eq!(dsb.get(2).unwrap(), &[6u8, 7, 8]);
59//! assert_eq!(dsb.get(3).unwrap(), &[9u8, 10, 11]);
60//! assert_eq!(dsb.get(4).unwrap(), &[12u8, 13, 14]);
61//! // get a linear slice
62//! let linear_slice = dsb.as_slice();
63//! assert_eq!(linear_slice, &[0u8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]);
64//! ```
65//!
66//! ### Example-2
67//!
68//! With an error handling.
69//!
70//! ```rust,ignore
71//! use dimension_shiftable_buffer::DimensionShiftableBuffer
72//!
73//!  // Index of 1D =>           0      |1   |2   |3   |4   |5   |6   |7   |8   |
74//!  // Index of 2D =>           0,0     0,1 |1,0  1,1 |2,0  2,1 |3,0  3,1 |4,0  4,1 |
75//!  // Index of 3D =>           0,0     0,1  0,2 |1,0  1,1  1,2 |2,0  2,1  2,2 |
76//!  // Index of 7D =>           0,0     0,1  0,2  0,3  0,4  0,5  0,6 |1,0  1,1  1,2  1,3  1,4  1,5  1,6 |
77//!  let entity = vec![1.1f32, 1.2, 1.3, 2.1, 2.2, 2.3, 3.1, 3.2, 3.3];
78//!  let dimension = 1;
79//!
80//!  let mut dsb = DimensionShiftableBuffer::<f32>::new(entity, dimension).unwrap();
81//!
82//!  let d1_i3 = dsb.get(3).unwrap();
83//!  assert_eq!(d1_i3, [2.1f32]);
84//!
85//!  dsb.shift_dimension(3).unwrap();
86//!
87//!  let d3_i2 = dsb.get(2).unwrap();
88//!  assert_eq!(d3_i2, [3.1f32, 3.2, 3.3]);
89//!
90//!  match dsb.shift_dimension(2)
91//!  {
92//!   Ok(_) => panic!("Detect an unexpected false-ok."),
93//!   Err(_) => eprintln!("Expected err.")
94//!  }
95//!
96//!  dsb.shift_dimension_truncate(2).unwrap();
97//!
98//!  let d2_i1 = dsb.get(1).unwrap();
99//!  assert_eq!(d2_i1, [1.3f32, 2.1]);
100//!
101//!  dsb.shift_dimension_padding(7, std::f32::NAN).unwrap();
102//!  let d7_i1 = dsb.get(1).unwrap();
103//!  assert_eq!(
104//!   // rounding, just easily
105//!   format!("{:?}", d7_i1),
106//!   format!("{:?}", &[
107//!    // [0] of 7-d
108//!    3.2f32,
109//!    // [1] of 7-d, truncated at .shift_dimension.truncate(2) -> padding at .shift_dimension_padding(7)
110//!    std::f32::NAN,
111//!    // [2] of 7-d, padding at .shift_dimension_padding(7)
112//!    std::f32::NAN,
113//!    std::f32::NAN,
114//!    std::f32::NAN,
115//!    std::f32::NAN,
116//!    std::f32::NAN
117//!   ])
118//!  )
119//! ```
120//!
121//! ### Example-3
122//!
123//! Additional feature="csv" (default).; CSV=Character-Separated-Values, also known as DSV or TSV.
124//!
125//! ```rust, ignore
126//! let delimiter = " ";
127//! let source = "0.0 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0 11.0 12.0 13.0 14.0 15.0 16.0 17.0";
128//! let dimension = 3;
129//! // construct
130//! let mut dsb = DimensionShiftableBuffer::<f32>::from_csv(source, dimension, delimiter).unwrap();
131//! // modify-1
132//! dsb.for_each(|v| *v += 0.1);
133//! // modify-2
134//! for n in 0..dsb.len().unwrap()
135//! {
136//!  for v in dsb.get_mut(n).unwrap()
137//!  {
138//!   *v *= *v;
139//!  }
140//! }
141//! // show
142//! eprintln!("{:?}", dsb);
143//! // enumerate
144//! for i in 0..18
145//! // maybe, 0..5 will ok, 6..18 will err
146//! {
147//!  match dsb.get(i)
148//!  {
149//!   Ok(dimensional_elements) =>
150//!   {
151//!    for (i_sub, actual) in dimensional_elements.iter().enumerate()
152//!    {
153//!     let i_flatten = i * dimension as usize + i_sub;
154//!     let expected = (i_flatten as f32 + 0.1) * (i_flatten as f32 + 0.1);
155//!     assert_eq!(actual, &expected);
156//!    }
157//!   },
158//!   Err(e) =>
159//!   {
160//!    match e
161//!    {
162//!     DimensionShiftableBufferError::OutOfRange {
163//!      limit: _,
164//!      requested: _
165//!     } => (),
166//!     _ => panic!("unexpected err: {:?}", e)
167//!    }
168//!   },
169//!  }
170//! }
171//! println!("<<print CSV, as a sequential slice>>\n{}", dsb.to_csv(", "));
172//! println!(
173//!  "<<print CSV, as a dimmensional slices>>\n{}",
174//!  dsb.to_csv_dimensional(", ", "\n").unwrap()
175//! );
176//! ```
177//!
178//! ```sh
179//! <<print CSV, as a sequential slice>>
180//! 0.010000001, 1.21, 4.4099994, 9.61, 16.81, 26.009998, 37.21, 50.41, 65.61001, 82.810005, 102.01001, 123.21001, 146.41, 171.61002, 198.81001, 228.01001, 259.21002, 292.41
181//! <<print CSV, as a dimmensional slices>>
182//! 0.010000001, 1.21, 4.4099994
183//! 9.61, 16.81, 26.009998
184//! 37.21, 50.41, 65.61001
185//! 82.810005, 102.01001, 123.21001
186//! 146.41, 171.61002, 198.81001
187//! 228.01001, 259.21002, 292.41
188//! ```
189//!
190//! More details and examples are exists in the [README.md][] and [examples/] and [tests/].
191//!
192//! [README.md]: https://github.com/usagi/dimension_shiftable_buffer
193//! [examples/]: https://github.com/usagi/dimension_shiftable_buffer/blob/master/examples
194//! [tests/]: https://github.com/usagi/dimension_shiftable_buffer/blob/master/tests
195//!
196
197use thiserror::Error;
198
199#[derive(Debug, Default)]
200pub struct DimensionShiftableBuffer<T: PartialOrd + Clone>
201{
202 entity:    Vec<T>,
203 dimension: usize
204}
205
206#[derive(Error, Debug)]
207pub enum DimensionShiftableBufferError
208{
209 #[error("Failed to parse from CSV.")]
210 FailedToParseFromCsv(String),
211 #[error("Dimension requirement was mismatched; current={current}, desired={desired}")]
212 DimensionRequirementMismatch
213 {
214  current: usize, desired: usize
215 },
216 #[error(
217  "Dimension requirement was mismatched; expected={expected}, # of source datum={number_of_source_datum}. The requirement is # of source \
218   datum % expected == 0"
219 )]
220 DimensionRequirementMismatchWithMultipleDatum
221 {
222  expected:               usize,
223  number_of_source_datum: usize
224 },
225 #[error("Out of range; limit={limit}, requested={requested}")]
226 OutOfRange
227 {
228  limit: usize, requested: usize
229 },
230 #[error("Empty data.")]
231 Empty,
232 #[error("Could not estimate the dimension. It might be need `.set_dimension(your_desired_dimension)` before the process.")]
233 DimensionUnknown
234}
235
236#[cfg(feature = "csv")]
237pub trait Csv<T: std::fmt::Display + std::str::FromStr + PartialOrd + Clone>
238{
239 fn from_csv(source: &str, dimension: usize, delimiter: &str) -> Result<DimensionShiftableBuffer<T>, DimensionShiftableBufferError>;
240 fn to_csv(&self, delimiter: &str) -> String;
241 fn to_csv_dimensional(&self, element_delimiter: &str, dimension_delimiter: &str) -> Result<String, DimensionShiftableBufferError>;
242}
243
244#[cfg(feature = "csv")]
245impl<T: std::fmt::Display + std::str::FromStr + PartialOrd + Clone> Csv<T> for DimensionShiftableBuffer<T>
246{
247 fn from_csv(source: &str, dimension: usize, delimiter: &str) -> Result<DimensionShiftableBuffer<T>, DimensionShiftableBufferError>
248 {
249  let values = source.split(delimiter).collect::<Vec<_>>();
250
251  if values.len() % dimension != 0
252  {
253   Err(DimensionShiftableBufferError::DimensionRequirementMismatchWithMultipleDatum {
254    expected:               dimension,
255    number_of_source_datum: values.len()
256   })?
257  }
258
259  let mut entity = Vec::<T>::new();
260  entity.reserve(values.len());
261  for v in values
262  {
263   entity.push(
264    v.parse()
265     .map_err(|_| DimensionShiftableBufferError::FailedToParseFromCsv(v.into()))?
266   )
267  }
268
269  Ok(DimensionShiftableBuffer {
270   entity,
271   dimension
272  })
273 }
274
275 fn to_csv(&self, delimiter: &str) -> String
276 {
277  self.entity.iter().map(|v| v.to_string()).collect::<Vec<_>>().join(delimiter)
278 }
279
280 fn to_csv_dimensional(&self, element_delimiter: &str, dimension_delimiter: &str) -> Result<String, DimensionShiftableBufferError>
281 {
282  let mut csv = String::new();
283
284  let stringfied = self.entity.iter().map(|v| v.to_string()).collect::<Vec<_>>();
285  for i in 0..self.len()?
286  {
287   let begin = i * self.dimension;
288   let end = begin + self.dimension;
289   let line = format!("{}{}", &stringfied[begin..end].join(element_delimiter), dimension_delimiter);
290   csv.push_str(&line[..]);
291  }
292  Ok(csv)
293 }
294}
295
296impl<T: PartialOrd + Clone> DimensionShiftableBuffer<T>
297{
298 pub fn new(entity: Vec<T>, dimension: usize) -> Result<DimensionShiftableBuffer<T>, DimensionShiftableBufferError>
299 {
300  if entity.len() % dimension != 0
301  {
302   Err(DimensionShiftableBufferError::DimensionRequirementMismatchWithMultipleDatum {
303    expected:               dimension,
304    number_of_source_datum: entity.len()
305   })?
306  }
307
308  Ok(DimensionShiftableBuffer {
309   entity,
310   dimension
311  })
312 }
313
314 pub fn shift_dimension(&mut self, new_dimension: usize) -> Result<(), DimensionShiftableBufferError>
315 {
316  match self.entity.len() % new_dimension == 0
317  {
318   true => Ok(self.dimension = new_dimension),
319   false =>
320   {
321    Err(DimensionShiftableBufferError::DimensionRequirementMismatchWithMultipleDatum {
322     expected:               new_dimension,
323     number_of_source_datum: self.entity.len()
324    })
325   },
326  }
327 }
328
329 pub fn shift_dimension_truncate(&mut self, new_dimension: usize) -> Result<(), DimensionShiftableBufferError>
330 {
331  let m = self.entity.len() % new_dimension;
332  self.entity.truncate(self.entity.len() - m);
333  self.shift_dimension(new_dimension)
334 }
335
336 pub fn shift_dimension_padding(&mut self, new_dimension: usize, padding_value: T) -> Result<(), DimensionShiftableBufferError>
337 {
338  let m = self.entity.len() % new_dimension;
339  let a = new_dimension - m;
340  self.entity.resize(self.entity.len() + a, padding_value);
341  self.shift_dimension(new_dimension)
342 }
343
344 pub fn for_each<F: FnMut(&mut T)>(&mut self, f: F) -> &Self
345 {
346  self.entity.iter_mut().for_each(f);
347  self
348 }
349
350 /// WARNING, It's MOVE and drop self.
351 pub fn move_entity(self) -> Vec<T>
352 {
353  self.entity
354 }
355
356 pub fn as_slice(&self) -> &[T]
357 {
358  &self.entity[..]
359 }
360
361 pub fn clear(&mut self)
362 {
363  self.entity.clear();
364  self.dimension = 0;
365 }
366
367 pub fn len(&self) -> Result<usize, DimensionShiftableBufferError>
368 {
369  match self.dimension
370  {
371   0 => Err(DimensionShiftableBufferError::DimensionUnknown),
372   _ => Ok(self.entity.len() / self.dimension)
373  }
374 }
375
376 pub fn get(&self, index: usize) -> Result<&[T], DimensionShiftableBufferError>
377 {
378  let begin = index * self.dimension;
379  let end = begin + self.dimension;
380  match end <= self.entity.len()
381  {
382   true => Ok(&self.entity[begin..end]),
383   false =>
384   {
385    Err(DimensionShiftableBufferError::OutOfRange {
386     limit:     self.entity.len() / self.dimension,
387     requested: index
388    })
389   },
390  }
391 }
392
393 pub fn get_mut(&mut self, index: usize) -> Result<&mut [T], DimensionShiftableBufferError>
394 {
395  let begin = index * self.dimension;
396  let end = begin + self.dimension;
397  match end <= self.entity.len()
398  {
399   true => Ok(&mut self.entity[begin..end]),
400   false =>
401   {
402    Err(DimensionShiftableBufferError::OutOfRange {
403     limit:     self.entity.len() / self.dimension,
404     requested: index
405    })
406   },
407  }
408 }
409
410 pub fn remove(&mut self, index: usize) -> Result<Vec<T>, DimensionShiftableBufferError>
411 {
412  let len = self.len()?;
413
414  match self.dimension
415  {
416   0 => Err(DimensionShiftableBufferError::DimensionUnknown)?,
417   n if len > index =>
418   {
419    let drained = self.entity.drain(index * n..index * n + n).collect::<Vec<_>>();
420    Ok(drained)
421   },
422   _ =>
423   {
424    match len
425    {
426     0 => Err(DimensionShiftableBufferError::Empty),
427     _ =>
428     {
429      Err(DimensionShiftableBufferError::OutOfRange {
430       limit:     len - 1,
431       requested: index
432      })
433     },
434    }
435   },
436  }
437 }
438
439 pub fn pop(&mut self) -> Result<Vec<T>, DimensionShiftableBufferError>
440 {
441  match self.dimension
442  {
443   0 => Err(DimensionShiftableBufferError::DimensionUnknown)?,
444   n if self.entity.len() >= n =>
445   {
446    let drained = self.entity.drain(self.entity.len() - n..).collect::<Vec<_>>();
447    Ok(drained)
448   },
449   _ => Err(DimensionShiftableBufferError::Empty)
450  }
451 }
452
453 pub fn push(&mut self, v: &[T]) -> Result<(), DimensionShiftableBufferError>
454 {
455  match self.dimension
456  {
457   0 =>
458   {
459    self.dimension = v.len();
460    self.entity = v.to_vec()
461   },
462   n if n == v.len() => self.entity.append(&mut v.to_vec()),
463   _ =>
464   {
465    Err(DimensionShiftableBufferError::DimensionRequirementMismatch {
466     current: self.dimension,
467     desired: v.len()
468    })?
469   },
470  }
471  Ok(())
472 }
473
474 pub fn append(&mut self, vs: &[T]) -> Result<(), DimensionShiftableBufferError>
475 {
476  match self.dimension
477  {
478   0 => Err(DimensionShiftableBufferError::DimensionUnknown),
479   n if vs.len() % n == 0 => Ok(self.entity.append(&mut vs.to_vec())),
480   _ =>
481   {
482    Err(DimensionShiftableBufferError::DimensionRequirementMismatchWithMultipleDatum {
483     expected:               self.dimension,
484     number_of_source_datum: vs.len()
485    })
486   },
487  }
488 }
489}