[A] Added adding_strategy property to the tables

This commit is contained in:
seleznevae 2020-01-08 15:32:06 +03:00
parent 7d313ee078
commit 27e1102878
19 changed files with 823 additions and 94 deletions

View File

@ -3,6 +3,8 @@
### API
- 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).
## v0.3.2

View File

@ -311,6 +311,12 @@ size_t vector_capacity(const f_vector_t *);
FT_INTERNAL
int vector_push(f_vector_t *, const void *item);
FT_INTERNAL
int vector_insert(f_vector_t *, const void *item, size_t pos);
FT_INTERNAL
f_vector_t *vector_split(f_vector_t *, size_t pos);
FT_INTERNAL
const void *vector_at_c(const f_vector_t *vector, size_t index);
@ -320,12 +326,15 @@ void *vector_at(f_vector_t *, size_t index);
FT_INTERNAL
f_status vector_swap(f_vector_t *cur_vec, f_vector_t *mv_vec, size_t pos);
FT_INTERNAL
void vector_clear(f_vector_t *);
FT_INTERNAL
int vector_erase(f_vector_t *, size_t index);
#ifdef FT_TEST_BUILD
f_vector_t *copy_vector(f_vector_t *);
size_t vector_index_of(const f_vector_t *, const void *item);
int vector_erase(f_vector_t *, size_t index);
void vector_clear(f_vector_t *);
#endif
#endif /* VECTOR_H */
@ -2020,6 +2029,7 @@ struct fort_entire_table_properties {
unsigned int top_margin;
unsigned int right_margin;
unsigned int bottom_margin;
enum ft_adding_strategy add_strategy;
};
typedef struct fort_entire_table_properties fort_entire_table_properties_t;
extern fort_entire_table_properties_t g_entire_table_properties;
@ -2134,6 +2144,9 @@ void destroy_row(f_row_t *row);
FT_INTERNAL
f_row_t *copy_row(f_row_t *row);
FT_INTERNAL
f_row_t *split_row(f_row_t *row, size_t pos);
FT_INTERNAL
f_row_t *create_row_from_string(const char *str);
@ -2152,9 +2165,15 @@ const f_cell_t *get_cell_c(const f_row_t *row, size_t col);
FT_INTERNAL
f_cell_t *get_cell_and_create_if_not_exists(f_row_t *row, size_t col);
FT_INTERNAL
f_cell_t *create_cell_in_position(f_row_t *row, size_t col);
FT_INTERNAL
f_status swap_row(f_row_t *cur_row, f_row_t *ins_row, size_t pos);
FT_INTERNAL
f_status insert_row(f_row_t *cur_row, f_row_t *ins_row, size_t pos);
FT_INTERNAL
size_t group_cell_number(const f_row_t *row, size_t master_cell_col);
@ -2611,7 +2630,14 @@ ft_table_t *ft_create_table(void)
F_FREE(result);
return NULL;
}
result->properties = NULL;
result->properties = create_table_properties();
if (result->properties == NULL) {
destroy_vector(result->separators);
destroy_vector(result->rows);
F_FREE(result);
return NULL;
}
result->conv_buffer = NULL;
result->cur_row = 0;
result->cur_col = 0;
@ -2677,7 +2703,12 @@ ft_table_t *ft_copy_table(ft_table_t *table)
vector_push(result->separators, &new_sep);
}
/* note: by default new table has allocated default properties, so we
* have to destroy them first.
*/
if (result->properties) {
destroy_table_properties(result->properties);
}
result->properties = copy_table_properties(table->properties);
if (result->properties == NULL) {
ft_destroy_table(result);
@ -2691,12 +2722,57 @@ ft_table_t *ft_copy_table(ft_table_t *table)
return result;
}
static int split_cur_row(ft_table_t *table, f_row_t **tail_of_cur_row)
{
if (table->cur_row >= vector_size(table->rows)) {
tail_of_cur_row = NULL;
return 0;
}
void ft_ln(ft_table_t *table)
f_row_t *row = *(f_row_t **)vector_at(table->rows, table->cur_row);
if (table->cur_col >= columns_in_row(row)) {
tail_of_cur_row = NULL;
return 0;
}
f_row_t *tail = split_row(row, table->cur_col);
if (!tail) {
tail_of_cur_row = NULL;
return FT_ERROR;
}
*tail_of_cur_row = tail;
return 0;
}
int ft_ln(ft_table_t *table)
{
assert(table);
fort_entire_table_properties_t *table_props = &table->properties->entire_table_properties;
switch (table_props->add_strategy) {
case FT_STRATEGY_INSERT: {
f_row_t *new_row = NULL;
if (FT_IS_ERROR(split_cur_row(table, &new_row))) {
return FT_ERROR;
}
if (new_row) {
if (FT_IS_ERROR(vector_insert(table->rows, &new_row, table->cur_row + 1))) {
destroy_row(new_row);
return FT_ERROR;
}
}
break;
}
case FT_STRATEGY_REPLACE:
// do nothing
break;
default:
assert(0 && "Unexpected situation inside libfort");
break;
}
table->cur_col = 0;
table->cur_row++;
return FT_SUCCESS;
}
size_t ft_cur_row(const ft_table_t *table)
@ -2760,7 +2836,23 @@ static int ft_row_printf_impl_(ft_table_t *table, size_t row, const struct f_str
new_cols = columns_in_row(new_row);
cur_row_p = (f_row_t **)vector_at(table->rows, row);
swap_row(*cur_row_p, new_row, table->cur_col);
fort_entire_table_properties_t *table_props = &table->properties->entire_table_properties;
switch (table_props->add_strategy) {
case FT_STRATEGY_INSERT: {
if (FT_IS_ERROR(insert_row(*cur_row_p, new_row, table->cur_col)))
goto clear;
break;
}
case FT_STRATEGY_REPLACE: {
if (FT_IS_ERROR(swap_row(*cur_row_p, new_row, table->cur_col)))
goto clear;
break;
}
default:
assert(0 && "Unexpected situation inside libfort");
break;
}
table->cur_col += new_cols;
destroy_row(new_row);
@ -4920,6 +5012,7 @@ fort_entire_table_properties_t g_entire_table_properties = {
0, /* top_margin */
0, /* right_margin */
0, /* bottom_margin */
FT_STRATEGY_REPLACE, /* add_strategy */
};
static f_status set_entire_table_property_internal(fort_entire_table_properties_t *properties, uint32_t property, int value)
@ -4934,6 +5027,8 @@ static f_status set_entire_table_property_internal(fort_entire_table_properties_
properties->right_margin = value;
} else if (PROP_IS_SET(property, FT_TPROP_BOTTOM_MARGIN)) {
properties->bottom_margin = value;
} else if (PROP_IS_SET(property, FT_TPROP_ADDING_STRATEGY)) {
properties->add_strategy = (enum ft_adding_strategy)value;
} else {
return FT_EINVAL;
}
@ -4989,7 +5084,8 @@ f_table_properties_t g_table_properties = {
0, /* left_margin */
0, /* top_margin */
0, /* right_margin */
0 /* bottom_margin */
0, /* bottom_margin */
FT_STRATEGY_REPLACE, /* add_strategy */
}
};
@ -5084,21 +5180,41 @@ struct f_row {
f_vector_t *cells;
};
FT_INTERNAL
f_row_t *create_row(void)
static
f_row_t *create_row_impl(f_vector_t *cells)
{
f_row_t *row = (f_row_t *)F_CALLOC(1, sizeof(f_row_t));
if (row == NULL)
return NULL;
row->cells = create_vector(sizeof(f_cell_t *), DEFAULT_VECTOR_CAPACITY);
if (row->cells == NULL) {
F_FREE(row);
return NULL;
if (cells) {
row->cells = cells;
} else {
row->cells = create_vector(sizeof(f_cell_t *), DEFAULT_VECTOR_CAPACITY);
if (row->cells == NULL) {
F_FREE(row);
return NULL;
}
}
return row;
}
FT_INTERNAL
f_row_t *create_row(void)
{
return create_row_impl(NULL);
}
static
void destroy_each_cell(f_vector_t *cells)
{
size_t i = 0;
size_t cells_n = vector_size(cells);
for (i = 0; i < cells_n; ++i) {
f_cell_t *cell = *(f_cell_t **)vector_at(cells, i);
destroy_cell(cell);
}
}
FT_INTERNAL
void destroy_row(f_row_t *row)
{
@ -5106,12 +5222,7 @@ void destroy_row(f_row_t *row)
return;
if (row->cells) {
size_t i = 0;
size_t cells_n = vector_size(row->cells);
for (i = 0; i < cells_n; ++i) {
f_cell_t *cell = *(f_cell_t **)vector_at(row->cells, i);
destroy_cell(cell);
}
destroy_each_cell(row->cells);
destroy_vector(row->cells);
}
@ -5141,6 +5252,23 @@ f_row_t *copy_row(f_row_t *row)
return result;
}
FT_INTERNAL
f_row_t *split_row(f_row_t *row, size_t pos)
{
assert(row);
f_vector_t *cells = vector_split(row->cells, pos);
if (!cells)
return NULL;
f_row_t *tail = create_row_impl(cells);
if (!tail) {
destroy_each_cell(cells);
destroy_vector(cells);
}
return tail;
}
FT_INTERNAL
size_t columns_in_row(const f_row_t *row)
{
@ -5202,6 +5330,23 @@ f_cell_t *get_cell_and_create_if_not_exists(f_row_t *row, size_t col)
return get_cell_impl(row, col, CREATE_ON_NULL);
}
FT_INTERNAL
f_cell_t *create_cell_in_position(f_row_t *row, size_t col)
{
if (row == NULL || row->cells == NULL) {
return NULL;
}
f_cell_t *new_cell = create_cell();
if (new_cell == NULL)
return NULL;
if (FT_IS_ERROR(vector_insert(row->cells, &new_cell, col))) {
destroy_cell(new_cell);
return NULL;
}
return *(f_cell_t **)vector_at(row->cells, col);
}
FT_INTERNAL
f_status swap_row(f_row_t *cur_row, f_row_t *ins_row, size_t pos)
@ -5220,6 +5365,37 @@ f_status swap_row(f_row_t *cur_row, f_row_t *ins_row, size_t pos)
return vector_swap(cur_row->cells, ins_row->cells, pos);
}
/* Ownership of cells of `ins_row` is passed to `cur_row`. */
FT_INTERNAL
f_status insert_row(f_row_t *cur_row, f_row_t *ins_row, size_t pos)
{
assert(cur_row);
assert(ins_row);
while (vector_size(cur_row->cells) < pos) {
f_cell_t *new_cell = create_cell();
if (!new_cell)
return FT_ERROR;
vector_push(cur_row->cells, &new_cell);
}
size_t sz = vector_size(ins_row->cells);
size_t i = 0;
for (i = 0; i < sz; ++i) {
f_cell_t *cell = *(f_cell_t **)vector_at(ins_row->cells, i);
if (FT_IS_ERROR(vector_insert(cur_row->cells, &cell, pos + i))) {
/* clean up what we have inserted */
while (i--) {
vector_erase(cur_row->cells, pos);
}
return FT_ERROR;
}
}
/* Clear cells so that it will be safe to destroy this row */
vector_clear(ins_row->cells);
return FT_SUCCESS;
}
FT_INTERNAL
size_t group_cell_number(const f_row_t *row, size_t master_cell_col)
@ -6672,7 +6848,6 @@ f_row_t *get_row_and_create_if_not_exists(ft_table_t *table, size_t row)
return get_row_impl(table, row, CREATE_ON_NULL);
}
FT_INTERNAL
f_string_buffer_t *get_cur_str_buffer_and_create_if_not_exists(ft_table_t *table)
{
@ -6681,7 +6856,21 @@ f_string_buffer_t *get_cur_str_buffer_and_create_if_not_exists(ft_table_t *table
f_row_t *row = get_row_and_create_if_not_exists(table, table->cur_row);
if (row == NULL)
return NULL;
f_cell_t *cell = get_cell_and_create_if_not_exists(row, table->cur_col);
f_cell_t *cell = NULL;
fort_entire_table_properties_t *table_props = &table->properties->entire_table_properties;
switch (table_props->add_strategy) {
case FT_STRATEGY_INSERT:
cell = create_cell_in_position(row, table->cur_col);
break;
case FT_STRATEGY_REPLACE:
cell = get_cell_and_create_if_not_exists(row, table->cur_col);
break;
default:
assert(0 && "Unexpected situation inside libfort");
break;
}
if (cell == NULL)
return NULL;
@ -6983,6 +7172,56 @@ int vector_push(f_vector_t *vector, const void *item)
return FT_SUCCESS;
}
FT_INTERNAL
int vector_insert(f_vector_t *vector, const void *item, size_t pos)
{
assert(vector);
assert(item);
size_t needed_capacity = MAX(pos + 1, vector->m_size + 1);
if (vector->m_capacity < needed_capacity) {
if (vector_reallocate_(vector, needed_capacity) == -1)
return FT_ERROR;
vector->m_capacity = needed_capacity;
}
size_t offset = pos * vector->m_item_size;
if (pos >= vector->m_size) {
/* Data in the middle are not initialized */
memcpy((char *)vector->m_data + offset, item, vector->m_item_size);
vector->m_size = pos + 1;
return FT_SUCCESS;
} else {
/* Shift following data by one position */
memmove((char *)vector->m_data + offset + vector->m_item_size,
(char *)vector->m_data + offset,
vector->m_item_size * (vector->m_size - pos));
memcpy((char *)vector->m_data + offset, item, vector->m_item_size);
++(vector->m_size);
return FT_SUCCESS;
}
}
FT_INTERNAL
f_vector_t *vector_split(f_vector_t *vector, size_t pos)
{
size_t trailing_sz = vector->m_size > pos ? vector->m_size - pos : 0;
f_vector_t *new_vector = create_vector(vector->m_item_size, trailing_sz);
if (!new_vector)
return new_vector;
if (new_vector->m_capacity < trailing_sz) {
destroy_vector(new_vector);
return NULL;
}
if (trailing_sz == 0)
return new_vector;
size_t offset = vector->m_item_size * pos;
memcpy(new_vector->m_data, (char *)vector->m_data + offset,
trailing_sz * vector->m_item_size);
new_vector->m_size = trailing_sz;
vector->m_size = pos;
return new_vector;
}
FT_INTERNAL
const void *vector_at_c(const f_vector_t *vector, size_t index)
@ -7058,6 +7297,26 @@ f_status vector_swap(f_vector_t *cur_vec, f_vector_t *mv_vec, size_t pos)
return FT_SUCCESS;
}
FT_INTERNAL
void vector_clear(f_vector_t *vector)
{
vector->m_size = 0;
}
FT_INTERNAL
int vector_erase(f_vector_t *vector, size_t index)
{
assert(vector);
if (vector->m_size == 0 || index >= vector->m_size)
return FT_ERROR;
memmove((char *)vector->m_data + vector->m_item_size * index,
(char *)vector->m_data + vector->m_item_size * (index + 1),
(vector->m_size - 1 - index) * vector->m_item_size);
vector->m_size--;
return FT_SUCCESS;
}
#ifdef FT_TEST_BUILD
@ -7091,26 +7350,6 @@ size_t vector_index_of(const f_vector_t *vector, const void *item)
return INVALID_VEC_INDEX;
}
int vector_erase(f_vector_t *vector, size_t index)
{
assert(vector);
if (vector->m_size == 0 || index >= vector->m_size)
return FT_ERROR;
memmove((char *)vector->m_data + vector->m_item_size * index,
(char *)vector->m_data + vector->m_item_size * (index + 1),
(vector->m_size - 1 - index) * vector->m_item_size);
vector->m_size--;
return FT_SUCCESS;
}
void vector_clear(f_vector_t *vector)
{
vector->m_size = 0;
}
#endif
/********************************************************

View File

@ -275,8 +275,14 @@ ft_table_t *ft_copy_table(ft_table_t *table);
*
* @param table
* Pointer to formatted table.
* @return
* - 0: Success; data were written
* - (<0): In case of error.
* @note
* This function can fail only in case FT_STRATEGY_INSERT adding strategy
* was set for the table.
*/
void ft_ln(ft_table_t *table);
int ft_ln(ft_table_t *table);
/**
* Get row number of the current cell.
@ -812,12 +818,23 @@ int ft_set_cell_prop(ft_table_t *table, size_t row, size_t col, uint32_t propert
* @name Table properties identifiers.
* @{
*/
#define FT_TPROP_LEFT_MARGIN (0x01U << 0)
#define FT_TPROP_TOP_MARGIN (0x01U << 1)
#define FT_TPROP_RIGHT_MARGIN (0x01U << 2)
#define FT_TPROP_BOTTOM_MARGIN (0x01U << 3)
#define FT_TPROP_LEFT_MARGIN (0x01U << 0)
#define FT_TPROP_TOP_MARGIN (0x01U << 1)
#define FT_TPROP_RIGHT_MARGIN (0x01U << 2)
#define FT_TPROP_BOTTOM_MARGIN (0x01U << 3)
#define FT_TPROP_ADDING_STRATEGY (0x01U << 4)
/** @} */
/**
* Adding strategy.
*
* Determines what happens with old content if current cell is not empty after
* adding data to it. Default strategy is FT_STRATEGY_REPLACE.
*/
enum ft_adding_strategy {
FT_STRATEGY_REPLACE = 0, /**< Replace old content. */
FT_STRATEGY_INSERT /**< Insert new conten. Old content is shifted. */
};
/**

View File

@ -60,6 +60,17 @@ enum class row_type {
header = FT_ROW_HEADER
};
/**
* Adding strategy.
*
* Determines what happens with old content if current cell is not empty after
* adding data to it. Default strategy is 'replace'.
*/
enum class add_strategy {
replace = FT_STRATEGY_REPLACE,
insert = FT_STRATEGY_INSERT
};
/**
* Colors.
*/
@ -893,6 +904,22 @@ public:
{
return FT_IS_SUCCESS(ft_set_tbl_prop(table_, FT_TPROP_BOTTOM_MARGIN, value));
}
/**
* Set table adding strategy.
*
* @param value
* Adding strategy.
* @return
* - true: Success; table property was changed.
* - false: In case of error.
*/
bool set_adding_strategy(fort::add_strategy value)
{
return FT_IS_SUCCESS(ft_set_tbl_prop(table_,
FT_TPROP_ADDING_STRATEGY,
static_cast<int>(value)));
}
private:
ft_table_t *table_;
mutable std::stringstream stream_;

