1use std::collections::HashMap;
13
14#[derive(Debug, Clone, PartialEq, Eq)]
32pub struct TensorShape {
33 pub dims: Vec<usize>,
35}
36
37impl TensorShape {
38 #[must_use]
42 pub fn total_elements(&self) -> usize {
43 self.dims.iter().product()
44 }
45
46 #[must_use]
52 pub fn strides(&self) -> Vec<usize> {
53 let rank = self.dims.len();
54 let mut strides = vec![1usize; rank];
55 for i in (0..rank.saturating_sub(1)).rev() {
57 strides[i] = strides[i + 1] * self.dims[i + 1];
58 }
59 strides
60 }
61
62 #[must_use]
68 pub fn flat_index(&self, indices: &[usize]) -> Option<usize> {
69 if indices.len() != self.dims.len() {
70 return None;
71 }
72 let strides = self.strides();
73 let mut flat = 0usize;
74 for (i, (&idx, &dim)) in indices.iter().zip(self.dims.iter()).enumerate() {
75 if idx >= dim {
76 return None;
77 }
78 flat += idx * strides[i];
79 }
80 Some(flat)
81 }
82}
83
84#[derive(Debug, Clone, PartialEq, Eq)]
94pub struct SliceSpec {
95 pub start: Vec<usize>,
97 pub end: Vec<usize>,
99}
100
101impl SliceSpec {
102 #[must_use]
107 pub fn element_count(&self) -> usize {
108 self.start
109 .iter()
110 .zip(self.end.iter())
111 .map(|(&s, &e)| e.saturating_sub(s))
112 .product()
113 }
114
115 #[must_use]
123 pub fn overlaps(&self, other: &SliceSpec) -> bool {
124 if self.start.len() != other.start.len() {
125 return false;
126 }
127 self.start
128 .iter()
129 .zip(self.end.iter())
130 .zip(other.start.iter().zip(other.end.iter()))
131 .all(|((&s, &e), (&os, &oe))| s < oe && os < e)
132 }
133}
134
135#[derive(Debug, Clone)]
141pub struct TensorSlice {
142 pub name: String,
144 pub spec: SliceSpec,
146 pub data: Vec<f32>,
148 pub version: u64,
150 pub dirty: bool,
152}
153
154#[derive(Debug, Clone, PartialEq, Eq)]
160pub struct SliceManagerStats {
161 pub total_slices: usize,
163 pub dirty_slices: usize,
165 pub total_elements: usize,
167}
168
169impl SliceManagerStats {
170 #[must_use]
174 pub fn memory_bytes(&self) -> usize {
175 self.total_elements * 4
176 }
177}
178
179pub struct TensorSliceManager {
195 pub shape: TensorShape,
197 pub slices: HashMap<String, TensorSlice>,
199}
200
201impl TensorSliceManager {
202 #[must_use]
204 pub fn new(shape: TensorShape) -> Self {
205 Self {
206 shape,
207 slices: HashMap::new(),
208 }
209 }
210
211 pub fn create_slice(
220 &mut self,
221 name: String,
222 spec: SliceSpec,
223 data: Vec<f32>,
224 ) -> Result<(), String> {
225 if self.slices.contains_key(&name) {
226 return Err(format!("slice '{}' already exists", name));
227 }
228
229 let rank = self.shape.dims.len();
231 if spec.start.len() != rank || spec.end.len() != rank {
232 return Err(format!(
233 "spec rank {} does not match shape rank {}",
234 spec.start.len(),
235 rank
236 ));
237 }
238
239 for d in 0..rank {
241 if spec.end[d] > self.shape.dims[d] {
242 return Err(format!(
243 "spec.end[{d}] = {} exceeds shape.dims[{d}] = {}",
244 spec.end[d], self.shape.dims[d]
245 ));
246 }
247 if spec.start[d] > spec.end[d] {
248 return Err(format!(
249 "spec.start[{d}] = {} > spec.end[{d}] = {}",
250 spec.start[d], spec.end[d]
251 ));
252 }
253 }
254
255 let expected = spec.element_count();
257 if data.len() != expected {
258 return Err(format!(
259 "data.len() = {} but spec.element_count() = {}",
260 data.len(),
261 expected
262 ));
263 }
264
265 self.slices.insert(
266 name.clone(),
267 TensorSlice {
268 name,
269 spec,
270 data,
271 version: 0,
272 dirty: false,
273 },
274 );
275 Ok(())
276 }
277
278 pub fn write_slice(&mut self, name: &str, data: Vec<f32>) -> bool {
284 match self.slices.get_mut(name) {
285 Some(slice) if slice.data.len() == data.len() => {
286 slice.data = data;
287 slice.version += 1;
288 slice.dirty = true;
289 true
290 }
291 _ => false,
292 }
293 }
294
295 #[must_use]
297 pub fn read_slice(&self, name: &str) -> Option<&TensorSlice> {
298 self.slices.get(name)
299 }
300
301 pub fn flush_all(&mut self) {
306 for slice in self.slices.values_mut() {
307 slice.dirty = false;
308 }
309 }
310
311 #[must_use]
316 pub fn overlapping_slices<'a>(&'a self, spec: &SliceSpec) -> Vec<&'a str> {
317 self.slices
318 .values()
319 .filter(|s| s.spec.overlaps(spec))
320 .map(|s| s.name.as_str())
321 .collect()
322 }
323
324 pub fn remove_slice(&mut self, name: &str) -> bool {
328 self.slices.remove(name).is_some()
329 }
330
331 #[must_use]
333 pub fn stats(&self) -> SliceManagerStats {
334 let total_slices = self.slices.len();
335 let dirty_slices = self.slices.values().filter(|s| s.dirty).count();
336 let total_elements = self.slices.values().map(|s| s.data.len()).sum();
337 SliceManagerStats {
338 total_slices,
339 dirty_slices,
340 total_elements,
341 }
342 }
343}
344
345#[cfg(test)]
350mod tests {
351 use super::*;
352
353 fn shape_3d() -> TensorShape {
356 TensorShape {
357 dims: vec![4, 8, 16],
358 }
359 }
360
361 fn spec(start: Vec<usize>, end: Vec<usize>) -> SliceSpec {
362 SliceSpec { start, end }
363 }
364
365 fn make_data(n: usize, fill: f32) -> Vec<f32> {
366 vec![fill; n]
367 }
368
369 #[test]
372 fn test_total_elements() {
373 let s = TensorShape {
374 dims: vec![4, 8, 16],
375 };
376 assert_eq!(s.total_elements(), 512);
377 }
378
379 #[test]
380 fn test_total_elements_scalar() {
381 let s = TensorShape { dims: vec![] };
382 assert_eq!(s.total_elements(), 1);
383 }
384
385 #[test]
386 fn test_strides_3d() {
387 let s = shape_3d();
388 assert_eq!(s.strides(), vec![128, 16, 1]);
389 }
390
391 #[test]
392 fn test_strides_1d() {
393 let s = TensorShape { dims: vec![10] };
394 assert_eq!(s.strides(), vec![1]);
395 }
396
397 #[test]
398 fn test_strides_2d() {
399 let s = TensorShape { dims: vec![3, 5] };
400 assert_eq!(s.strides(), vec![5, 1]);
401 }
402
403 #[test]
404 fn test_flat_index_in_bounds() {
405 let s = shape_3d();
406 assert_eq!(s.flat_index(&[1, 2, 3]), Some(128 + 2 * 16 + 3));
407 assert_eq!(s.flat_index(&[0, 0, 0]), Some(0));
408 assert_eq!(s.flat_index(&[3, 7, 15]), Some(3 * 128 + 7 * 16 + 15));
409 }
410
411 #[test]
412 fn test_flat_index_out_of_bounds() {
413 let s = shape_3d();
414 assert_eq!(s.flat_index(&[4, 0, 0]), None);
416 assert_eq!(s.flat_index(&[0, 0, 16]), None);
418 assert_eq!(s.flat_index(&[0, 0]), None);
420 }
421
422 #[test]
425 fn test_element_count() {
426 let sp = spec(vec![1, 2, 4], vec![3, 6, 12]);
427 assert_eq!(sp.element_count(), 64);
429 }
430
431 #[test]
432 fn test_element_count_full_slice() {
433 let sp = spec(vec![0, 0, 0], vec![4, 8, 16]);
434 assert_eq!(sp.element_count(), 512);
435 }
436
437 #[test]
438 fn test_overlaps_true() {
439 let a = spec(vec![0, 0], vec![4, 4]);
440 let b = spec(vec![2, 2], vec![6, 6]);
441 assert!(a.overlaps(&b));
442 assert!(b.overlaps(&a));
443 }
444
445 #[test]
446 fn test_overlaps_false_adjacent() {
447 let a = spec(vec![0, 0], vec![4, 4]);
449 let b = spec(vec![4, 0], vec![8, 4]);
450 assert!(!a.overlaps(&b));
451 assert!(!b.overlaps(&a));
452 }
453
454 #[test]
455 fn test_overlaps_false_separated() {
456 let a = spec(vec![0, 0], vec![2, 2]);
457 let b = spec(vec![5, 5], vec![8, 8]);
458 assert!(!a.overlaps(&b));
459 }
460
461 #[test]
462 fn test_overlaps_contained() {
463 let outer = spec(vec![0, 0], vec![8, 8]);
464 let inner = spec(vec![2, 2], vec![4, 4]);
465 assert!(outer.overlaps(&inner));
466 assert!(inner.overlaps(&outer));
467 }
468
469 #[test]
472 fn test_create_slice_success() {
473 let mut mgr = TensorSliceManager::new(shape_3d());
474 let sp = spec(vec![0, 0, 0], vec![2, 4, 8]);
475 let n = sp.element_count(); let result = mgr.create_slice("a".to_string(), sp, make_data(n, 1.0));
477 assert!(result.is_ok());
478 assert!(mgr.read_slice("a").is_some());
479 }
480
481 #[test]
482 fn test_create_slice_duplicate_name_error() {
483 let mut mgr = TensorSliceManager::new(shape_3d());
484 let sp = spec(vec![0, 0, 0], vec![1, 1, 1]);
485 mgr.create_slice("s".to_string(), sp.clone(), make_data(1, 0.0))
486 .expect("test: should succeed");
487 let err = mgr.create_slice("s".to_string(), sp, make_data(1, 0.0));
488 assert!(err.is_err());
489 let msg = err.unwrap_err();
490 assert!(msg.contains("already exists"), "unexpected message: {msg}");
491 }
492
493 #[test]
494 fn test_create_slice_bounds_check_error() {
495 let mut mgr = TensorSliceManager::new(shape_3d());
496 let sp = spec(vec![0, 0, 0], vec![1, 1, 17]);
498 let err = mgr.create_slice("bad".to_string(), sp, make_data(17, 0.0));
499 assert!(err.is_err());
500 }
501
502 #[test]
503 fn test_create_slice_wrong_data_length_error() {
504 let mut mgr = TensorSliceManager::new(shape_3d());
505 let sp = spec(vec![0, 0, 0], vec![2, 2, 2]); let err = mgr.create_slice("bad".to_string(), sp, make_data(5, 0.0)); assert!(err.is_err());
508 }
509
510 #[test]
513 fn test_write_slice_increments_version() {
514 let mut mgr = TensorSliceManager::new(shape_3d());
515 let sp = spec(vec![0, 0, 0], vec![1, 1, 4]);
516 mgr.create_slice("v".to_string(), sp, make_data(4, 0.0))
517 .expect("test: should succeed");
518
519 assert_eq!(
520 mgr.read_slice("v").expect("test: should succeed").version,
521 0
522 );
523
524 mgr.write_slice("v", make_data(4, 1.0));
525 assert_eq!(
526 mgr.read_slice("v").expect("test: should succeed").version,
527 1
528 );
529
530 mgr.write_slice("v", make_data(4, 2.0));
531 assert_eq!(
532 mgr.read_slice("v").expect("test: should succeed").version,
533 2
534 );
535 }
536
537 #[test]
538 fn test_write_slice_sets_dirty_flag() {
539 let mut mgr = TensorSliceManager::new(shape_3d());
540 let sp = spec(vec![0, 0, 0], vec![1, 1, 4]);
541 mgr.create_slice("d".to_string(), sp, make_data(4, 0.0))
542 .expect("test: should succeed");
543
544 assert!(!mgr.read_slice("d").expect("test: should succeed").dirty);
545 mgr.write_slice("d", make_data(4, 9.0));
546 assert!(mgr.read_slice("d").expect("test: should succeed").dirty);
547 }
548
549 #[test]
550 fn test_write_slice_returns_false_missing() {
551 let mut mgr = TensorSliceManager::new(shape_3d());
552 assert!(!mgr.write_slice("nonexistent", make_data(4, 0.0)));
553 }
554
555 #[test]
556 fn test_write_slice_returns_false_wrong_length() {
557 let mut mgr = TensorSliceManager::new(shape_3d());
558 let sp = spec(vec![0, 0, 0], vec![1, 1, 4]); mgr.create_slice("w".to_string(), sp, make_data(4, 0.0))
560 .expect("test: should succeed");
561 assert!(!mgr.write_slice("w", make_data(5, 0.0)));
563 assert_eq!(
565 mgr.read_slice("w").expect("test: should succeed").version,
566 0
567 );
568 }
569
570 #[test]
573 fn test_flush_all_clears_dirty() {
574 let mut mgr = TensorSliceManager::new(shape_3d());
575 for name in ["x", "y", "z"] {
576 let sp = spec(vec![0, 0, 0], vec![1, 1, 2]);
577 mgr.create_slice(name.to_string(), sp, make_data(2, 0.0))
578 .expect("test: should succeed");
579 mgr.write_slice(name, make_data(2, 1.0));
580 }
581 assert_eq!(mgr.stats().dirty_slices, 3);
583
584 mgr.flush_all();
585
586 assert_eq!(mgr.stats().dirty_slices, 0);
587 for name in ["x", "y", "z"] {
588 assert!(!mgr.read_slice(name).expect("test: should succeed").dirty);
589 }
590 }
591
592 #[test]
595 fn test_overlapping_slices_correct() {
596 let shape = TensorShape { dims: vec![10, 10] };
597 let mut mgr = TensorSliceManager::new(shape);
598
599 let sa = spec(vec![0, 0], vec![4, 4]);
601 mgr.create_slice("A".to_string(), sa, make_data(16, 1.0))
602 .expect("test: should succeed");
603
604 let sb = spec(vec![3, 3], vec![7, 7]);
606 mgr.create_slice("B".to_string(), sb, make_data(16, 2.0))
607 .expect("test: should succeed");
608
609 let sc = spec(vec![6, 6], vec![10, 10]);
611 mgr.create_slice("C".to_string(), sc, make_data(16, 3.0))
612 .expect("test: should succeed");
613
614 let query = spec(vec![0, 0], vec![4, 4]);
616 let mut hits = mgr.overlapping_slices(&query);
617 hits.sort_unstable();
618 assert_eq!(hits, vec!["A", "B"]);
619 }
620
621 #[test]
622 fn test_non_overlapping_not_returned() {
623 let shape = TensorShape { dims: vec![10, 10] };
624 let mut mgr = TensorSliceManager::new(shape);
625
626 let sa = spec(vec![0, 0], vec![4, 4]);
627 mgr.create_slice("A".to_string(), sa, make_data(16, 0.0))
628 .expect("test: should succeed");
629
630 let query = spec(vec![6, 6], vec![10, 10]);
632 let hits = mgr.overlapping_slices(&query);
633 assert!(hits.is_empty());
634 }
635
636 #[test]
639 fn test_read_slice_returns_correct_data() {
640 let mut mgr = TensorSliceManager::new(shape_3d());
641 let sp = spec(vec![0, 0, 0], vec![1, 1, 4]);
642 let data = vec![1.0_f32, 2.0, 3.0, 4.0];
643 mgr.create_slice("r".to_string(), sp, data.clone())
644 .expect("test: should succeed");
645
646 let slice = mgr.read_slice("r").expect("slice must exist");
647 assert_eq!(slice.data, data);
648 assert_eq!(slice.name, "r");
649 }
650
651 #[test]
652 fn test_read_slice_missing_returns_none() {
653 let mgr = TensorSliceManager::new(shape_3d());
654 assert!(mgr.read_slice("nope").is_none());
655 }
656
657 #[test]
658 fn test_remove_slice_existing() {
659 let mut mgr = TensorSliceManager::new(shape_3d());
660 let sp = spec(vec![0, 0, 0], vec![1, 1, 1]);
661 mgr.create_slice("rm".to_string(), sp, make_data(1, 0.0))
662 .expect("test: should succeed");
663
664 assert!(mgr.remove_slice("rm"));
665 assert!(mgr.read_slice("rm").is_none());
666 }
667
668 #[test]
669 fn test_remove_slice_missing_returns_false() {
670 let mut mgr = TensorSliceManager::new(shape_3d());
671 assert!(!mgr.remove_slice("ghost"));
672 }
673
674 #[test]
677 fn test_stats_and_memory_bytes() {
678 let shape = TensorShape { dims: vec![2, 2] };
679 let mut mgr = TensorSliceManager::new(shape);
680
681 let sp1 = spec(vec![0, 0], vec![2, 2]); mgr.create_slice("p".to_string(), sp1, make_data(4, 0.0))
683 .expect("test: should succeed");
684
685 let sp2 = spec(vec![0, 0], vec![1, 2]); mgr.create_slice("q".to_string(), sp2, make_data(2, 0.0))
687 .expect("test: should succeed");
688
689 mgr.write_slice("p", make_data(4, 1.0));
691
692 let st = mgr.stats();
693 assert_eq!(st.total_slices, 2);
694 assert_eq!(st.dirty_slices, 1);
695 assert_eq!(st.total_elements, 6); assert_eq!(st.memory_bytes(), 24); }
698}