@@ -387,7 +387,7 @@ public boolean deleteBlock(final PositionValueAccess pva, final long[] gridPosit
387387 throw new N5Exception ("The shard at " + Arrays .toString (key ) + " could not be deleted." , e );
388388 }
389389 } else {
390- final ReadData existingData = pva .get (key );
390+ final ReadData existingData = pva .get (key ); // TODO: use getExistingReadData() instead !?
391391 final ReadData modifiedData = deleteBlockRecursive (existingData , position , grid .numLevels () - 1 );
392392 if (existingData != null && modifiedData == null ) {
393393 return pva .remove (key );
@@ -438,6 +438,115 @@ private ReadData deleteBlockRecursive(
438438 }
439439 }
440440
441+ //
442+ // -- deleteBlocks --------------------------------------------------------
443+
444+ @ Override
445+ public boolean deleteBlocks (final PositionValueAccess pva , final List <long []> gridPositions ) throws N5IOException {
446+
447+ boolean deleted = false ;
448+
449+ // for non-sharded datasets, just delete the blocks individually
450+ if (grid .numLevels () == 1 ) {
451+ for (long [] pos : gridPositions ) {
452+ deleted |= deleteBlock (pva , pos );
453+ }
454+ return deleted ;
455+ }
456+
457+ // Create a list of DataBlockRequests and sort it such that requests
458+ // from the same (nested) shard are grouped contiguously.
459+ // Despite the name, createReadRequests() works for delete requests as well ...
460+ final DataBlockRequests <T > requests = createReadRequests (gridPositions );
461+ requests .removeDuplicates ();
462+
463+ final List <DataBlockRequests <T >> split = requests .split ();
464+ for (final DataBlockRequests <T > subRequests : split ) {
465+ final boolean writeFully = subRequests .coversShard ();
466+ final long [] key = subRequests .relativeGridPosition ();
467+ final ReadData existingData = writeFully ? null : getExistingReadData (pva , key );
468+ final ReadData modifiedData = deleteBlocksRecursive (existingData , subRequests );
469+ if (existingData != null && modifiedData == null ) {
470+ deleted |= pva .remove (key );
471+ } else if (modifiedData != existingData ) {
472+ pva .put (key , modifiedData );
473+ deleted = true ;
474+ }
475+ }
476+
477+ return deleted ;
478+ }
479+
480+ /**
481+ * Bulk Delete operation on a shard.
482+ *
483+ * @param existingReadData encoded existing shard data (to decode and partially override)
484+ * @param requests for blocks within the shard to be deleted
485+ */
486+ private ReadData deleteBlocksRecursive (
487+ final ReadData existingReadData , // may be null
488+ final DataBlockRequests <T > requests
489+ ) {
490+ assert !requests .requests .isEmpty ();
491+ assert requests .level > 0 ;
492+
493+ if (existingReadData == null ) {
494+ return null ;
495+ }
496+
497+ final int level = requests .level ();
498+ @ SuppressWarnings ("unchecked" )
499+ final BlockCodec <RawShard > codec = (BlockCodec <RawShard >) codecs [level ];
500+ final long [] gridPos = requests .gridPosition ();
501+ final RawShard shard = codec .decode (existingReadData , gridPos ).getData ();
502+
503+ boolean modified = false ;
504+ boolean shardElementSetToNull = false ;
505+ if ( level == 1 ) {
506+ // Base case, delete the blocks
507+ for (final DataBlockRequest <T > request : requests ) {
508+ final long [] elementPos = request .position .relative (0 );
509+ if (shard .getElementData (elementPos ) != null ) {
510+ shard .setElementData (null , elementPos );
511+ modified = true ;
512+ shardElementSetToNull = true ;
513+ }
514+ }
515+ } else { // level > 1
516+ final List <DataBlockRequests <T >> split = requests .split ();
517+ for (final DataBlockRequests <T > subRequests : split ) {
518+ final boolean writeFully = subRequests .coversShard ();
519+ final long [] elementPos = subRequests .relativeGridPosition ();
520+ final ReadData existingElementData = writeFully ? null : shard .getElementData (elementPos );
521+ final ReadData modifiedElementData = deleteBlocksRecursive (existingElementData , subRequests );
522+ if (modifiedElementData != existingElementData ) {
523+ shard .setElementData (modifiedElementData , elementPos );
524+ modified = true ;
525+ shardElementSetToNull |= (modifiedElementData == null );
526+ }
527+ }
528+ }
529+
530+ if (!modified ) {
531+ // No nested shard or DataBlock was modified.
532+ // This shard remains unchanged.
533+ return existingReadData ;
534+ }
535+
536+ if (shardElementSetToNull ) {
537+ // At least one DataBlock or nested shard was removed.
538+ // Check whether this shard becomes empty.
539+ if (shard .index ().allElementsNull ()) {
540+ // This shard is empty and should be removed.
541+ return null ;
542+ }
543+ }
544+
545+ return codec .encode (new RawShardDataBlock (gridPos , shard ));
546+ }
547+
548+
549+
441550 //
442551 // -- readShard -----------------------------------------------------------
443552
0 commit comments