View File

@ -275,8 +275,14 @@ ft_table_t *ft_copy_table(ft_table_t *table);
*
* @param table
* Pointer to formatted table.
* @return
* - 0: Success; data were written
* - (<0): In case of error.
* @note
* This function can fail only in case FT_STRATEGY_INSERT adding strategy
* was set for the table.
*/
void ft_ln(ft_table_t *table);
int ft_ln(ft_table_t *table);
/**
* Get row number of the current cell.
@ -812,12 +818,23 @@ int ft_set_cell_prop(ft_table_t *table, size_t row, size_t col, uint32_t propert
* @name Table properties identifiers.
* @{
*/
#define FT_TPROP_LEFT_MARGIN (0x01U << 0)
#define FT_TPROP_TOP_MARGIN (0x01U << 1)
#define FT_TPROP_RIGHT_MARGIN (0x01U << 2)
#define FT_TPROP_BOTTOM_MARGIN (0x01U << 3)
#define FT_TPROP_LEFT_MARGIN (0x01U << 0)
#define FT_TPROP_TOP_MARGIN (0x01U << 1)
#define FT_TPROP_RIGHT_MARGIN (0x01U << 2)
#define FT_TPROP_BOTTOM_MARGIN (0x01U << 3)
#define FT_TPROP_ADDING_STRATEGY (0x01U << 4)
/** @} */
/**
* Adding strategy.
*
* Determines what happens with old content if current cell is not empty after
* adding data to it. Default strategy is FT_STRATEGY_REPLACE.
*/
enum ft_adding_strategy {
FT_STRATEGY_REPLACE = 0, /**< Replace old content. */
FT_STRATEGY_INSERT /**< Insert new conten. Old content is shifted. */
};
/**

View File

@ -60,6 +60,17 @@ enum class row_type {
header = FT_ROW_HEADER
};
/**
* Adding strategy.
*
* Determines what happens with old content if current cell is not empty after
* adding data to it. Default strategy is 'replace'.
*/
enum class add_strategy {
replace = FT_STRATEGY_REPLACE,
insert = FT_STRATEGY_INSERT
};
/**
* Colors.
*/
@ -893,6 +904,22 @@ public:
{
return FT_IS_SUCCESS(ft_set_tbl_prop(table_, FT_TPROP_BOTTOM_MARGIN, value));
}
/**
* Set table adding strategy.
*
* @param value
* Adding strategy.
* @return
* - true: Success; table property was changed.
* - false: In case of error.
*/
bool set_adding_strategy(fort::add_strategy value)
{
return FT_IS_SUCCESS(ft_set_tbl_prop(table_,
FT_TPROP_ADDING_STRATEGY,
static_cast<int>(value)));
}
private:
ft_table_t *table_;
mutable std::stringstream stream_;

