Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build-main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Set up Java
uses: actions/setup-java@v4
uses: actions/setup-java@v5
with:
java-version: '8'
distribution: 'zulu'
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/build-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Set up Java
uses: actions/setup-java@v4
uses: actions/setup-java@v5
with:
java-version: '8'
distribution: 'zulu'
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/platform-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Set up Java
uses: actions/setup-java@v4
uses: actions/setup-java@v5
with:
java-version: '8'
distribution: 'zulu'
Expand Down
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>org.scijava</groupId>
<artifactId>pom-scijava</artifactId>
<version>40.0.0</version>
<version>43.0.0</version>
<relativePath />
</parent>

Expand Down Expand Up @@ -139,7 +139,7 @@

<!-- NB: Deploy releases to the SciJava Maven repository. -->
<releaseProfiles>sign,deploy-to-scijava</releaseProfiles>
<n5.version>4.0.0-alpha-9</n5.version>
<n5.version>4.0.0-alpha-12</n5.version>
</properties>

<dependencies>
Expand Down
14 changes: 7 additions & 7 deletions src/main/java/org/janelia/saalfeldlab/n5/hdf5/N5HDF5Reader.java
Original file line number Diff line number Diff line change
Expand Up @@ -689,7 +689,7 @@ public DatasetAttributes getDatasetAttributes(String pathName) {
}

