From 5257358958cca6a8c44aed0a03aa962bcf4563a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Sch=C3=B6bel?= <jonathan@xn--schbel-yxa.info> Date: Sun, 2 Jul 2023 12:31:07 +0200 Subject: [PATCH] Text: added stupid replacement function The copy_and_replace function replaces a single character with a string, while copying. This may be replaced by an elaborate function as manipulating a text normally means that manipulating is deferred until needed, which this function contradicts to. --- sefht.geany | 10 +-- src/lib/sefht/text.c | 31 +++++++ src/lib/sefht/text.h | 11 +++ src/lib/sefht/text_segment.c | 155 +++++++++++++++++++++++++++++++++++ tests/test_text.c | 28 +++++++ 5 files changed, 230 insertions(+), 5 deletions(-) diff --git a/sefht.geany b/sefht.geany index f42fa15..59edf8e 100644 --- a/sefht.geany +++ b/sefht.geany @@ -28,7 +28,7 @@ long_line_behaviour=1 long_line_column=72 [files] -current_page=25 +current_page=45 FILE_NAME_0=139;None;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2FREADME;0;8 FILE_NAME_1=134;None;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2F.gitignore;0;8 FILE_NAME_2=1737;Sh;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fconfigure.ac;0;8 @@ -51,10 +51,10 @@ FILE_NAME_18=19;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprg FILE_NAME_19=19;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Fattr.h;0;8 FILE_NAME_20=26;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Fattr_static.c;0;8 FILE_NAME_21=24;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Fattr_data.h;0;8 -FILE_NAME_22=2765;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Ftext.c;0;8 -FILE_NAME_23=19;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Ftext.h;0;8 +FILE_NAME_22=2929;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Ftext.c;0;8 +FILE_NAME_23=2065;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Ftext.h;0;8 FILE_NAME_24=1036;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Ftext_data.h;0;8 -FILE_NAME_25=4650;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Ftext_segment.c;0;8 +FILE_NAME_25=8479;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Ftext_segment.c;0;8 FILE_NAME_26=1083;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Ftext_segment.h;0;8 FILE_NAME_27=900;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Ftext_segment_mark.c;0;8 FILE_NAME_28=1867;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Ftext_mark_static.c;0;8 @@ -74,7 +74,7 @@ FILE_NAME_41=8232;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fp FILE_NAME_42=33;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2Ftest_node_fragment.c;0;8 FILE_NAME_43=4771;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2Ftest_text_fragment.c;0;8 FILE_NAME_44=24;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2Ftest_attr.c;0;8 -FILE_NAME_45=3584;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2Ftest_text.c;0;8 +FILE_NAME_45=4221;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2Ftest_text.c;0;8 FILE_NAME_46=994;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2Ftest_text_mark.c;0;8 FILE_NAME_47=29;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2Ftest_validator.c;0;8 FILE_NAME_48=536;None;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftodo.txt;0;8 diff --git a/src/lib/sefht/text.c b/src/lib/sefht/text.c index 5b97cb0..988efb4 100644 --- a/src/lib/sefht/text.c +++ b/src/lib/sefht/text.c @@ -144,6 +144,37 @@ SH_Text_copy (const struct SH_Text * text, return copy; } +/*@null@*/ +/*@only@*/ +struct SH_Text * +SH_Text_copy_and_replace (const struct SH_Text * text, + char old, char * new, + /*@null@*/ /*@out@*/ struct SH_Status * status) + /*@globals fileSystem@*/ + /*@modifies fileSystem@*/ + /*@modifies status@*/ +{ + struct SH_Text * copy; + + copy = malloc (sizeof (struct SH_Text)); + if (NULL == copy) + { + set_status (status, E_ALLOC, 3, "malloc failed"); + return NULL; + } + + copy->data = copy_and_replace_text_segment (text->data, + old, new, status); + if (NULL == copy->data) + { + free (copy); + return NULL; + } + + set_success (status); + return copy; +} + size_t SH_Text_get_length (const struct SH_Text * text, /*@null@*/ /*@out@*/ struct SH_Status * status) diff --git a/src/lib/sefht/text.h b/src/lib/sefht/text.h index 59e3173..278fccf 100644 --- a/src/lib/sefht/text.h +++ b/src/lib/sefht/text.h @@ -68,6 +68,17 @@ SH_Text_copy (const SH_Text * text, /*@modifies fileSystem@*/ /*@modifies status@*/; + +/*@null@*/ +/*@only@*/ +SH_Text * +SH_Text_copy_and_replace (const SH_Text * text, + char old, char * new, + /*@null@*/ /*@out@*/ struct SH_Status * status) + /*@globals fileSystem@*/ + /*@modifies fileSystem@*/ + /*@modifies status@*/; + size_t SH_Text_get_length (const struct SH_Text * text, /*@null@*/ /*@out@*/ struct SH_Status * status) diff --git a/src/lib/sefht/text_segment.c b/src/lib/sefht/text_segment.c index 0d911cd..4cda050 100644 --- a/src/lib/sefht/text_segment.c +++ b/src/lib/sefht/text_segment.c @@ -367,3 +367,158 @@ squash_text_segment (const struct text_segment * segment, copy_seg->next = NULL; } } + +static inline +/*@null@*/ +/*@only@*/ +struct text_segment * +copy_and_replace_text_segment (const struct text_segment * segment, + char old, char * new, + /*@null@*/ /*@out@*/ + struct SH_Status * status) + /*@globals fileSystem@*/ + /*@modifies fileSystem@*/ + /*@modifies status@*/ +{ + const struct text_segment * start; + const struct text_segment * end; + struct text_segment * copy; + struct text_segment * copy_seg; + + + copy = malloc (sizeof (struct text_segment)); + if (NULL == copy) + { + set_status (status, E_ALLOC, 3, "malloc failed"); + return NULL; + } + + copy->next = NULL; + + /* copy_seg is the segment we're currently copying to */ + copy_seg = copy; + + /* start to end are the segments we're copying from. */ + /* start points to the first segment to copy. + * end points to the first segment not being copied in + * the current iteration. */ + end = segment; + start = end; + + /* At the beginning of each iteration start == end. + * This is ensured before the loop and at the end of the outer + * loop with the last for loop, which does the actual copying. + * + * Then end is adjusted to point after the last segment to be + * copied. If it isn't NULL afterwards another loop iteration + * has to take place. + * + * With start and end properly set, and the length and size of + * the resulting segment determined, the copying can start, + * which is just a repeated string concatenation. + * + * If another iteration is following, the next segment has to be + * allocated. This can't be done at the beginning, because + * before the first iteration the pointer to the allocated + * segment must be written to copy directly to create the + * anchor of the linked list. (So there is no ?->next + * to write to.) + * Actually copy_seg could be typed (struct text_segment **) + * and then be set to &(copy) before the loop and to + * &((*copy_seg)->next) after each iteration, but that would + * complicate things with another indirection... + * Implementing the loop as for loop and writing the allocation + * code into their head is also not possible, because compound + * statements aren't allowed there. + * Thus the allocation has to take place at the end of the loop. */ + while (TRUE) + { + char * text; + size_t r_length; + + /* calculate length, adjust end */ + for (copy_seg->length = 0; end != NULL; end = end->next) + { + /* catch overflow, use <= instead of < + * to prevent overflow of copy_seg->size */ + if (SIZE_MAX - end->length <= copy_seg->length) + { + /* stop copying */ + end = end->next; + + /*@innerbreak@*/ + break; + } + + copy_seg->length += end->length; + } + + /* initialize marks + * TODO: copy and adjust the marks regarding to + * the concatenated segment */ + init_marks (copy_seg); + + /* This addition can not overflow, because in the + * for-loop it was tested with > instead of >= which + * would be appropriate. See comment there. */ + copy_seg->size = get_alloc_size (copy_seg->length + 1); + + copy_seg->text = malloc (copy_seg->size); + if (NULL == copy_seg->text) + { + set_status (status, E_ALLOC, 3, "malloc failed"); + free_text_segment (copy); + return NULL; + } + + + text = copy_seg->text; + r_length = strlen (new); + /* actual copying */ + /* Initialization was done since the beginning of + * the outer loop. So think of all this code standing + * | here. + * v */ + for (; start != end; start = start->next) + { + char * s_start; + char * s_end; + + s_start = start->text, + s_end = s_start + start->length; + + while (s_end != s_start) + { + if (old == *s_start) + { + memcpy (text, new, r_length); + text += r_length; + copy_seg->length += r_length-1; + s_start++; + } + else + { + *text++ = *s_start++; + } + } + } + text[0] = '\0'; + + /* See comment of outer loop. (4th paragraph) */ + if (end == NULL) + { + return copy; + } + + copy_seg->next = malloc (sizeof (struct text_segment)); + if (NULL == copy_seg->next) + { + set_status (status, E_ALLOC, 3, "malloc failed"); + free_text_segment (copy); + return NULL; + } + + copy_seg = copy_seg->next; + copy_seg->next = NULL; + } +} diff --git a/tests/test_text.c b/tests/test_text.c index 4f03a78..247beb1 100644 --- a/tests/test_text.c +++ b/tests/test_text.c @@ -161,6 +161,33 @@ START_TEST (test_text_copy_with_status) } END_TEST +START_TEST (test_text_copy_replace_with_status) +{ + struct SH_Status status; + struct SH_Text * text; + struct SH_Text * copy; + + /* setup */ + text = SH_Text_new_from_string ("Text12345", NULL); + ck_assert_ptr_ne (NULL, text); + + /* test */ + _status_preinit (status); + copy = SH_Text_copy_and_replace (text, '1', "000", &status); + ck_assert_int_eq (status.status, SUCCESS); + ck_assert_ptr_ne (copy, NULL); + + ck_assert_ptr_ne (copy, text); + ck_assert_ptr_ne (copy->data->text, text->data->text); + ck_assert_str_ne (copy->data->text, text->data->text); + ck_assert_str_eq ("Text0002345", copy->data->text); + + /* cleanup */ + SH_Text_free (copy); + SH_Text_free (text); +} +END_TEST + START_TEST(test_text_get_length_no_status) { struct SH_Text * text; @@ -671,6 +698,7 @@ Suite * text_suite (void) tcase_add_test (tc_core, test_text_string_with_status); tcase_add_test (tc_core, test_text_copy_no_status); tcase_add_test (tc_core, test_text_copy_with_status); + tcase_add_test (tc_core, test_text_copy_replace_with_status); tcase_add_test (tc_core, test_text_get_length_no_status); tcase_add_test (tc_core, test_text_get_length_with_status); tcase_add_test (tc_core, test_text_get_char_no_status); -- GitLab