View File

@ -57,7 +57,14 @@ ft_table_t *ft_create_table(void)
F_FREE(result);
return NULL;
}
result->properties = NULL;
result->properties = create_table_properties();
if (result->properties == NULL) {
destroy_vector(result->separators);
destroy_vector(result->rows);
F_FREE(result);
return NULL;
}
result->conv_buffer = NULL;
result->cur_row = 0;
result->cur_col = 0;
@ -123,7 +130,12 @@ ft_table_t *ft_copy_table(ft_table_t *table)
vector_push(result->separators, &new_sep);
}
/* note: by default new table has allocated default properties, so we
* have to destroy them first.
*/
if (result->properties) {
destroy_table_properties(result->properties);
}
result->properties = copy_table_properties(table->properties);
if (result->properties == NULL) {
ft_destroy_table(result);
@ -137,12 +149,57 @@ ft_table_t *ft_copy_table(ft_table_t *table)
return result;
}
static int split_cur_row(ft_table_t *table, f_row_t **tail_of_cur_row)
{
if (table->cur_row >= vector_size(table->rows)) {
tail_of_cur_row = NULL;
return 0;
}
void ft_ln(ft_table_t *table)
f_row_t *row = *(f_row_t **)vector_at(table->rows, table->cur_row);
if (table->cur_col >= columns_in_row(row)) {
tail_of_cur_row = NULL;
return 0;
}
f_row_t *tail = split_row(row, table->cur_col);
if (!tail) {
tail_of_cur_row = NULL;
return FT_ERROR;
}
*tail_of_cur_row = tail;
return 0;
}
int ft_ln(ft_table_t *table)
{
assert(table);
fort_entire_table_properties_t *table_props = &table->properties->entire_table_properties;
switch (table_props->add_strategy) {
case FT_STRATEGY_INSERT: {
f_row_t *new_row = NULL;
if (FT_IS_ERROR(split_cur_row(table, &new_row))) {
return FT_ERROR;
}
if (new_row) {
if (FT_IS_ERROR(vector_insert(table->rows, &new_row, table->cur_row + 1))) {
destroy_row(new_row);
return FT_ERROR;
}
}
break;
}
case FT_STRATEGY_REPLACE:
// do nothing
break;
default:
assert(0 && "Unexpected situation inside libfort");
break;
}
table->cur_col = 0;
table->cur_row++;
return FT_SUCCESS;
}
size_t ft_cur_row(const ft_table_t *table)
@ -206,7 +263,23 @@ static int ft_row_printf_impl_(ft_table_t *table, size_t row, const struct f_str
new_cols = columns_in_row(new_row);
cur_row_p = (f_row_t **)vector_at(table->rows, row);
swap_row(*cur_row_p, new_row, table->cur_col);
fort_entire_table_properties_t *table_props = &table->properties->entire_table_properties;
switch (table_props->add_strategy) {
case FT_STRATEGY_INSERT: {
if (FT_IS_ERROR(insert_row(*cur_row_p, new_row, table->cur_col)))
goto clear;
break;
}
case FT_STRATEGY_REPLACE: {
if (FT_IS_ERROR(swap_row(*cur_row_p, new_row, table->cur_col)))
goto clear;
break;
}
default:
assert(0 && "Unexpected situation inside libfort");
break;
}
table->cur_col += new_cols;
destroy_row(new_row);

View File

@ -874,6 +874,7 @@ fort_entire_table_properties_t g_entire_table_properties = {
0, /* top_margin */
0, /* right_margin */
0, /* bottom_margin */
FT_STRATEGY_REPLACE, /* add_strategy */
};
static f_status set_entire_table_property_internal(fort_entire_table_properties_t *properties, uint32_t property, int value)
@ -888,6 +889,8 @@ static f_status set_entire_table_property_internal(fort_entire_table_properties_
properties->right_margin = value;
} else if (PROP_IS_SET(property, FT_TPROP_BOTTOM_MARGIN)) {
properties->bottom_margin = value;
} else if (PROP_IS_SET(property, FT_TPROP_ADDING_STRATEGY)) {
properties->add_strategy = (enum ft_adding_strategy)value;
} else {
return FT_EINVAL;
}
@ -943,7 +946,8 @@ f_table_properties_t g_table_properties = {
0, /* left_margin */
0, /* top_margin */
0, /* right_margin */
0 /* bottom_margin */
0, /* bottom_margin */
FT_STRATEGY_REPLACE, /* add_strategy */
}
};

