diff --git a/+io/+backend/+hdf5/@HDF5LazyArray/load_mat_style.m b/+io/+backend/+hdf5/@HDF5LazyArray/load_mat_style.m index 7617d10c8..bc384478f 100644 --- a/+io/+backend/+hdf5/@HDF5LazyArray/load_mat_style.m +++ b/+io/+backend/+hdf5/@HDF5LazyArray/load_mat_style.m @@ -9,7 +9,6 @@ %% Select from Space dataDimensions = obj.dims; - spaceId = obj.getSpace(); userSelection = varargin; selectionErrorId = 'NWB:DataStub:Load:InvalidSelection'; @@ -41,8 +40,15 @@ data = obj.load_mat_style(1); data = getEmptyRepresentation(data); return + end + + % The dataspace and the read use the same open dataset, so each call + % opens the file once. + fileId = H5F.open(obj.Filename); + datasetId = H5D.open(fileId, obj.DatasetPath); + spaceId = H5D.get_space(datasetId); - elseif isscalar(userSelection) && ~ischar(userSelection{1}) + if isscalar(userSelection) && ~ischar(userSelection{1}) % linear index into the fast dimension. orderedSelection = unique(userSelection{1}); @@ -66,11 +72,20 @@ dataDimensions = [dataDimensions, 1]; end - [points{:}] = ind2sub(dataDimensions, orderedSelection); readSpaceId = H5S.copy(spaceId); H5S.select_none(readSpaceId); - H5S.select_elements(readSpaceId, 'H5S_SELECT_SET', ... - cell2mat(flipud(points)) - 1); + isOneRun = orderedSelection(end) - orderedSelection(1) + 1 == numel(orderedSelection); + if isscalar(obj.dims) && isOneRun + % One contiguous run of a one-dimensional dataset is a single + % hyperslab, which HDF5 selects faster than one point per element. + % The selection can be of any integer class, and H5S takes doubles. + H5S.select_hyperslab(readSpaceId, 'H5S_SELECT_SET', ... + double(orderedSelection(1)) - 1, [], numel(orderedSelection), []); + else + [points{:}] = ind2sub(dataDimensions, orderedSelection); + H5S.select_elements(readSpaceId, 'H5S_SELECT_SET', ... + cell2mat(flipud(points)) - 1); + end memorySpaceId = H5S.create_simple(length(selectionDimensions), ... selectionDimensions, selectionDimensions); else @@ -81,8 +96,6 @@ H5S.close(spaceId); %% Read Data - fileId = H5F.open(obj.Filename); - datasetId = H5D.open(fileId, obj.DatasetPath); data = H5D.read(datasetId, 'H5ML_DEFAULT', memorySpaceId, readSpaceId, 'H5P_DEFAULT'); %% Retype Data diff --git a/+tests/+unit/+io/+backend/HDF5LazyArrayTest.m b/+tests/+unit/+io/+backend/HDF5LazyArrayTest.m index e40c98304..d1f100b69 100644 --- a/+tests/+unit/+io/+backend/HDF5LazyArrayTest.m +++ b/+tests/+unit/+io/+backend/HDF5LazyArrayTest.m @@ -70,6 +70,24 @@ function reorderedFirstSubscriptBeforeFoldedColon(testCase, Subscript) testCase.verifyEqual(lazyArray.load_mat_style(Subscript, ':'), data(Subscript, :)); end + + function loadMatStyleReadsOneDimensionalSelections(testCase) + % A selection that is one contiguous run is read as a hyperslab + % and any other selection as points. Both return the elements + % in the order they were asked for. + filename = "lazy-array-1d.h5"; + data = (101:120)'; + h5create(filename, "/data", numel(data)); + h5write(filename, "/data", data); + lazyArray = io.backend.hdf5.HDF5LazyArray(filename, "/data"); + + selections = {5:9, (9:-1:5)', [3 3 4], 1:20, 20, uint64(5:9), [2 5 9]}; + for iSelection = 1:numel(selections) + selection = selections{iSelection}; + testCase.verifyEqual(lazyArray.load_mat_style(selection), data(selection), ... + sprintf('Unexpected data for selection %s.', mat2str(selection))); + end + end end methods (Access = private)