@Override
public DataBlock<?> readBlock(
public <T> DataBlock<T> readChunk(
String pathName,
final DatasetAttributes datasetAttributes,
final long... gridPosition) throws N5Exception {
Expand All @@ -713,7 +713,7 @@ public DataBlock<?> readBlock(
if (datasetAttributes.getDataType() == DataType.STRING) {
final int[] intHdf5CroppedBlockSize = Arrays.stream(hdf5CroppedBlockSize).mapToInt(i -> (int)i).toArray();
MDArray<String> data = reader.string().readMDArrayBlockWithOffset(normalizedPathName, intHdf5CroppedBlockSize, hdf5Offset);
return new StringDataBlock(croppedBlockSize, gridPosition, data.getAsFlatArray());
return (DataBlock<T>)new StringDataBlock(croppedBlockSize, gridPosition, data.getAsFlatArray());
}

final DataType dataType = datasetAttributes.getDataType();
Expand All @@ -733,21 +733,21 @@ public DataBlock<?> readBlock(
H5Sclose(fileSpaceId);
H5Sclose(memorySpaceId);
}
return block;
return (DataBlock<T>)block;
}

@Override
public DataBlock<?> readShard(
public <T> DataBlock<T> readBlock(
final String pathName,
final DatasetAttributes datasetAttributes,
final long... gridPosition) throws N5Exception {

// HDF5 does not support sharding, so readShard is always equivalent to readBlock
return readBlock(pathName, datasetAttributes, gridPosition);
// HDF5 does not support sharding, so readBlock is always equivalent to readChunk
return (DataBlock<T>)readChunk(pathName, datasetAttributes, gridPosition);
}

@Override
public boolean shardExists(String pathName, DatasetAttributes datasetAttributes, long... gridPosition) throws N5Exception {
public boolean blockExists(String pathName, DatasetAttributes datasetAttributes, long... gridPosition) throws N5Exception {

return true;
}
Expand Down
82 changes: 73 additions & 9 deletions src/main/java/org/janelia/saalfeldlab/n5/hdf5/N5HDF5Writer.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@
import org.janelia.saalfeldlab.n5.N5Writer;
import org.janelia.saalfeldlab.n5.RawCompression;
import org.janelia.saalfeldlab.n5.hdf5.N5HDF5Util.OpenDataSetCache.OpenDataSet;
import org.janelia.saalfeldlab.n5.shard.Nesting.NestedGrid;
import org.janelia.saalfeldlab.n5.shard.Nesting.NestedPosition;
import org.janelia.saalfeldlab.n5.shard.Region;

import java.io.File;
import java.io.IOException;
Expand All @@ -55,6 +58,8 @@
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;

import static hdf.hdf5lib.H5.H5Dget_space;
import static hdf.hdf5lib.H5.H5Dwrite;
Expand Down Expand Up @@ -527,7 +532,7 @@ public boolean removeAttributes(
}

@Override
public <T> void writeBlock(
public <T> void writeChunk(
String pathName,
final DatasetAttributes datasetAttributes,
final DataBlock<T> dataBlock) throws N5Exception {
Expand Down Expand Up @@ -555,29 +560,74 @@ public <T> void writeBlock(
}
}

public <T> void writeShard(
@Override
public <T> void writeBlock(
final String pathName,
final DatasetAttributes datasetAttributes,
final DataBlock<T> dataBlock) throws N5Exception {

// HDF5 does not support sharding, so readShard is always equivalent to readBlock
writeBlock(pathName, datasetAttributes, dataBlock);
// HDF5 does not support sharding, so writeBlock is always equivalent to writeChunk
writeChunk(pathName, datasetAttributes, dataBlock);
}

@Override
public boolean deleteBlock(String pathName, final long... gridPosition) throws N5Exception {
public <T> void writeRegion(
String datasetPath,
DatasetAttributes datasetAttributes,
long[] min,
long[] size,
DataBlockSupplier<T> dataBlocks,
boolean writeFully) throws N5Exception {

final NestedGrid grid = datasetAttributes.getNestedBlockGrid();
final Region region = new Region(min, size, grid);
for (long[] key : Region.gridPositions(region.minPos().key(), region.maxPos().key())) {
final NestedPosition pos = grid.nestedPosition(key, 0); // HDF5 is never nested, get level 0
final long[] gridPosition = pos.absolute(0);
final DataBlock<T> existingDataBlock = writeFully || region.fullyContains(pos)
? null
: readBlock(datasetPath, datasetAttributes, gridPosition);
final DataBlock<T> dataBlock = dataBlocks.get(gridPosition, existingDataBlock);
// null blocks may be provided when they contain only the fill value
// and only non-empty blocks should be written, for example
if (dataBlock == null) {
deleteBlock(datasetPath, datasetAttributes, gridPosition);
} else {
writeBlock(datasetPath, datasetAttributes, dataBlock);
}
}

}

public <T> void writeRegion(
String datasetPath,
DatasetAttributes datasetAttributes,
long[] min,
long[] size,
DataBlockSupplier<T> dataBlocks,
boolean writeFully,
ExecutorService exec) throws N5Exception, InterruptedException, ExecutionException {

// block until the write is complete
exec.submit(() -> {
writeRegion(datasetPath, datasetAttributes, min, size, dataBlocks, writeFully);
}).get();
}

@Override
public boolean deleteChunk(String pathName, final long... gridPosition) throws N5Exception {

if (pathName.equals(""))
pathName = "/";

final DatasetAttributes datasetAttributes = getDatasetAttributes(pathName);
return deleteBlock(pathName, datasetAttributes, gridPosition);
return deleteChunk(pathName, datasetAttributes, gridPosition);
}

@Override
public boolean deleteBlock(String datasetPath, DatasetAttributes datasetAttributes, long... gridPosition) throws N5Exception {
public boolean deleteChunk(String datasetPath, DatasetAttributes datasetAttributes, long... gridPosition) throws N5Exception {

// deletion is not supported in HDF5, so the block is overwritten with zeros instead
// deletion is not supported in HDF5, so the chunk is overwritten with zeros instead
// Consider using defaultValue instead of zero?

if (datasetPath.equals(""))
Expand All @@ -597,13 +647,27 @@ public boolean deleteBlock(String datasetPath, DatasetAttributes datasetAttribut
case FLOAT32:
case FLOAT64:
final DataBlock<?> empty = dataType.createDataBlock(datasetAttributes.getBlockSize(), gridPosition);
writeBlock(datasetPath, datasetAttributes, empty);
writeChunk(datasetPath, datasetAttributes, empty);
return true;
default:
return false;
}
}

@Override
public boolean deleteBlock(String pathName, final long... gridPosition) throws N5Exception {

// HDF5 does not support sharding, so deleteBlock is always equivalent to deleteChunk
return deleteChunk(pathName, gridPosition);
}

@Override
public boolean deleteBlock(String datasetPath, DatasetAttributes datasetAttributes, long... gridPosition) throws N5Exception {

// HDF5 does not support sharding, so deleteBlock is always equivalent to deleteChunk
return deleteChunk(datasetPath, datasetAttributes, gridPosition);
}

@Override
public boolean remove() {

Expand Down
28 changes: 17 additions & 11 deletions src/test/java/org/janelia/saalfeldlab/n5/hdf5/N5HDF5Test.java
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,12 @@ public void testMode1WriteReadByteBlock() {
public void testWriteReadSerializableBlock() {
}

@Override
@Test
@Ignore("HDF5 does not support modifying dataset attributes after creating the dataset.")
public void testDatasetAttributes() {
}

@Override
@Test
@Ignore("Writing blocks larger than the dimensions of the dataset is invalid for HDF5.")
Expand Down Expand Up @@ -522,30 +528,30 @@ public void testDelete() {
final long[] position1 = {0, 0, 0};
final long[] position2 = {0, 1, 2};

// non-existant block should be zeros
final DataBlock<?> emptyBlock = n5.readBlock(datasetName, attributes, position1);
// non-existant chunk should be zeros
final DataBlock<?> emptyBlock = n5.readChunk(datasetName, attributes, position1);
assertTrue(emptyBlock instanceof ByteArrayDataBlock);
final byte[] zeros = new byte[byteBlock.length];
assertArrayEquals(zeros, ((ByteArrayDataBlock)emptyBlock).getData());

final ByteArrayDataBlock dataBlock = new ByteArrayDataBlock(blockSize, position1, byteBlock);
n5.writeBlock(datasetName, attributes, dataBlock);
n5.writeChunk(datasetName, attributes, dataBlock);

// block should exist at position1 but not at position2
final DataBlock<?> readBlock = n5.readBlock(datasetName, attributes, position1);
// chunk should exist at position1 but not at position2
final DataBlock<?> readBlock = n5.readChunk(datasetName, attributes, position1);
assertNotNull(readBlock);
assertTrue(readBlock instanceof ByteArrayDataBlock);
assertArrayEquals(byteBlock, ((ByteArrayDataBlock)readBlock).getData());

assertTrue("deleting existing block should return true", n5.deleteBlock(datasetName, position1));
assertTrue("hdf5 returns true even on non-existent blocks, since they can only be zeroed out", n5.deleteBlock(datasetName, position1));
assertTrue("hdf5 returns true even on non-existent blocks, since they can only be zeroed out", n5.deleteBlock(datasetName, position2));
assertTrue("deleting existing chunk should return true", n5.deleteChunk(datasetName, position1));
assertTrue("hdf5 returns true even on non-existent chunks, since they can only be zeroed out", n5.deleteChunk(datasetName, position1));
assertTrue("hdf5 returns true even on non-existent chunks, since they can only be zeroed out", n5.deleteChunk(datasetName, position2));

// no block should exist anymore
final DataBlock<?> pos1EmptyBlock = n5.readBlock(datasetName, attributes, position1);
// no chunk should exist anymore
final DataBlock<?> pos1EmptyBlock = n5.readChunk(datasetName, attributes, position1);
assertArrayEquals(zeros, ((ByteArrayDataBlock)pos1EmptyBlock).getData());

final DataBlock<?> pos2EmptyBlock = n5.readBlock(datasetName, attributes, position2);
final DataBlock<?> pos2EmptyBlock = n5.readChunk(datasetName, attributes, position2);
assertArrayEquals(zeros, ((ByteArrayDataBlock)pos2EmptyBlock).getData());
}
}
Expand Down
Loading