View File

@ -177,6 +177,7 @@ struct fort_entire_table_properties {
unsigned int top_margin;
unsigned int right_margin;
unsigned int bottom_margin;
enum ft_adding_strategy add_strategy;
};
typedef struct fort_entire_table_properties fort_entire_table_properties_t;
extern fort_entire_table_properties_t g_entire_table_properties;

106
src/row.c
View File

@ -10,21 +10,41 @@ struct f_row {
f_vector_t *cells;
};
FT_INTERNAL
f_row_t *create_row(void)
static
f_row_t *create_row_impl(f_vector_t *cells)
{
f_row_t *row = (f_row_t *)F_CALLOC(1, sizeof(f_row_t));
if (row == NULL)
return NULL;
row->cells = create_vector(sizeof(f_cell_t *), DEFAULT_VECTOR_CAPACITY);
if (row->cells == NULL) {
F_FREE(row);
return NULL;
if (cells) {
row->cells = cells;
} else {
row->cells = create_vector(sizeof(f_cell_t *), DEFAULT_VECTOR_CAPACITY);
if (row->cells == NULL) {
F_FREE(row);
return NULL;
}
}
return row;
}
FT_INTERNAL
f_row_t *create_row(void)
{
return create_row_impl(NULL);
}
static
void destroy_each_cell(f_vector_t *cells)
{
size_t i = 0;
size_t cells_n = vector_size(cells);
for (i = 0; i < cells_n; ++i) {
f_cell_t *cell = *(f_cell_t **)vector_at(cells, i);
destroy_cell(cell);
}
}
FT_INTERNAL
void destroy_row(f_row_t *row)
{
@ -32,12 +52,7 @@ void destroy_row(f_row_t *row)
return;
if (row->cells) {
size_t i = 0;
size_t cells_n = vector_size(row->cells);
for (i = 0; i < cells_n; ++i) {
f_cell_t *cell = *(f_cell_t **)vector_at(row->cells, i);
destroy_cell(cell);
}
destroy_each_cell(row->cells);
destroy_vector(row->cells);
}
@ -67,6 +82,23 @@ f_row_t *copy_row(f_row_t *row)
return result;
}
FT_INTERNAL
f_row_t *split_row(f_row_t *row, size_t pos)
{
assert(row);
f_vector_t *cells = vector_split(row->cells, pos);
if (!cells)
return NULL;
f_row_t *tail = create_row_impl(cells);
if (!tail) {
destroy_each_cell(cells);
destroy_vector(cells);
}
return tail;
}
FT_INTERNAL
size_t columns_in_row(const f_row_t *row)
{
@ -128,6 +160,23 @@ f_cell_t *get_cell_and_create_if_not_exists(f_row_t *row, size_t col)
return get_cell_impl(row, col, CREATE_ON_NULL);
}
FT_INTERNAL
f_cell_t *create_cell_in_position(f_row_t *row, size_t col)
{
if (row == NULL || row->cells == NULL) {
return NULL;
}
f_cell_t *new_cell = create_cell();
if (new_cell == NULL)
return NULL;
if (FT_IS_ERROR(vector_insert(row->cells, &new_cell, col))) {
destroy_cell(new_cell);
return NULL;
}
return *(f_cell_t **)vector_at(row->cells, col);
}
FT_INTERNAL
f_status swap_row(f_row_t *cur_row, f_row_t *ins_row, size_t pos)
@ -146,6 +195,37 @@ f_status swap_row(f_row_t *cur_row, f_row_t *ins_row, size_t pos)
return vector_swap(cur_row->cells, ins_row->cells, pos);
}
/* Ownership of cells of `ins_row` is passed to `cur_row`. */
FT_INTERNAL
f_status insert_row(f_row_t *cur_row, f_row_t *ins_row, size_t pos)
{
assert(cur_row);
assert(ins_row);
while (vector_size(cur_row->cells) < pos) {
f_cell_t *new_cell = create_cell();
if (!new_cell)
return FT_ERROR;
vector_push(cur_row->cells, &new_cell);
}
size_t sz = vector_size(ins_row->cells);
size_t i = 0;
for (i = 0; i < sz; ++i) {
f_cell_t *cell = *(f_cell_t **)vector_at(ins_row->cells, i);
if (FT_IS_ERROR(vector_insert(cur_row->cells, &cell, pos + i))) {
/* clean up what we have inserted */
while (i--) {
vector_erase(cur_row->cells, pos);
}
return FT_ERROR;
}
}
/* Clear cells so that it will be safe to destroy this row */
vector_clear(ins_row->cells);
return FT_SUCCESS;
}
FT_INTERNAL
size_t group_cell_number(const f_row_t *row, size_t master_cell_col)

