-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
sheet: update to use heap memory, fix zsv_new mem leak (#214)
* sheet: update to use heap memory, fix zsv_new mem leak * Use ubuntu-22.04 runner for clang-format * use conditional includes for curses.h * break out app/test/Makefile test-sheet into separate entries --------- Co-authored-by: Azeem Sajid <[email protected]>
- Loading branch information
Showing
12 changed files
with
412 additions
and
183 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
#include "buffer.h" | ||
|
||
struct zsv_sheet_buffer { | ||
size_t cols; | ||
size_t long_cell_count; | ||
struct zsv_sheet_buffer_opts opts; | ||
unsigned char *data; | ||
// to do: add hooks for extension | ||
}; | ||
|
||
static inline size_t buffer_data_offset(zsv_sheet_buffer_t buff, size_t row, size_t col) { | ||
assert(row < buff->opts.rows && col < buff->cols); | ||
return row * buff->cols * buff->opts.cell_buff_len + col * buff->opts.cell_buff_len; | ||
} | ||
|
||
static void set_long_cell(zsv_sheet_buffer_t buff, size_t offset, char *heap) { | ||
char **target = (char **)(buff->data + offset); | ||
*target = heap; | ||
// memcpy(buff->data + offset, heap, sizeof(heap)); | ||
// set flag indicating that this is long cell | ||
*(buff->data + offset + buff->opts.cell_buff_len - 1) = (char)1; | ||
} | ||
|
||
static char *get_long_cell(zsv_sheet_buffer_t buff, size_t offset) { | ||
char **valuep = (char **)(buff->data + offset); | ||
if (valuep) | ||
return *valuep; | ||
return NULL; | ||
} | ||
|
||
static inline int is_long_cell(const unsigned char *mem, size_t cell_buff_len) { | ||
return *(mem + cell_buff_len - 1) != '\0'; | ||
} | ||
|
||
size_t zsv_sheet_buffer_cols(zsv_sheet_buffer_t buff) { | ||
return buff->cols; | ||
} | ||
|
||
size_t zsv_sheet_buffer_rows(zsv_sheet_buffer_t buff) { | ||
return buff->opts.rows; | ||
} | ||
|
||
static void free_long_cell(zsv_sheet_buffer_t buff, size_t offset) { | ||
if (is_long_cell(buff->data + offset, buff->opts.cell_buff_len)) { | ||
char *value_copy = get_long_cell(buff, offset); | ||
free(value_copy); | ||
memset(buff->data + offset, 0, buff->opts.cell_buff_len); | ||
buff->long_cell_count--; | ||
} | ||
} | ||
|
||
void zsv_sheet_buffer_delete(zsv_sheet_buffer_t buff) { | ||
if (buff) { | ||
for (size_t i = 0; i < buff->opts.rows && buff->long_cell_count > 0; i++) { | ||
for (size_t j = 0; j < buff->cols && buff->long_cell_count > 0; j++) { | ||
size_t offset = buffer_data_offset(buff, i, j); | ||
free_long_cell(buff, offset); | ||
} | ||
} | ||
free(buff->data); | ||
free(buff); | ||
} | ||
} | ||
|
||
zsv_sheet_buffer_t zsv_sheet_buffer_new(size_t cols, struct zsv_sheet_buffer_opts *opts, | ||
enum zsv_sheet_buffer_status *stat) { | ||
if (opts->rows == 0) | ||
opts->rows = ZSV_SHEET_BUFFER_DEFAULT_ROW_COUNT; | ||
else if (opts->rows < 256) | ||
opts->rows = 256; | ||
if (opts->cell_buff_len == 0) | ||
opts->cell_buff_len = ZSV_SHEET_BUFFER_DEFAULT_CELL_BUFF_LEN; | ||
if (opts->max_cell_len == 0) | ||
opts->max_cell_len = ZSV_SHEET_BUFFER_DEFAULT_MAX_CELL_LEN; | ||
if (opts->cell_buff_len < sizeof(void *) * 2) | ||
*stat = zsv_sheet_buffer_status_error; | ||
else { | ||
if (!opts->no_rownum_column) | ||
cols++; | ||
void *data = calloc(opts->rows, cols * opts->cell_buff_len); | ||
if (!data) | ||
*stat = zsv_sheet_buffer_status_memory; | ||
else { | ||
struct zsv_sheet_buffer *buff = calloc(1, sizeof(*buff)); | ||
if (!buff) | ||
*stat = zsv_sheet_buffer_status_memory; | ||
else { | ||
buff->opts.rows = opts->rows; | ||
buff->cols = cols; | ||
buff->data = data; | ||
buff->opts = *opts; | ||
return buff; | ||
} | ||
} | ||
free(data); | ||
} | ||
return NULL; | ||
} | ||
|
||
#ifndef UTF8_NOT_FIRST_CHAR | ||
#define UTF8_NOT_FIRST_CHAR(x) ((x & 0xC0) == 0x80) | ||
#endif | ||
|
||
enum zsv_sheet_buffer_status zsv_sheet_buffer_write_cell_w_len(zsv_sheet_buffer_t buff, size_t row, size_t col, | ||
const unsigned char *value, size_t len) { | ||
enum zsv_sheet_buffer_status stat = zsv_sheet_buffer_status_ok; | ||
size_t offset = buffer_data_offset(buff, row, col); | ||
free_long_cell(buff, offset); | ||
if (len < buff->opts.cell_buff_len) { | ||
if (len) | ||
// copy into fixed-size buff | ||
memcpy(buff->data + offset, value, len); | ||
*(buff->data + offset + len) = '\0'; | ||
} else { | ||
// we have a long cell | ||
if (len > buff->opts.max_cell_len) { | ||
len = buff->opts.max_cell_len; | ||
while (len > 0 && value[len] >= 128 && UTF8_NOT_FIRST_CHAR(value[len])) | ||
// we are in the middle of a multibyte char, so back up | ||
len--; | ||
} | ||
if (!len) // the only reason len could be 0 is if our input was not valid utf8, but check to make sure anyway | ||
stat = zsv_sheet_buffer_status_utf8; | ||
else { | ||
char *value_copy = malloc(1 + len); | ||
if (value_copy) { | ||
memcpy(value_copy, value, len); | ||
value_copy[len] = '\0'; | ||
set_long_cell(buff, offset, value_copy); | ||
buff->long_cell_count++; | ||
} else | ||
stat = zsv_sheet_buffer_status_memory; | ||
} | ||
} | ||
return stat; | ||
} | ||
|
||
enum zsv_sheet_buffer_status zsv_sheet_buffer_write_cell(zsv_sheet_buffer_t buff, size_t row, size_t col, | ||
const unsigned char *value) { | ||
return zsv_sheet_buffer_write_cell_w_len(buff, row, col, value, strlen((void *)value)); | ||
} | ||
|
||
const unsigned char *zsv_sheet_buffer_cell_display(zsv_sheet_buffer_t buff, size_t row, size_t col) { | ||
if (row < buff->opts.rows && col < buff->cols) { | ||
size_t offset = row * buff->cols * buff->opts.cell_buff_len + col * buff->opts.cell_buff_len; | ||
const unsigned char *cell = &buff->data[offset]; | ||
if (!is_long_cell(cell, buff->opts.cell_buff_len)) | ||
return cell; | ||
|
||
// if the fixed-length cell memory does not end with NULL, | ||
// then the cell value is a pointer to memory holding the value | ||
return (unsigned char *)get_long_cell(buff, offset); | ||
} | ||
return NULL; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
#ifndef ZSV_SHEET_BUFFER_H | ||
#define ZSV_SHEET_BUFFER_H | ||
|
||
#define ZSV_SHEET_BUFFER_DEFAULT_CELL_BUFF_LEN 16 | ||
#define ZSV_SHEET_BUFFER_DEFAULT_MAX_CELL_LEN 32768 - 1 | ||
#define ZSV_SHEET_BUFFER_DEFAULT_ROW_COUNT 1000 | ||
|
||
typedef struct zsv_sheet_buffer *zsv_sheet_buffer_t; | ||
|
||
enum zsv_sheet_buffer_status { | ||
zsv_sheet_buffer_status_ok = 0, | ||
zsv_sheet_buffer_status_memory, | ||
zsv_sheet_buffer_status_error, // generic error | ||
zsv_sheet_buffer_status_utf8 | ||
}; | ||
|
||
struct zsv_sheet_buffer_opts { | ||
size_t cell_buff_len; // default = 16. must be >= 2 * sizeof(void *) | ||
size_t max_cell_len; // length in bytes; defaults to 32767 | ||
size_t rows; // rows to buffer. cannot be < 256 | ||
char no_rownum_column; // reserved. TO DO: if set, omit row num column | ||
}; | ||
|
||
zsv_sheet_buffer_t zsv_sheet_buffer_new(size_t cols, struct zsv_sheet_buffer_opts *opts, | ||
enum zsv_sheet_buffer_status *stat); | ||
|
||
enum zsv_sheet_buffer_status zsv_sheet_buffer_write_cell(zsv_sheet_buffer_t buff, size_t row, size_t col, | ||
const unsigned char *value); | ||
|
||
enum zsv_sheet_buffer_status zsv_sheet_buffer_write_cell_w_len(zsv_sheet_buffer_t buff, size_t row, size_t col, | ||
const unsigned char *value, size_t len); | ||
|
||
const unsigned char *zsv_sheet_buffer_cell_display(zsv_sheet_buffer_t buff, size_t row, size_t col); | ||
|
||
void zsv_sheet_buffer_delete(zsv_sheet_buffer_t); | ||
|
||
size_t zsv_sheet_buffer_cols(zsv_sheet_buffer_t); | ||
size_t zsv_sheet_buffer_rows(zsv_sheet_buffer_t buff); | ||
|
||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.