takane
Validators for ArtifactDB file formats
Loading...
Searching...
No Matches
dense_array.hpp
Go to the documentation of this file.
1#ifndef TAKANE_DENSE_ARRAY_HPP
2#define TAKANE_DENSE_ARRAY_HPP
3
4#include "H5Cpp.h"
5#include "ritsuko/ritsuko.hpp"
6#include "sanisizer/sanisizer.hpp"
7
8#include "utils_public.hpp"
9#include "utils_array.hpp"
10#include "utils_json.hpp"
11#include "utils_missing.hpp"
12#include "utils_string.hpp"
13
14#include <vector>
15#include <string>
16#include <stdexcept>
17#include <filesystem>
18#include <cstdint>
19#include <cstddef>
20
26namespace takane {
27
33inline void validate_dense_array(const std::filesystem::path& path, const ObjectMetadata& metadata, const Options& options) {
34 const std::string type_name = "dense_array"; // use a separate variable to avoid dangling reference warnings from GCC.
35
36 ritsuko::Version version;
37 try {
38 const auto& type_meta = extract_json_object(metadata.other, type_name);
39 try {
40 const auto& vstring = extract_json_version_string(type_meta);
41 version = ritsuko::parse_version_string(vstring.c_str(), vstring.size(), /* skip_patch = */ true);
42 if (version.major != 1) {
43 throw std::runtime_error("unsupported version '" + vstring + "'");
44 }
45 } catch (...) {
46 std::throw_with_nested(std::runtime_error("failed to validate 'version'"));
47 }
48 } catch (...) {
49 std::throw_with_nested(std::runtime_error("failed to validate '" + type_name + "' in the object metadata"));
50 }
51
52 try {
53 H5::H5File handle(path / "array.h5", H5F_ACC_RDONLY);
54 auto ghandle = handle.openGroup(type_name);
55
56 if (ghandle.attrExists("transposed")) {
57 auto ahandle = ghandle.openAttribute("transposed");
58 if (ahandle.getSpace().getSimpleExtentNdims() != 0) {
59 throw std::runtime_error("expected 'transposed' attribute to be a scalar");
60 }
61 if (ritsuko::hdf5::exceeds_integer_limit(ahandle, 32, true)) {
62 throw std::runtime_error("expected 'transposed' attribute to have a datatype that fits in a 32-bit signed integer");
63 }
64 }
65
66 std::string type;
67 try {
68 auto thandle = ghandle.openAttribute("type");
69 type = open_and_load_scalar_string_attribute(thandle);
70 } catch (...) {
71 std::throw_with_nested(std::runtime_error("failed to validate the 'type' attribute"));
72 }
73
74 const char* missing_attr_name = "missing-value-placeholder";
75 std::vector<hsize_t> extents;
76
77 if (type == "vls") {
78 if (version.lt(1, 1, 0)) {
79 throw std::runtime_error("unsupported type '" + type + "'");
80 }
81
82 enum Failure { HEAP, POINTERS };
83 Failure who_failed = HEAP;
84
85 try {
86 auto hhandle = ghandle.openDataSet("heap");
87 const auto hlen = ritsuko::cvls::validate_heap(hhandle);
88
89 who_failed = POINTERS;
90 auto phandle = ghandle.openDataSet("pointers");
91 auto pspace = phandle.getSpace();
92 auto ndim = pspace.getSimpleExtentNdims();
93 if (ndim == 0) {
94 throw std::runtime_error("expected 'pointers' to have at least one dimension");
95 }
96 sanisizer::resize(extents, ndim);
97 pspace.getSimpleExtentDims(extents.data());
98
99 ritsuko::cvls::validate_nd_pointers<std::uint64_t, std::uint64_t>(
100 phandle,
101 extents,
102 hlen,
103 [&]{
104 ritsuko::cvls::ValidateNdPointersOptions opt;
105 opt.contiguous_chunk_size = options.hdf5_buffer_size;
106 return opt;
107 }()
108 );
109
110 validate_string_missing_placeholder(phandle, missing_attr_name);
111 } catch (...) {
112 std::string desc;
113 switch (who_failed) {
114 case HEAP: desc = "heap"; break;
115 case POINTERS: desc = "pointers"; break;
116 }
117 std::throw_with_nested(std::runtime_error("failed to validate '" + desc + "'"));
118 }
119
120 } else {
121 try {
122 auto dhandle = ghandle.openDataSet("data");
123 auto dspace = dhandle.getSpace();
124 auto ndim = dspace.getSimpleExtentNdims();
125 if (ndim == 0) {
126 throw std::runtime_error("expected dataset to have at least one dimension");
127 }
128 sanisizer::resize(extents, ndim);
129 dspace.getSimpleExtentDims(extents.data());
130
131 if (type == "string") {
132 if (!ritsuko::hdf5::is_utf8_string(dhandle)) {
133 throw std::runtime_error("expected a datatype that can be represented by a UTF-8 encoded string");
134 }
135
136 ritsuko::hdf5::validate_nd_strings(
137 dhandle,
138 extents,
139 [&]{
140 ritsuko::hdf5::ValidateNdStringsOptions opt;
141 opt.contiguous_chunk_size = options.hdf5_buffer_size;
142 return opt;
143 }()
144 );
145
146 validate_string_missing_placeholder(dhandle, missing_attr_name);
147
148 } else {
149 if (type == "integer") {
150 if (ritsuko::hdf5::exceeds_integer_limit(dhandle, 32, true)) {
151 throw std::runtime_error("expected a datatype that fits into a 32-bit signed integer");
152 }
153 } else if (type == "boolean") {
154 if (ritsuko::hdf5::exceeds_integer_limit(dhandle, 32, true)) {
155 throw std::runtime_error("expected a datatype that fits into a 32-bit signed integer");
156 }
157 } else if (type == "number") {
158 if (ritsuko::hdf5::exceeds_float_limit(dhandle, 64)) {
159 throw std::runtime_error("expected a datatype that fits into a 64-bit float");
160 }
161 } else {
162 throw std::runtime_error("unknown array type '" + type + "'");
163 }
164
165 validate_numeric_missing_placeholder(dhandle, missing_attr_name);
166 }
167 } catch (...) {
168 std::throw_with_nested(std::runtime_error("failed to validate 'data'"));
169 }
170 }
171
172 validate_array_dimnames(ghandle, "names", extents, options);
173 } catch (...) {
174 std::throw_with_nested(std::runtime_error("failed to validate '" + type_name + "' in 'contents.h5'"));
175 }
176}
177
184inline std::size_t height_of_dense_array(const std::filesystem::path& path, [[maybe_unused]] const ObjectMetadata& metadata, [[maybe_unused]] const Options& options) {
185 H5::H5File handle(path / "array.h5", H5F_ACC_RDONLY);
186 auto ghandle = handle.openGroup("dense_array");
187 auto type = open_and_load_scalar_string_attribute(ghandle.openAttribute("type"));
188
189 H5::DataSpace space;
190 if (type == "vls") {
191 space = ghandle.openDataSet("pointers").getSpace();
192 } else {
193 space = ghandle.openDataSet("data").getSpace();
194 }
195 auto ndims = space.getSimpleExtentNdims();
196 auto extents = sanisizer::create<std::vector<hsize_t> >(ndims);
197 space.getSimpleExtentDims(extents.data());
198
199 std::int32_t transposed = 0;
200 if (ghandle.attrExists("transposed")) {
201 auto ahandle = ghandle.openAttribute("transposed");
202 ahandle.read(H5::PredType::NATIVE_INT32, &transposed);
203 }
204
205 return sanisizer::cast<std::size_t>(transposed ? extents.back() : extents.front());
206}
207
214inline std::vector<std::size_t> dimensions_of_dense_array(const std::filesystem::path& path, [[maybe_unused]] const ObjectMetadata& metadata, [[maybe_unused]] const Options& options) {
215 H5::H5File handle(path / "array.h5", H5F_ACC_RDONLY);
216 auto ghandle = handle.openGroup("dense_array");
217 auto type = open_and_load_scalar_string_attribute(ghandle.openAttribute("type"));
218
219 H5::DataSpace space;
220 if (type == "vls") {
221 space = ghandle.openDataSet("pointers").getSpace();
222 } else {
223 space = ghandle.openDataSet("data").getSpace();
224 }
225
226 auto ndims = space.getSimpleExtentNdims();
227 auto extents = sanisizer::create<std::vector<hsize_t> >(ndims);
228 space.getSimpleExtentDims(extents.data());
229
230 std::int32_t transposed = 0;
231 if (ghandle.attrExists("transposed")) {
232 auto ahandle = ghandle.openAttribute("transposed");
233 ahandle.read(H5::PredType::NATIVE_INT32, &transposed);
234 if (transposed) {
235 std::reverse(extents.begin(), extents.end());
236 }
237 }
238
239 return cast_array_dimensions<std::size_t>(std::move(extents));
240}
241
242}
243
244#endif
takane validation functions.
Definition _derived_from.hpp:15
void validate_dense_array(const std::filesystem::path &path, const ObjectMetadata &metadata, const Options &options)
Definition dense_array.hpp:33
std::size_t height_of_dense_array(const std::filesystem::path &path, const ObjectMetadata &metadata, const Options &options)
Definition dense_array.hpp:184
std::vector< std::size_t > dimensions_of_dense_array(const std::filesystem::path &path, const ObjectMetadata &metadata, const Options &options)
Definition dense_array.hpp:214
Object metadata, including the type and other fields.
Definition utils_public.hpp:29
std::unordered_map< std::string, std::shared_ptr< millijson::Base > > other
Definition utils_public.hpp:38
Validation options.
Definition utils_public.hpp:99
hsize_t hdf5_buffer_size
Definition utils_public.hpp:109
Exported utilities.