View File

@ -18,6 +18,9 @@ void destroy_row(f_row_t *row);
FT_INTERNAL
f_row_t *copy_row(f_row_t *row);
FT_INTERNAL
f_row_t *split_row(f_row_t *row, size_t pos);
FT_INTERNAL
f_row_t *create_row_from_string(const char *str);
@ -36,9 +39,15 @@ const f_cell_t *get_cell_c(const f_row_t *row, size_t col);
FT_INTERNAL
f_cell_t *get_cell_and_create_if_not_exists(f_row_t *row, size_t col);
FT_INTERNAL
f_cell_t *create_cell_in_position(f_row_t *row, size_t col);
FT_INTERNAL
f_status swap_row(f_row_t *cur_row, f_row_t *ins_row, size_t pos);
FT_INTERNAL
f_status insert_row(f_row_t *cur_row, f_row_t *ins_row, size_t pos);
FT_INTERNAL
size_t group_cell_number(const f_row_t *row, size_t master_cell_col);

View File

@ -81,7 +81,6 @@ f_row_t *get_row_and_create_if_not_exists(ft_table_t *table, size_t row)
return get_row_impl(table, row, CREATE_ON_NULL);
}
FT_INTERNAL
f_string_buffer_t *get_cur_str_buffer_and_create_if_not_exists(ft_table_t *table)
{
@ -90,7 +89,21 @@ f_string_buffer_t *get_cur_str_buffer_and_create_if_not_exists(ft_table_t *table
f_row_t *row = get_row_and_create_if_not_exists(table, table->cur_row);
if (row == NULL)
return NULL;
f_cell_t *cell = get_cell_and_create_if_not_exists(row, table->cur_col);
f_cell_t *cell = NULL;
fort_entire_table_properties_t *table_props = &table->properties->entire_table_properties;
switch (table_props->add_strategy) {
case FT_STRATEGY_INSERT:
cell = create_cell_in_position(row, table->cur_col);
break;
case FT_STRATEGY_REPLACE:
cell = get_cell_and_create_if_not_exists(row, table->cur_col);
break;
default:
assert(0 && "Unexpected situation inside libfort");
break;
}
if (cell == NULL)
return NULL;

View File

@ -91,6 +91,56 @@ int vector_push(f_vector_t *vector, const void *item)
return FT_SUCCESS;
}
FT_INTERNAL
int vector_insert(f_vector_t *vector, const void *item, size_t pos)
{
assert(vector);
assert(item);
size_t needed_capacity = MAX(pos + 1, vector->m_size + 1);
if (vector->m_capacity < needed_capacity) {
if (vector_reallocate_(vector, needed_capacity) == -1)
return FT_ERROR;
vector->m_capacity = needed_capacity;
}
size_t offset = pos * vector->m_item_size;
if (pos >= vector->m_size) {
/* Data in the middle are not initialized */
memcpy((char *)vector->m_data + offset, item, vector->m_item_size);
vector->m_size = pos + 1;
return FT_SUCCESS;
} else {
/* Shift following data by one position */
memmove((char *)vector->m_data + offset + vector->m_item_size,
(char *)vector->m_data + offset,
vector->m_item_size * (vector->m_size - pos));
memcpy((char *)vector->m_data + offset, item, vector->m_item_size);
++(vector->m_size);
return FT_SUCCESS;
}
}
FT_INTERNAL
f_vector_t *vector_split(f_vector_t *vector, size_t pos)
{
size_t trailing_sz = vector->m_size > pos ? vector->m_size - pos : 0;
f_vector_t *new_vector = create_vector(vector->m_item_size, trailing_sz);
if (!new_vector)
return new_vector;
if (new_vector->m_capacity < trailing_sz) {
destroy_vector(new_vector);
return NULL;
}
if (trailing_sz == 0)
return new_vector;
size_t offset = vector->m_item_size * pos;
memcpy(new_vector->m_data, (char *)vector->m_data + offset,
trailing_sz * vector->m_item_size);
new_vector->m_size = trailing_sz;
vector->m_size = pos;
return new_vector;
}
FT_INTERNAL
const void *vector_at_c(const f_vector_t *vector, size_t index)
@ -166,6 +216,26 @@ f_status vector_swap(f_vector_t *cur_vec, f_vector_t *mv_vec, size_t pos)
return FT_SUCCESS;
}
FT_INTERNAL
void vector_clear(f_vector_t *vector)
{
vector->m_size = 0;
}
FT_INTERNAL
int vector_erase(f_vector_t *vector, size_t index)
{
assert(vector);
if (vector->m_size == 0 || index >= vector->m_size)
return FT_ERROR;
memmove((char *)vector->m_data + vector->m_item_size * index,
(char *)vector->m_data + vector->m_item_size * (index + 1),
(vector->m_size - 1 - index) * vector->m_item_size);
vector->m_size--;
return FT_SUCCESS;
}
#ifdef FT_TEST_BUILD
@ -199,24 +269,4 @@ size_t vector_index_of(const f_vector_t *vector, const void *item)
return INVALID_VEC_INDEX;
}
int vector_erase(f_vector_t *vector, size_t index)
{
assert(vector);
if (vector->m_size == 0 || index >= vector->m_size)
return FT_ERROR;
memmove((char *)vector->m_data + vector->m_item_size * index,
(char *)vector->m_data + vector->m_item_size * (index + 1),
(vector->m_size - 1 - index) * vector->m_item_size);
vector->m_size--;
return FT_SUCCESS;
}
void vector_clear(f_vector_t *vector)
{
vector->m_size = 0;
}
#endif

