@@ -502,6 +502,89 @@ class BufferPrototype(NamedTuple):
502
502
nd_buffer : type [NDBuffer ]
503
503
504
504
505
+ class DelayedBuffer (Buffer ):
506
+ """
507
+ A Buffer that is the virtual concatenation of other buffers.
508
+ """
509
+ _BufferImpl : type
510
+ _concatenate : callable
511
+
512
+ def __init__ (self , array : NDArrayLike | list [NDArrayLike ] | None ) -> None :
513
+ if array is None :
514
+ self ._data_list = []
515
+ elif isinstance (array , list ):
516
+ self ._data_list = list (array )
517
+ else :
518
+ self ._data_list = [array ]
519
+ for array in self ._data_list :
520
+ if array .ndim != 1 :
521
+ raise ValueError ("array: only 1-dim allowed" )
522
+ if array .dtype != np .dtype ("b" ):
523
+ raise ValueError ("array: only byte dtype allowed" )
524
+
525
+ @property
526
+ def _data (self ) -> npt .NDArray [Any ]:
527
+ return type (self )._concatenate (self ._data_list )
528
+
529
+ @classmethod
530
+ def from_buffer (cls , buffer : Buffer ) -> Self :
531
+ if isinstance (buffer , cls ):
532
+ return cls (buffer ._data_list )
533
+ else :
534
+ return cls (buffer ._data )
535
+
536
+ def __add__ (self , other : Buffer ) -> Self :
537
+ if isinstance (other , self .__class__ ):
538
+ return self .__class__ (self ._data_list + other ._data_list )
539
+ else :
540
+ return self .__class__ (self ._data_list + [other ._data ])
541
+
542
+ def __radd__ (self , other : Buffer ) -> Self :
543
+ if isinstance (other , self .__class__ ):
544
+ return self .__class__ (other ._data_list + self ._data_list )
545
+ else :
546
+ return self .__class__ ([other ._data ] + self ._data_list )
547
+
548
+ def __len__ (self ) -> int :
549
+ return sum (map (len , self ._data_list ))
550
+
551
+ def __getitem__ (self , key : slice ) -> Self :
552
+ check_item_key_is_1d_contiguous (key )
553
+ start , stop = key .start , key .stop
554
+ if start is None :
555
+ start = 0
556
+ if stop is None :
557
+ stop = len (self )
558
+ new_list = []
559
+ offset = 0
560
+ found_last = False
561
+ for chunk in self ._data_list :
562
+ chunk_size = len (chunk )
563
+ skip = False
564
+ if offset <= start < offset + chunk_size :
565
+ # first chunk
566
+ if stop <= offset + chunk_size :
567
+ # also last chunk
568
+ chunk = chunk [start - offset :stop - offset ]
569
+ found_last = True
570
+ else :
571
+ chunk = chunk [start - offset :]
572
+ elif offset <= stop <= offset + chunk_size :
573
+ # last chunk
574
+ chunk = chunk [:stop - offset ]
575
+ found_last = True
576
+ elif offset + chunk_size <= start :
577
+ skip = True
578
+
579
+ if not skip :
580
+ new_list .append (chunk )
581
+ if found_last :
582
+ break
583
+ offset += chunk_size
584
+ assert sum (map (len , new_list )) == stop - start
585
+ return self .__class__ (new_list )
586
+
587
+
505
588
# The default buffer prototype used throughout the Zarr codebase.
506
589
def default_buffer_prototype () -> BufferPrototype :
507
590
from zarr .registry import (
0 commit comments