From e644f0a5047185e96300c743fde44f79cd010c4e Mon Sep 17 00:00:00 2001 From: seleznevae Date: Sun, 19 Jan 2020 13:37:31 +0300 Subject: [PATCH] [A] add function `ft_delete_range` --- ChangeLog.md | 1 + lib/fort.c | 80 +++++++++++++++++++ lib/fort.h | 24 ++++++ src/fort.h | 24 ++++++ src/fort_impl.c | 52 +++++++++++++ src/row.c | 21 +++++ src/row.h | 4 + src/vector.h | 3 + tests/bb_tests/test_table_basic.c | 125 ++++++++++++++++++++++++++++++ tests/main_test.c | 2 + 10 files changed, 336 insertions(+) diff --git a/ChangeLog.md b/ChangeLog.md index 5351a78..fbbfd9e 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -2,6 +2,7 @@ ### API +- Add function `ft_delete_range` to delete range of the cells. - Add functions to check if table is empty to the API. - `ft_ln` returns status of operation. - Add new table property `adding_strategy` (2 strategies available - replace(default) and insert). diff --git a/lib/fort.c b/lib/fort.c index 86d147f..d525227 100644 --- a/lib/fort.c +++ b/lib/fort.c @@ -337,6 +337,9 @@ f_vector_t *copy_vector(f_vector_t *); size_t vector_index_of(const f_vector_t *, const void *item); #endif +#define VECTOR_AT(vector, pos, data_type) \ + *(data_type *)vector_at((vector), (pos)) + #endif /* VECTOR_H */ /******************************************************** @@ -2147,6 +2150,10 @@ f_row_t *copy_row(f_row_t *row); FT_INTERNAL f_row_t *split_row(f_row_t *row, size_t pos); +// Delete range [left; right] of cells (both ends included) +FT_INTERNAL +int ft_row_delete_range(f_row_t *row, size_t left, size_t right); + FT_INTERNAL f_row_t *create_row_from_string(const char *str); @@ -2806,6 +2813,58 @@ size_t ft_row_count(const ft_table_t *table) return vector_size(table->rows); } +int ft_delete_range(ft_table_t *table, + size_t top_left_row, size_t top_left_col, + size_t bottom_right_row, size_t bottom_right_col) +{ + assert(table && table->rows); + int status = FT_SUCCESS; + + size_t rows_n = vector_size(table->rows); + + if (top_left_row == FT_CUR_ROW) + top_left_row = table->cur_row; + if (bottom_right_row == FT_CUR_ROW) + bottom_right_row = table->cur_row; + + if (top_left_col == FT_CUR_COLUMN) + top_left_col = table->cur_row; + if (bottom_right_col == FT_CUR_COLUMN) + bottom_right_col = table->cur_row; + + if (top_left_row > bottom_right_row || top_left_col > bottom_right_col) + return FT_EINVAL; + + f_row_t *row = NULL; + size_t i = top_left_row; + while (i < rows_n && i <= bottom_right_row) { + row = VECTOR_AT(table->rows, i, f_row_t *); + status = ft_row_delete_range(row, top_left_col, bottom_right_col); + if (FT_IS_ERROR(status)) + goto clear; + ++i; + } + + size_t n_iterations = MIN(rows_n - 1, bottom_right_row) - top_left_row + 1; + size_t j = 0; + i = top_left_row; + for (j = 0; j < n_iterations; ++j) { + row = VECTOR_AT(table->rows, i, f_row_t *); + if (columns_in_row(row)) { + ++i; + } else { + destroy_row(row); + status = vector_erase(table->rows, i); + if (FT_IS_ERROR(status)) + goto clear; + } + } + +clear: + return status; +} + + static int ft_row_printf_impl_(ft_table_t *table, size_t row, const struct f_string_view *fmt, va_list *va) { size_t i = 0; @@ -5270,6 +5329,27 @@ f_row_t *split_row(f_row_t *row, size_t pos) return tail; } +FT_INTERNAL +int ft_row_delete_range(f_row_t *row, size_t left, size_t right) +{ + assert(row); + size_t cols_n = vector_size(row->cells); + if (cols_n == 0 || (right < left)) + return FT_SUCCESS; + + f_cell_t *cell = NULL; + size_t i = left; + while (i < cols_n && i <= right) { + cell = VECTOR_AT(row->cells, i, f_cell_t *); + destroy_cell(cell); + ++i; + } + size_t n_destroy = MIN(cols_n - 1, right) - left + 1; + while (n_destroy--) { + vector_erase(row->cells, left); + } + return FT_SUCCESS; +} FT_INTERNAL size_t columns_in_row(const f_row_t *row) diff --git a/lib/fort.h b/lib/fort.h index 0e9aed5..06b8695 100644 --- a/lib/fort.h +++ b/lib/fort.h @@ -340,6 +340,30 @@ int ft_is_empty(const ft_table_t *table); */ size_t ft_row_count(const ft_table_t *table); +/** + * Delete range of cells. + * + * Range of cells is determined by 2 points (top-left and bottom-right) (both + * ends are included). + * + * @param table + * Pointer to formatted table. + * @param top_left_row + * Row number of the top left cell in the range. + * @param top_left_col + * Column number of the top left cell in the range. + * @param bottom_right_row + * Row number of the bottom right cell in the range. + * @param bottom_right_col + * Column number of the bottom right cell in the range. + * @return + * - 0 - Operation was successfully implemented + * - (<0): In case of error + */ +int ft_delete_range(ft_table_t *table, + size_t top_left_row, size_t top_left_col, + size_t bottom_right_row, size_t bottom_right_col); + #if defined(FT_CLANG_COMPILER) || defined(FT_GCC_COMPILER) /** diff --git a/src/fort.h b/src/fort.h index 0e9aed5..06b8695 100644 --- a/src/fort.h +++ b/src/fort.h @@ -340,6 +340,30 @@ int ft_is_empty(const ft_table_t *table); */ size_t ft_row_count(const ft_table_t *table); +/** + * Delete range of cells. + * + * Range of cells is determined by 2 points (top-left and bottom-right) (both + * ends are included). + * + * @param table + * Pointer to formatted table. + * @param top_left_row + * Row number of the top left cell in the range. + * @param top_left_col + * Column number of the top left cell in the range. + * @param bottom_right_row + * Row number of the bottom right cell in the range. + * @param bottom_right_col + * Column number of the bottom right cell in the range. + * @return + * - 0 - Operation was successfully implemented + * - (<0): In case of error + */ +int ft_delete_range(ft_table_t *table, + size_t top_left_row, size_t top_left_col, + size_t bottom_right_row, size_t bottom_right_col); + #if defined(FT_CLANG_COMPILER) || defined(FT_GCC_COMPILER) /** diff --git a/src/fort_impl.c b/src/fort_impl.c index d52ecea..f493f6b 100644 --- a/src/fort_impl.c +++ b/src/fort_impl.c @@ -233,6 +233,58 @@ size_t ft_row_count(const ft_table_t *table) return vector_size(table->rows); } +int ft_delete_range(ft_table_t *table, + size_t top_left_row, size_t top_left_col, + size_t bottom_right_row, size_t bottom_right_col) +{ + assert(table && table->rows); + int status = FT_SUCCESS; + + size_t rows_n = vector_size(table->rows); + + if (top_left_row == FT_CUR_ROW) + top_left_row = table->cur_row; + if (bottom_right_row == FT_CUR_ROW) + bottom_right_row = table->cur_row; + + if (top_left_col == FT_CUR_COLUMN) + top_left_col = table->cur_row; + if (bottom_right_col == FT_CUR_COLUMN) + bottom_right_col = table->cur_row; + + if (top_left_row > bottom_right_row || top_left_col > bottom_right_col) + return FT_EINVAL; + + f_row_t *row = NULL; + size_t i = top_left_row; + while (i < rows_n && i <= bottom_right_row) { + row = VECTOR_AT(table->rows, i, f_row_t *); + status = ft_row_delete_range(row, top_left_col, bottom_right_col); + if (FT_IS_ERROR(status)) + goto clear; + ++i; + } + + size_t n_iterations = MIN(rows_n - 1, bottom_right_row) - top_left_row + 1; + size_t j = 0; + i = top_left_row; + for (j = 0; j < n_iterations; ++j) { + row = VECTOR_AT(table->rows, i, f_row_t *); + if (columns_in_row(row)) { + ++i; + } else { + destroy_row(row); + status = vector_erase(table->rows, i); + if (FT_IS_ERROR(status)) + goto clear; + } + } + +clear: + return status; +} + + static int ft_row_printf_impl_(ft_table_t *table, size_t row, const struct f_string_view *fmt, va_list *va) { size_t i = 0; diff --git a/src/row.c b/src/row.c index 4f45329..da67664 100644 --- a/src/row.c +++ b/src/row.c @@ -98,6 +98,27 @@ f_row_t *split_row(f_row_t *row, size_t pos) return tail; } +FT_INTERNAL +int ft_row_delete_range(f_row_t *row, size_t left, size_t right) +{ + assert(row); + size_t cols_n = vector_size(row->cells); + if (cols_n == 0 || (right < left)) + return FT_SUCCESS; + + f_cell_t *cell = NULL; + size_t i = left; + while (i < cols_n && i <= right) { + cell = VECTOR_AT(row->cells, i, f_cell_t *); + destroy_cell(cell); + ++i; + } + size_t n_destroy = MIN(cols_n - 1, right) - left + 1; + while (n_destroy--) { + vector_erase(row->cells, left); + } + return FT_SUCCESS; +} FT_INTERNAL size_t columns_in_row(const f_row_t *row) diff --git a/src/row.h b/src/row.h index ec45b19..ae68a15 100644 --- a/src/row.h +++ b/src/row.h @@ -21,6 +21,10 @@ f_row_t *copy_row(f_row_t *row); FT_INTERNAL f_row_t *split_row(f_row_t *row, size_t pos); +// Delete range [left; right] of cells (both ends included) +FT_INTERNAL +int ft_row_delete_range(f_row_t *row, size_t left, size_t right); + FT_INTERNAL f_row_t *create_row_from_string(const char *str); diff --git a/src/vector.h b/src/vector.h index a83c96d..fbae084 100644 --- a/src/vector.h +++ b/src/vector.h @@ -47,4 +47,7 @@ f_vector_t *copy_vector(f_vector_t *); size_t vector_index_of(const f_vector_t *, const void *item); #endif +#define VECTOR_AT(vector, pos, data_type) \ + *(data_type *)vector_at((vector), (pos)) + #endif /* VECTOR_H */ diff --git a/tests/bb_tests/test_table_basic.c b/tests/bb_tests/test_table_basic.c index 3fecb53..e68c562 100644 --- a/tests/bb_tests/test_table_basic.c +++ b/tests/bb_tests/test_table_basic.c @@ -1808,3 +1808,128 @@ void test_table_changing_cell(void) ft_destroy_table(table); } } + +static struct ft_table *create_test_table() +{ + ft_table_t *table = ft_create_table(); + assert_true(table != NULL); + ft_set_cell_prop(table, FT_ANY_ROW, FT_ANY_COLUMN, FT_CPROP_BOTTOM_PADDING, 0); + ft_set_cell_prop(table, FT_ANY_ROW, FT_ANY_COLUMN, FT_CPROP_TOP_PADDING, 0); + + ft_write_ln(table, "00", "01", "02"); + ft_write_ln(table, "10", "11", "12"); + ft_write_ln(table, "20", "21", "22"); + + return table; +} + +void test_table_cell_deletion(void) +{ + WHEN("Test invalid arguments") { + ft_table_t *table = create_test_table(); + + // invalid rows + assert_true(ft_delete_range(table, 1, 1, 0, 2) == FT_EINVAL); + + // invalid colums + assert_true(ft_delete_range(table, 1, 1, 2, 0) == FT_EINVAL); + + ft_destroy_table(table); + } + + WHEN("Delete one cell") { + ft_table_t *table = create_test_table(); + assert_true(FT_IS_SUCCESS(ft_delete_range(table, 1, 1, 1, 1))); + + const char *table_str = ft_to_string(table); + assert_true(table_str != NULL); + const char *table_str_etalon = + "+----+----+----+\n" + "| 00 | 01 | 02 |\n" + "| 10 | 12 | |\n" + "| 20 | 21 | 22 |\n" + "+----+----+----+\n"; + assert_str_equal(table_str, table_str_etalon); + ft_destroy_table(table); + } + + WHEN("Delete one last cell") { + ft_table_t *table = create_test_table(); + ft_write_ln(table, "30"); + assert_true(FT_IS_SUCCESS(ft_delete_range(table, 3, 0, 3, 0))); + + const char *table_str = ft_to_string(table); + assert_true(table_str != NULL); + const char *table_str_etalon = + "+----+----+----+\n" + "| 00 | 01 | 02 |\n" + "| 10 | 11 | 12 |\n" + "| 20 | 21 | 22 |\n" + "+----+----+----+\n"; + assert_str_equal(table_str, table_str_etalon); + ft_destroy_table(table); + } + + WHEN("Delete row") { + ft_table_t *table = create_test_table(); + assert_true(FT_IS_SUCCESS(ft_delete_range(table, 1, 0, 1, 999))); + + const char *table_str = ft_to_string(table); + assert_true(table_str != NULL); + const char *table_str_etalon = + "+----+----+----+\n" + "| 00 | 01 | 02 |\n" + "| 20 | 21 | 22 |\n" + "+----+----+----+\n"; + assert_str_equal(table_str, table_str_etalon); + ft_destroy_table(table); + } + + WHEN("Delete last row") { + ft_table_t *table = create_test_table(); + assert_true(FT_IS_SUCCESS(ft_delete_range(table, 2, 0, 2, 999))); + + const char *table_str = ft_to_string(table); + assert_true(table_str != NULL); + const char *table_str_etalon = + "+----+----+----+\n" + "| 00 | 01 | 02 |\n" + "| 10 | 11 | 12 |\n" + "+----+----+----+\n"; + assert_str_equal(table_str, table_str_etalon); + ft_destroy_table(table); + } + + WHEN("Delete column") { + ft_table_t *table = create_test_table(); + assert_true(FT_IS_SUCCESS(ft_delete_range(table, 0, 1, 999, 1))); + + const char *table_str = ft_to_string(table); + assert_true(table_str != NULL); + const char *table_str_etalon = + "+----+----+\n" + "| 00 | 02 |\n" + "| 10 | 12 |\n" + "| 20 | 22 |\n" + "+----+----+\n"; + assert_str_equal(table_str, table_str_etalon); + ft_destroy_table(table); + } + + WHEN("Delete last column") { + ft_table_t *table = create_test_table(); + assert_true(FT_IS_SUCCESS(ft_delete_range(table, 0, 2, 999, 2))); + + const char *table_str = ft_to_string(table); + assert_true(table_str != NULL); + const char *table_str_etalon = + "+----+----+\n" + "| 00 | 01 |\n" + "| 10 | 11 |\n" + "| 20 | 21 |\n" + "+----+----+\n"; + assert_str_equal(table_str, table_str_etalon); + ft_destroy_table(table); + } + +} diff --git a/tests/main_test.c b/tests/main_test.c index 0a990e0..fa93034 100644 --- a/tests/main_test.c +++ b/tests/main_test.c @@ -12,6 +12,7 @@ void test_table_geometry(void); void test_table_basic(void); void test_table_copy(void); void test_table_changing_cell(void); +void test_table_cell_deletion(void); #ifdef FT_HAVE_WCHAR void test_wcs_table_boundaries(void); #endif @@ -52,6 +53,7 @@ struct test_case bb_test_suite [] = { {"test_table_write", test_table_write}, {"test_table_insert_strategy", test_table_insert_strategy}, {"test_table_changing_cell", test_table_changing_cell}, + {"test_table_cell_deletion", test_table_cell_deletion}, {"test_table_border_style", test_table_border_style}, {"test_table_builtin_border_styles", test_table_builtin_border_styles}, {"test_table_cell_properties", test_table_cell_properties},