1use crate::attributes::NDAttributeList;
2use crate::codec::Codec;
3use crate::error::{ADError, ADResult};
4use crate::timestamp::EpicsTimestamp;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8#[repr(u8)]
9pub enum NDDataType {
10 Int8 = 0,
11 UInt8 = 1,
12 Int16 = 2,
13 UInt16 = 3,
14 Int32 = 4,
15 UInt32 = 5,
16 Int64 = 6,
17 UInt64 = 7,
18 Float32 = 8,
19 Float64 = 9,
20}
21
22impl NDDataType {
23 pub fn element_size(&self) -> usize {
24 match self {
25 Self::Int8 | Self::UInt8 => 1,
26 Self::Int16 | Self::UInt16 => 2,
27 Self::Int32 | Self::UInt32 | Self::Float32 => 4,
28 Self::Int64 | Self::UInt64 | Self::Float64 => 8,
29 }
30 }
31
32 pub fn from_ordinal(v: u8) -> Option<Self> {
33 match v {
34 0 => Some(Self::Int8),
35 1 => Some(Self::UInt8),
36 2 => Some(Self::Int16),
37 3 => Some(Self::UInt16),
38 4 => Some(Self::Int32),
39 5 => Some(Self::UInt32),
40 6 => Some(Self::Int64),
41 7 => Some(Self::UInt64),
42 8 => Some(Self::Float32),
43 9 => Some(Self::Float64),
44 _ => None,
45 }
46 }
47}
48
49#[derive(Debug, Clone)]
51pub enum NDDataBuffer {
52 I8(Vec<i8>),
53 U8(Vec<u8>),
54 I16(Vec<i16>),
55 U16(Vec<u16>),
56 I32(Vec<i32>),
57 U32(Vec<u32>),
58 I64(Vec<i64>),
59 U64(Vec<u64>),
60 F32(Vec<f32>),
61 F64(Vec<f64>),
62}
63
64impl NDDataBuffer {
65 pub fn zeros(data_type: NDDataType, count: usize) -> Self {
66 match data_type {
67 NDDataType::Int8 => Self::I8(vec![0; count]),
68 NDDataType::UInt8 => Self::U8(vec![0; count]),
69 NDDataType::Int16 => Self::I16(vec![0; count]),
70 NDDataType::UInt16 => Self::U16(vec![0; count]),
71 NDDataType::Int32 => Self::I32(vec![0; count]),
72 NDDataType::UInt32 => Self::U32(vec![0; count]),
73 NDDataType::Int64 => Self::I64(vec![0; count]),
74 NDDataType::UInt64 => Self::U64(vec![0; count]),
75 NDDataType::Float32 => Self::F32(vec![0.0; count]),
76 NDDataType::Float64 => Self::F64(vec![0.0; count]),
77 }
78 }
79
80 pub fn data_type(&self) -> NDDataType {
81 match self {
82 Self::I8(_) => NDDataType::Int8,
83 Self::U8(_) => NDDataType::UInt8,
84 Self::I16(_) => NDDataType::Int16,
85 Self::U16(_) => NDDataType::UInt16,
86 Self::I32(_) => NDDataType::Int32,
87 Self::U32(_) => NDDataType::UInt32,
88 Self::I64(_) => NDDataType::Int64,
89 Self::U64(_) => NDDataType::UInt64,
90 Self::F32(_) => NDDataType::Float32,
91 Self::F64(_) => NDDataType::Float64,
92 }
93 }
94
95 pub fn len(&self) -> usize {
96 match self {
97 Self::I8(v) => v.len(),
98 Self::U8(v) => v.len(),
99 Self::I16(v) => v.len(),
100 Self::U16(v) => v.len(),
101 Self::I32(v) => v.len(),
102 Self::U32(v) => v.len(),
103 Self::I64(v) => v.len(),
104 Self::U64(v) => v.len(),
105 Self::F32(v) => v.len(),
106 Self::F64(v) => v.len(),
107 }
108 }
109
110 pub fn is_empty(&self) -> bool {
111 self.len() == 0
112 }
113
114 pub fn total_bytes(&self) -> usize {
115 self.len() * self.data_type().element_size()
116 }
117
118 pub fn capacity_bytes(&self) -> usize {
120 let cap = match self {
121 Self::I8(v) => v.capacity(),
122 Self::U8(v) => v.capacity(),
123 Self::I16(v) => v.capacity(),
124 Self::U16(v) => v.capacity(),
125 Self::I32(v) => v.capacity(),
126 Self::U32(v) => v.capacity(),
127 Self::I64(v) => v.capacity(),
128 Self::U64(v) => v.capacity(),
129 Self::F32(v) => v.capacity(),
130 Self::F64(v) => v.capacity(),
131 };
132 cap * self.data_type().element_size()
133 }
134
135 pub fn resize(&mut self, new_len: usize) {
137 match self {
138 Self::I8(v) => v.resize(new_len, 0),
139 Self::U8(v) => v.resize(new_len, 0),
140 Self::I16(v) => v.resize(new_len, 0),
141 Self::U16(v) => v.resize(new_len, 0),
142 Self::I32(v) => v.resize(new_len, 0),
143 Self::U32(v) => v.resize(new_len, 0),
144 Self::I64(v) => v.resize(new_len, 0),
145 Self::U64(v) => v.resize(new_len, 0),
146 Self::F32(v) => v.resize(new_len, 0.0),
147 Self::F64(v) => v.resize(new_len, 0.0),
148 }
149 }
150
151 pub fn as_u8_slice(&self) -> &[u8] {
153 match self {
154 Self::I8(v) => unsafe { std::slice::from_raw_parts(v.as_ptr() as *const u8, v.len()) },
155 Self::U8(v) => v.as_slice(),
156 Self::I16(v) => unsafe {
157 std::slice::from_raw_parts(v.as_ptr() as *const u8, v.len() * 2)
158 },
159 Self::U16(v) => unsafe {
160 std::slice::from_raw_parts(v.as_ptr() as *const u8, v.len() * 2)
161 },
162 Self::I32(v) => unsafe {
163 std::slice::from_raw_parts(v.as_ptr() as *const u8, v.len() * 4)
164 },
165 Self::U32(v) => unsafe {
166 std::slice::from_raw_parts(v.as_ptr() as *const u8, v.len() * 4)
167 },
168 Self::I64(v) => unsafe {
169 std::slice::from_raw_parts(v.as_ptr() as *const u8, v.len() * 8)
170 },
171 Self::U64(v) => unsafe {
172 std::slice::from_raw_parts(v.as_ptr() as *const u8, v.len() * 8)
173 },
174 Self::F32(v) => unsafe {
175 std::slice::from_raw_parts(v.as_ptr() as *const u8, v.len() * 4)
176 },
177 Self::F64(v) => unsafe {
178 std::slice::from_raw_parts(v.as_ptr() as *const u8, v.len() * 8)
179 },
180 }
181 }
182
183 pub fn get_as_f64(&self, index: usize) -> Option<f64> {
185 match self {
186 Self::I8(v) => v.get(index).map(|&x| x as f64),
187 Self::U8(v) => v.get(index).map(|&x| x as f64),
188 Self::I16(v) => v.get(index).map(|&x| x as f64),
189 Self::U16(v) => v.get(index).map(|&x| x as f64),
190 Self::I32(v) => v.get(index).map(|&x| x as f64),
191 Self::U32(v) => v.get(index).map(|&x| x as f64),
192 Self::I64(v) => v.get(index).map(|&x| x as f64),
193 Self::U64(v) => v.get(index).map(|&x| x as f64),
194 Self::F32(v) => v.get(index).map(|&x| x as f64),
195 Self::F64(v) => v.get(index).copied(),
196 }
197 }
198
199 pub fn set_from_f64(&mut self, index: usize, value: f64) {
201 match self {
202 Self::I8(v) => {
203 if let Some(e) = v.get_mut(index) {
204 *e = value as i8;
205 }
206 }
207 Self::U8(v) => {
208 if let Some(e) = v.get_mut(index) {
209 *e = value as u8;
210 }
211 }
212 Self::I16(v) => {
213 if let Some(e) = v.get_mut(index) {
214 *e = value as i16;
215 }
216 }
217 Self::U16(v) => {
218 if let Some(e) = v.get_mut(index) {
219 *e = value as u16;
220 }
221 }
222 Self::I32(v) => {
223 if let Some(e) = v.get_mut(index) {
224 *e = value as i32;
225 }
226 }
227 Self::U32(v) => {
228 if let Some(e) = v.get_mut(index) {
229 *e = value as u32;
230 }
231 }
232 Self::I64(v) => {
233 if let Some(e) = v.get_mut(index) {
234 *e = value as i64;
235 }
236 }
237 Self::U64(v) => {
238 if let Some(e) = v.get_mut(index) {
239 *e = value as u64;
240 }
241 }
242 Self::F32(v) => {
243 if let Some(e) = v.get_mut(index) {
244 *e = value as f32;
245 }
246 }
247 Self::F64(v) => {
248 if let Some(e) = v.get_mut(index) {
249 *e = value;
250 }
251 }
252 }
253 }
254}
255
256#[derive(Debug, Clone)]
258pub struct NDDimension {
259 pub size: usize,
260 pub offset: usize,
261 pub binning: usize,
262 pub reverse: bool,
263}
264
265impl NDDimension {
266 pub fn new(size: usize) -> Self {
267 Self {
268 size,
269 offset: 0,
270 binning: 1,
271 reverse: false,
272 }
273 }
274}
275
276#[derive(Debug, Clone)]
278pub struct NDArrayInfo {
279 pub total_bytes: usize,
280 pub bytes_per_element: usize,
281 pub num_elements: usize,
282 pub x_size: usize,
283 pub y_size: usize,
284 pub color_size: usize,
285 pub x_dim: usize,
287 pub y_dim: usize,
289 pub color_dim: usize,
291 pub x_stride: usize,
293 pub y_stride: usize,
295 pub color_stride: usize,
297 pub color_mode: crate::color::NDColorMode,
299}
300
301#[derive(Debug, Clone)]
303pub struct NDArray {
304 pub unique_id: i32,
305 pub timestamp: EpicsTimestamp,
306 pub time_stamp: f64,
308 pub dims: Vec<NDDimension>,
309 pub data: NDDataBuffer,
310 pub attributes: NDAttributeList,
311 pub codec: Option<Codec>,
312 pub pool_id: u64,
316 pub data_size: usize,
320}
321
322impl NDArray {
323 pub fn new(dims: Vec<NDDimension>, data_type: NDDataType) -> Self {
325 let num_elements: usize = if dims.is_empty() {
326 0
327 } else {
328 dims.iter().map(|d| d.size).product()
329 };
330 Self {
331 unique_id: 0,
332 timestamp: EpicsTimestamp::default(),
333 time_stamp: 0.0,
334 dims,
335 data: NDDataBuffer::zeros(data_type, num_elements),
336 attributes: NDAttributeList::new(),
337 codec: None,
338 pool_id: 0,
339 data_size: num_elements * data_type.element_size(),
340 }
341 }
342
343 pub fn with_data(dims: Vec<NDDimension>, data: NDDataBuffer) -> Self {
349 let data_size = data.len() * data.data_type().element_size();
350 Self {
351 unique_id: 0,
352 timestamp: EpicsTimestamp::default(),
353 time_stamp: 0.0,
354 dims,
355 data,
356 attributes: NDAttributeList::new(),
357 codec: None,
358 pool_id: 0,
359 data_size,
360 }
361 }
362
363 pub fn info(&self) -> NDArrayInfo {
368 use crate::color::NDColorMode;
369
370 let bytes_per_element = self.data.data_type().element_size();
371 let num_elements = self.data.len();
372 let total_bytes = num_elements * bytes_per_element;
373
374 let ndims = self.dims.len();
375
376 let color_mode = self
378 .attributes
379 .get("ColorMode")
380 .and_then(|a| a.value.as_i64())
381 .map(|v| NDColorMode::from_i32(v as i32))
382 .unwrap_or(NDColorMode::Mono);
383
384 let (x_size, y_size, color_size, x_dim, y_dim, color_dim, x_stride, y_stride, color_stride) =
385 match ndims {
386 0 => (0, 0, 0, 0, 0, 0, 0, 0, 0),
390 1 => (self.dims[0].size, 0, 0, 0, 0, 0, 1, 0, 0),
391 2 => {
392 let xs = self.dims[0].size;
393 let ys = self.dims[1].size;
394 (xs, ys, 0, 0, 1, 0, 1, xs, 0)
396 }
397 3 => {
398 match color_mode {
400 NDColorMode::RGB1 => {
401 let cs = self.dims[0].size;
403 let xs = self.dims[1].size;
404 let ys = self.dims[2].size;
405 (xs, ys, cs, 1, 2, 0, cs, xs * cs, 1)
406 }
407 NDColorMode::RGB2 => {
408 let xs = self.dims[0].size;
410 let cs = self.dims[1].size;
411 let ys = self.dims[2].size;
412 (xs, ys, cs, 0, 2, 1, 1, xs * cs, xs)
413 }
414 NDColorMode::RGB3 => {
415 let xs = self.dims[0].size;
417 let ys = self.dims[1].size;
418 let cs = self.dims[2].size;
419 (xs, ys, cs, 0, 1, 2, 1, xs, xs * ys)
420 }
421 _ => {
422 let xs = self.dims[0].size;
431 let ys = self.dims[1].size;
432 let cs = self.dims[2].size;
433 (xs, ys, cs, 0, 1, 2, 1, xs, xs * ys)
434 }
435 }
436 }
437 _ => {
442 let xs = self.dims[0].size;
443 let ys = self.dims[1].size;
444 (xs, ys, 0, 0, 1, 0, 1, xs, 0)
445 }
446 };
447
448 NDArrayInfo {
449 total_bytes,
450 bytes_per_element,
451 num_elements,
452 x_size,
453 y_size,
454 color_size,
455 x_dim,
456 y_dim,
457 color_dim,
458 x_stride,
459 y_stride,
460 color_stride,
461 color_mode,
462 }
463 }
464
465 pub fn report(&self, details: i32) -> String {
469 let mut out = String::new();
470 out.push('\n');
471 out.push_str("NDArray:\n");
472 let dim_sizes: Vec<String> = self.dims.iter().map(|d| d.size.to_string()).collect();
473 out.push_str(&format!(
474 " ndims={} dims=[{}]\n",
475 self.dims.len(),
476 dim_sizes.join(" ")
477 ));
478 out.push_str(&format!(
479 " dataType={:?}, dataSize={}, numElements={}\n",
480 self.data.data_type(),
481 self.data_size,
482 self.data.len()
483 ));
484 out.push_str(&format!(
485 " uniqueId={}, timeStamp={}, epicsTS.secPastEpoch={}, epicsTS.nsec={}\n",
486 self.unique_id, self.time_stamp, self.timestamp.sec, self.timestamp.nsec
487 ));
488 out.push_str(&format!(" poolId={}\n", self.pool_id));
489 match &self.codec {
490 Some(c) => out.push_str(&format!(
491 " codec={:?}, compressedSize={}\n",
492 c.name, c.compressed_size
493 )),
494 None => out.push_str(" codec=none\n"),
495 }
496 out.push_str(&format!(
497 " number of attributes={}\n",
498 self.attributes.len()
499 ));
500 if details > 5 {
501 for attr in self.attributes.iter() {
502 out.push_str(&format!(
503 " attribute name={}, value={}, source={:?}\n",
504 attr.name,
505 attr.value.as_string(),
506 attr.source
507 ));
508 }
509 }
510 out
511 }
512
513 pub fn validate(&self) -> ADResult<()> {
515 let expected: usize = if self.dims.is_empty() {
516 0
517 } else {
518 self.dims.iter().map(|d| d.size).product()
519 };
520 if self.data.len() != expected {
521 return Err(ADError::BufferSizeMismatch {
522 expected,
523 actual: self.data.len(),
524 });
525 }
526 Ok(())
527 }
528}
529
530#[cfg(test)]
531mod tests {
532 use super::*;
533
534 #[test]
535 fn test_element_size_all_types() {
536 assert_eq!(NDDataType::Int8.element_size(), 1);
537 assert_eq!(NDDataType::UInt8.element_size(), 1);
538 assert_eq!(NDDataType::Int16.element_size(), 2);
539 assert_eq!(NDDataType::UInt16.element_size(), 2);
540 assert_eq!(NDDataType::Int32.element_size(), 4);
541 assert_eq!(NDDataType::UInt32.element_size(), 4);
542 assert_eq!(NDDataType::Int64.element_size(), 8);
543 assert_eq!(NDDataType::UInt64.element_size(), 8);
544 assert_eq!(NDDataType::Float32.element_size(), 4);
545 assert_eq!(NDDataType::Float64.element_size(), 8);
546 }
547
548 #[test]
549 fn test_from_ordinal_roundtrip() {
550 for i in 0..10u8 {
551 let dt = NDDataType::from_ordinal(i).unwrap();
552 assert_eq!(dt as u8, i);
553 }
554 assert!(NDDataType::from_ordinal(10).is_none());
555 }
556
557 #[test]
558 fn test_buffer_zeros_type_and_len() {
559 let buf = NDDataBuffer::zeros(NDDataType::UInt16, 100);
560 assert_eq!(buf.data_type(), NDDataType::UInt16);
561 assert_eq!(buf.len(), 100);
562 assert_eq!(buf.total_bytes(), 200);
563 }
564
565 #[test]
566 fn test_buffer_zeros_all_types() {
567 for i in 0..10u8 {
568 let dt = NDDataType::from_ordinal(i).unwrap();
569 let buf = NDDataBuffer::zeros(dt, 10);
570 assert_eq!(buf.data_type(), dt);
571 assert_eq!(buf.len(), 10);
572 assert_eq!(buf.total_bytes(), 10 * dt.element_size());
573 }
574 }
575
576 #[test]
577 fn test_buffer_as_u8_slice() {
578 let buf = NDDataBuffer::U8(vec![1, 2, 3]);
579 assert_eq!(buf.as_u8_slice(), &[1, 2, 3]);
580 }
581
582 #[test]
583 fn test_ndarray_new_allocates() {
584 let dims = vec![NDDimension::new(256), NDDimension::new(256)];
585 let arr = NDArray::new(dims, NDDataType::UInt8);
586 assert_eq!(arr.data.len(), 256 * 256);
587 assert_eq!(arr.data.data_type(), NDDataType::UInt8);
588 }
589
590 #[test]
591 fn test_ndarray_validate_ok() {
592 let dims = vec![NDDimension::new(10), NDDimension::new(20)];
593 let arr = NDArray::new(dims, NDDataType::Float64);
594 arr.validate().unwrap();
595 }
596
597 #[test]
598 fn test_ndarray_validate_mismatch() {
599 let mut arr = NDArray::new(
600 vec![NDDimension::new(10), NDDimension::new(20)],
601 NDDataType::UInt8,
602 );
603 arr.data = NDDataBuffer::U8(vec![0; 100]);
604 assert!(arr.validate().is_err());
605 }
606
607 #[test]
608 fn test_ndarray_info_2d_mono() {
609 let dims = vec![NDDimension::new(640), NDDimension::new(480)];
610 let arr = NDArray::new(dims, NDDataType::UInt16);
611 let info = arr.info();
612 assert_eq!(info.x_size, 640);
613 assert_eq!(info.y_size, 480);
614 assert_eq!(info.color_size, 0);
616 assert_eq!(info.num_elements, 640 * 480);
617 assert_eq!(info.bytes_per_element, 2);
618 assert_eq!(info.total_bytes, 640 * 480 * 2);
619 }
620
621 #[test]
622 fn test_ndarray_info_3d_rgb() {
623 use crate::attributes::{NDAttrSource, NDAttrValue, NDAttribute};
624 use crate::color::NDColorMode;
625
626 let dims = vec![
628 NDDimension::new(3),
629 NDDimension::new(640),
630 NDDimension::new(480),
631 ];
632 let arr = NDArray::new(dims, NDDataType::UInt8);
633 let info = arr.info();
634 assert_eq!(info.x_size, 3);
635 assert_eq!(info.y_size, 640);
636 assert_eq!(info.color_size, 480);
637
638 let dims = vec![
640 NDDimension::new(3),
641 NDDimension::new(640),
642 NDDimension::new(480),
643 ];
644 let mut arr = NDArray::new(dims, NDDataType::UInt8);
645 arr.attributes.add(NDAttribute {
646 name: "ColorMode".into(),
647 description: "Color Mode".into(),
648 source: NDAttrSource::Driver,
649 value: NDAttrValue::Int32(NDColorMode::RGB1 as i32),
650 source_impl: None,
651 });
652 let info = arr.info();
653 assert_eq!(info.color_size, 3);
654 assert_eq!(info.x_size, 640);
655 assert_eq!(info.y_size, 480);
656 assert_eq!(info.x_dim, 1);
657 assert_eq!(info.y_dim, 2);
658 assert_eq!(info.color_dim, 0);
659 assert_eq!(info.num_elements, 3 * 640 * 480);
660 }
661
662 #[test]
663 fn test_ndarray_info_1d() {
664 let dims = vec![NDDimension::new(1024)];
665 let arr = NDArray::new(dims, NDDataType::Float64);
666 let info = arr.info();
667 assert_eq!(info.x_size, 1024);
668 assert_eq!(info.y_size, 0);
670 assert_eq!(info.color_size, 0);
671 }
672
673 #[test]
674 fn test_ndarray_info_4d_not_color() {
675 let dims = vec![
679 NDDimension::new(8),
680 NDDimension::new(640),
681 NDDimension::new(480),
682 NDDimension::new(5),
683 ];
684 let arr = NDArray::new(dims, NDDataType::UInt8);
685 let info = arr.info();
686 assert_eq!(info.x_size, 8);
687 assert_eq!(info.y_size, 640);
688 assert_eq!(info.color_size, 0, "4-D array must not get a color size");
689 assert_eq!(info.x_dim, 0);
690 assert_eq!(info.y_dim, 1);
691 assert_eq!(info.color_dim, 0);
692 assert_eq!(info.x_stride, 1);
693 assert_eq!(info.y_stride, 8);
694 assert_eq!(info.color_stride, 0);
695 assert_eq!(info.num_elements, 8 * 640 * 480 * 5);
696 }
697
698 #[test]
699 fn test_buffer_is_empty() {
700 let buf = NDDataBuffer::zeros(NDDataType::UInt8, 0);
701 assert!(buf.is_empty());
702 let buf2 = NDDataBuffer::zeros(NDDataType::UInt8, 1);
703 assert!(!buf2.is_empty());
704 }
705
706 #[test]
707 fn test_codec_field_preserved() {
708 let mut arr = NDArray::new(vec![NDDimension::new(10)], NDDataType::UInt8);
709 arr.codec = Some(Codec {
710 name: crate::codec::CodecName::JPEG,
711 compressed_size: 42,
712 level: 0,
713 shuffle: 0,
714 compressor: 0,
715 });
716 let cloned = arr.clone();
717 assert_eq!(cloned.codec.as_ref().unwrap().compressed_size, 42);
718 }
719}