cuDDL
Loading...
Searching...
No Matches
reference_database_file.cuh
Go to the documentation of this file.
1#pragma once
2
3#include <algorithm>
4#include <bit>
5#include <cctype>
6#include <cerrno>
7#include <condition_variable>
8#include <cstdio>
9#include <cstdlib>
10#include <exception>
11#include <filesystem>
12#include <fstream>
13#include <limits>
14#include <mutex>
15#include <optional>
16#include <span>
17#include <string>
18#include <system_error>
19#include <thread>
20#include <vector>
21
22#include <fcntl.h>
23#include <libdeflate.h>
24#include <unistd.h>
25
26#include <cub/device/device_reduce.cuh>
27#include <cuda/iterator>
28#include <cuda/std/functional>
29
32#include <cuddl/fastx.hpp>
34#include <cuddl/sketch.cuh>
35
36namespace cuddl::detail {
37
38// The file format uses little-endian integers, never native struct representations.
39template <typename T>
40inline T database_file_little_endian(T value) {
41 if constexpr (std::endian::native == std::endian::little) {
42 return value;
43 } else {
44 T result = 0;
45 for (size_t i = 0; i < sizeof(T); ++i) {
46 result = static_cast<T>((result << 8U) | (value & 0xffU));
47 value >>= 8U;
48 }
49 return result;
50 }
51}
52
54 std::ifstream input;
55 uint64_t remaining{};
56 // libdeflate's CRC-32 equals zlib's and uses carry-less multiplication where available.
57 uint32_t checksum = 0;
58
59 Result<void> bytes(void* destination, size_t size) {
60 if (size > remaining ||
61 size > static_cast<size_t>(std::numeric_limits<std::streamsize>::max())) {
62 return Err(Error::invalid_argument("truncated reference database file"));
63 }
64 if (size != 0) {
65 input.read(static_cast<char*>(destination), static_cast<std::streamsize>(size));
66 if (!input) return Err(Error::resource("cannot read reference database file"));
67 checksum = libdeflate_crc32(checksum, destination, size);
68 }
69 remaining -= size;
70 return Ok();
71 }
72
73 template <typename T>
75 CUDDL_TRY(bytes(&item, sizeof(item)));
76 item = database_file_little_endian(item);
77 return Ok();
78 }
79
80 template <typename T>
81 Result<void> words(std::vector<T>& items) {
82 CUDDL_TRY(bytes(items.data(), items.size() * sizeof(T)));
83 if constexpr (std::endian::native != std::endian::little) {
84 for (auto& item : items) {
85 item = database_file_little_endian(item);
86 }
87 }
88 return Ok();
89 }
90};
91
93 std::string temporary;
94 FILE* output{};
95 uint32_t checksum = 0;
96
98 if (output) std::fclose(output);
99 if (!temporary.empty()) {
100 ::unlink(temporary.c_str());
101 }
102 }
103
104 Result<void> bytes(void const* source, size_t size) {
105 if (size != 0) {
106 if (std::fwrite(source, 1, size, output) != size) {
107 return Err(Error::resource("cannot write reference database file"));
108 }
109 checksum = libdeflate_crc32(checksum, source, size);
110 }
111 return Ok();
112 }
113
114 template <typename T>
116 item = database_file_little_endian(item);
117 return bytes(&item, sizeof(item));
118 }
119
120 template <typename T>
121 Result<void> words(std::vector<T> const& items) {
122 if constexpr (std::endian::native == std::endian::little) {
123 return bytes(items.data(), items.size() * sizeof(T));
124 } else {
125 for (auto item : items) {
126 CUDDL_TRY(value(item));
127 }
128 return Ok();
129 }
130 }
131};
132
133template <typename IO>
135database_file_metadata(IO& io, reference_database_metadata& metadata, uint32_t& blacklist_count) {
136 auto& c = metadata.compatibility;
137 CUDDL_TRY(io.value(c.kmer_length));
138 CUDDL_TRY(io.value(c.exponent_bits));
139 CUDDL_TRY(io.value(c.bucket_count));
140 CUDDL_TRY(io.value(c.key_mask));
141 CUDDL_TRY(io.value(metadata.reference_count));
142 CUDDL_TRY(io.value(blacklist_count));
143 CUDDL_TRY(io.value(c.blacklist_identity));
144 return Ok();
145}
146
147} // namespace cuddl::detail
148
149namespace cuddl {
150
153
156
159
162
166[[nodiscard]] inline unsigned default_parser_workers() {
167 auto const hardware = std::thread::hardware_concurrency();
168 return hardware == 0U ? 8U : hardware;
169}
170
179 std::optional<size_t> staging_bytes{};
185 transfer_mode transfer = transfer_mode::automatic;
186 decompression_backend decompression = decompression_backend::automatic;
191 bool direct_reads = false;
193 std::optional<std::reference_wrapper<device_blacklist const>> blacklist = std::nullopt;
194};
195
201 std::optional<size_t> staging_bytes{};
202 std::optional<std::reference_wrapper<device_blacklist const>> blacklist = std::nullopt;
203};
204
215 std::filesystem::path const& directory
216) try {
217 std::vector<std::filesystem::path> paths;
218 for (auto const& entry : std::filesystem::recursive_directory_iterator(
219 directory, std::filesystem::directory_options::skip_permission_denied
220 )) {
221 if (!entry.is_symlink() && !entry.is_regular_file()) continue;
222 auto name = entry.path().filename().string();
223 std::ranges::transform(name, name.begin(), [](unsigned char c) {
224 return static_cast<char>(std::tolower(c));
225 });
226 auto extension = std::filesystem::path(name).extension().string();
227 if (extension == ".gz" || extension == ".bgz" || extension == ".bgzf") {
228 extension = std::filesystem::path(name).stem().extension().string();
229 }
230 if (extension == ".fa" || extension == ".fna" || extension == ".fasta" ||
231 extension == ".ffn" || extension == ".frn" || extension == ".fq" ||
232 extension == ".fastq") {
233 paths.push_back(entry.path());
234 }
235 }
236 std::ranges::sort(paths);
237 return paths;
238} catch (std::filesystem::filesystem_error const& error) {
239 return Err(Error::resource(error.what()));
240}
241
245 std::filesystem::path const& input
246) {
247 if (std::filesystem::is_directory(input)) return genome_paths(input);
248 std::ifstream list(input);
249 if (!list) return Err(Error::resource("cannot read genome list " + input.string()));
250 std::vector<std::filesystem::path> paths;
251 for (std::string line; std::getline(list, line);) {
252 if (!line.empty()) paths.emplace_back(line);
253 }
254 return paths;
255}
256
257namespace detail { struct reference_file_assembly; } // namespace detail
258
269 static constexpr uint16_t database_file_version = 1U;
270
271 public:
272 [[nodiscard]] kmer_blacklist const& blacklist() const noexcept {
273 return blacklist_;
274 }
275
277 [[nodiscard]] reference_database_metadata metadata() const noexcept {
278 return metadata_;
279 }
281 [[nodiscard]] std::span<std::string const> names() const noexcept {
282 return names_;
283 }
285 [[nodiscard]] std::span<uint16_t const> rows() const noexcept {
286 return rows_;
287 }
288
298 template <uint32_t K, size_t BucketCount, typename Layout = default_register_layout>
300 std::span<std::filesystem::path const> paths,
301 cuda::stream_ref stream,
302 path_build_options options = {}
303 ) try {
304 // NVCC 13.3 crashes on CUDDL_TRY directly inside a try block. Keep its GNU statement
305 // expressions in a separate lambda scope, outside the exception-catching function.
306 return [&]() -> Result<reference_database_file> {
307 using database_type = reference_database<K, BucketCount, Layout>;
308 if (paths.size() > std::numeric_limits<uint32_t>::max() ||
309 paths.size() >
310 std::numeric_limits<size_t>::max() / (BucketCount * sizeof(uint32_t))) {
311 return Err(Error::resource("reference collection exceeds database capacity"));
312 }
313 reference_database_file result;
314 result.metadata_ = {
315 score_compatibility::current<K, BucketCount, Layout>(),
316 static_cast<uint32_t>(paths.size())
317 };
318 CUDDL_TRY((result.set_blacklist<K, BucketCount>(options.blacklist, stream)));
319 result.rows_.resize(paths.size() * BucketCount);
320 result.names_.reserve(paths.size());
321 // No inputs means no staging budget to resolve and nothing to encode.
322 if (paths.empty()) return result;
323 CUDDL_TRY((detail::stage_paths<K, BucketCount, Layout>(
324 paths,
325 stream,
326 options.staging_bytes,
327 options.parser_workers,
328 options.transfer,
329 options.decompression,
330 options.direct_reads,
331 options.statistics,
332 result.download_rows<BucketCount>(stream),
333 options.blacklist ? options.blacklist->get().view() : cuda::std::nullopt
334 )));
335 for (auto const& path : paths) result.names_.push_back(path.string());
336 // Instantiate the same constraints as the destination GPU database.
337 static_assert(sizeof(database_type) > 0);
338 return result;
339 }();
340 } catch (cuda::cuda_error const& error) {
341 return Err(Error::cuda(static_cast<cudaError_t>(error.status())));
342 } catch (std::system_error const& error) {
343 return Err(Error::resource(error.what()));
344 } catch (std::bad_alloc const& error) {
345 return Err(Error::resource(error.what()));
346 }
347
356 template <uint32_t K, size_t BucketCount, typename Layout = default_register_layout>
358 std::span<sequence_genome const> genomes,
359 cuda::stream_ref stream,
360 sequence_build_options options = {}
361 ) try {
362 return [&]() -> Result<reference_database_file> {
363 using database_type = reference_database<K, BucketCount, Layout>;
364 if (genomes.size() > std::numeric_limits<uint32_t>::max() ||
365 genomes.size() >
366 std::numeric_limits<size_t>::max() / (BucketCount * sizeof(uint32_t))) {
367 return Err(Error::resource("reference collection exceeds database capacity"));
368 }
369 reference_database_file result;
370 result.metadata_ = {
371 score_compatibility::current<K, BucketCount, Layout>(),
372 static_cast<uint32_t>(genomes.size())
373 };
374 CUDDL_TRY((result.set_blacklist<K, BucketCount>(options.blacklist, stream)));
375 result.rows_.resize(genomes.size() * BucketCount);
376 result.names_.reserve(genomes.size());
377 if (genomes.empty()) return result;
378 CUDDL_TRY((detail::stage_sequences<K, BucketCount, Layout>(
379 genomes,
380 stream,
381 options.staging_bytes,
382 options.statistics,
383 result.download_rows<BucketCount>(stream),
384 options.blacklist ? options.blacklist->get().view() : cuda::std::nullopt
385 )));
386 for (auto const& genome : genomes) result.names_.emplace_back(genome.name);
387 static_assert(sizeof(database_type) > 0);
388 return result;
389 }();
390 } catch (cuda::cuda_error const& error) {
391 return Err(Error::cuda(static_cast<cudaError_t>(error.status())));
392 } catch (std::system_error const& error) {
393 return Err(Error::resource(error.what()));
394 } catch (std::bad_alloc const& error) {
395 return Err(Error::resource(error.what()));
396 }
397
408 template <uint32_t K, size_t BucketCount, typename Layout = default_register_layout>
411 std::span<std::string const> names,
412 cuda::stream_ref stream,
414 ) try {
415 return [&]() -> Result<reference_database_file> {
416 if (store.size() % BucketCount != 0) {
417 return Err(Error::invalid_argument("a store must contain whole sketches"));
418 }
419 size_t const count = store.size() / BucketCount;
420 if (!names.empty() && names.size() != count) {
421 return Err(Error::invalid_argument("labels must be empty or name every reference"));
422 }
423 if (count != 0 && store.data() == nullptr) {
424 return Err(Error::invalid_argument("a nonempty store must not be null"));
425 }
427 result.metadata_ = {
428 score_compatibility::current<K, BucketCount, Layout>(), static_cast<uint32_t>(count)
429 };
430 if (!blacklist.keys().empty() && blacklist.kmer_length() != K) {
431 return Err(Error::invalid_argument("store blacklist k mismatch"));
432 }
433 result.blacklist_ = blacklist;
436 result.rows_.resize(count * BucketCount);
437 if (count == 0) return result;
438 CUDDL_TRY(detail::download_store<BucketCount>(store, result.rows_, stream));
439 CUDDL_CUDA_TRY(stream.sync());
440 result.names_.assign(names.begin(), names.end());
441 return result;
442 }();
443 } catch (cuda::cuda_error const& error) {
444 return Err(Error::cuda(static_cast<cudaError_t>(error.status())));
445 } catch (std::bad_alloc const& error) {
446 return Err(Error::resource(error.what()));
447 }
448
450 template <uint32_t K, size_t BucketCount, typename Layout = default_register_layout>
452 cuda::stream_ref stream
453 ) const {
454 CUDDL_TRY((detail::validate_indexed_score_compatibility<K, BucketCount, Layout>(
455 metadata_.compatibility
456 )));
457 auto rows =
458 CUDDL_CUDA_TRY(cuda::make_device_buffer<uint16_t>(stream, stream.device(), rows_));
460 {rows.data(), rows.size()}, metadata_.compatibility, stream
461 )));
462 database.names_ = names_;
463 CUDDL_CUDA_TRY(stream.sync());
464 return std::move(database);
465 }
466
474 [[nodiscard]] Result<void> save(std::filesystem::path const& path) const try {
475 return [&]() -> Result<void> {
477 writer.temporary = path.string() + ".tmp.XXXXXX";
478 auto fd = ::mkstemp(writer.temporary.data());
479 if (fd < 0) {
480 writer.temporary.clear();
481 return Err(Error::resource("cannot create database file beside: " + path.string()));
482 }
483 writer.output = ::fdopen(fd, "wb");
484 if (!writer.output) {
485 ::close(fd);
486 return Err(Error::resource("cannot open temporary database file"));
487 }
488 CUDDL_TRY(writer.bytes("CUDDLDB\0", 8));
489 CUDDL_TRY(writer.value(database_file_version));
490 auto metadata = metadata_;
493 return Err(
494 Error::invalid_argument("database is missing its construction blacklist")
495 );
496 }
497 auto blacklist_count = static_cast<uint32_t>(blacklist_.keys().size());
498 CUDDL_TRY(detail::database_file_metadata(writer, metadata, blacklist_count));
499 CUDDL_TRY(writer.words(blacklist_.keys()));
500 auto const prefix = ::ftello(writer.output);
501 auto const limit = static_cast<uint64_t>(std::numeric_limits<off_t>::max());
502 if (prefix < 0 || static_cast<uint64_t>(prefix) > limit - sizeof(uint32_t)) {
503 return Err(Error::resource("cannot size temporary database file"));
504 }
505 uint64_t bytes = static_cast<uint64_t>(prefix) + sizeof(uint32_t);
506 if (rows_.size() > (limit - bytes) / sizeof(uint16_t)) {
507 return Err(Error::resource("database exceeds file size capacity"));
508 }
509 bytes += rows_.size() * sizeof(uint16_t);
510 for (auto const& name : names_) {
511 if (name.size() > std::numeric_limits<uint32_t>::max()) {
512 return Err(Error::resource("reference label exceeds binary format capacity"));
513 }
514 if (bytes > limit - sizeof(uint32_t) ||
515 name.size() > limit - bytes - sizeof(uint32_t)) {
516 return Err(Error::resource("database exceeds file size capacity"));
517 }
518 bytes += sizeof(uint32_t) + name.size();
519 }
520 // Allocate before names spill from stdio, avoiding delayed extent allocation when
521 // the completed temporary file atomically replaces an existing database.
522 auto const allocation = ::posix_fallocate(fd, 0, static_cast<off_t>(bytes));
523 if (allocation != 0 && allocation != EOPNOTSUPP && allocation != ENOSYS) {
524 return Err(
526 "cannot allocate database file: " +
527 std::generic_category().message(allocation)
528 )
529 );
530 }
531 for (auto const& name : names_) {
532 CUDDL_TRY(writer.value(static_cast<uint32_t>(name.size())));
533 CUDDL_TRY(writer.bytes(name.data(), name.size()));
534 }
535 CUDDL_TRY(writer.words(rows_));
536 CUDDL_TRY(writer.value(static_cast<uint32_t>(writer.checksum)));
537 auto closed = std::fclose(std::exchange(writer.output, nullptr));
538 if (closed != 0) return Err(Error::resource("cannot close database file"));
539 std::filesystem::rename(writer.temporary, path);
540 writer.temporary.clear();
541 return Ok();
542 }();
543 } catch (std::filesystem::filesystem_error const& error) {
544 return Err(Error::resource(error.what()));
545 } catch (std::bad_alloc const& error) {
546 return Err(Error::resource(error.what()));
547 }
548
551 std::filesystem::path const& path
552 ) try {
553 return [&]() -> Result<reference_database_file> {
555 std::ifstream(path, std::ios::binary | std::ios::ate)
556 };
557 auto size = reader.input.tellg();
558 if (size < 0) {
559 return Err(Error::resource("cannot open database file: " + path.string()));
560 }
561 reader.remaining = static_cast<uint64_t>(size);
562 reader.input.seekg(0);
563 char magic[8];
564 CUDDL_TRY(reader.bytes(magic, sizeof(magic)));
565 if (std::string_view(magic, sizeof(magic)) != std::string_view("CUDDLDB\0", 8)) {
566 return Err(Error::invalid_argument("not a cuDDL reference database file"));
567 }
568 uint16_t version{};
569 CUDDL_TRY(reader.value(version));
570 if (version != database_file_version) {
571 return Err(Error::invalid_argument("unsupported database file version"));
572 }
574 uint32_t blacklist_count = 0;
575 CUDDL_TRY(detail::database_file_metadata(reader, result.metadata_, blacklist_count));
576 auto& c = result.metadata_.compatibility;
577 if (c.kmer_length < 1 || c.kmer_length > 31 || c.bucket_count < 2048 ||
578 c.bucket_count > 8192 || !std::has_single_bit(c.bucket_count) ||
579 (c.key_mask != 0xffffU && c.key_mask != 0x7fffU) || c.exponent_bits == 0 ||
580 c.exponent_bits >= 16) {
581 return Err(Error::invalid_argument("unsupported database construction metadata"));
582 }
583 c.indexed_bucket_count = c.bucket_count;
584 c.mantissa_bits = 16U - c.exponent_bits;
585 c.score_encoder_identity = 1U;
586 c.hash_identity = 1U;
587 c.hash_seed = detail::seed;
588 c.canonicalisation_policy = 1U;
589 c.blacklist_version = blacklist_count == 0 ? 0U : 1U;
590 if (blacklist_count > reader.remaining / sizeof(uint64_t)) {
591 return Err(Error::invalid_argument("invalid blacklist extent"));
592 }
593 std::vector<uint64_t> keys(blacklist_count);
594 CUDDL_TRY(reader.words(keys));
595 if (!std::is_sorted(keys.begin(), keys.end()) ||
596 std::adjacent_find(keys.begin(), keys.end()) != keys.end()) {
597 return Err(Error::invalid_argument("blacklist payload is not sorted and unique"));
598 }
599 result.blacklist_ = kmer_blacklist(c.kmer_length, std::move(keys));
600 if (result.blacklist_.identity() != c.blacklist_identity ||
601 result.blacklist_.version() != c.blacklist_version) {
602 return Err(Error::invalid_argument("blacklist identity mismatch"));
603 }
604 uint64_t const count = result.metadata_.reference_count;
605 uint64_t const words = count * c.bucket_count;
606 uint64_t const payload_bytes = words * sizeof(uint16_t);
607 if (words > std::numeric_limits<size_t>::max() / sizeof(uint16_t) ||
608 payload_bytes + count * sizeof(uint32_t) + sizeof(uint32_t) > reader.remaining) {
609 return Err(Error::invalid_argument("invalid reference database extents"));
610 }
611 result.names_.reserve(count);
612 for (uint64_t id = 0; id < count; ++id) {
613 uint32_t length{};
614 CUDDL_TRY(reader.value(length));
615 auto const required =
616 payload_bytes + (count - id - 1) * sizeof(uint32_t) + sizeof(uint32_t);
617 if (required > reader.remaining || length > reader.remaining - required) {
618 return Err(Error::invalid_argument("invalid reference label extent"));
619 }
620 std::string name(length, '\0');
621 CUDDL_TRY(reader.bytes(name.data(), name.size()));
622 result.names_.push_back(std::move(name));
623 }
624 if (reader.remaining != payload_bytes + sizeof(uint32_t)) {
625 return Err(Error::invalid_argument("unexpected reference database payload size"));
626 }
627 result.rows_.resize(words);
628 CUDDL_TRY(reader.words(result.rows_));
629 auto const expected_checksum = static_cast<uint32_t>(reader.checksum);
630 uint32_t checksum{};
631 CUDDL_TRY(reader.value(checksum));
632 if (checksum != expected_checksum) {
633 return Err(Error::invalid_argument("database checksum mismatch"));
634 }
635 return result;
636 }();
637 } catch (std::invalid_argument const& error) {
638 return Err(Error::invalid_argument(error.what()));
639 } catch (std::bad_alloc const& error) {
640 return Err(Error::resource(error.what()));
641 }
642
643 private:
646 reference_database_file() = default;
647
650 template <size_t BucketCount>
651 [[nodiscard]] auto download_rows(cuda::stream_ref stream) {
652 return [this, stream](device_span<uint32_t const> group, size_t base, size_t count) {
653 return detail::download_store<BucketCount>(
654 group, std::span{rows_}.subspan(base * BucketCount, count * BucketCount), stream
655 );
656 };
657 }
658
659 template <uint32_t K, size_t BucketCount>
660 Result<void> set_blacklist(
661 std::optional<std::reference_wrapper<device_blacklist const>> filter,
662 cuda::stream_ref stream
663 ) {
664 if (!filter) return Ok();
665 CUDDL_TRY(filter->get().validate(K, BucketCount, stream));
666 blacklist_ = filter->get().source();
667 metadata_.compatibility.blacklist_identity = blacklist_.identity();
668 metadata_.compatibility.blacklist_version = blacklist_.version();
669 return Ok();
670 }
671
672 kmer_blacklist blacklist_;
673 reference_database_metadata metadata_{};
674 std::vector<std::string> names_;
675 std::vector<uint16_t> rows_;
676};
677
684template <uint32_t K, size_t BucketCount, typename Layout = default_register_layout>
686 std::span<std::filesystem::path const> paths,
687 cuda::stream_ref stream,
688 path_build_options options = {}
689) try {
690 return [&]() -> Result<cuda::device_buffer<uint32_t>> {
691 if (options.blacklist) CUDDL_TRY(options.blacklist->get().validate(K, BucketCount, stream));
692
693 auto store = cuda::make_device_buffer<uint32_t>(
694 stream, stream.device(), paths.size() * BucketCount, cuda::no_init
695 );
696
697 CUDDL_TRY((detail::stage_paths<K, BucketCount, Layout>(
698 paths,
699 stream,
700 options.staging_bytes,
701 options.parser_workers,
702 options.transfer,
703 options.decompression,
704 options.direct_reads,
705 options.statistics,
706 [&](device_span<uint32_t const> group, size_t base, size_t count) -> Result<void> {
707 CUDDL_CUDA_TRY(
708 cuda::copy_bytes(
709 stream,
710 group,
711 device_span<uint32_t>{
712 store.data() + base * BucketCount, count * BucketCount
713 }
714 )
715 );
716 return Ok();
717 },
718 options.blacklist ? options.blacklist->get().view() : cuda::std::nullopt
719 )));
720 return store;
721 }();
722} catch (cuda::cuda_error const& error) {
723 return Err(Error::cuda(static_cast<cudaError_t>(error.status())));
724} catch (std::system_error const& error) {
725 return Err(Error::resource(error.what()));
726} catch (std::bad_alloc const& error) {
727 return Err(Error::resource(error.what()));
728}
729
730} // namespace cuddl
Immutable canonical k-mers excluded from construction, independent of sketch size.
Definition blacklist.cuh:77
uint32_t kmer_length() const noexcept
std::vector< uint64_t > const & keys() const noexcept
uint64_t identity() const noexcept
uint8_t version() const noexcept
Host-owned reference sketches and labels, ready for binary storage or GPU upload.
static Result< reference_database_file > from_store(device_span< uint32_t const > store, std::span< std::string const > names, cuda::stream_ref stream, kmer_blacklist const &blacklist)
Adopts a device store as the host rows this form saves.
kmer_blacklist const & blacklist() const noexcept
Result< reference_database< K, BucketCount, Layout > > upload(cuda::stream_ref stream) const
Uploads reference score rows and labels without constructing an index.
Result< void > save(std::filesystem::path const &path) const
Writes a versioned, little-endian, CRC32-checked binary file.
std::span< std::string const > names() const noexcept
Reference labels in reference-ID order.
std::span< uint16_t const > rows() const noexcept
Winner scores in reference-ID order, one bucket row per genome.
static Result< reference_database_file > build_from_sequences(std::span< sequence_genome const > genomes, cuda::stream_ref stream, sequence_build_options options={})
Builds one sketch per genome from bases the caller already holds.
static Result< reference_database_file > load(std::filesystem::path const &path)
Reads and validates a binary database without requiring a GPU.
reference_database_metadata metadata() const noexcept
Compatibility metadata and reference count.
static Result< reference_database_file > build(std::span< std::filesystem::path const > paths, cuda::stream_ref stream, path_build_options options={})
Builds one sketch per plain or gzip/BGZF FASTA/FASTQ file on the GPU.
Move-only owner of one immutable contiguous reference database.
Index-file persistence and the validation boundary for reference_index.
#define CUDDL_CUDA_TRY(...)
Propagates a CUDA error wrapped in cuddl::Result<void>.
Definition error.hpp:248
#define CUDDL_TRY(expr)
Propagates a cuddl::Result failure from the enclosing function (GNU statement expression).
Definition error.hpp:225
T database_file_little_endian(T value)
transfer_mode
How a build gets CPU-parsed sequence bytes to the device.
constexpr uint64_t seed
Domain separation constant folded into every packed k-mer before hashing.
Definition hash.cuh:18
decompression_backend
Where inputs are decompressed; sketch construction always runs on the GPU.
auto const items
Definition kernels.cuh:1549
Result< void > database_file_metadata(IO &io, reference_database_metadata &metadata, uint32_t &blacklist_count)
Batch operations over contiguous device rows.
Definition a48.hpp:14
Result< void > Ok() noexcept
Success return for Result<void>; same as return {}.
Definition error.hpp:181
Result< std::vector< std::filesystem::path > > list_genomes(std::filesystem::path const &input)
Paths from a directory (see genome_paths) or from a text file with one path per line,...
cuda::std::unexpected< Error > Err(Error error)
Failure return; converts to any Result<T> via cuda::std::unexpected.
Definition error.hpp:176
Result< std::vector< std::filesystem::path > > genome_paths(std::filesystem::path const &directory)
The FASTA/FASTQ files under directory, searched recursively and sorted by path, which is the order th...
unsigned default_parser_workers()
Loaders a path build runs unless the caller asks for another count.
Result< cuda::device_buffer< uint32_t > > build_sketch_store(std::span< std::filesystem::path const > paths, cuda::stream_ref stream, path_build_options options={})
Sketches one genome per plain or gzip/BGZF FASTA/FASTQ file into a device store.
static Error cuda(cudaError_t code, std::source_location location=std::source_location::current())
Definition error.hpp:90
static Error invalid_argument(std::string message)
Definition error.hpp:99
static Error resource(std::string message)
Definition error.hpp:103
Fallible API result: cuda::std::expected<T, Error> with cuDDL factories.
Definition error.hpp:118
Result< void > bytes(void *destination, size_t size)
Result< void > words(std::vector< T > &items)
Result< void > words(std::vector< T > const &items)
Result< void > bytes(void const *source, size_t size)
Builds a reference_database_file from gathered parts; the file's members are private.
One genome as its records, in order. No records keeps the genome's ID with a zero row.
One genome of bases the caller already holds.
A span that is assumed to point to device-accessible memory.
Knobs for a build the caller feeds with FASTA/FASTQ paths.
transfer_mode transfer
How the build moves bytes.
std::optional< size_t > staging_bytes
Arena bytes.
unsigned parser_workers
Loaders to run.
std::optional< std::reference_wrapper< device_blacklist const > > blacklist
Reusable lookup; keep alive through this synchronous build.
reference_build_statistics * statistics
bool direct_reads
Read the files with O_DIRECT, past the page cache.
How a build moved sequence bytes to the device.
Recorded metadata for an immutable reference database.
Knobs for a build the caller feeds with bases it already holds.
reference_build_statistics * statistics
std::optional< std::reference_wrapper< device_blacklist const > > blacklist
std::optional< size_t > staging_bytes
Arena bytes.