View File

@ -21,6 +21,12 @@ size_t vector_capacity(const f_vector_t *);
FT_INTERNAL
int vector_push(f_vector_t *, const void *item);
FT_INTERNAL
int vector_insert(f_vector_t *, const void *item, size_t pos);
FT_INTERNAL
f_vector_t *vector_split(f_vector_t *, size_t pos);
FT_INTERNAL
const void *vector_at_c(const f_vector_t *vector, size_t index);
@ -30,12 +36,15 @@ void *vector_at(f_vector_t *, size_t index);
FT_INTERNAL
f_status vector_swap(f_vector_t *cur_vec, f_vector_t *mv_vec, size_t pos);
FT_INTERNAL
void vector_clear(f_vector_t *);
FT_INTERNAL
int vector_erase(f_vector_t *, size_t index);
#ifdef FT_TEST_BUILD
f_vector_t *copy_vector(f_vector_t *);
size_t vector_index_of(const f_vector_t *, const void *item);
int vector_erase(f_vector_t *, size_t index);
void vector_clear(f_vector_t *);
#endif
#endif /* VECTOR_H */

View File

@ -1608,8 +1608,95 @@ void test_table_write(void)
}
#endif
SCENARIO("Test write and printf functions simultaneously") {
table = ft_create_table();
assert_true(table != NULL);
assert_true(FT_IS_SUCCESS(ft_write(table, "0", "1")));
assert_true(FT_IS_SUCCESS(ft_printf(table, "2|3")));
assert_true(FT_IS_SUCCESS(ft_write(table, "5", "6")));
const char *table_str = ft_to_string(table);
assert_true(table_str != NULL);
const char *table_str_etalon =
"+---+---+---+---+---+---+\n"
"| 0 | 1 | 2 | 3 | 5 | 6 |\n"
"+---+---+---+---+---+---+\n";
assert_str_equal(table_str, table_str_etalon);
ft_destroy_table(table);
}
}
void test_table_insert_strategy(void)
{
SCENARIO("Test ft_ln") {
ft_table_t *table = ft_create_table();
assert_true(table != NULL);
ft_set_tbl_prop(table, FT_TPROP_ADDING_STRATEGY, FT_STRATEGY_INSERT);
assert_true(FT_IS_SUCCESS(ft_write_ln(table, "0", "1", "2")));
ft_set_cur_cell(table, 0, 2);
assert_true(FT_IS_SUCCESS(ft_ln(table)));
ft_set_cur_cell(table, 1, 1);
assert_true(FT_IS_SUCCESS(ft_write(table, "3")));
const char *table_str = ft_to_string(table);
assert_true(table_str != NULL);
const char *table_str_etalon =
"+---+---+\n"
"| 0 | 1 |\n"
"| 2 | 3 |\n"
"+---+---+\n";
assert_str_equal(table_str, table_str_etalon);
ft_destroy_table(table);
}
SCENARIO("Test write functions") {
ft_table_t *table = ft_create_table();
assert_true(table != NULL);
ft_set_tbl_prop(table, FT_TPROP_ADDING_STRATEGY, FT_STRATEGY_INSERT);
assert_true(FT_IS_SUCCESS(ft_write_ln(table, "0", "1", "2", "4", "5")));
ft_set_cur_cell(table, 0, 2);
assert_true(FT_IS_SUCCESS(ft_ln(table)));
ft_set_cur_cell(table, 1, 1);
assert_true(FT_IS_SUCCESS(ft_write_ln(table, "3")));
const char *table_str = ft_to_string(table);
assert_true(table_str != NULL);
const char *table_str_etalon =
"+---+---+\n"
"| 0 | 1 |\n"
"| 2 | 3 |\n"
"| 4 | 5 |\n"
"+---+---+\n";
assert_str_equal(table_str, table_str_etalon);
ft_destroy_table(table);
}
SCENARIO("Test printf functions") {
ft_table_t *table = ft_create_table();
assert_true(table != NULL);
ft_set_tbl_prop(table, FT_TPROP_ADDING_STRATEGY, FT_STRATEGY_INSERT);
assert_true(FT_IS_SUCCESS(ft_write_ln(table, "0", "1", "2", "5", "6")));
ft_set_cur_cell(table, 0, 2);
assert_true(FT_IS_SUCCESS(ft_ln(table)));
ft_set_cur_cell(table, 1, 1);
assert_true(FT_IS_SUCCESS(ft_printf_ln(table, "3|4")));
const char *table_str = ft_to_string(table);
assert_true(table_str != NULL);
const char *table_str_etalon =
"+---+---+---+\n"
"| 0 | 1 | |\n"
"| 2 | 3 | 4 |\n"
"| 5 | 6 | |\n"
"+---+---+---+\n";
assert_str_equal(table_str, table_str_etalon);
ft_destroy_table(table);
}
}
void test_table_copy(void)
{

View File

@ -300,6 +300,52 @@ void test_cpp_table_write(void)
}
}
void test_cpp_table_insert(void)
{
SCENARIO("Test insert into beginning") {
fort::char_table table;
table.set_adding_strategy(fort::add_strategy::insert);
table.set_border_style(FT_BOLD_STYLE);
table << "val1" << "val2" << fort::endr
<< "val3" << "val4" << fort::endr;
table.set_cur_cell(0, 0);
table << fort::header
<< "hdr1" << "hdr2" << fort::endr;
std::string table_str = table.to_string();
std::string table_str_etalon =
"┏━━━━━━┳━━━━━━┓\n"
"┃ hdr1 ┃ hdr2 ┃\n"
"┣━━━━━━╋━━━━━━┫\n"
"┃ val1 ┃ val2 ┃\n"
"┃ val3 ┃ val4 ┃\n"
"┗━━━━━━┻━━━━━━┛\n";
assert_string_equal(table_str, table_str_etalon);
}
SCENARIO("Test insert into the middle") {
fort::char_table table;
table.set_adding_strategy(fort::add_strategy::insert);
table.set_border_style(FT_BOLD_STYLE);
table << fort::header << "hdr1" << "hdr2" << fort::endr
<< "val1" << "val4" << fort::endr;
table.set_cur_cell(1, 1);
table << "val2" << fort::endr << "val3";
std::string table_str = table.to_string();
std::string table_str_etalon =
"┏━━━━━━┳━━━━━━┓\n"
"┃ hdr1 ┃ hdr2 ┃\n"
"┣━━━━━━╋━━━━━━┫\n"
"┃ val1 ┃ val2 ┃\n"
"┃ val3 ┃ val4 ┃\n"
"┗━━━━━━┻━━━━━━┛\n";
assert_string_equal(table_str, table_str_etalon);
}
}
void test_cpp_table_changing_cell(void)
{
WHEN("All columns are equal and not empty") {

View File

@ -16,6 +16,7 @@ void test_table_changing_cell(void);
void test_wcs_table_boundaries(void);
#endif
void test_table_write(void);
void test_table_insert_strategy(void);
void test_table_border_style(void);
void test_table_builtin_border_styles(void);
void test_table_cell_properties(void);
@ -49,6 +50,7 @@ struct test_case bb_test_suite [] = {
{"test_utf8_table", test_utf8_table},
#endif
{"test_table_write", test_table_write},
{"test_table_insert_strategy", test_table_insert_strategy},
{"test_table_changing_cell", test_table_changing_cell},
{"test_table_border_style", test_table_border_style},
{"test_table_builtin_border_styles", test_table_builtin_border_styles},

View File

@ -5,6 +5,7 @@
/* Test cases */
void test_cpp_table_basic(void);
void test_cpp_table_write(void);
void test_cpp_table_insert(void);
void test_cpp_table_changing_cell(void);
void test_cpp_table_tbl_properties(void);
void test_cpp_table_cell_properties(void);
@ -15,6 +16,7 @@ void test_cpp_bug_fixes(void);
struct test_case bb_test_suite [] = {
{"test_cpp_table_basic", test_cpp_table_basic},
{"test_cpp_table_write", test_cpp_table_write},
{"test_cpp_table_insert", test_cpp_table_insert},
{"test_cpp_table_changing_cell", test_cpp_table_changing_cell},
{"test_cpp_table_tbl_properties", test_cpp_table_tbl_properties},
{"test_cpp_table_cell_properties", test_cpp_table_cell_properties},

View File

@ -87,6 +87,30 @@ void test_vector_basic(void)
}
}
WHEN("Testing insert method vector") {
vector_clear(vector);
size_t capacity = 10 * init_capacity;
for (i = 0; i < capacity; ++i) {
item_t item = (item_t)i;
vector_insert(vector, &item, 0);
}
assert_true(vector_size(vector) == capacity);
for (i = 0; i < capacity; ++i) {
assert_true(*(item_t *)vector_at(vector, i) == (item_t)(capacity - i) - 1);
}
item_t item_666 = 666;
vector_insert(vector, &item_666, 5 * capacity - 1);
assert_true(vector_size(vector) == 5 * capacity);
assert_true(*(item_t *)vector_at(vector, 5 * capacity - 1) == item_666);
item_t item_777 = 777;
vector_insert(vector, &item_777, 10);
assert_true(vector_size(vector) == 5 * capacity + 1);
assert_true(*(item_t *)vector_at(vector, 10) == item_777);
}
WHEN("Moving from another vector") {
vector_clear(vector);
for (i = 0; i < 10; ++i) {