From 74391420fa9890d33e8d89a15c816e6378c7aaef Mon Sep 17 00:00:00 2001 From: Arun Isaac Date: Sun, 12 Jul 2026 02:07:47 +0100 Subject: Initial commit --- c/importgfa.c | 374 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 374 insertions(+) create mode 100644 c/importgfa.c (limited to 'c') diff --git a/c/importgfa.c b/c/importgfa.c new file mode 100644 index 0000000..43cc948 --- /dev/null +++ b/c/importgfa.c @@ -0,0 +1,374 @@ +/// domagi --- DuckDB-powered pangenome Swiss Army knife +/// Copyright © 2026 Arun Isaac +/// +/// This file is part of domagi. +/// +/// domagi is free software: you can redistribute it and/or modify it under the +/// terms of the GNU General Public License as published by the Free Software +/// Foundation, either version 3 of the License, or (at your option) any later +/// version. +/// +/// domagi is distributed in the hope that it will be useful, but WITHOUT ANY +/// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +/// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +/// details. +/// +/// You should have received a copy of the GNU General Public License along with +/// domagi. If not, see . + +#include +#include +#include +#include + +#include + +#define NAME str_int_map +#define KEY_TY const char* +#define VAL_TY int +#include + +void die(const char *message) +{ + fprintf(stderr, "%s\n", message); + exit(EXIT_FAILURE); +} + +static int ceildiv (int x, int y) +{ + return (x / y) + (x % y ? 1 : 0); +} + +static int hashtable_get (str_int_map *table, const char *key) +{ + // Look up key in hash table and return the corresponding key. The program is + // aborted if the key is not found. This is ok for our limited purposes since + // all keys we look up are guaranteed to be present. + str_int_map_itr itr = vt_get(table, key); + assert(!vt_is_end(itr)); + return itr.data->val; +} + +static uint8_t orientation2int (char c) +{ + return c == '+' ? 0 : 1; +} + +static void pass1_handle_s_line (char *line, str_int_map *segment_id_table, duckdb_appender *appender) +{ + static int segment_id = 0; + + // Split S line. + strsep(&line, "\t"); + char *segment_name = strsep(&line, "\t"); + char *sequence = strsep(&line, "\t\n"); + + // Map segment name to ID in table. + vt_insert(segment_id_table, strdup(segment_name), segment_id); + // Append to database. + duckdb_append_int32(*appender, segment_id); + duckdb_append_varchar(*appender, segment_name); + duckdb_append_varchar(*appender, sequence); + duckdb_appender_end_row(*appender); + // Increment segment ID for next segment. + segment_id++; +} + +static void pass1_handle_p_line (char *line, duckdb_appender *appender) +{ + static int path_id = 0; + + // Split P line. + strsep(&line, "\t"); + char *path_name = strsep(&line, "\t"); + + // Append to database. There are usually relatively few P lines. So, we're + // content with a simple row-wise appender. + duckdb_append_int32(*appender, path_id); + duckdb_append_varchar(*appender, path_name); + duckdb_appender_end_row(*appender); + // Increment path ID for next path. + path_id++; +} + +#define LINK_COLUMN_COUNT 4 +static void process_l_lines (char **lines, size_t line_count, str_int_map *segment_id_table, duckdb_database *db) +{ + idx_t vector_size = duckdb_vector_size(); + duckdb_logical_type int_type = duckdb_create_logical_type(DUCKDB_TYPE_INTEGER); + duckdb_logical_type orientation_type = duckdb_create_enum_type((const char *[]){"+", "-"}, 2); + duckdb_logical_type types[LINK_COLUMN_COUNT] + = {int_type, orientation_type, int_type, orientation_type}; +#pragma omp parallel + { + duckdb_connection con; + if (duckdb_connect(*db, &con) == DuckDBError) + die("Unable to connect to DuckDB database"); + duckdb_appender appender; + if (duckdb_appender_create(con, NULL, "link", &appender) == DuckDBError) + die("Unable to create appender for link table"); + duckdb_data_chunk chunk = duckdb_create_data_chunk(types, LINK_COLUMN_COUNT); + duckdb_data_chunk_set_size(chunk, vector_size); + int32_t *from_segment_data = duckdb_vector_get_data(duckdb_data_chunk_get_vector(chunk, 0)); + uint8_t *from_orientation_data = duckdb_vector_get_data(duckdb_data_chunk_get_vector(chunk, 1)); + int32_t *to_segment_data = duckdb_vector_get_data(duckdb_data_chunk_get_vector(chunk, 2)); + uint8_t *to_orientation_data = duckdb_vector_get_data(duckdb_data_chunk_get_vector(chunk, 3)); +#pragma omp for + for (size_t slice_index=0; slice_index 0) { + switch (line[0]) { + case 'S': + pass1_handle_s_line(line, &segment_id_table, &segment_appender); + break; + case 'P': + pass1_handle_p_line(line, &path_appender); + break; + case 'L': + l_line_count++; + break; + default: + break; + } + } + if (duckdb_appender_destroy(&segment_appender) == DuckDBError) + die("Unable to destroy segment appender"); + if (duckdb_appender_destroy(&path_appender) == DuckDBError) + die("Unable to destroy path appender"); + } + rewind(gfa_fp); + // Pass 2 + { + char **l_lines = malloc(l_line_count*sizeof(char*)); + size_t l_line_index = 0, path_id = 0; + while ((line_length = getline(&line, &n, gfa_fp)) > 0) { + switch (line[0]) { + case 'L': + l_lines[l_line_index] = strdup(line); + l_line_index++; + break; + case 'P': + process_p_line(line, line_length, &segment_id_table, path_id, &db); + path_id++; + break; + default: + break; + } + } + process_l_lines(l_lines, l_line_count, &segment_id_table, &db); + free(l_lines); + } + + free(line); + fclose(gfa_fp); + duckdb_disconnect(&con); + duckdb_close(&db); + for (str_int_map_itr itr=vt_first(&segment_id_table); !vt_is_end(itr); itr=vt_next(itr)) + free((char*)itr.data->key); + vt_cleanup(&segment_id_table); + + return 0; +} + +int main (int argc, char *argv[]) +{ + if (argc != 3) { + fprintf(stderr, "Usage: %s \n", argv[0]); + exit(EXIT_FAILURE); + } + return import_gfa(argv[1], argv[2]); +} -- cgit 1.4.1