Skip to content
Closed
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
15 changes: 9 additions & 6 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
<parent>
<groupId>org.scijava</groupId>
<artifactId>pom-scijava</artifactId>
<version>43.0.0</version>
<version>44.0.0</version>
<relativePath />
</parent>

<groupId>org.janelia.saalfeldlab</groupId>
<artifactId>n5-zarr</artifactId>
<version>2.0.0-alpha-9-SNAPSHOT</version>
<version>2.0.0</version>

<name>N5 Zarr</name>
<description>Zarr filesystem backend for N5</description>
Expand Down Expand Up @@ -131,17 +131,20 @@
<license.organizationName>Saalfeld Lab</license.organizationName>
<license.copyrightOwners>Stephan Saalfeld</license.copyrightOwners>

<scijava.jvm.version>8</scijava.jvm.version>
<scijava.jvm.build.version>[1.8.0-101,)</scijava.jvm.build.version>

<doclint>none</doclint>

<!-- NB: Deploy releases to the SciJava Maven repository. -->
<releaseProfiles>sign,deploy-to-scijava</releaseProfiles>

<json-simple.version>1.1.1</json-simple.version>

<n5.version>4.0.0-alpha-12</n5.version>
<n5-blosc.version>2.0.0-alpha-4</n5-blosc.version>
<n5-imglib2.version>7.1.0-alpha-8</n5-imglib2.version>
<n5-zstandard.version>2.0.0-alpha-4</n5-zstandard.version>
<n5.version>4.0.0</n5.version>
<n5-blosc.version>2.0.0</n5-blosc.version>
<n5-imglib2.version>8.0.0</n5-imglib2.version>
<n5-zstandard.version>2.0.0</n5-zstandard.version>
</properties>

<dependencies>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,9 +217,6 @@ public Blosc(final BloscCompression compression)
case BloscCompression.BITSHUFFLE:
shuffle = "bitshuffle";
break;
case BloscCompression.AUTOSHUFFLE:
shuffle = typesize == 1 ? "bitshuffle" : "shuffle";
break;
default:
throw new N5Exception("Invalid shuffle: " + _shuffle);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -607,6 +607,11 @@ public static Builder builder(final long[] dimensions, final DataType dataType)
return new Builder(dimensions, dataType);
}

public static Builder builder(DatasetAttributes attributes) {

return new Builder(attributes);
}

