diff --git a/Zarr.m b/Zarr.m index 66d6046..26956d6 100644 --- a/Zarr.m +++ b/Zarr.m @@ -103,6 +103,13 @@ function pyReloadInProcess() paramName) end + if any(params>dims) + error("MATLAB:Zarr:PartialReadOutOfBounds",... + "Elements in %s must not exceed "+... + "the corresponding Zarr array dimensions.",... + paramName) + end + newParams = params; end @@ -315,6 +322,12 @@ function makeZarrGroups(existingParentPath, newGroupsPath) count = Zarr.processPartialReadParams(count, info.shape,... maxCount, "Count"); + if any(count>maxCount) + error("MATLAB:Zarr:PartialReadOutOfBounds",... + "Requested Count in combination with other "+... + "parameters exceeds Zarr array dimensions.") + end + % Convert partial read parameters to tensorstore-style % indexing start = start - 1; % tensorstore is 0-based @@ -322,13 +335,26 @@ function makeZarrGroups(existingParentPath, newGroupsPath) % (it does NOT include element at the end index) endInds = start + stride.*count; + % Store the datatype + obj.Datatype = ZarrDatatype.fromZarrType(info.dtype); + + % Check if reading requested data might exceed available memory + try + zeros(count, obj.Datatype.MATLABType); + catch ME + if strcmp(ME.identifier, 'MATLAB:array:SizeLimitExceeded') + error("MATLAB:Zarr:OutOfMemory",... + "Reading requested data (%s %s array) "+... + "would exceed maximum array size preference. "+... + "Select a smaller subset of data to read.",... + join(string(count), "-by-"), obj.Datatype.MATLABType) + end + end + % Read the data ndArrayData = Zarr.ZarrPy.readZarr(obj.KVStoreSchema,... start, endInds, stride); - % Store the datatype - obj.Datatype = ZarrDatatype.fromTensorstoreType(ndArrayData.dtype.name); - % Convert the numpy array to MATLAB array data = cast(ndArrayData, obj.Datatype.MATLABType); end diff --git a/ZarrDatatype.m b/ZarrDatatype.m index b908eaa..899ba5b 100644 --- a/ZarrDatatype.m +++ b/ZarrDatatype.m @@ -74,6 +74,15 @@ obj = ZarrDatatype(ind); end + function obj = fromZarrType(zarrType) + % Create a datatype object based on Zarr datatype name + arguments + zarrType (1,1) string {ZarrDatatype.mustBeZarrType} + end + + ind = find(zarrType == ZarrDatatype.ZarrTypes); + obj = ZarrDatatype(ind); + end function mustBeMATLABType(type) % Validator for MATLAB types @@ -84,6 +93,11 @@ function mustBeTensorstoreType(type) % Validator for Tensorstore types mustBeMember(type, ZarrDatatype.TensorstoreTypes) end + + function mustBeZarrType(type) + % Validator for Zarr types + mustBeMember(type, ZarrDatatype.ZarrTypes) + end end end \ No newline at end of file diff --git a/test/tZarrRead.m b/test/tZarrRead.m index 6a2b25b..7201be5 100644 --- a/test/tZarrRead.m +++ b/test/tZarrRead.m @@ -109,6 +109,16 @@ function nonExistentArray(testcase) testcase.verifyError(@()zarrread('nonexistent/'),errID); end + function tooBigArray(testcase) + % Verify zarrread error when a user tries to read data that is + % too large + + bigDataPath = "bigData/myzarr"; + zarrcreate(bigDataPath, [100000,100000], Datatype='single'); + errID = 'MATLAB:Zarr:OutOfMemory'; + testcase.verifyError(@()zarrread(bigDataPath),errID); + end + function invalidFilePath(testcase) % Verify zarrread error when an invalid file path is used. @@ -147,7 +157,7 @@ function invalidPartialReadParams(testcase) testcase.verifyError(@()zarrread(zpath,Count=wrongDims),errID); % Invalid type - errID = 'MATLAB:validators:mustBeNumericOrLogical'; + errID = 'MATLAB:validators:mustBeNumeric'; testcase.verifyError(@()zarrread(zpath,"Start",""),errID); testcase.verifyError(@()zarrread(zpath,"Stride",""),errID); testcase.verifyError(@()zarrread(zpath,"Count",""),errID); @@ -159,12 +169,17 @@ function invalidPartialReadParams(testcase) testcase.verifyError(@()zarrread(zpath,"Stride",inpVal),errID); testcase.verifyError(@()zarrread(zpath,"Count",inpVal),errID); - % Input out of bounds + + % Parameters out of bounds inpVal = [100 200]; - errID = 'MATLAB:Python:PyException'; + errID = 'MATLAB:Zarr:PartialReadOutOfBounds'; testcase.verifyError(@()zarrread(zpath,"Start",inpVal),errID); - %testcase.verifyError(@()zarrread(zpath,"Stride",inpVal),errID); + testcase.verifyError(@()zarrread(zpath,"Stride",inpVal),errID); testcase.verifyError(@()zarrread(zpath,"Count",inpVal),errID); + + % Combination of parameters out of bounds + testcase.verifyError(... + @()zarrread(zpath,Start=[3 4],Count=[2 2]),errID) end end end \ No newline at end of file diff --git a/zarrread.m b/zarrread.m index fbfd458..3d7b068 100644 --- a/zarrread.m +++ b/zarrread.m @@ -27,9 +27,9 @@ arguments filepath {mustBeTextScalar, mustBeNonzeroLengthText} - options.Start (1,:) {mustBeInteger, mustBePositive} = []; - options.Count (1,:) {mustBeInteger, mustBePositive} = []; - options.Stride (1,:) {mustBeInteger, mustBePositive} = []; + options.Start (1,:) {mustBeNumeric, mustBeInteger, mustBePositive} = []; + options.Count (1,:) {mustBeNumeric, mustBeInteger, mustBePositive} = []; + options.Stride (1,:) {mustBeNumeric, mustBeInteger, mustBePositive} = []; end zarrObj = Zarr(filepath);