dimension_shiftable_buffer/lib.rs
1//! [![github]](https://github.com/usagi/dimension_shiftable_buffer) [![crates-io]](https://crates.io/crates/dimension_shiftable_buffer) [![docs-rs]](https://docs.rs/dimension_shiftable_buffer)<br>
2//! [](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}