/**
* Builder for constructing {@link ZarrV3DatasetAttributes} instances.
* <p>
Expand All @@ -623,7 +628,7 @@ public static Builder builder(final long[] dimensions, final DataType dataType)
* .build();
* }</pre>
*/
public static class Builder {
public static class Builder extends DatasetAttributes.Builder {

// Required parameters
private final long[] dimensions;
Expand All @@ -641,7 +646,7 @@ public static class Builder {
private DataCodecInfo[] shardIndexDataCodecInfos = new DataCodecInfo[0];

// For sharding
private int[] shardSize;
private int[] chunkSize;

/**
* Creates a new builder with the required parameters.
Expand All @@ -651,9 +656,11 @@ public static class Builder {
*/
public Builder(final long[] dimensions, final DataType dataType) {


super(dimensions, dataType);
this.dimensions = dimensions.clone();
this.dataType = ZarrV3DataType.fromDataType(dataType);
this.blockSize = Arrays.stream(dimensions).mapToInt(x -> (int)x).toArray();
this.blockSize = defaultChunkShape(dimensions);
}

/**
Expand All @@ -664,9 +671,24 @@ public Builder(final long[] dimensions, final DataType dataType) {
*/
public Builder(final long[] dimensions, final ZarrV3DataType dataType) {

super(dimensions, dataType.getDataType());
this.dimensions = dimensions.clone();
this.dataType = dataType;
this.blockSize = Arrays.stream(dimensions).mapToInt(x -> (int)x).toArray();
this.blockSize = defaultChunkShape(dimensions);
}

public Builder(final DatasetAttributes attributes) {

super( attributes.getDimensions(), attributes.getDataType());
this.dimensions = attributes.getDimensions();
this.dataType = ZarrV3DataType.fromDataType(attributes.getDataType());
dataCodecInfos(attributes.getDataCodecInfos());
if (attributes.isSharded()) {
chunkSize(attributes.getChunkSize());
blockSize(attributes.getBlockSize());
} else {
blockSize(attributes.getBlockSize());
}
}

/**
Expand All @@ -682,17 +704,38 @@ public Builder blockSize(final int[] blockSize) {
}

/**
* Sets the shard shape for sharded arrays.
* Sets the chunk shape for sharded arrays.
* When set, chunks will be grouped into shards of this size.
*
* @param shardShape the shard dimensions
* @param chunkSize the shard dimensions
* @return this builder
*/
public Builder shardSize(final int[] shardSize) {
public Builder chunkSize(final int[] chunkSize) {

this.shardSize = shardSize.clone();
validateLength("chunk size", chunkSize.length);
validateBlockChunkSize(blockSize, chunkSize);

this.chunkSize = chunkSize.clone();
return this;
}

private void validateBlockChunkSize( int[] blockSize, int[] chunkSize ) {

if (blockSize != null && chunkSize != null) {
for (int i = 0; i < chunkSize.length; i++) {
if (chunkSize[i] > blockSize[i])
throw new IllegalArgumentException(
String.format("index %d: chunk size (%d) is greater than block size (%d)",
i, chunkSize[i], blockSize[i]));
}
}
}

private void validateLength(final String name, final int length) {
if (length != dimensions.length)
throw new IllegalArgumentException(name + " must be same length as dimensions (" +
+dimensions.length + ") but is (" + length + ")");
}

/**
* Sets the fill value for uninitialized chunks.
Expand Down Expand Up @@ -751,7 +794,7 @@ public Builder chunkKeyEncoding(final DefaultChunkKeyEncoding chunkKeyEncoding)
public Builder compression(final Compression compression) {

if (compression != null && !(compression instanceof RawCompression)) {
this.dataCodecInfos = new DataCodecInfo[]{ZarrV3Compressor.fromCompression(compression)};
this.dataCodecInfos = new DataCodecInfo[]{compression};
}
return this;
}
Expand Down Expand Up @@ -821,28 +864,41 @@ public ZarrV3DatasetAttributes build() {
? chunkKeyEncoding
: new DefaultChunkKeyEncoding(dimensionSeparator);

final int[] resolvedBlockSize = blockSize != null
? blockSize
: defaultChunkShape(dimensions);

// Determine if sharding is enabled
if (shardSize != null) {
if (blockSize != null && chunkSize != null) {
// if both blockSize and chunkSize are specified, the dataset is sharded
final int[] resolvedChunkSize = chunkSize != null
? chunkSize
: defaultChunkShape(dimensions);

// Sharded configuration
final BlockCodecInfo resolvedBlockCodecInfo = blockCodecInfo != null
? blockCodecInfo
: defaultShardCodecInfo(resolvedBlockSize, dataCodecInfos, shardIndexDataCodecInfos);
: defaultShardCodecInfo(resolvedChunkSize, dataCodecInfos, shardIndexDataCodecInfos);

// For sharding, the outer chunk is the shard size
return new ZarrV3DatasetAttributes(
dimensions,
shardSize,
blockSize,
dataType,
fillValue,
resolvedDimensionNames,
resolvedChunkKeyEncoding,
resolvedBlockCodecInfo,
datasetCodecInfos);
} else {
// one or both of blockSize or chunkSize is null.
// either one sets the block size
final int[] resolvedBlockSize;
if( chunkSize != null )
resolvedBlockSize = chunkSize;
else if( blockSize != null )

resolvedBlockSize = blockSize;
else
resolvedBlockSize = defaultChunkShape(dimensions);

// Non-sharded configuration
final BlockCodecInfo resolvedBlockCodecInfo = blockCodecInfo != null
? blockCodecInfo
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package org.janelia.saalfeldlab.n5.zarr.v3;

import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

import org.janelia.saalfeldlab.n5.DataType;
import org.janelia.saalfeldlab.n5.GzipCompression;
import org.janelia.saalfeldlab.n5.RawCompression;
import org.janelia.saalfeldlab.n5.zarr.chunks.DefaultChunkKeyEncoding;
import org.junit.Test;

public class ZarrV3DatasetAttributesTest {

@Test
public void builderTests() {

final long[] dims = new long[]{100, 200, 300};
final int[] blk = new int[]{32, 32, 32};

// default blockSize uses defaultChunkShape, not full dimensions
final ZarrV3DatasetAttributes defaultBlk = ZarrV3DatasetAttributes.builder(dims, DataType.FLOAT32).build();
assertArrayEquals(ZarrV3DatasetAttributes.defaultChunkShape(dims), defaultBlk.getBlockSize());

// blockSize is reflected in output
final ZarrV3DatasetAttributes withBlk = ZarrV3DatasetAttributes.builder(dims, DataType.FLOAT32)
.blockSize(blk).build();
assertArrayEquals(blk, withBlk.getBlockSize());
assertFalse(withBlk.isSharded());

// fillValue is reflected
final ZarrV3DatasetAttributes withFill = ZarrV3DatasetAttributes.builder(dims, DataType.FLOAT32)
.blockSize(blk).fillValue("1.5").build();
assertEquals("1.5", withFill.getFillValue());

// dimensionNames are reflected
final String[] names = new String[]{"x", "y", "z"};
final ZarrV3DatasetAttributes withNames = ZarrV3DatasetAttributes.builder(dims, DataType.FLOAT32)
.blockSize(blk).dimensionNames(names).build();
assertArrayEquals(names, withNames.getDimensionNames());

// dimensionSeparator is reflected in chunk key encoding
final ZarrV3DatasetAttributes withSlash = ZarrV3DatasetAttributes.builder(dims, DataType.FLOAT32)
.blockSize(blk).dimensionSeparator("/").build();
assertTrue(withSlash.relativeBlockPath(1, 2, 3).contains("/"));

final ZarrV3DatasetAttributes withDot = ZarrV3DatasetAttributes.builder(dims, DataType.FLOAT32)
.blockSize(blk).dimensionSeparator(".").build();
assertTrue(withDot.relativeBlockPath(1, 2, 3).contains("."));

// chunkKeyEncoding overrides dimensionSeparator
final DefaultChunkKeyEncoding encoding = new DefaultChunkKeyEncoding(".");
final ZarrV3DatasetAttributes withEncoding = ZarrV3DatasetAttributes.builder(dims, DataType.FLOAT32)
.blockSize(blk).chunkKeyEncoding(encoding).build();
assertTrue(withEncoding.relativeBlockPath(1, 2, 3).contains("."));

// compression sets a data codec; RawCompression is a no-op
final ZarrV3DatasetAttributes withGzip = ZarrV3DatasetAttributes.builder(dims, DataType.FLOAT32)
.blockSize(blk).compression(new GzipCompression()).build();
assertEquals(1, withGzip.getDataCodecInfos().length);

final ZarrV3DatasetAttributes withRaw = ZarrV3DatasetAttributes.builder(dims, DataType.FLOAT32)
.blockSize(blk).compression(new RawCompression()).build();
assertEquals(0, withRaw.getDataCodecInfos().length);

// sharding: both blockSize (shard) and chunkSize (inner) set
final int[] shardSize = new int[]{64, 64, 64};
final int[] chunkSize = new int[]{16, 16, 16};
final ZarrV3DatasetAttributes sharded = ZarrV3DatasetAttributes.builder(dims, DataType.UINT16)
.blockSize(shardSize).chunkSize(chunkSize).build();
assertTrue(sharded.isSharded());
assertArrayEquals(shardSize, sharded.getBlockSize());
assertArrayEquals(chunkSize, sharded.getChunkSize());
assertNotNull(sharded.getBlockCodecInfo());

// round-trip through Builder(DatasetAttributes)
final ZarrV3DatasetAttributes roundTrip = ZarrV3DatasetAttributes.builder(withGzip).build();
assertArrayEquals(withGzip.getDimensions(), roundTrip.getDimensions());
assertArrayEquals(withGzip.getBlockSize(), roundTrip.getBlockSize());
assertEquals(withGzip.getDataType(), roundTrip.getDataType());
assertEquals(withGzip.getDataCodecInfos().length, roundTrip.getDataCodecInfos().length);

// validateLength: blockSize wrong length throws
try {
ZarrV3DatasetAttributes.builder(dims, DataType.FLOAT32).blockSize(new int[]{32, 32}).build();
// blockSize setter does not validate length — only chunkSize() does, so no exception expected here
} catch (final IllegalArgumentException e) {
// acceptable if validation is added to blockSize()
}

// validateBlockChunkSize: chunkSize > blockSize throws
try {
ZarrV3DatasetAttributes.builder(dims, DataType.FLOAT32)
.blockSize(new int[]{16, 16, 16})
.chunkSize(new int[]{32, 32, 32})
.build();
throw new AssertionError("Expected IllegalArgumentException for chunkSize > blockSize");
} catch (final IllegalArgumentException expected) {}
}

}
Loading