takane
Validators for ArtifactDB file formats
Loading...
Searching...
No Matches
sequence_information.hpp
Go to the documentation of this file.
1#ifndef TAKANE_SEQUENCE_INFORMATION_HPP
2#define TAKANE_SEQUENCE_INFORMATION_HPP
3
4#include "H5Cpp.h"
5#include "ritsuko/ritsuko.hpp"
6
7#include <filesystem>
8#include <stdexcept>
9#include <exception>
10#include <unordered_set>
11#include <string>
12
13#include "utils_public.hpp"
14#include "utils_json.hpp"
15#include "utils_missing.hpp"
16#include "utils_other.hpp"
17
23namespace takane {
24
30inline void validate_sequence_information(const std::filesystem::path& path, const ObjectMetadata& metadata, const Options& options) {
31 const std::string type_name = "sequence_information"; // use a separate variable to avoid dangling reference warnings from GCC.
32
33 try {
34 const auto& type_meta = extract_json_object(metadata.other, type_name);
35 try {
36 const auto& vstring = extract_json_version_string(type_meta);
37 auto version = ritsuko::parse_version_string(vstring.c_str(), vstring.size(), /* skip_patch = */ true);
38 if (version.major != 1) {
39 throw std::runtime_error("unsupported version string '" + vstring + "'");
40 }
41 } catch (...) {
42 std::throw_with_nested(std::runtime_error("failed to read 'version'"));
43 }
44 } catch (...) {
45 std::throw_with_nested(std::runtime_error("failed to validate 'sequence_information' in the object metadata"));
46 }
47
48 try {
49 H5::H5File handle(path / "info.h5", H5F_ACC_RDONLY);
50 auto ghandle = handle.openGroup(type_name);
51
52 hsize_t nseq = 0;
53 try {
54 auto nhandle = ghandle.openDataSet("name");
55 if (!ritsuko::hdf5::is_utf8_string(nhandle)) {
56 throw std::runtime_error("expected a datatype that can be represented by a UTF-8 encoded string");
57 }
58
59 auto nspace = nhandle.getSpace();
60 if (nspace.getSimpleExtentNdims() != 1) {
61 throw std::runtime_error("expected a 1-dimensional dataset");
62 }
63 nspace.getSimpleExtentDims(&nseq);
64
65 ritsuko::hdf5::Stream1dStringDataset stream(
66 &nhandle,
67 nseq,
68 [&]{
69 ritsuko::hdf5::Stream1dStringDatasetOptions opt;
70 opt.contiguous_chunk_size = options.hdf5_buffer_size;
71 return opt;
72 }()
73 );
74
75 std::unordered_set<std::string> collected;
76 iterate_stream<std::string>(
77 stream,
78 [&](hsize_t, std::string x) -> void {
79 if (collected.find(x) != collected.end()) {
80 throw std::runtime_error("detected duplicated sequence name '" + x + "'");
81 }
82 collected.insert(std::move(x));
83 }
84 );
85 } catch (...) {
86 std::throw_with_nested(std::runtime_error("failed to validate 'name'"));
87 }
88
89 try {
90 auto lhandle = ghandle.openDataSet("length");
91 if (ritsuko::hdf5::exceeds_integer_limit(lhandle, 64, false)) {
92 throw std::runtime_error("expected a datatype that fits in a 64-bit unsigned integer");
93 }
94
95 auto lspace = lhandle.getSpace();
96 if (lspace.getSimpleExtentNdims() != 1) {
97 throw std::runtime_error("expected a 1-dimensional dataset");
98 }
99 hsize_t nlen;
100 lspace.getSimpleExtentDims(&nlen);
101 if (nlen != nseq) {
102 throw std::runtime_error("dataset extent should the same as that of 'name'");
103 }
104
105 validate_numeric_missing_placeholder(lhandle, "missing-value-placeholder");
106 } catch (...) {
107 std::throw_with_nested(std::runtime_error("failed to validate 'length'"));
108 }
109
110 try {
111 auto chandle = ghandle.openDataSet("circular");
112 if (ritsuko::hdf5::exceeds_integer_limit(chandle, 32, true)) {
113 throw std::runtime_error("expected a datatype that fits in a 32-bit signed integer");
114 }
115
116 auto cspace = chandle.getSpace();
117 if (cspace.getSimpleExtentNdims() != 1) {
118 throw std::runtime_error("expected a 1-dimensional dataset");
119 }
120 hsize_t ncirc;
121 cspace.getSimpleExtentDims(&ncirc);
122 if (ncirc != nseq) {
123 throw std::runtime_error("dataset extent should the same as that of 'name'");
124 }
125
126 validate_numeric_missing_placeholder(chandle, "missing-value-placeholder");
127 } catch (...) {
128 std::throw_with_nested(std::runtime_error("failed to validate 'circular'"));
129 }
130
131 try {
132 auto gnhandle = ghandle.openDataSet("genome");
133 if (!ritsuko::hdf5::is_utf8_string(gnhandle)) {
134 throw std::runtime_error("expected a datatype that can be represented by a UTF-8 encoded string");
135 }
136
137 auto gnspace = gnhandle.getSpace();
138 if (gnspace.getSimpleExtentNdims() != 1) {
139 throw std::runtime_error("expected a 1-dimensional dataset");
140 }
141 hsize_t ngen;
142 gnspace.getSimpleExtentDims(&ngen);
143 if (ngen != nseq) {
144 throw std::runtime_error("dataset extent should the same as that of 'name'");
145 }
146
147 validate_string_missing_placeholder(gnhandle, "missing-value-placeholder");
148 } catch (...) {
149 std::throw_with_nested(std::runtime_error("failed to validate 'circular'"));
150 }
151 } catch (...) {
152 std::throw_with_nested(std::runtime_error("failed to validate 'sequence_information' in 'info.h5'"));
153 }
154}
155
156}
157
158#endif
takane validation functions.
Definition _derived_from.hpp:15
void validate_sequence_information(const std::filesystem::path &path, const ObjectMetadata &metadata, const Options &options)
Definition sequence_information.hpp:30
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.