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
39 changes: 23 additions & 16 deletions src/main/java/dev/zarr/zarrjava/core/Array.java
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ public void write(long[] offset, ucar.ma2.Array array, boolean parallel) {
throw new IllegalArgumentException("'array' needs to have rank '" + metadata.ndim() + "'.");
}

int[] shape = array.getShape();
long[] shape = Utils.toLongArray(array.getShape());

final int[] chunkShape = metadata.chunkShape();
Stream<long[]> chunkStream = Arrays.stream(IndexingUtils.computeChunkCoords(metadata.shape, chunkShape, offset, shape));
Expand Down Expand Up @@ -122,8 +122,14 @@ public void write(long[] offset, ucar.ma2.Array array, boolean parallel) {
);
}
writeChunk(chunkCoords, chunkArray);
} catch (ZarrException | InvalidRangeException e) {
throw new RuntimeException(e);
} catch (ZarrException e) {
throw new RuntimeException(
"Failed to write chunk at coordinates " + Arrays.toString(chunkCoords) +
": " + e.getMessage(), e);
} catch (InvalidRangeException e) {
throw new RuntimeException(
"Invalid array range when writing chunk at coordinates " + Arrays.toString(chunkCoords) +
": " + e.getMessage(), e);
}
});

Expand Down Expand Up @@ -221,7 +227,7 @@ public void write(ucar.ma2.Array array, boolean parallel) {
*/
@Nonnull
public ucar.ma2.Array read() throws ZarrException {
return read(new long[metadata().ndim()], Utils.toIntArray(metadata().shape));
return read(new long[metadata().ndim()], metadata().shape);
}

/**
Expand All @@ -233,7 +239,7 @@ public ucar.ma2.Array read() throws ZarrException {
* @throws ZarrException throws ZarrException if the requested data is outside the array's domain or if the read fails
*/
@Nonnull
public ucar.ma2.Array read(final long[] offset, final int[] shape) throws ZarrException {
public ucar.ma2.Array read(final long[] offset, final long[] shape) throws ZarrException {
return read(offset, shape, false);
}

Expand All @@ -245,7 +251,7 @@ public ucar.ma2.Array read(final long[] offset, final int[] shape) throws ZarrEx
*/
@Nonnull
public ucar.ma2.Array read(final boolean parallel) throws ZarrException {
return read(new long[metadata().ndim()], Utils.toIntArray(metadata().shape), parallel);
return read(new long[metadata().ndim()], metadata().shape, parallel);
}

boolean chunkIsInArray(long[] chunkCoords) {
Expand All @@ -268,7 +274,7 @@ boolean chunkIsInArray(long[] chunkCoords) {
* @throws ZarrException throws ZarrException if the requested data is outside the array's domain or if the read fails
*/
@Nonnull
public ucar.ma2.Array read(final long[] offset, final int[] shape, final boolean parallel) throws ZarrException {
public ucar.ma2.Array read(final long[] offset, final long[] shape, final boolean parallel) throws ZarrException {
ArrayMetadata metadata = metadata();
if (offset.length != metadata.ndim()) {
throw new IllegalArgumentException("'offset' needs to have rank '" + metadata.ndim() + "'.");
Expand All @@ -288,7 +294,7 @@ public ucar.ma2.Array read(final long[] offset, final int[] shape, final boolean
}

final ucar.ma2.Array outputArray = ucar.ma2.Array.factory(metadata.dataType().getMA2DataType(),
shape);
Utils.toIntArray(shape));
Stream<long[]> chunkStream = Arrays.stream(IndexingUtils.computeChunkCoords(metadata.shape, chunkShape, offset, shape));
if (parallel) {
chunkStream = chunkStream.parallel();
Expand All @@ -311,18 +317,19 @@ public ucar.ma2.Array read(final long[] offset, final int[] shape, final boolean
final String[] chunkKeys = metadata.chunkKeyEncoding().encodeChunkKey(chunkCoords);
final StoreHandle chunkHandle = storeHandle.resolve(chunkKeys);

if (!chunkHandle.exists()) return;

if (codecPipeline.supportsPartialDecode()) {
final ucar.ma2.Array chunkArray = codecPipeline.decodePartial(chunkHandle,
Utils.toLongArray(chunkProjection.chunkOffset), chunkProjection.shape);
MultiArrayUtils.copyRegion(chunkArray, new int[metadata.ndim()], outputArray,
chunkProjection.outOffset, chunkProjection.shape
);
} else {
MultiArrayUtils.copyRegion(readChunk(chunkCoords), chunkProjection.chunkOffset,
outputArray, chunkProjection.outOffset, chunkProjection.shape
);
ByteBuffer chunkBytes = chunkHandle.read();
if (chunkBytes != null) {
MultiArrayUtils.copyRegion(codecPipeline.decode(chunkBytes), chunkProjection.chunkOffset,
outputArray, chunkProjection.outOffset, chunkProjection.shape
);
}
}

} catch (ZarrException e) {
Expand All @@ -340,7 +347,7 @@ public static final class ArrayAccessor {
@Nullable
long[] offset;
@Nullable
int[] shape;
long[] shape;
@Nonnull
Array array;

Expand All @@ -357,13 +364,13 @@ public ArrayAccessor withOffset(@Nonnull long... offset) {

@Nonnull
public ArrayAccessor withShape(@Nonnull int... shape) {
this.shape = shape;
this.shape = Utils.toLongArray(shape);
return this;
}

@Nonnull
public ArrayAccessor withShape(@Nonnull long... shape) {
this.shape = Utils.toIntArray(shape);
this.shape = shape;
return this;
}

Expand Down
19 changes: 3 additions & 16 deletions src/main/java/dev/zarr/zarrjava/core/codec/CodecPipeline.java
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,9 @@ ArrayBytesCodec getArrayBytesCodec() {
return (ArrayBytesCodec) codec;
}
}
throw new RuntimeException(
"Unreachable because the existence of exactly 1 ArrayBytes codec is asserted upon construction.");
throw new IllegalStateException(
"No ArrayBytesCodec found in codec pipeline. This should never happen as the existence " +
"of exactly 1 ArrayBytesCodec is validated during construction.");
}

BytesBytesCodec[] getBytesBytesCodecs() {
Expand Down Expand Up @@ -158,18 +159,4 @@ public long computeEncodedSize(long inputByteLength, CoreArrayMetadata arrayMeta
}
return inputByteLength;
}

public Array partialDecode(
StoreHandle valueHandle, long[] offset, int[] shape,
CoreArrayMetadata arrayMetadata
) {
return null; // TODO
}

public ByteBuffer partialEncode(
StoreHandle oldValueHandle, Array array, long[] offset, int[] shape,
CoreArrayMetadata arrayMetadata
) {
return null; // TODO
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ public ByteOrder getByteOrder() {
case BIG:
return ByteOrder.BIG_ENDIAN;
default:
throw new RuntimeException("Unreachable");
throw new IllegalStateException("Unknown endian type: " + this);
}
}
}
Expand Down
15 changes: 12 additions & 3 deletions src/main/java/dev/zarr/zarrjava/store/BufferedZipStore.java
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,10 @@ public BufferedZipStore(@Nonnull StoreHandle underlyingStore, @Nonnull Store.Lis
try {
loadBuffer();
} catch (IOException e) {
throw new RuntimeException("Failed to load buffer from underlying store", e);
throw StoreException.readFailed(
underlyingStore.toString(),
new String[]{},
new IOException("Failed to load ZIP buffer from underlying store: " + underlyingStore, e));
}
}

Expand Down Expand Up @@ -285,7 +288,10 @@ public void set(String[] keys, ByteBuffer bytes) {
try {
writeBuffer();
} catch (IOException e) {
throw new RuntimeException("Failed to flush buffer to underlying store after set operation", e);
throw StoreException.writeFailed(
underlyingStore.toString(),
keys,
new IOException("Failed to flush ZIP buffer to underlying store after set operation", e));
}
}
}
Expand All @@ -297,7 +303,10 @@ public void delete(String[] keys) {
try {
writeBuffer();
} catch (IOException e) {
throw new RuntimeException("Failed to flush buffer to underlying store after delete operation", e);
throw StoreException.deleteFailed(
underlyingStore.toString(),
keys,
new IOException("Failed to flush ZIP buffer to underlying store after delete operation", e));
}
}
}
Expand Down
72 changes: 54 additions & 18 deletions src/main/java/dev/zarr/zarrjava/store/FilesystemStore.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,13 @@ Path resolveKeys(String[] keys) {
for (String key : keys) {
newPath = newPath.resolve(key);
}
return newPath;
Path absRoot = path.toAbsolutePath().normalize();
Path absTarget = newPath.toAbsolutePath().normalize();

if (!absTarget.startsWith(absRoot)) {
throw new IllegalArgumentException("Key resolves outside of store root: " + absTarget);
}
return newPath.normalize();
}

@Override
Expand All @@ -43,8 +49,10 @@ public boolean exists(String[] keys) {
public ByteBuffer get(String[] keys) {
try {
return ByteBuffer.wrap(Files.readAllBytes(resolveKeys(keys)));
} catch (IOException e) {
} catch (NoSuchFileException e) {
return null;
} catch (IOException e) {
throw StoreException.readFailed(this.toString(), keys, e);
}
}

Expand All @@ -64,8 +72,10 @@ public ByteBuffer get(String[] keys, long start) {
byteChannel.read(bytes);
bytes.rewind();
return bytes;
} catch (IOException e) {
} catch (NoSuchFileException e) {
return null;
} catch (IOException e) {
throw StoreException.readFailed(this.toString(), keys, e);
}
}

Expand All @@ -84,8 +94,10 @@ public ByteBuffer get(String[] keys, long start, long end) {
byteChannel.read(bytes);
bytes.rewind();
return bytes;
} catch (IOException e) {
} catch (NoSuchFileException e) {
return null;
} catch (IOException e) {
throw StoreException.readFailed(this.toString(), keys, e);
}
}

Expand All @@ -96,7 +108,10 @@ public void set(String[] keys, ByteBuffer bytes) {
try {
Files.createDirectories(keyPath.getParent());
} catch (IOException e) {
throw new RuntimeException(e);
throw StoreException.writeFailed(
this.toString(),
keys,
new IOException("Failed to create parent directories for path: " + keyPath.getParent(), e));
}
try (SeekableByteChannel channel = Files.newByteChannel(keyPath.toAbsolutePath(),
StandardOpenOption.CREATE,
Expand All @@ -105,18 +120,25 @@ public void set(String[] keys, ByteBuffer bytes) {
)) {
channel.write(bytes);
} catch (IOException e) {
throw new RuntimeException(e);
throw StoreException.writeFailed(
this.toString(),
keys,
new IOException("Failed to write " + bytes.remaining() + " bytes to file: " + keyPath, e));
}
}

@Override
public void delete(String[] keys) {
Path keyPath = resolveKeys(keys);
try {
Files.delete(resolveKeys(keys));
Files.delete(keyPath);
} catch (NoSuchFileException e) {
// ignore
// ignore - file doesn't exist, which is the desired outcome
} catch (IOException e) {
throw new RuntimeException(e);
throw StoreException.deleteFailed(
this.toString(),
keys,
new IOException("Failed to delete file: " + keyPath, e));
}
}

Expand All @@ -143,7 +165,10 @@ public Stream<String[]> list(String[] prefix) {
.filter(Files::isRegularFile)
.map(path -> pathToKeyArray(rootPath, path, prefix));
} catch (IOException e) {
throw new RuntimeException("Failed to list store content", e);
throw StoreException.listFailed(
this.toString(),
prefix,
new IOException("Failed to walk directory tree at: " + rootPath, e));
}
}

Expand All @@ -156,7 +181,10 @@ public Stream<String> listChildren(String[] prefix) {
try {
return Files.list(rootPath).map(path -> path.getFileName().toString());
} catch (IOException e) {
throw new RuntimeException("Failed to list store children", e);
throw StoreException.listFailed(
this.toString(),
prefix,
new IOException("Failed to list directory contents at: " + rootPath, e));
}
}

Expand All @@ -175,14 +203,12 @@ public String toString() {
public InputStream getInputStream(String[] keys, long start, long end) {
Path keyPath = resolveKeys(keys);
try {
if (!Files.exists(keyPath)) {
return null;
}
InputStream inputStream = Files.newInputStream(keyPath);
if (start > 0) {
long skipped = inputStream.skip(start);
if (skipped < start) {
throw new IOException("Unable to skip to the desired start position.");
throw new IOException("Unable to skip to position " + start +
", only skipped " + skipped + " bytes in file: " + keyPath);
}
}
if (end != -1) {
Expand All @@ -191,18 +217,28 @@ public InputStream getInputStream(String[] keys, long start, long end) {
} else {
return inputStream;
}
} catch (NoSuchFileException e) {
return null;
} catch (IOException e) {
throw new RuntimeException(e);
throw StoreException.readFailed(
this.toString(),
keys,
new IOException("Failed to open input stream for file: " + keyPath +
" (start: " + start + ", end: " + end + ")", e));
}
}

public long getSize(String[] keys) {
Path keyPath = resolveKeys(keys);
try {
return Files.size(resolveKeys(keys));
return Files.size(keyPath);
} catch (NoSuchFileException e) {
return -1;
} catch (IOException e) {
throw new RuntimeException(e);
throw StoreException.readFailed(
this.toString(),
keys,
new IOException("Failed to get file size for: " + keyPath, e));
}
}
}
Loading
Loading