diff --git a/c/tests/test_tables.c b/c/tests/test_tables.c index 2975dfe622..0e7e4dc498 100644 --- a/c/tests/test_tables.c +++ b/c/tests/test_tables.c @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2019-2020 Tskit Developers + * Copyright (c) 2019-2021 Tskit Developers * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -753,6 +753,88 @@ test_node_table(void) free(individual); } +static void +test_node_table_update_row(void) +{ + int ret; + tsk_id_t ret_id; + tsk_node_table_t table; + tsk_node_t row; + const char *metadata = "ABC"; + + ret = tsk_node_table_init(&table, 0); + CU_ASSERT_EQUAL_FATAL(ret, 0); + + ret_id = tsk_node_table_add_row(&table, 0, 1.0, 2, 3, metadata, 1); + CU_ASSERT_FATAL(ret_id >= 0); + ret_id = tsk_node_table_add_row(&table, 1, 2.0, 3, 4, metadata, 2); + CU_ASSERT_FATAL(ret_id >= 0); + ret_id = tsk_node_table_add_row(&table, 2, 3.0, 4, 5, metadata, 3); + CU_ASSERT_FATAL(ret_id >= 0); + + ret = tsk_node_table_update_row(&table, 0, 1, 2.0, 3, 4, &metadata[1], 1); + CU_ASSERT_EQUAL_FATAL(ret, 0); + ret = tsk_node_table_get_row(&table, 0, &row); + CU_ASSERT_EQUAL_FATAL(ret, 0); + CU_ASSERT_EQUAL_FATAL(row.flags, 1); + CU_ASSERT_EQUAL_FATAL(row.time, 2.0); + CU_ASSERT_EQUAL_FATAL(row.population, 3); + CU_ASSERT_EQUAL_FATAL(row.individual, 4); + CU_ASSERT_EQUAL_FATAL(row.metadata_length, 1); + CU_ASSERT_EQUAL_FATAL(row.metadata[0], 'B'); + + ret = tsk_node_table_update_row(&table, 0, row.flags + 1, row.time + 1, + row.population + 1, row.individual + 1, row.metadata, row.metadata_length); + CU_ASSERT_EQUAL_FATAL(ret, 0); + ret = tsk_node_table_get_row(&table, 0, &row); + CU_ASSERT_EQUAL_FATAL(ret, 0); + CU_ASSERT_EQUAL_FATAL(row.flags, 2); + CU_ASSERT_EQUAL_FATAL(row.time, 3.0); + CU_ASSERT_EQUAL_FATAL(row.population, 4); + CU_ASSERT_EQUAL_FATAL(row.individual, 5); + CU_ASSERT_EQUAL_FATAL(row.metadata_length, 1); + CU_ASSERT_EQUAL_FATAL(row.metadata[0], 'B'); + + ret = tsk_node_table_update_row(&table, 0, 0, 0, 0, 0, metadata, 3); + CU_ASSERT_EQUAL_FATAL(ret, 0); + ret = tsk_node_table_get_row(&table, 0, &row); + CU_ASSERT_EQUAL_FATAL(ret, 0); + CU_ASSERT_EQUAL_FATAL(row.flags, 0); + CU_ASSERT_EQUAL_FATAL(row.time, 0); + CU_ASSERT_EQUAL_FATAL(row.population, 0); + CU_ASSERT_EQUAL_FATAL(row.individual, 0); + CU_ASSERT_EQUAL_FATAL(row.metadata_length, 3); + CU_ASSERT_EQUAL_FATAL(row.metadata[0], 'A'); + CU_ASSERT_EQUAL_FATAL(row.metadata[1], 'B'); + CU_ASSERT_EQUAL_FATAL(row.metadata[2], 'C'); + + ret = tsk_node_table_update_row(&table, 1, 0, 0, 0, 0, NULL, 0); + CU_ASSERT_EQUAL_FATAL(ret, 0); + ret = tsk_node_table_get_row(&table, 1, &row); + CU_ASSERT_EQUAL_FATAL(ret, 0); + CU_ASSERT_EQUAL_FATAL(row.flags, 0); + CU_ASSERT_EQUAL_FATAL(row.time, 0); + CU_ASSERT_EQUAL_FATAL(row.population, 0); + CU_ASSERT_EQUAL_FATAL(row.individual, 0); + CU_ASSERT_EQUAL_FATAL(row.metadata_length, 0); + + ret = tsk_node_table_get_row(&table, 2, &row); + CU_ASSERT_EQUAL_FATAL(ret, 0); + CU_ASSERT_EQUAL_FATAL(row.flags, 2); + CU_ASSERT_EQUAL_FATAL(row.time, 3.0); + CU_ASSERT_EQUAL_FATAL(row.population, 4); + CU_ASSERT_EQUAL_FATAL(row.individual, 5); + CU_ASSERT_EQUAL_FATAL(row.metadata_length, 3); + CU_ASSERT_EQUAL_FATAL(row.metadata[0], 'A'); + CU_ASSERT_EQUAL_FATAL(row.metadata[1], 'B'); + CU_ASSERT_EQUAL_FATAL(row.metadata[2], 'C'); + + ret = tsk_node_table_update_row(&table, 3, 0, 0, 0, 0, NULL, 0); + CU_ASSERT_EQUAL_FATAL(ret, TSK_ERR_NODE_OUT_OF_BOUNDS); + + tsk_node_table_free(&table); +} + static void test_edge_table_with_options(tsk_flags_t options) { @@ -1161,6 +1243,134 @@ test_edge_table(void) test_edge_table_with_options(TSK_NO_METADATA); } +static void +test_edge_table_update_row(void) +{ + int ret; + tsk_id_t ret_id; + tsk_edge_table_t table; + tsk_edge_t row; + const char *metadata = "ABC"; + + ret = tsk_edge_table_init(&table, 0); + CU_ASSERT_EQUAL_FATAL(ret, 0); + + ret_id = tsk_edge_table_add_row(&table, 0, 1.0, 2, 3, metadata, 1); + CU_ASSERT_FATAL(ret_id >= 0); + ret_id = tsk_edge_table_add_row(&table, 1, 2.0, 3, 4, metadata, 2); + CU_ASSERT_FATAL(ret_id >= 0); + ret_id = tsk_edge_table_add_row(&table, 2, 3.0, 4, 5, metadata, 3); + CU_ASSERT_FATAL(ret_id >= 0); + + ret = tsk_edge_table_update_row(&table, 0, 1, 2.0, 3, 4, &metadata[1], 1); + CU_ASSERT_EQUAL_FATAL(ret, 0); + ret = tsk_edge_table_get_row(&table, 0, &row); + CU_ASSERT_EQUAL_FATAL(ret, 0); + CU_ASSERT_EQUAL_FATAL(row.left, 1); + CU_ASSERT_EQUAL_FATAL(row.right, 2.0); + CU_ASSERT_EQUAL_FATAL(row.parent, 3); + CU_ASSERT_EQUAL_FATAL(row.child, 4); + CU_ASSERT_EQUAL_FATAL(row.metadata_length, 1); + CU_ASSERT_EQUAL_FATAL(row.metadata[0], 'B'); + + ret = tsk_edge_table_update_row(&table, 0, row.left + 1, row.right + 1, + row.parent + 1, row.child + 1, row.metadata, row.metadata_length); + CU_ASSERT_EQUAL_FATAL(ret, 0); + ret = tsk_edge_table_get_row(&table, 0, &row); + CU_ASSERT_EQUAL_FATAL(ret, 0); + CU_ASSERT_EQUAL_FATAL(row.left, 2); + CU_ASSERT_EQUAL_FATAL(row.right, 3.0); + CU_ASSERT_EQUAL_FATAL(row.parent, 4); + CU_ASSERT_EQUAL_FATAL(row.child, 5); + CU_ASSERT_EQUAL_FATAL(row.metadata_length, 1); + CU_ASSERT_EQUAL_FATAL(row.metadata[0], 'B'); + + ret = tsk_edge_table_update_row(&table, 0, 0, 0, 0, 0, metadata, 3); + CU_ASSERT_EQUAL_FATAL(ret, 0); + ret = tsk_edge_table_get_row(&table, 0, &row); + CU_ASSERT_EQUAL_FATAL(ret, 0); + CU_ASSERT_EQUAL_FATAL(row.left, 0); + CU_ASSERT_EQUAL_FATAL(row.right, 0); + CU_ASSERT_EQUAL_FATAL(row.parent, 0); + CU_ASSERT_EQUAL_FATAL(row.child, 0); + CU_ASSERT_EQUAL_FATAL(row.metadata_length, 3); + CU_ASSERT_EQUAL_FATAL(row.metadata[0], 'A'); + CU_ASSERT_EQUAL_FATAL(row.metadata[1], 'B'); + CU_ASSERT_EQUAL_FATAL(row.metadata[2], 'C'); + + ret = tsk_edge_table_update_row(&table, 1, 0, 0, 0, 0, NULL, 0); + CU_ASSERT_EQUAL_FATAL(ret, 0); + ret = tsk_edge_table_get_row(&table, 1, &row); + CU_ASSERT_EQUAL_FATAL(ret, 0); + CU_ASSERT_EQUAL_FATAL(row.left, 0); + CU_ASSERT_EQUAL_FATAL(row.right, 0); + CU_ASSERT_EQUAL_FATAL(row.parent, 0); + CU_ASSERT_EQUAL_FATAL(row.child, 0); + CU_ASSERT_EQUAL_FATAL(row.metadata_length, 0); + + ret = tsk_edge_table_get_row(&table, 2, &row); + CU_ASSERT_EQUAL_FATAL(ret, 0); + CU_ASSERT_EQUAL_FATAL(row.left, 2); + CU_ASSERT_EQUAL_FATAL(row.right, 3.0); + CU_ASSERT_EQUAL_FATAL(row.parent, 4); + CU_ASSERT_EQUAL_FATAL(row.child, 5); + CU_ASSERT_EQUAL_FATAL(row.metadata_length, 3); + CU_ASSERT_EQUAL_FATAL(row.metadata[0], 'A'); + CU_ASSERT_EQUAL_FATAL(row.metadata[1], 'B'); + CU_ASSERT_EQUAL_FATAL(row.metadata[2], 'C'); + + ret = tsk_edge_table_update_row(&table, 3, 0, 0, 0, 0, NULL, 0); + CU_ASSERT_EQUAL_FATAL(ret, TSK_ERR_EDGE_OUT_OF_BOUNDS); + + tsk_edge_table_free(&table); +} + +static void +test_edge_table_update_row_no_metadata(void) +{ + int ret; + tsk_id_t ret_id; + tsk_edge_table_t table; + tsk_edge_t row; + const char *metadata = "ABC"; + + ret = tsk_edge_table_init(&table, TSK_NO_METADATA); + CU_ASSERT_EQUAL_FATAL(ret, 0); + + ret_id = tsk_edge_table_add_row(&table, 0, 1.0, 2, 3, NULL, 0); + CU_ASSERT_FATAL(ret_id >= 0); + ret_id = tsk_edge_table_add_row(&table, 1, 2.0, 3, 4, NULL, 0); + CU_ASSERT_FATAL(ret_id >= 0); + ret_id = tsk_edge_table_add_row(&table, 2, 3.0, 4, 5, NULL, 0); + CU_ASSERT_FATAL(ret_id >= 0); + + ret = tsk_edge_table_update_row(&table, 0, 1, 2.0, 3, 4, NULL, 0); + CU_ASSERT_EQUAL_FATAL(ret, 0); + ret = tsk_edge_table_get_row(&table, 0, &row); + CU_ASSERT_EQUAL_FATAL(ret, 0); + CU_ASSERT_EQUAL_FATAL(row.left, 1); + CU_ASSERT_EQUAL_FATAL(row.right, 2.0); + CU_ASSERT_EQUAL_FATAL(row.parent, 3); + CU_ASSERT_EQUAL_FATAL(row.child, 4); + CU_ASSERT_EQUAL_FATAL(row.metadata_length, 0); + + ret = tsk_edge_table_update_row(&table, 0, row.left + 1, row.right + 1, + row.parent + 1, row.child + 1, row.metadata, row.metadata_length); + CU_ASSERT_EQUAL_FATAL(ret, 0); + ret = tsk_edge_table_get_row(&table, 0, &row); + CU_ASSERT_EQUAL_FATAL(ret, 0); + CU_ASSERT_EQUAL_FATAL(row.left, 2); + CU_ASSERT_EQUAL_FATAL(row.right, 3.0); + CU_ASSERT_EQUAL_FATAL(row.parent, 4); + CU_ASSERT_EQUAL_FATAL(row.child, 5); + CU_ASSERT_EQUAL_FATAL(row.metadata_length, 0); + + ret = tsk_edge_table_update_row(&table, 1, 0, 0, 0, 0, metadata, 3); + CU_ASSERT_EQUAL_FATAL(ret, TSK_ERR_METADATA_DISABLED); + + tsk_edge_table_free(&table); +} + static void test_edge_table_copy_semantics(void) { @@ -1736,6 +1946,109 @@ test_site_table(void) free(metadata_offset); } +static void +test_site_table_update_row(void) +{ + int ret; + tsk_id_t ret_id; + tsk_site_table_t table; + tsk_site_t row; + const char *ancestral_state = "XYZ"; + const char *metadata = "ABC"; + + ret = tsk_site_table_init(&table, 0); + CU_ASSERT_EQUAL_FATAL(ret, 0); + + ret_id = tsk_site_table_add_row(&table, 0, ancestral_state, 1, metadata, 1); + CU_ASSERT_FATAL(ret_id >= 0); + ret_id = tsk_site_table_add_row(&table, 1, ancestral_state, 2, metadata, 2); + CU_ASSERT_FATAL(ret_id >= 0); + ret_id = tsk_site_table_add_row(&table, 2, ancestral_state, 3, metadata, 3); + CU_ASSERT_FATAL(ret_id >= 0); + + ret = tsk_site_table_update_row( + &table, 0, 1, &ancestral_state[1], 1, &metadata[1], 1); + CU_ASSERT_EQUAL_FATAL(ret, 0); + ret = tsk_site_table_get_row(&table, 0, &row); + CU_ASSERT_EQUAL_FATAL(ret, 0); + CU_ASSERT_EQUAL_FATAL(row.position, 1); + CU_ASSERT_EQUAL_FATAL(row.ancestral_state_length, 1); + CU_ASSERT_EQUAL_FATAL(row.ancestral_state[0], 'Y'); + CU_ASSERT_EQUAL_FATAL(row.metadata_length, 1); + CU_ASSERT_EQUAL_FATAL(row.metadata[0], 'B'); + + ret = tsk_site_table_update_row(&table, 0, row.position + 1, row.ancestral_state, + row.ancestral_state_length, row.metadata, row.metadata_length); + CU_ASSERT_EQUAL_FATAL(ret, 0); + ret = tsk_site_table_get_row(&table, 0, &row); + CU_ASSERT_EQUAL_FATAL(ret, 0); + CU_ASSERT_EQUAL_FATAL(row.position, 2); + CU_ASSERT_EQUAL_FATAL(row.ancestral_state_length, 1); + CU_ASSERT_EQUAL_FATAL(row.ancestral_state[0], 'Y'); + CU_ASSERT_EQUAL_FATAL(row.metadata_length, 1); + CU_ASSERT_EQUAL_FATAL(row.metadata[0], 'B'); + + ret = tsk_site_table_update_row(&table, 0, row.position, row.ancestral_state, + row.ancestral_state_length, row.metadata, row.metadata_length); + CU_ASSERT_EQUAL_FATAL(ret, 0); + ret = tsk_site_table_get_row(&table, 0, &row); + CU_ASSERT_EQUAL_FATAL(ret, 0); + CU_ASSERT_EQUAL_FATAL(row.position, 2); + CU_ASSERT_EQUAL_FATAL(row.ancestral_state_length, 1); + CU_ASSERT_EQUAL_FATAL(row.ancestral_state[0], 'Y'); + CU_ASSERT_EQUAL_FATAL(row.metadata_length, 1); + CU_ASSERT_EQUAL_FATAL(row.metadata[0], 'B'); + + ret = tsk_site_table_update_row( + &table, 0, row.position, NULL, 0, row.metadata, row.metadata_length); + CU_ASSERT_EQUAL_FATAL(ret, 0); + ret = tsk_site_table_get_row(&table, 0, &row); + CU_ASSERT_EQUAL_FATAL(ret, 0); + CU_ASSERT_EQUAL_FATAL(row.position, 2); + CU_ASSERT_EQUAL_FATAL(row.ancestral_state_length, 0); + CU_ASSERT_EQUAL_FATAL(row.metadata_length, 1); + CU_ASSERT_EQUAL_FATAL(row.metadata[0], 'B'); + + ret = tsk_site_table_update_row(&table, 0, 2, ancestral_state, 3, metadata, 3); + CU_ASSERT_EQUAL_FATAL(ret, 0); + ret = tsk_site_table_get_row(&table, 0, &row); + CU_ASSERT_EQUAL_FATAL(ret, 0); + CU_ASSERT_EQUAL_FATAL(row.position, 2); + CU_ASSERT_EQUAL_FATAL(row.ancestral_state_length, 3); + CU_ASSERT_EQUAL_FATAL(row.ancestral_state[0], 'X'); + CU_ASSERT_EQUAL_FATAL(row.ancestral_state[1], 'Y'); + CU_ASSERT_EQUAL_FATAL(row.ancestral_state[2], 'Z'); + CU_ASSERT_EQUAL_FATAL(row.metadata_length, 3); + CU_ASSERT_EQUAL_FATAL(row.metadata[0], 'A'); + CU_ASSERT_EQUAL_FATAL(row.metadata[1], 'B'); + CU_ASSERT_EQUAL_FATAL(row.metadata[2], 'C'); + + ret = tsk_site_table_update_row(&table, 1, 5, NULL, 0, NULL, 0); + CU_ASSERT_EQUAL_FATAL(ret, 0); + ret = tsk_site_table_get_row(&table, 1, &row); + CU_ASSERT_EQUAL_FATAL(ret, 0); + CU_ASSERT_EQUAL_FATAL(row.position, 5); + CU_ASSERT_EQUAL_FATAL(row.ancestral_state_length, 0); + CU_ASSERT_EQUAL_FATAL(row.metadata_length, 0); + + ret = tsk_site_table_get_row(&table, 2, &row); + CU_ASSERT_EQUAL_FATAL(ret, 0); + CU_ASSERT_EQUAL_FATAL(row.position, 2); + CU_ASSERT_EQUAL_FATAL(row.ancestral_state_length, 3); + CU_ASSERT_EQUAL_FATAL(row.ancestral_state[0], 'X'); + CU_ASSERT_EQUAL_FATAL(row.ancestral_state[1], 'Y'); + CU_ASSERT_EQUAL_FATAL(row.ancestral_state[2], 'Z'); + CU_ASSERT_EQUAL_FATAL(row.metadata_length, 3); + CU_ASSERT_EQUAL_FATAL(row.metadata[0], 'A'); + CU_ASSERT_EQUAL_FATAL(row.metadata[1], 'B'); + CU_ASSERT_EQUAL_FATAL(row.metadata[2], 'C'); + + ret = tsk_site_table_update_row(&table, 3, 0, NULL, 0, NULL, 0); + CU_ASSERT_EQUAL_FATAL(ret, TSK_ERR_SITE_OUT_OF_BOUNDS); + + tsk_site_table_free(&table); +} + static void test_mutation_table(void) { @@ -2135,6 +2448,136 @@ test_mutation_table(void) free(metadata_offset); } +static void +test_mutation_table_update_row(void) +{ + int ret; + tsk_id_t ret_id; + tsk_mutation_table_t table; + tsk_mutation_t row; + const char *derived_state = "XYZ"; + const char *metadata = "ABC"; + + ret = tsk_mutation_table_init(&table, 0); + CU_ASSERT_EQUAL_FATAL(ret, 0); + + ret_id + = tsk_mutation_table_add_row(&table, 0, 1, 2, 3, derived_state, 1, metadata, 1); + CU_ASSERT_FATAL(ret_id >= 0); + ret_id + = tsk_mutation_table_add_row(&table, 1, 2, 3, 4, derived_state, 2, metadata, 2); + CU_ASSERT_FATAL(ret_id >= 0); + ret_id + = tsk_mutation_table_add_row(&table, 2, 3, 4, 5, derived_state, 3, metadata, 3); + CU_ASSERT_FATAL(ret_id >= 0); + + ret = tsk_mutation_table_update_row( + &table, 0, 1, 2, 3, 4, &derived_state[1], 1, &metadata[1], 1); + CU_ASSERT_EQUAL_FATAL(ret, 0); + ret = tsk_mutation_table_get_row(&table, 0, &row); + CU_ASSERT_EQUAL_FATAL(ret, 0); + CU_ASSERT_EQUAL_FATAL(row.site, 1); + CU_ASSERT_EQUAL_FATAL(row.node, 2); + CU_ASSERT_EQUAL_FATAL(row.parent, 3); + CU_ASSERT_EQUAL_FATAL(row.time, 4); + CU_ASSERT_EQUAL_FATAL(row.derived_state_length, 1); + CU_ASSERT_EQUAL_FATAL(row.derived_state[0], 'Y'); + CU_ASSERT_EQUAL_FATAL(row.metadata_length, 1); + CU_ASSERT_EQUAL_FATAL(row.metadata[0], 'B'); + + ret = tsk_mutation_table_update_row(&table, 0, row.site + 1, row.node + 1, + row.parent + 1, row.time + 1, row.derived_state, row.derived_state_length, + row.metadata, row.metadata_length); + CU_ASSERT_EQUAL_FATAL(ret, 0); + ret = tsk_mutation_table_get_row(&table, 0, &row); + CU_ASSERT_EQUAL_FATAL(ret, 0); + CU_ASSERT_EQUAL_FATAL(row.site, 2); + CU_ASSERT_EQUAL_FATAL(row.node, 3); + CU_ASSERT_EQUAL_FATAL(row.parent, 4); + CU_ASSERT_EQUAL_FATAL(row.time, 5); + CU_ASSERT_EQUAL_FATAL(row.derived_state_length, 1); + CU_ASSERT_EQUAL_FATAL(row.derived_state[0], 'Y'); + CU_ASSERT_EQUAL_FATAL(row.metadata_length, 1); + CU_ASSERT_EQUAL_FATAL(row.metadata[0], 'B'); + + ret = tsk_mutation_table_update_row(&table, 0, row.site, row.node, row.parent, + row.time, row.derived_state, row.derived_state_length, row.metadata, + row.metadata_length); + CU_ASSERT_EQUAL_FATAL(ret, 0); + ret = tsk_mutation_table_get_row(&table, 0, &row); + CU_ASSERT_EQUAL_FATAL(ret, 0); + CU_ASSERT_EQUAL_FATAL(row.site, 2); + CU_ASSERT_EQUAL_FATAL(row.node, 3); + CU_ASSERT_EQUAL_FATAL(row.parent, 4); + CU_ASSERT_EQUAL_FATAL(row.time, 5); + CU_ASSERT_EQUAL_FATAL(row.derived_state_length, 1); + CU_ASSERT_EQUAL_FATAL(row.derived_state[0], 'Y'); + CU_ASSERT_EQUAL_FATAL(row.metadata_length, 1); + CU_ASSERT_EQUAL_FATAL(row.metadata[0], 'B'); + + ret = tsk_mutation_table_update_row(&table, 0, row.site, row.node, row.parent, + row.time, NULL, 0, row.metadata, row.metadata_length); + CU_ASSERT_EQUAL_FATAL(ret, 0); + ret = tsk_mutation_table_get_row(&table, 0, &row); + CU_ASSERT_EQUAL_FATAL(ret, 0); + CU_ASSERT_EQUAL_FATAL(row.site, 2); + CU_ASSERT_EQUAL_FATAL(row.node, 3); + CU_ASSERT_EQUAL_FATAL(row.parent, 4); + CU_ASSERT_EQUAL_FATAL(row.time, 5); + CU_ASSERT_EQUAL_FATAL(row.derived_state_length, 0); + CU_ASSERT_EQUAL_FATAL(row.metadata_length, 1); + CU_ASSERT_EQUAL_FATAL(row.metadata[0], 'B'); + + ret = tsk_mutation_table_update_row( + &table, 0, 2, 3, 4, 5, derived_state, 3, metadata, 3); + CU_ASSERT_EQUAL_FATAL(ret, 0); + ret = tsk_mutation_table_get_row(&table, 0, &row); + CU_ASSERT_EQUAL_FATAL(ret, 0); + CU_ASSERT_EQUAL_FATAL(row.site, 2); + CU_ASSERT_EQUAL_FATAL(row.node, 3); + CU_ASSERT_EQUAL_FATAL(row.parent, 4); + CU_ASSERT_EQUAL_FATAL(row.time, 5); + CU_ASSERT_EQUAL_FATAL(row.derived_state_length, 3); + CU_ASSERT_EQUAL_FATAL(row.derived_state[0], 'X'); + CU_ASSERT_EQUAL_FATAL(row.derived_state[1], 'Y'); + CU_ASSERT_EQUAL_FATAL(row.derived_state[2], 'Z'); + CU_ASSERT_EQUAL_FATAL(row.metadata_length, 3); + CU_ASSERT_EQUAL_FATAL(row.metadata[0], 'A'); + CU_ASSERT_EQUAL_FATAL(row.metadata[1], 'B'); + CU_ASSERT_EQUAL_FATAL(row.metadata[2], 'C'); + + ret = tsk_mutation_table_update_row(&table, 1, 5, 6, 7, 8, NULL, 0, NULL, 0); + CU_ASSERT_EQUAL_FATAL(ret, 0); + ret = tsk_mutation_table_get_row(&table, 1, &row); + CU_ASSERT_EQUAL_FATAL(ret, 0); + CU_ASSERT_EQUAL_FATAL(row.site, 5); + CU_ASSERT_EQUAL_FATAL(row.node, 6); + CU_ASSERT_EQUAL_FATAL(row.parent, 7); + CU_ASSERT_EQUAL_FATAL(row.time, 8); + CU_ASSERT_EQUAL_FATAL(row.derived_state_length, 0); + CU_ASSERT_EQUAL_FATAL(row.metadata_length, 0); + + ret = tsk_mutation_table_get_row(&table, 2, &row); + CU_ASSERT_EQUAL_FATAL(ret, 0); + CU_ASSERT_EQUAL_FATAL(row.site, 2); + CU_ASSERT_EQUAL_FATAL(row.node, 3); + CU_ASSERT_EQUAL_FATAL(row.parent, 4); + CU_ASSERT_EQUAL_FATAL(row.time, 5); + CU_ASSERT_EQUAL_FATAL(row.derived_state_length, 3); + CU_ASSERT_EQUAL_FATAL(row.derived_state[0], 'X'); + CU_ASSERT_EQUAL_FATAL(row.derived_state[1], 'Y'); + CU_ASSERT_EQUAL_FATAL(row.derived_state[2], 'Z'); + CU_ASSERT_EQUAL_FATAL(row.metadata_length, 3); + CU_ASSERT_EQUAL_FATAL(row.metadata[0], 'A'); + CU_ASSERT_EQUAL_FATAL(row.metadata[1], 'B'); + CU_ASSERT_EQUAL_FATAL(row.metadata[2], 'C'); + + ret = tsk_mutation_table_update_row(&table, 3, 0, 0, 0, 0, NULL, 0, NULL, 0); + CU_ASSERT_EQUAL_FATAL(ret, TSK_ERR_MUTATION_OUT_OF_BOUNDS); + + tsk_mutation_table_free(&table); +} + static void test_migration_table(void) { @@ -2494,6 +2937,99 @@ test_migration_table(void) free(metadata_offset); } +static void +test_migration_table_update_row(void) +{ + int ret; + tsk_id_t ret_id; + tsk_migration_table_t table; + tsk_migration_t row; + const char *metadata = "ABC"; + + ret = tsk_migration_table_init(&table, 0); + CU_ASSERT_EQUAL_FATAL(ret, 0); + + ret_id = tsk_migration_table_add_row(&table, 0, 1.0, 2, 3, 4, 5, metadata, 1); + CU_ASSERT_FATAL(ret_id >= 0); + ret_id = tsk_migration_table_add_row(&table, 1, 2.0, 3, 4, 5, 6, metadata, 2); + CU_ASSERT_FATAL(ret_id >= 0); + ret_id = tsk_migration_table_add_row(&table, 2, 3.0, 4, 5, 6, 7, metadata, 3); + CU_ASSERT_FATAL(ret_id >= 0); + + ret = tsk_migration_table_update_row(&table, 0, 1, 2.0, 3, 4, 5, 6, &metadata[1], 1); + CU_ASSERT_EQUAL_FATAL(ret, 0); + ret = tsk_migration_table_get_row(&table, 0, &row); + CU_ASSERT_EQUAL_FATAL(ret, 0); + CU_ASSERT_EQUAL_FATAL(row.left, 1); + CU_ASSERT_EQUAL_FATAL(row.right, 2.0); + CU_ASSERT_EQUAL_FATAL(row.node, 3); + CU_ASSERT_EQUAL_FATAL(row.source, 4); + CU_ASSERT_EQUAL_FATAL(row.dest, 5); + CU_ASSERT_EQUAL_FATAL(row.time, 6); + CU_ASSERT_EQUAL_FATAL(row.metadata_length, 1); + CU_ASSERT_EQUAL_FATAL(row.metadata[0], 'B'); + + ret = tsk_migration_table_update_row(&table, 0, row.left + 1, row.right + 1, + row.node + 1, row.source + 1, row.dest + 1, row.time + 1, row.metadata, + row.metadata_length); + CU_ASSERT_EQUAL_FATAL(ret, 0); + ret = tsk_migration_table_get_row(&table, 0, &row); + CU_ASSERT_EQUAL_FATAL(ret, 0); + CU_ASSERT_EQUAL_FATAL(row.left, 2); + CU_ASSERT_EQUAL_FATAL(row.right, 3.0); + CU_ASSERT_EQUAL_FATAL(row.node, 4); + CU_ASSERT_EQUAL_FATAL(row.source, 5); + CU_ASSERT_EQUAL_FATAL(row.dest, 6); + CU_ASSERT_EQUAL_FATAL(row.time, 7); + CU_ASSERT_EQUAL_FATAL(row.metadata_length, 1); + CU_ASSERT_EQUAL_FATAL(row.metadata[0], 'B'); + + ret = tsk_migration_table_update_row(&table, 0, 0, 0, 0, 0, 0, 0, metadata, 3); + CU_ASSERT_EQUAL_FATAL(ret, 0); + ret = tsk_migration_table_get_row(&table, 0, &row); + CU_ASSERT_EQUAL_FATAL(ret, 0); + CU_ASSERT_EQUAL_FATAL(row.left, 0); + CU_ASSERT_EQUAL_FATAL(row.right, 0); + CU_ASSERT_EQUAL_FATAL(row.node, 0); + CU_ASSERT_EQUAL_FATAL(row.source, 0); + CU_ASSERT_EQUAL_FATAL(row.dest, 0); + CU_ASSERT_EQUAL_FATAL(row.time, 0); + CU_ASSERT_EQUAL_FATAL(row.metadata_length, 3); + CU_ASSERT_EQUAL_FATAL(row.metadata[0], 'A'); + CU_ASSERT_EQUAL_FATAL(row.metadata[1], 'B'); + CU_ASSERT_EQUAL_FATAL(row.metadata[2], 'C'); + + ret = tsk_migration_table_update_row(&table, 1, 0, 0, 0, 0, 0, 0, NULL, 0); + CU_ASSERT_EQUAL_FATAL(ret, 0); + ret = tsk_migration_table_get_row(&table, 1, &row); + CU_ASSERT_EQUAL_FATAL(ret, 0); + CU_ASSERT_EQUAL_FATAL(row.left, 0); + CU_ASSERT_EQUAL_FATAL(row.right, 0); + CU_ASSERT_EQUAL_FATAL(row.node, 0); + CU_ASSERT_EQUAL_FATAL(row.source, 0); + CU_ASSERT_EQUAL_FATAL(row.dest, 0); + CU_ASSERT_EQUAL_FATAL(row.time, 0); + CU_ASSERT_EQUAL_FATAL(row.metadata_length, 0); + + ret = tsk_migration_table_get_row(&table, 2, &row); + CU_ASSERT_EQUAL_FATAL(ret, 0); + CU_ASSERT_EQUAL_FATAL(row.left, 2); + CU_ASSERT_EQUAL_FATAL(row.right, 3.0); + CU_ASSERT_EQUAL_FATAL(row.node, 4); + CU_ASSERT_EQUAL_FATAL(row.source, 5); + CU_ASSERT_EQUAL_FATAL(row.dest, 6); + CU_ASSERT_EQUAL_FATAL(row.time, 7); + CU_ASSERT_EQUAL_FATAL(row.metadata_length, 3); + CU_ASSERT_EQUAL_FATAL(row.metadata[0], 'A'); + CU_ASSERT_EQUAL_FATAL(row.metadata[1], 'B'); + CU_ASSERT_EQUAL_FATAL(row.metadata[2], 'C'); + + ret = tsk_migration_table_update_row(&table, 3, 0, 0, 0, 0, 0, 0, NULL, 0); + CU_ASSERT_EQUAL_FATAL(ret, TSK_ERR_MIGRATION_OUT_OF_BOUNDS); + + tsk_migration_table_free(&table); +} + static void test_individual_table(void) { @@ -2948,6 +3484,132 @@ test_individual_table(void) free(metadata_offset); } +static void +test_individual_table_update_row(void) +{ + int ret; + tsk_id_t ret_id; + tsk_individual_table_t table; + tsk_individual_t row; + double location[] = { 0, 1, 2 }; + tsk_id_t parents[] = { 0, 1, 2 }; + const char *metadata = "ABC"; + + ret = tsk_individual_table_init(&table, 0); + CU_ASSERT_EQUAL_FATAL(ret, 0); + + ret_id + = tsk_individual_table_add_row(&table, 0, location, 1, parents, 1, metadata, 1); + CU_ASSERT_FATAL(ret_id >= 0); + ret_id + = tsk_individual_table_add_row(&table, 1, location, 2, parents, 2, metadata, 2); + CU_ASSERT_FATAL(ret_id >= 0); + ret_id + = tsk_individual_table_add_row(&table, 2, location, 3, parents, 3, metadata, 3); + CU_ASSERT_FATAL(ret_id >= 0); + + ret = tsk_individual_table_update_row( + &table, 0, 1, &location[1], 1, &parents[1], 1, &metadata[1], 1); + CU_ASSERT_EQUAL_FATAL(ret, 0); + ret = tsk_individual_table_get_row(&table, 0, &row); + CU_ASSERT_EQUAL_FATAL(ret, 0); + CU_ASSERT_EQUAL_FATAL(row.flags, 1); + CU_ASSERT_EQUAL_FATAL(row.location_length, 1); + CU_ASSERT_EQUAL_FATAL(row.location[0], 1.0); + CU_ASSERT_EQUAL_FATAL(row.parents_length, 1); + CU_ASSERT_EQUAL_FATAL(row.parents[0], 1); + CU_ASSERT_EQUAL_FATAL(row.metadata_length, 1); + CU_ASSERT_EQUAL_FATAL(row.metadata[0], 'B'); + + ret = tsk_individual_table_update_row(&table, 0, row.flags + 1, row.location, + row.location_length, row.parents, row.parents_length, row.metadata, + row.metadata_length); + CU_ASSERT_EQUAL_FATAL(ret, 0); + ret = tsk_individual_table_get_row(&table, 0, &row); + CU_ASSERT_EQUAL_FATAL(ret, 0); + CU_ASSERT_EQUAL_FATAL(row.flags, 2); + CU_ASSERT_EQUAL_FATAL(row.location_length, 1); + CU_ASSERT_EQUAL_FATAL(row.location[0], 1.0); + CU_ASSERT_EQUAL_FATAL(row.parents_length, 1); + CU_ASSERT_EQUAL_FATAL(row.parents[0], 1); + CU_ASSERT_EQUAL_FATAL(row.metadata_length, 1); + CU_ASSERT_EQUAL_FATAL(row.metadata[0], 'B'); + + ret = tsk_individual_table_update_row(&table, 0, row.flags, location, 1, row.parents, + row.parents_length, row.metadata, row.metadata_length); + CU_ASSERT_EQUAL_FATAL(ret, 0); + ret = tsk_individual_table_get_row(&table, 0, &row); + CU_ASSERT_EQUAL_FATAL(ret, 0); + CU_ASSERT_EQUAL_FATAL(row.flags, 2); + CU_ASSERT_EQUAL_FATAL(row.location_length, 1); + CU_ASSERT_EQUAL_FATAL(row.location[0], 0.0); + CU_ASSERT_EQUAL_FATAL(row.parents_length, 1); + CU_ASSERT_EQUAL_FATAL(row.parents[0], 1); + CU_ASSERT_EQUAL_FATAL(row.metadata_length, 1); + CU_ASSERT_EQUAL_FATAL(row.metadata[0], 'B'); + + ret = tsk_individual_table_update_row(&table, 0, row.flags, NULL, 0, row.parents, + row.parents_length, row.metadata, row.metadata_length); + CU_ASSERT_EQUAL_FATAL(ret, 0); + ret = tsk_individual_table_get_row(&table, 0, &row); + CU_ASSERT_EQUAL_FATAL(ret, 0); + CU_ASSERT_EQUAL_FATAL(row.flags, 2); + CU_ASSERT_EQUAL_FATAL(row.location_length, 0); + CU_ASSERT_EQUAL_FATAL(row.parents_length, 1); + CU_ASSERT_EQUAL_FATAL(row.parents[0], 1); + CU_ASSERT_EQUAL_FATAL(row.metadata_length, 1); + CU_ASSERT_EQUAL_FATAL(row.metadata[0], 'B'); + + ret = tsk_individual_table_update_row( + &table, 0, 2, location, 3, parents, 3, metadata, 3); + CU_ASSERT_EQUAL_FATAL(ret, 0); + ret = tsk_individual_table_get_row(&table, 0, &row); + CU_ASSERT_EQUAL_FATAL(ret, 0); + CU_ASSERT_EQUAL_FATAL(row.flags, 2); + CU_ASSERT_EQUAL_FATAL(row.location_length, 3); + CU_ASSERT_EQUAL_FATAL(row.location[0], 0); + CU_ASSERT_EQUAL_FATAL(row.location[1], 1); + CU_ASSERT_EQUAL_FATAL(row.location[2], 2); + CU_ASSERT_EQUAL_FATAL(row.parents_length, 3); + CU_ASSERT_EQUAL_FATAL(row.parents[0], 0); + CU_ASSERT_EQUAL_FATAL(row.parents[1], 1); + CU_ASSERT_EQUAL_FATAL(row.parents[2], 2); + CU_ASSERT_EQUAL_FATAL(row.metadata_length, 3); + CU_ASSERT_EQUAL_FATAL(row.metadata[0], 'A'); + CU_ASSERT_EQUAL_FATAL(row.metadata[1], 'B'); + CU_ASSERT_EQUAL_FATAL(row.metadata[2], 'C'); + + ret = tsk_individual_table_update_row(&table, 1, 5, NULL, 0, NULL, 0, NULL, 0); + CU_ASSERT_EQUAL_FATAL(ret, 0); + ret = tsk_individual_table_get_row(&table, 1, &row); + CU_ASSERT_EQUAL_FATAL(ret, 0); + CU_ASSERT_EQUAL_FATAL(row.flags, 5); + CU_ASSERT_EQUAL_FATAL(row.location_length, 0); + CU_ASSERT_EQUAL_FATAL(row.parents_length, 0); + CU_ASSERT_EQUAL_FATAL(row.metadata_length, 0); + + ret = tsk_individual_table_get_row(&table, 2, &row); + CU_ASSERT_EQUAL_FATAL(ret, 0); + CU_ASSERT_EQUAL_FATAL(row.flags, 2); + CU_ASSERT_EQUAL_FATAL(row.location_length, 3); + CU_ASSERT_EQUAL_FATAL(row.location[0], 0); + CU_ASSERT_EQUAL_FATAL(row.location[1], 1); + CU_ASSERT_EQUAL_FATAL(row.location[2], 2); + CU_ASSERT_EQUAL_FATAL(row.parents_length, 3); + CU_ASSERT_EQUAL_FATAL(row.parents[0], 0); + CU_ASSERT_EQUAL_FATAL(row.parents[1], 1); + CU_ASSERT_EQUAL_FATAL(row.parents[2], 2); + CU_ASSERT_EQUAL_FATAL(row.metadata_length, 3); + CU_ASSERT_EQUAL_FATAL(row.metadata[0], 'A'); + CU_ASSERT_EQUAL_FATAL(row.metadata[1], 'B'); + CU_ASSERT_EQUAL_FATAL(row.metadata[2], 'C'); + + ret = tsk_individual_table_update_row(&table, 3, 0, NULL, 0, NULL, 0, NULL, 0); + CU_ASSERT_EQUAL_FATAL(ret, TSK_ERR_INDIVIDUAL_OUT_OF_BOUNDS); + + tsk_individual_table_free(&table); +} + static void test_population_table(void) { @@ -3193,6 +3855,67 @@ test_population_table(void) free(metadata_offset); } +static void +test_population_table_update_row(void) +{ + int ret; + tsk_id_t ret_id; + tsk_population_table_t table; + tsk_population_t row; + const char *metadata = "ABC"; + + ret = tsk_population_table_init(&table, 0); + CU_ASSERT_EQUAL_FATAL(ret, 0); + + ret_id = tsk_population_table_add_row(&table, metadata, 1); + CU_ASSERT_FATAL(ret_id >= 0); + ret_id = tsk_population_table_add_row(&table, metadata, 2); + CU_ASSERT_FATAL(ret_id >= 0); + ret_id = tsk_population_table_add_row(&table, metadata, 3); + CU_ASSERT_FATAL(ret_id >= 0); + + ret = tsk_population_table_update_row(&table, 0, &metadata[1], 1); + CU_ASSERT_EQUAL_FATAL(ret, 0); + ret = tsk_population_table_get_row(&table, 0, &row); + CU_ASSERT_EQUAL_FATAL(ret, 0); + CU_ASSERT_EQUAL_FATAL(row.metadata_length, 1); + CU_ASSERT_EQUAL_FATAL(row.metadata[0], 'B'); + + ret = tsk_population_table_update_row(&table, 0, row.metadata, row.metadata_length); + CU_ASSERT_EQUAL_FATAL(ret, 0); + ret = tsk_population_table_get_row(&table, 0, &row); + CU_ASSERT_EQUAL_FATAL(ret, 0); + CU_ASSERT_EQUAL_FATAL(row.metadata_length, 1); + CU_ASSERT_EQUAL_FATAL(row.metadata[0], 'B'); + + ret = tsk_population_table_update_row(&table, 0, metadata, 3); + CU_ASSERT_EQUAL_FATAL(ret, 0); + ret = tsk_population_table_get_row(&table, 0, &row); + CU_ASSERT_EQUAL_FATAL(ret, 0); + CU_ASSERT_EQUAL_FATAL(row.metadata_length, 3); + CU_ASSERT_EQUAL_FATAL(row.metadata[0], 'A'); + CU_ASSERT_EQUAL_FATAL(row.metadata[1], 'B'); + CU_ASSERT_EQUAL_FATAL(row.metadata[2], 'C'); + + ret = tsk_population_table_update_row(&table, 1, NULL, 0); + CU_ASSERT_EQUAL_FATAL(ret, 0); + ret = tsk_population_table_get_row(&table, 1, &row); + CU_ASSERT_EQUAL_FATAL(ret, 0); + CU_ASSERT_EQUAL_FATAL(row.metadata_length, 0); + + ret = tsk_population_table_get_row(&table, 2, &row); + CU_ASSERT_EQUAL_FATAL(ret, 0); + CU_ASSERT_EQUAL_FATAL(row.metadata_length, 3); + CU_ASSERT_EQUAL_FATAL(row.metadata[0], 'A'); + CU_ASSERT_EQUAL_FATAL(row.metadata[1], 'B'); + CU_ASSERT_EQUAL_FATAL(row.metadata[2], 'C'); + + ret = tsk_population_table_update_row(&table, 3, NULL, 0); + CU_ASSERT_EQUAL_FATAL(ret, TSK_ERR_POPULATION_OUT_OF_BOUNDS); + + tsk_population_table_free(&table); +} + static void test_provenance_table(void) { @@ -3448,6 +4171,91 @@ test_provenance_table(void) free(record_offset); } +static void +test_provenance_table_update_row(void) +{ + int ret; + tsk_id_t ret_id; + tsk_provenance_table_t table; + tsk_provenance_t row; + const char *timestamp = "XYZ"; + const char *record = "ABC"; + + ret = tsk_provenance_table_init(&table, 0); + CU_ASSERT_EQUAL_FATAL(ret, 0); + + ret_id = tsk_provenance_table_add_row(&table, timestamp, 1, record, 1); + CU_ASSERT_FATAL(ret_id >= 0); + ret_id = tsk_provenance_table_add_row(&table, timestamp, 2, record, 2); + CU_ASSERT_FATAL(ret_id >= 0); + ret_id = tsk_provenance_table_add_row(&table, timestamp, 3, record, 3); + CU_ASSERT_FATAL(ret_id >= 0); + + ret = tsk_provenance_table_update_row(&table, 0, ×tamp[1], 1, &record[1], 1); + CU_ASSERT_EQUAL_FATAL(ret, 0); + ret = tsk_provenance_table_get_row(&table, 0, &row); + CU_ASSERT_EQUAL_FATAL(ret, 0); + CU_ASSERT_EQUAL_FATAL(row.timestamp_length, 1); + CU_ASSERT_EQUAL_FATAL(row.timestamp[0], 'Y'); + CU_ASSERT_EQUAL_FATAL(row.record_length, 1); + CU_ASSERT_EQUAL_FATAL(row.record[0], 'B'); + + ret = tsk_provenance_table_update_row( + &table, 0, row.timestamp, row.timestamp_length, row.record, row.record_length); + CU_ASSERT_EQUAL_FATAL(ret, 0); + ret = tsk_provenance_table_get_row(&table, 0, &row); + CU_ASSERT_EQUAL_FATAL(ret, 0); + CU_ASSERT_EQUAL_FATAL(row.timestamp_length, 1); + CU_ASSERT_EQUAL_FATAL(row.timestamp[0], 'Y'); + CU_ASSERT_EQUAL_FATAL(row.record_length, 1); + CU_ASSERT_EQUAL_FATAL(row.record[0], 'B'); + + ret = tsk_provenance_table_update_row(&table, 0, row.timestamp, + row.timestamp_length - 1, row.record, row.record_length); + CU_ASSERT_EQUAL_FATAL(ret, 0); + ret = tsk_provenance_table_get_row(&table, 0, &row); + CU_ASSERT_EQUAL_FATAL(ret, 0); + CU_ASSERT_EQUAL_FATAL(row.timestamp_length, 0); + CU_ASSERT_EQUAL_FATAL(row.record_length, 1); + CU_ASSERT_EQUAL_FATAL(row.record[0], 'B'); + + ret = tsk_provenance_table_update_row(&table, 0, timestamp, 3, record, 3); + CU_ASSERT_EQUAL_FATAL(ret, 0); + ret = tsk_provenance_table_get_row(&table, 0, &row); + CU_ASSERT_EQUAL_FATAL(ret, 0); + CU_ASSERT_EQUAL_FATAL(row.timestamp_length, 3); + CU_ASSERT_EQUAL_FATAL(row.timestamp[0], 'X'); + CU_ASSERT_EQUAL_FATAL(row.timestamp[1], 'Y'); + CU_ASSERT_EQUAL_FATAL(row.timestamp[2], 'Z'); + CU_ASSERT_EQUAL_FATAL(row.record_length, 3); + CU_ASSERT_EQUAL_FATAL(row.record[0], 'A'); + CU_ASSERT_EQUAL_FATAL(row.record[1], 'B'); + CU_ASSERT_EQUAL_FATAL(row.record[2], 'C'); + + ret = tsk_provenance_table_update_row(&table, 1, NULL, 0, NULL, 0); + CU_ASSERT_EQUAL_FATAL(ret, 0); + ret = tsk_provenance_table_get_row(&table, 1, &row); + CU_ASSERT_EQUAL_FATAL(ret, 0); + CU_ASSERT_EQUAL_FATAL(row.timestamp_length, 0); + CU_ASSERT_EQUAL_FATAL(row.record_length, 0); + + ret = tsk_provenance_table_get_row(&table, 2, &row); + CU_ASSERT_EQUAL_FATAL(ret, 0); + CU_ASSERT_EQUAL_FATAL(row.timestamp_length, 3); + CU_ASSERT_EQUAL_FATAL(row.timestamp[0], 'X'); + CU_ASSERT_EQUAL_FATAL(row.timestamp[1], 'Y'); + CU_ASSERT_EQUAL_FATAL(row.timestamp[2], 'Z'); + CU_ASSERT_EQUAL_FATAL(row.record_length, 3); + CU_ASSERT_EQUAL_FATAL(row.record[0], 'A'); + CU_ASSERT_EQUAL_FATAL(row.record[1], 'B'); + CU_ASSERT_EQUAL_FATAL(row.record[2], 'C'); + + ret = tsk_provenance_table_update_row(&table, 3, NULL, 0, NULL, 0); + CU_ASSERT_EQUAL_FATAL(ret, TSK_ERR_PROVENANCE_OUT_OF_BOUNDS); + + tsk_provenance_table_free(&table); +} + static void test_table_size_increments(void) { @@ -6939,7 +7747,11 @@ main(int argc, char **argv) { CU_TestInfo tests[] = { { "test_node_table", test_node_table }, + { "test_node_table_update_row", test_node_table_update_row }, { "test_edge_table", test_edge_table }, + { "test_edge_table_update_row", test_edge_table_update_row }, + { "test_edge_table_update_row_no_metadata", + test_edge_table_update_row_no_metadata }, { "test_edge_table_copy_semantics", test_edge_table_copy_semantics }, { "test_edge_table_squash", test_edge_table_squash }, { "test_edge_table_squash_multiple_parents", @@ -6949,11 +7761,17 @@ main(int argc, char **argv) { "test_edge_table_squash_bad_intervals", test_edge_table_squash_bad_intervals }, { "test_edge_table_squash_metadata", test_edge_table_squash_metadata }, { "test_site_table", test_site_table }, + { "test_site_table_update_row", test_site_table_update_row }, { "test_mutation_table", test_mutation_table }, + { "test_mutation_table_update_row", test_mutation_table_update_row }, { "test_migration_table", test_migration_table }, + { "test_migration_table_update_row", test_migration_table_update_row }, { "test_individual_table", test_individual_table }, + { "test_individual_table_update_row", test_individual_table_update_row }, { "test_population_table", test_population_table }, + { "test_population_table_update_row", test_population_table_update_row }, { "test_provenance_table", test_provenance_table }, + { "test_provenance_table_update_row", test_provenance_table_update_row }, { "test_table_size_increments", test_table_size_increments }, { "test_table_collection_equals_options", test_table_collection_equals_options }, { "test_table_collection_simplify_errors", diff --git a/c/tskit/tables.c b/c/tskit/tables.c index a3ae0ed890..9929653d44 100644 --- a/c/tskit/tables.c +++ b/c/tskit/tables.c @@ -774,22 +774,21 @@ tsk_individual_table_add_row_internal(tsk_individual_table_t *self, tsk_flags_t const double *location, tsk_size_t location_length, const tsk_id_t *parents, const tsk_size_t parents_length, const char *metadata, tsk_size_t metadata_length) { - tsk_bug_assert(self->num_rows < self->max_rows); tsk_bug_assert(self->parents_length + parents_length <= self->max_parents_length); tsk_bug_assert(self->metadata_length + metadata_length <= self->max_metadata_length); tsk_bug_assert(self->location_length + location_length <= self->max_location_length); self->flags[self->num_rows] = flags; - memcpy(self->location + self->location_length, location, - location_length * sizeof(double)); + memmove(self->location + self->location_length, location, + location_length * sizeof(*self->location)); self->location_offset[self->num_rows + 1] = self->location_length + location_length; self->location_length += location_length; - memcpy(self->parents + self->parents_length, parents, - parents_length * sizeof(tsk_id_t)); + memmove(self->parents + self->parents_length, parents, + parents_length * sizeof(*self->parents)); self->parents_offset[self->num_rows + 1] = self->parents_length + parents_length; self->parents_length += parents_length; - memcpy(self->metadata + self->metadata_length, metadata, - metadata_length * sizeof(char)); + memmove(self->metadata + self->metadata_length, metadata, + metadata_length * sizeof(*self->metadata)); self->metadata_offset[self->num_rows + 1] = self->metadata_length + metadata_length; self->metadata_length += metadata_length; self->num_rows++; @@ -807,21 +806,101 @@ tsk_individual_table_add_row(tsk_individual_table_t *self, tsk_flags_t flags, if (ret != 0) { goto out; } - ret = tsk_individual_table_expand_location(self, (tsk_size_t) location_length); + ret = tsk_individual_table_expand_location(self, location_length); if (ret != 0) { goto out; } - ret = tsk_individual_table_expand_parents(self, (tsk_size_t) parents_length); + ret = tsk_individual_table_expand_parents(self, parents_length); if (ret != 0) { goto out; } - ret = tsk_individual_table_expand_metadata(self, (tsk_size_t) metadata_length); + ret = tsk_individual_table_expand_metadata(self, metadata_length); if (ret != 0) { goto out; } - ret = tsk_individual_table_add_row_internal(self, flags, location, - (tsk_size_t) location_length, parents, (tsk_size_t) parents_length, metadata, - (tsk_size_t) metadata_length); + ret = tsk_individual_table_add_row_internal(self, flags, location, location_length, + parents, parents_length, metadata, metadata_length); +out: + return ret; +} + +static int +tsk_individual_table_update_row_rewrite(tsk_individual_table_t *self, tsk_id_t index, + tsk_flags_t flags, const double *location, tsk_size_t location_length, + const tsk_id_t *parents, tsk_size_t parents_length, const char *metadata, + tsk_size_t metadata_length) +{ + int ret = 0; + tsk_id_t j, ret_id; + tsk_individual_table_t copy; + tsk_size_t num_rows; + tsk_id_t *rows = NULL; + + ret = tsk_individual_table_copy(self, ©, 0); + if (ret != 0) { + goto out; + } + rows = malloc(self->num_rows * sizeof(*rows)); + if (rows == NULL) { + ret = TSK_ERR_NO_MEMORY; + goto out; + } + + ret = tsk_individual_table_truncate(self, (tsk_size_t) index); + tsk_bug_assert(ret == 0); + ret_id = tsk_individual_table_add_row(self, flags, location, location_length, + parents, parents_length, metadata, metadata_length); + if (ret_id < 0) { + ret = (int) ret_id; + goto out; + } + num_rows = 0; + for (j = index + 1; j < (tsk_id_t) copy.num_rows; j++) { + rows[num_rows] = j; + num_rows++; + } + ret = tsk_individual_table_extend(self, ©, num_rows, rows, 0); + if (ret != 0) { + goto out; + } +out: + tsk_individual_table_free(©); + tsk_safe_free(rows); + return ret; +} + +int +tsk_individual_table_update_row(tsk_individual_table_t *self, tsk_id_t index, + tsk_flags_t flags, const double *location, tsk_size_t location_length, + const tsk_id_t *parents, tsk_size_t parents_length, const char *metadata, + tsk_size_t metadata_length) +{ + int ret = 0; + tsk_individual_t current_row; + + ret = tsk_individual_table_get_row(self, index, ¤t_row); + if (ret != 0) { + goto out; + } + if (current_row.location_length == location_length + && current_row.parents_length == parents_length + && current_row.metadata_length == metadata_length) { + self->flags[index] = flags; + /* Note: important to use memmove here as we may be provided pointers + * to the column memory as input via get_row */ + memmove(&self->location[self->location_offset[index]], location, + location_length * sizeof(*location)); + memmove(&self->parents[self->parents_offset[index]], parents, + parents_length * sizeof(*parents)); + memmove(&self->metadata[self->metadata_offset[index]], metadata, + metadata_length * sizeof(*metadata)); + } else { + ret = tsk_individual_table_update_row_rewrite(self, index, flags, location, + location_length, parents, parents_length, metadata, metadata_length); + if (ret != 0) { + goto out; + } + } out: return ret; } @@ -921,7 +1000,7 @@ tsk_individual_table_print_state(const tsk_individual_table_t *self, FILE *out) write_metadata_schema_header( out, self->metadata_schema, self->metadata_schema_length); fprintf(out, "id\tflags\tlocation_offset\tlocation\t"); - fprintf(out, "parents_offset\tparents\n"); + fprintf(out, "parents_offset\tparents\t"); fprintf(out, "metadata_offset\tmetadata\n"); for (j = 0; j < self->num_rows; j++) { fprintf(out, "%lld\t%lld\t", (long long) j, (long long) self->flags[j]); @@ -1373,7 +1452,7 @@ tsk_node_table_add_row_internal(tsk_node_table_t *self, tsk_flags_t flags, doubl { tsk_bug_assert(self->num_rows < self->max_rows); tsk_bug_assert(self->metadata_length + metadata_length <= self->max_metadata_length); - memcpy(self->metadata + self->metadata_length, metadata, metadata_length); + memmove(self->metadata + self->metadata_length, metadata, metadata_length); self->flags[self->num_rows] = flags; self->time[self->num_rows] = time; self->population[self->num_rows] = population; @@ -1405,6 +1484,82 @@ tsk_node_table_add_row(tsk_node_table_t *self, tsk_flags_t flags, double time, return ret; } +static int +tsk_node_table_update_row_rewrite(tsk_node_table_t *self, tsk_id_t index, + tsk_flags_t flags, double time, tsk_id_t population, tsk_id_t individual, + const char *metadata, tsk_size_t metadata_length) +{ + int ret = 0; + tsk_id_t j, ret_id; + tsk_node_table_t copy; + tsk_size_t num_rows; + tsk_id_t *rows = NULL; + + ret = tsk_node_table_copy(self, ©, 0); + if (ret != 0) { + goto out; + } + rows = malloc(self->num_rows * sizeof(*rows)); + if (rows == NULL) { + ret = TSK_ERR_NO_MEMORY; + goto out; + } + + ret = tsk_node_table_truncate(self, (tsk_size_t) index); + tsk_bug_assert(ret == 0); + ret_id = tsk_node_table_add_row( + self, flags, time, population, individual, metadata, metadata_length); + if (ret_id < 0) { + ret = (int) ret_id; + goto out; + } + num_rows = 0; + for (j = index + 1; j < (tsk_id_t) copy.num_rows; j++) { + rows[num_rows] = j; + num_rows++; + } + ret = tsk_node_table_extend(self, ©, num_rows, rows, 0); + if (ret != 0) { + goto out; + } +out: + tsk_node_table_free(©); + tsk_safe_free(rows); + return ret; +} + +int +tsk_node_table_update_row(tsk_node_table_t *self, tsk_id_t index, tsk_flags_t flags, + double time, tsk_id_t population, tsk_id_t individual, const char *metadata, + tsk_size_t metadata_length) +{ + int ret = 0; + tsk_node_t current_row; + + ret = tsk_node_table_get_row(self, index, ¤t_row); + if (ret != 0) { + goto out; + } + if (current_row.metadata_length == metadata_length) { + self->flags[index] = flags; + self->time[index] = time; + self->population[index] = population; + self->individual[index] = individual; + /* Note: important to use memmove here as we may be provided pointers + * to the column memory as input via get_row */ + memmove(&self->metadata[self->metadata_offset[index]], metadata, + metadata_length * sizeof(*metadata)); + } else { + ret = tsk_node_table_update_row_rewrite( + self, index, flags, time, population, individual, metadata, metadata_length); + if (ret != 0) { + goto out; + } + } +out: + return ret; +} + int TSK_WARN_UNUSED tsk_node_table_clear(tsk_node_table_t *self) { @@ -1820,7 +1975,7 @@ tsk_edge_table_add_row(tsk_edge_table_t *self, double left, double right, } tsk_bug_assert( self->metadata_length + metadata_length <= self->max_metadata_length); - memcpy(self->metadata + self->metadata_length, metadata, metadata_length); + memmove(self->metadata + self->metadata_length, metadata, metadata_length); self->metadata_offset[self->num_rows + 1] = self->metadata_length + metadata_length; self->metadata_length += metadata_length; @@ -1831,6 +1986,84 @@ tsk_edge_table_add_row(tsk_edge_table_t *self, double left, double right, return ret; } +static int +tsk_edge_table_update_row_rewrite(tsk_edge_table_t *self, tsk_id_t index, double left, + double right, tsk_id_t parent, tsk_id_t child, const char *metadata, + tsk_size_t metadata_length) +{ + int ret = 0; + tsk_id_t j, ret_id; + tsk_edge_table_t copy; + tsk_size_t num_rows; + tsk_id_t *rows = NULL; + + ret = tsk_edge_table_copy(self, ©, 0); + if (ret != 0) { + goto out; + } + rows = malloc(self->num_rows * sizeof(*rows)); + if (rows == NULL) { + ret = TSK_ERR_NO_MEMORY; + goto out; + } + + ret = tsk_edge_table_truncate(self, (tsk_size_t) index); + tsk_bug_assert(ret == 0); + ret_id = tsk_edge_table_add_row( + self, left, right, parent, child, metadata, metadata_length); + if (ret_id < 0) { + ret = (int) ret_id; + goto out; + } + num_rows = 0; + for (j = index + 1; j < (tsk_id_t) copy.num_rows; j++) { + rows[num_rows] = j; + num_rows++; + } + ret = tsk_edge_table_extend(self, ©, num_rows, rows, 0); + if (ret != 0) { + goto out; + } +out: + tsk_edge_table_free(©); + tsk_safe_free(rows); + return ret; +} + +int +tsk_edge_table_update_row(tsk_edge_table_t *self, tsk_id_t index, double left, + double right, tsk_id_t parent, tsk_id_t child, const char *metadata, + tsk_size_t metadata_length) +{ + int ret = 0; + tsk_edge_t current_row; + + ret = tsk_edge_table_get_row(self, index, ¤t_row); + if (ret != 0) { + goto out; + } + if (current_row.metadata_length == metadata_length) { + self->left[index] = left; + self->right[index] = right; + self->parent[index] = parent; + self->child[index] = child; + if (tsk_edge_table_has_metadata(self)) { + /* Note: important to use memmove here as we may be provided pointers + * to the column memory as input via get_row */ + memmove(&self->metadata[self->metadata_offset[index]], metadata, + metadata_length * sizeof(*metadata)); + } + } else { + ret = tsk_edge_table_update_row_rewrite( + self, index, left, right, parent, child, metadata, metadata_length); + if (ret != 0) { + goto out; + } + } +out: + return ret; +} + int TSK_WARN_UNUSED tsk_edge_table_copy( const tsk_edge_table_t *self, tsk_edge_table_t *dest, tsk_flags_t options) @@ -2424,7 +2657,7 @@ tsk_site_table_add_row(tsk_site_table_t *self, double position, goto out; } self->ancestral_state_length += ancestral_state_length; - memcpy(self->ancestral_state + ancestral_state_offset, ancestral_state, + memmove(self->ancestral_state + ancestral_state_offset, ancestral_state, ancestral_state_length); self->ancestral_state_offset[self->num_rows + 1] = self->ancestral_state_length; @@ -2435,7 +2668,7 @@ tsk_site_table_add_row(tsk_site_table_t *self, double position, goto out; } self->metadata_length += metadata_length; - memcpy(self->metadata + metadata_offset, metadata, metadata_length); + memmove(self->metadata + metadata_offset, metadata, metadata_length); self->metadata_offset[self->num_rows + 1] = self->metadata_length; ret = (tsk_id_t) self->num_rows; @@ -2444,6 +2677,82 @@ tsk_site_table_add_row(tsk_site_table_t *self, double position, return ret; } +static int +tsk_site_table_update_row_rewrite(tsk_site_table_t *self, tsk_id_t index, + double position, const char *ancestral_state, tsk_size_t ancestral_state_length, + const char *metadata, tsk_size_t metadata_length) +{ + int ret = 0; + tsk_id_t j, ret_id; + tsk_site_table_t copy; + tsk_size_t num_rows; + tsk_id_t *rows = NULL; + + ret = tsk_site_table_copy(self, ©, 0); + if (ret != 0) { + goto out; + } + rows = malloc(self->num_rows * sizeof(*rows)); + if (rows == NULL) { + ret = TSK_ERR_NO_MEMORY; + goto out; + } + + ret = tsk_site_table_truncate(self, (tsk_size_t) index); + tsk_bug_assert(ret == 0); + ret_id = tsk_site_table_add_row(self, position, ancestral_state, + ancestral_state_length, metadata, metadata_length); + if (ret_id < 0) { + ret = (int) ret_id; + goto out; + } + num_rows = 0; + for (j = index + 1; j < (tsk_id_t) copy.num_rows; j++) { + rows[num_rows] = j; + num_rows++; + } + ret = tsk_site_table_extend(self, ©, num_rows, rows, 0); + if (ret != 0) { + goto out; + } +out: + tsk_site_table_free(©); + tsk_safe_free(rows); + return ret; +} + +int +tsk_site_table_update_row(tsk_site_table_t *self, tsk_id_t index, double position, + const char *ancestral_state, tsk_size_t ancestral_state_length, const char *metadata, + tsk_size_t metadata_length) +{ + int ret = 0; + tsk_site_t current_row; + + ret = tsk_site_table_get_row(self, index, ¤t_row); + if (ret != 0) { + goto out; + } + if (current_row.metadata_length == metadata_length + && current_row.ancestral_state_length == ancestral_state_length) { + self->position[index] = position; + /* Note: important to use memmove here as we may be provided pointers + * to the column memory as input via get_row */ + memmove(&self->ancestral_state[self->ancestral_state_offset[index]], + ancestral_state, ancestral_state_length * sizeof(*ancestral_state)); + memmove(&self->metadata[self->metadata_offset[index]], metadata, + metadata_length * sizeof(*metadata)); + } else { + ret = tsk_site_table_update_row_rewrite(self, index, position, ancestral_state, + ancestral_state_length, metadata, metadata_length); + if (ret != 0) { + goto out; + } + } +out: + return ret; +} + int tsk_site_table_append_columns(tsk_site_table_t *self, tsk_size_t num_rows, const double *position, const char *ancestral_state, @@ -2993,7 +3302,7 @@ tsk_mutation_table_add_row(tsk_mutation_table_t *self, tsk_id_t site, tsk_id_t n goto out; } self->derived_state_length += derived_state_length; - memcpy( + memmove( self->derived_state + derived_state_offset, derived_state, derived_state_length); self->derived_state_offset[self->num_rows + 1] = self->derived_state_length; @@ -3004,7 +3313,7 @@ tsk_mutation_table_add_row(tsk_mutation_table_t *self, tsk_id_t site, tsk_id_t n goto out; } self->metadata_length += metadata_length; - memcpy(self->metadata + metadata_offset, metadata, metadata_length); + memmove(self->metadata + metadata_offset, metadata, metadata_length); self->metadata_offset[self->num_rows + 1] = self->metadata_length; ret = (tsk_id_t) self->num_rows; @@ -3013,6 +3322,86 @@ tsk_mutation_table_add_row(tsk_mutation_table_t *self, tsk_id_t site, tsk_id_t n return ret; } +static int +tsk_mutation_table_update_row_rewrite(tsk_mutation_table_t *self, tsk_id_t index, + tsk_id_t site, tsk_id_t node, tsk_id_t parent, double time, + const char *derived_state, tsk_size_t derived_state_length, const char *metadata, + tsk_size_t metadata_length) +{ + int ret = 0; + tsk_id_t j, ret_id; + tsk_mutation_table_t copy; + tsk_size_t num_rows; + tsk_id_t *rows = NULL; + + ret = tsk_mutation_table_copy(self, ©, 0); + if (ret != 0) { + goto out; + } + rows = malloc(self->num_rows * sizeof(*rows)); + if (rows == NULL) { + ret = TSK_ERR_NO_MEMORY; + goto out; + } + + ret = tsk_mutation_table_truncate(self, (tsk_size_t) index); + tsk_bug_assert(ret == 0); + ret_id = tsk_mutation_table_add_row(self, site, node, parent, time, derived_state, + derived_state_length, metadata, metadata_length); + if (ret_id < 0) { + ret = (int) ret_id; + goto out; + } + num_rows = 0; + for (j = index + 1; j < (tsk_id_t) copy.num_rows; j++) { + rows[num_rows] = j; + num_rows++; + } + ret = tsk_mutation_table_extend(self, ©, num_rows, rows, 0); + if (ret != 0) { + goto out; + } +out: + tsk_mutation_table_free(©); + tsk_safe_free(rows); + return ret; +} + +int +tsk_mutation_table_update_row(tsk_mutation_table_t *self, tsk_id_t index, tsk_id_t site, + tsk_id_t node, tsk_id_t parent, double time, const char *derived_state, + tsk_size_t derived_state_length, const char *metadata, tsk_size_t metadata_length) +{ + int ret = 0; + tsk_mutation_t current_row; + + ret = tsk_mutation_table_get_row(self, index, ¤t_row); + if (ret != 0) { + goto out; + } + if (current_row.metadata_length == metadata_length + && current_row.derived_state_length == derived_state_length) { + self->site[index] = site; + self->node[index] = node; + self->parent[index] = parent; + self->time[index] = time; + /* Note: important to use memmove here as we may be provided pointers + * to the column memory as input via get_row */ + memmove(&self->derived_state[self->derived_state_offset[index]], derived_state, + derived_state_length * sizeof(*derived_state)); + memmove(&self->metadata[self->metadata_offset[index]], metadata, + metadata_length * sizeof(*metadata)); + } else { + ret = tsk_mutation_table_update_row_rewrite(self, index, site, node, parent, + time, derived_state, derived_state_length, metadata, metadata_length); + if (ret != 0) { + goto out; + } + } +out: + return ret; +} + int tsk_mutation_table_append_columns(tsk_mutation_table_t *self, tsk_size_t num_rows, const tsk_id_t *site, const tsk_id_t *node, const tsk_id_t *parent, @@ -3669,7 +4058,7 @@ tsk_migration_table_add_row(tsk_migration_table_t *self, double left, double rig tsk_bug_assert(self->num_rows < self->max_rows); tsk_bug_assert(self->metadata_length + metadata_length <= self->max_metadata_length); - memcpy(self->metadata + self->metadata_length, metadata, metadata_length); + memmove(self->metadata + self->metadata_length, metadata, metadata_length); self->left[self->num_rows] = left; self->right[self->num_rows] = right; self->node[self->num_rows] = node; @@ -3685,6 +4074,84 @@ tsk_migration_table_add_row(tsk_migration_table_t *self, double left, double rig return ret; } +static int +tsk_migration_table_update_row_rewrite(tsk_migration_table_t *self, tsk_id_t index, + double left, double right, tsk_id_t node, tsk_id_t source, tsk_id_t dest, + double time, const char *metadata, tsk_size_t metadata_length) +{ + int ret = 0; + tsk_id_t j, ret_id; + tsk_migration_table_t copy; + tsk_size_t num_rows; + tsk_id_t *rows = NULL; + + ret = tsk_migration_table_copy(self, ©, 0); + if (ret != 0) { + goto out; + } + rows = malloc(self->num_rows * sizeof(*rows)); + if (rows == NULL) { + ret = TSK_ERR_NO_MEMORY; + goto out; + } + + ret = tsk_migration_table_truncate(self, (tsk_size_t) index); + tsk_bug_assert(ret == 0); + ret_id = tsk_migration_table_add_row( + self, left, right, node, source, dest, time, metadata, metadata_length); + if (ret_id < 0) { + ret = (int) ret_id; + goto out; + } + num_rows = 0; + for (j = index + 1; j < (tsk_id_t) copy.num_rows; j++) { + rows[num_rows] = j; + num_rows++; + } + ret = tsk_migration_table_extend(self, ©, num_rows, rows, 0); + if (ret != 0) { + goto out; + } +out: + tsk_migration_table_free(©); + tsk_safe_free(rows); + return ret; +} + +int +tsk_migration_table_update_row(tsk_migration_table_t *self, tsk_id_t index, double left, + double right, tsk_id_t node, tsk_id_t source, tsk_id_t dest, double time, + const char *metadata, tsk_size_t metadata_length) +{ + int ret = 0; + tsk_migration_t current_row; + + ret = tsk_migration_table_get_row(self, index, ¤t_row); + if (ret != 0) { + goto out; + } + if (current_row.metadata_length == metadata_length) { + self->left[index] = left; + self->right[index] = right; + self->node[index] = node; + self->source[index] = source; + self->dest[index] = dest; + self->time[index] = time; + /* Note: important to use memmove here as we may be provided pointers + * to the column memory as input via get_row */ + memmove(&self->metadata[self->metadata_offset[index]], metadata, + metadata_length * sizeof(*metadata)); + } else { + ret = tsk_migration_table_update_row_rewrite(self, index, left, right, node, + source, dest, time, metadata, metadata_length); + if (ret != 0) { + goto out; + } + } +out: + return ret; +} + int tsk_migration_table_clear(tsk_migration_table_t *self) { @@ -4126,7 +4593,7 @@ tsk_population_table_add_row_internal( tsk_bug_assert(self->num_rows < self->max_rows); tsk_bug_assert(self->metadata_length + metadata_length <= self->max_metadata_length); - memcpy(self->metadata + self->metadata_length, metadata, metadata_length); + memmove(self->metadata + self->metadata_length, metadata, metadata_length); self->metadata_offset[self->num_rows + 1] = self->metadata_length + metadata_length; self->metadata_length += metadata_length; ret = (tsk_id_t) self->num_rows; @@ -4153,6 +4620,75 @@ tsk_population_table_add_row( return ret; } +static int +tsk_population_table_update_row_rewrite(tsk_population_table_t *self, tsk_id_t index, + const char *metadata, tsk_size_t metadata_length) +{ + int ret = 0; + tsk_id_t j, ret_id; + tsk_population_table_t copy; + tsk_size_t num_rows; + tsk_id_t *rows = NULL; + + ret = tsk_population_table_copy(self, ©, 0); + if (ret != 0) { + goto out; + } + rows = malloc(self->num_rows * sizeof(*rows)); + if (rows == NULL) { + ret = TSK_ERR_NO_MEMORY; + goto out; + } + + ret = tsk_population_table_truncate(self, (tsk_size_t) index); + tsk_bug_assert(ret == 0); + ret_id = tsk_population_table_add_row(self, metadata, metadata_length); + if (ret_id < 0) { + ret = (int) ret_id; + goto out; + } + num_rows = 0; + for (j = index + 1; j < (tsk_id_t) copy.num_rows; j++) { + rows[num_rows] = j; + num_rows++; + } + ret = tsk_population_table_extend(self, ©, num_rows, rows, 0); + if (ret != 0) { + goto out; + } +out: + tsk_population_table_free(©); + tsk_safe_free(rows); + return ret; +} + +int +tsk_population_table_update_row(tsk_population_table_t *self, tsk_id_t index, + const char *metadata, tsk_size_t metadata_length) +{ + int ret = 0; + tsk_population_t current_row; + + ret = tsk_population_table_get_row(self, index, ¤t_row); + if (ret != 0) { + goto out; + } + if (current_row.metadata_length == metadata_length) { + /* Note: important to use memmove here as we may be provided pointers + * to the column memory as input via get_row */ + memmove(&self->metadata[self->metadata_offset[index]], metadata, + metadata_length * sizeof(*metadata)); + } else { + ret = tsk_population_table_update_row_rewrite( + self, index, metadata, metadata_length); + if (ret != 0) { + goto out; + } + } +out: + return ret; +} + int tsk_population_table_clear(tsk_population_table_t *self) { @@ -4611,12 +5147,12 @@ tsk_provenance_table_add_row_internal(tsk_provenance_table_t *self, tsk_bug_assert(self->num_rows < self->max_rows); tsk_bug_assert( self->timestamp_length + timestamp_length <= self->max_timestamp_length); - memcpy(self->timestamp + self->timestamp_length, timestamp, timestamp_length); + memmove(self->timestamp + self->timestamp_length, timestamp, timestamp_length); self->timestamp_offset[self->num_rows + 1] = self->timestamp_length + timestamp_length; self->timestamp_length += timestamp_length; tsk_bug_assert(self->record_length + record_length <= self->max_record_length); - memcpy(self->record + self->record_length, record, record_length); + memmove(self->record + self->record_length, record, record_length); self->record_offset[self->num_rows + 1] = self->record_length + record_length; self->record_length += record_length; ret = (tsk_id_t) self->num_rows; @@ -4648,6 +5184,81 @@ tsk_provenance_table_add_row(tsk_provenance_table_t *self, const char *timestamp return ret; } +static int +tsk_provenance_table_update_row_rewrite(tsk_provenance_table_t *self, tsk_id_t index, + const char *timestamp, tsk_size_t timestamp_length, const char *record, + tsk_size_t record_length) +{ + int ret = 0; + tsk_id_t j, ret_id; + tsk_provenance_table_t copy; + tsk_size_t num_rows; + tsk_id_t *rows = NULL; + + ret = tsk_provenance_table_copy(self, ©, 0); + if (ret != 0) { + goto out; + } + rows = malloc(self->num_rows * sizeof(*rows)); + if (rows == NULL) { + ret = TSK_ERR_NO_MEMORY; + goto out; + } + + ret = tsk_provenance_table_truncate(self, (tsk_size_t) index); + tsk_bug_assert(ret == 0); + ret_id = tsk_provenance_table_add_row( + self, timestamp, timestamp_length, record, record_length); + if (ret_id < 0) { + ret = (int) ret_id; + goto out; + } + num_rows = 0; + for (j = index + 1; j < (tsk_id_t) copy.num_rows; j++) { + rows[num_rows] = j; + num_rows++; + } + ret = tsk_provenance_table_extend(self, ©, num_rows, rows, 0); + if (ret != 0) { + goto out; + } +out: + tsk_provenance_table_free(©); + tsk_safe_free(rows); + return ret; +} + +int +tsk_provenance_table_update_row(tsk_provenance_table_t *self, tsk_id_t index, + const char *timestamp, tsk_size_t timestamp_length, const char *record, + tsk_size_t record_length) +{ + int ret = 0; + tsk_provenance_t current_row; + + ret = tsk_provenance_table_get_row(self, index, ¤t_row); + if (ret != 0) { + goto out; + } + if (current_row.timestamp_length == timestamp_length + && current_row.record_length == record_length) { + /* Note: important to use memmove here as we may be provided pointers + * to the column memory as input via get_row */ + memmove(&self->timestamp[self->timestamp_offset[index]], timestamp, + timestamp_length * sizeof(*timestamp)); + memmove(&self->record[self->record_offset[index]], record, + record_length * sizeof(*record)); + } else { + ret = tsk_provenance_table_update_row_rewrite( + self, index, timestamp, timestamp_length, record, record_length); + if (ret != 0) { + goto out; + } + } +out: + return ret; +} + int tsk_provenance_table_clear(tsk_provenance_table_t *self) { diff --git a/c/tskit/tables.h b/c/tskit/tables.h index b3584d624c..6ad0d6ecb3 100644 --- a/c/tskit/tables.h +++ b/c/tskit/tables.h @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2019 Tskit Developers + * Copyright (c) 2019-2021 Tskit Developers * Copyright (c) 2017-2018 University of Oxford * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -845,6 +845,46 @@ tsk_id_t tsk_individual_table_add_row(tsk_individual_table_t *self, tsk_flags_t const double *location, tsk_size_t location_length, const tsk_id_t *parents, tsk_size_t parents_length, const char *metadata, tsk_size_t metadata_length); +/** +@brief Updates the row at the specified index. + +@rst +Rewrite the row at the specified index in this table to use the specified +values. Copies of the ``location``, ``parents`` and ``metadata`` +parameters are taken immediately. See the :ref:`table definition +` for details of the columns in this table. + +.. warning:: + Because of the way that ragged columns are encoded, this method requires a + full rewrite of the internal column memory in worst case, and would + therefore be inefficient for bulk updates for such columns. However, if the + sizes of all ragged column values are unchanged in the updated row, this + method is guaranteed to only update the memory for the row in question. +@endrst + +@param self A pointer to a tsk_individual_table_t object. +@param index The row to update. +@param flags The bitwise flags for the individual. +@param location A pointer to a double array representing the spatial location + of the new individual. Can be ``NULL`` if ``location_length`` is 0. +@param location_length The number of dimensions in the locations position. + Note this the number of elements in the corresponding double array + not the number of bytes. +@param parents A pointer to a ``tsk_id`` array representing the parents + of the new individual. Can be ``NULL`` if ``parents_length`` is 0. +@param parents_length The number of parents. + Note this the number of elements in the corresponding ``tsk_id`` array + not the number of bytes. +@param metadata The metadata to be associated with the new individual. This + is a pointer to arbitrary memory. Can be ``NULL`` if ``metadata_length`` is 0. +@param metadata_length The size of the metadata array in bytes. +@return Return 0 on success or a negative value on failure. +*/ +int tsk_individual_table_update_row(tsk_individual_table_t *self, tsk_id_t index, + tsk_flags_t flags, const double *location, tsk_size_t location_length, + const tsk_id_t *parents, tsk_size_t parents_length, const char *metadata, + tsk_size_t metadata_length); + /** @brief Clears this table, setting the number of rows to zero. @@ -1061,6 +1101,38 @@ tsk_id_t tsk_node_table_add_row(tsk_node_table_t *self, tsk_flags_t flags, doubl tsk_id_t population, tsk_id_t individual, const char *metadata, tsk_size_t metadata_length); +/** +@brief Updates the row at the specified index. + +@rst +Rewrite the row at the specified index in this table to use the specified +values. A copy of the ``metadata`` parameter is taken immediately. See the +:ref:`table definition ` for details of the columns +in this table. + +.. warning:: + Because of the way that ragged columns are encoded, this method requires a + full rewrite of the internal column memory in worst case, and would + therefore be inefficient for bulk updates for such columns. However, if the + sizes of all ragged column values are unchanged in the updated row, this + method is guaranteed to only update the memory for the row in question. +@endrst + +@param self A pointer to a tsk_node_table_t object. +@param index The row to update. +@param flags The bitwise flags for the node. +@param time The time for the node. +@param population The population for the node. Set to TSK_NULL if not known. +@param individual The individual for the node. Set to TSK_NULL if not known. +@param metadata The metadata to be associated with the node. This + is a pointer to arbitrary memory. Can be ``NULL`` if ``metadata_length`` is 0. +@param metadata_length The size of the metadata array in bytes. +@return Return 0 on success or a negative value on failure. +*/ +int tsk_node_table_update_row(tsk_node_table_t *self, tsk_id_t index, tsk_flags_t flags, + double time, tsk_id_t population, tsk_id_t individual, const char *metadata, + tsk_size_t metadata_length); + /** @brief Clears this table, setting the number of rows to zero. @@ -1274,6 +1346,38 @@ for details of the columns in this table. tsk_id_t tsk_edge_table_add_row(tsk_edge_table_t *self, double left, double right, tsk_id_t parent, tsk_id_t child, const char *metadata, tsk_size_t metadata_length); +/** +@brief Updates the row at the specified index. + +@rst +Rewrite the row at the specified index in this table to use the specified +values. A copy of the ``metadata`` parameter is taken immediately. See the +:ref:`table definition ` for details of the columns +in this table. + +.. warning:: + Because of the way that ragged columns are encoded, this method requires a + full rewrite of the internal column memory in worst case, and would + therefore be inefficient for bulk updates for such columns. However, if the + sizes of all ragged column values are unchanged in the updated row, this + method is guaranteed to only update the memory for the row in question. +@endrst + +@param self A pointer to a tsk_edge_table_t object. +@param index The row to update. +@param left The left coordinate for the edge. +@param right The right coordinate for the edge. +@param parent The parent node for the edge. +@param child The child node for the edge. +@param metadata The metadata to be associated with the edge. This + is a pointer to arbitrary memory. Can be ``NULL`` if ``metadata_length`` is 0. +@param metadata_length The size of the metadata array in bytes. +@return Return 0 on success or a negative value on failure. +*/ +int tsk_edge_table_update_row(tsk_edge_table_t *self, tsk_id_t index, double left, + double right, tsk_id_t parent, tsk_id_t child, const char *metadata, + tsk_size_t metadata_length); + /** @brief Clears this table, setting the number of rows to zero. @@ -1483,6 +1587,40 @@ tsk_id_t tsk_migration_table_add_row(tsk_migration_table_t *self, double left, double right, tsk_id_t node, tsk_id_t source, tsk_id_t dest, double time, const char *metadata, tsk_size_t metadata_length); +/** +@brief Updates the row at the specified index. + +@rst +Rewrite the row at the specified index in this table to use the specified +values. A copy of the ``metadata`` parameter is taken immediately. See the +:ref:`table definition ` for details of the columns +in this table. + +.. warning:: + Because of the way that ragged columns are encoded, this method requires a + full rewrite of the internal column memory in worst case, and would + therefore be inefficient for bulk updates for such columns. However, if the + sizes of all ragged column values are unchanged in the updated row, this + method is guaranteed to only update the memory for the row in question. +@endrst + +@param self A pointer to a tsk_migration_table_t object. +@param index The row to update. +@param left The left coordinate for the migration. +@param right The right coordinate for the migration. +@param node The node ID for the migration. +@param source The source population ID for the migration. +@param dest The destination population ID for the migration. +@param time The time for the migration. +@param metadata The metadata to be associated with the migration. This + is a pointer to arbitrary memory. Can be ``NULL`` if ``metadata_length`` is 0. +@param metadata_length The size of the metadata array in bytes. +@return Return 0 on success or a negative value on failure. +*/ +int tsk_migration_table_update_row(tsk_migration_table_t *self, tsk_id_t index, + double left, double right, tsk_id_t node, tsk_id_t source, tsk_id_t dest, + double time, const char *metadata, tsk_size_t metadata_length); + /** @brief Clears this table, setting the number of rows to zero. @@ -1690,6 +1828,37 @@ tsk_id_t tsk_site_table_add_row(tsk_site_table_t *self, double position, const char *ancestral_state, tsk_size_t ancestral_state_length, const char *metadata, tsk_size_t metadata_length); +/** +@brief Updates the row at the specified index. + +@rst +Rewrite the row at the specified index in this table to use the specified +values. Copies of the ``ancestral_state`` and ``metadata`` parameters are taken +immediately. See the :ref:`table definition ` for +details of the columns in this table. + +.. warning:: + Because of the way that ragged columns are encoded, this method requires a + full rewrite of the internal column memory in worst case, and would + therefore be inefficient for bulk updates for such columns. However, if the + sizes of all ragged column values are unchanged in the updated row, this + method is guaranteed to only update the memory for the row in question. +@endrst + +@param self A pointer to a tsk_site_table_t object. +@param index The row to update. +@param position The position coordinate for the site. +@param ancestral_state The ancestral_state for the site. +@param ancestral_state_length The length of the ancestral_state in bytes. +@param metadata The metadata to be associated with the site. This + is a pointer to arbitrary memory. Can be ``NULL`` if ``metadata_length`` is 0. +@param metadata_length The size of the metadata array in bytes. +@return Return 0 on success or a negative value on failure. +*/ +int tsk_site_table_update_row(tsk_site_table_t *self, tsk_id_t index, double position, + const char *ancestral_state, tsk_size_t ancestral_state_length, const char *metadata, + tsk_size_t metadata_length); + /** @brief Clears this table, setting the number of rows to zero. @@ -1900,6 +2069,41 @@ tsk_id_t tsk_mutation_table_add_row(tsk_mutation_table_t *self, tsk_id_t site, tsk_id_t node, tsk_id_t parent, double time, const char *derived_state, tsk_size_t derived_state_length, const char *metadata, tsk_size_t metadata_length); +/** +@brief Updates the row at the specified index. + +@rst +Rewrite the row at the specified index in this table to use the specified +values. Copies of the ``derived_state`` and ``metadata`` parameters are taken +immediately. See the :ref:`table definition ` for +details of the columns in this table. + +.. warning:: + Because of the way that ragged columns are encoded, this method requires a + full rewrite of the internal column memory in worst case, and would + therefore be inefficient for bulk updates for such columns. However, if the + sizes of all ragged column values are unchanged in the updated row, this + method is guaranteed to only update the memory for the row in question. +@endrst + +@param self A pointer to a tsk_mutation_table_t object. +@param index The row to update. +@param site The site ID for the mutation. +@param node The ID of the node this mutation occurs over. +@param parent The ID of the parent mutation. +@param time The time of the mutation. +@param derived_state The derived_state for the mutation. +@param derived_state_length The length of the derived_state in bytes. +@param metadata The metadata to be associated with the mutation. This + is a pointer to arbitrary memory. Can be ``NULL`` if ``metadata_length`` is 0. +@param metadata_length The size of the metadata array in bytes. +@return Return 0 on success or a negative value on failure. +*/ +int tsk_mutation_table_update_row(tsk_mutation_table_t *self, tsk_id_t index, + tsk_id_t site, tsk_id_t node, tsk_id_t parent, double time, + const char *derived_state, tsk_size_t derived_state_length, const char *metadata, + tsk_size_t metadata_length); + /** @brief Clears this table, setting the number of rows to zero. @@ -2105,6 +2309,33 @@ Add a new population with the specified ``metadata`` to the table. A copy of the tsk_id_t tsk_population_table_add_row( tsk_population_table_t *self, const char *metadata, tsk_size_t metadata_length); +/** +@brief Updates the row at the specified index. + +@rst +Rewrite the row at the specified index in this table to use the specified +values. A copy of the ``metadata`` parameter is taken immediately. See the +:ref:`table definition ` for details of the +columns in this table. + +.. warning:: + Because of the way that ragged columns are encoded, this method requires a + full rewrite of the internal column memory in worst case, and would + therefore be inefficient for bulk updates for such columns. However, if the + sizes of all ragged column values are unchanged in the updated row, this + method is guaranteed to only update the memory for the row in question. +@endrst + +@param self A pointer to a tsk_population_table_t object. +@param index The row to update. +@param metadata The metadata to be associated with the population. This + is a pointer to arbitrary memory. Can be ``NULL`` if ``metadata_length`` is 0. +@param metadata_length The size of the metadata array in bytes. +@return Return 0 on success or a negative value on failure. +*/ +int tsk_population_table_update_row(tsk_population_table_t *self, tsk_id_t index, + const char *metadata, tsk_size_t metadata_length); + /** @brief Clears this table, setting the number of rows to zero. @@ -2309,6 +2540,37 @@ tsk_id_t tsk_provenance_table_add_row(tsk_provenance_table_t *self, const char *timestamp, tsk_size_t timestamp_length, const char *record, tsk_size_t record_length); +/** +@brief Updates the row at the specified index. + +@rst +Rewrite the row at the specified index in this table to use the specified +values. Copies of the ``timestamp`` and ``record`` parameters are taken +immediately. See the :ref:`table definition ` +for details of the columns in this table. + +.. warning:: + Because of the way that ragged columns are encoded, this method requires a + full rewrite of the internal column memory in worst case, and would + therefore be inefficient for bulk updates for such columns. However, if the + sizes of all ragged column values are unchanged in the updated row, this + method is guaranteed to only update the memory for the row in question. +@endrst + +@param self A pointer to a tsk_provenance_table_t object. +@param index The row to update. +@param timestamp The timestamp to be associated with new provenance. This + is a pointer to arbitrary memory. Can be ``NULL`` if ``timestamp_length`` is 0. +@param timestamp_length The size of the timestamp array in bytes. +@param record The record to be associated with the provenance. This + is a pointer to arbitrary memory. Can be ``NULL`` if ``record_length`` is 0. +@param record_length The size of the record array in bytes. +@return Return 0 on success or a negative value on failure. +*/ +int tsk_provenance_table_update_row(tsk_provenance_table_t *self, tsk_id_t index, + const char *timestamp, tsk_size_t timestamp_length, const char *record, + tsk_size_t record_length); + /** @brief Clears this table, setting the number of rows to zero.