commit b699b7a2017f8e1fd81b5bb8700c2ddf0e2f1ef1
parent b50aaf88815505e6e9381f364ca8061ae6b10377
Author: vaplv <vaplv@free.fr>
Date: Fri, 29 Nov 2019 11:02:55 +0100
Merge branch 'feature_text_reader' into develop
Diffstat:
4 files changed, 415 insertions(+), 2 deletions(-)
diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt
@@ -52,7 +52,8 @@ set(RSYS_FILES_SRC
mem_lifo_allocator.c
mem_proxy_allocator.c
quaternion.c
- str.c)
+ str.c
+ text_reader.c)
if(CMAKE_USE_PTHREADS_INIT)
set(RSYS_FILES_SRC_THREAD
@@ -121,7 +122,8 @@ set(RSYS_FILES_INC_API
rsys.h
signal.h
str.h
- stretchy_array.h)
+ stretchy_array.h
+ text_reader.h)
set(RSYS_FILES_DOC COPYING COPYING.LESSER README.md)
@@ -229,6 +231,7 @@ if(NOT NO_TEST)
new_test(test_signal rsys)
new_test(test_str rsys)
new_test(test_stretchy_array rsys)
+ new_test(test_text_reader rsys)
new_test(test_time rsys)
add_library(test_lib SHARED ${RSYS_SOURCE_DIR}/test_library.c)
diff --git a/src/test_text_reader.c b/src/test_text_reader.c
@@ -0,0 +1,107 @@
+/* Copyright (C) 2013-2019 Vincent Forest (vaplv@free.fr)
+ *
+ * The RSys library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * The RSys library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the RSys library. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "mem_allocator.h"
+#include "test_utils.h"
+#include "text_reader.h"
+
+#include <string.h>
+
+static const char* text[] = {
+ "Znxr rnpu cebtenz qb bar guvat jryy. Gb qb n arj wbo, ohvyq nserfu engure"
+ "guna pbzcyvpngr byq cebtenzf ol nqqvat arj \"srngherf\"\n",
+ " \t\t # rzcgl yvar\n",
+ "Rkcrpg gur bhgchg bs rirel cebtenz gb orpbzr gur vachg gb nabgure, nf lrg"
+ "haxabja, cebtenz. Qba'g pyhggre bhgchg jvgu rkgenarbhf vasbezngvba. Nibvq"
+ "fgevatragyl pbyhzane be ovanel vachg sbezngf. Qba'g vafvfg ba vagrenpgvir"
+ "vachg.\n",
+ "# pbzzrag\n",
+ "Qrfvta naq ohvyq fbsgjner, rira bcrengvat flfgrzf, gb or gevrq rneyl, vqrnyyl"
+ "jvguva jrrxf. Qba'g urfvgngr gb guebj njnl gur pyhzfl cnegf naq erohvyq gurz.\n",
+ "\n",
+ "Hfr gbbyf va cersrerapr gb hafxvyyrq uryc gb yvtugra n cebtenzzvat gnfx, rira"
+ "vs lbh unir gb qrgbhe gb ohvyq gur gbbyf naq rkcrpg gb guebj fbzr bs gurz bhg"
+ "nsgre lbh'ir svavfurq hfvat gurz.\n"
+};
+static const size_t nlines = sizeof(text)/sizeof(const char*);
+
+static void
+check_text_reader(struct txtrdr* txtrdr)
+{
+ size_t iline = 0;
+
+ CHK(txtrdr_read_line(txtrdr) == RES_OK);
+ while(txtrdr_get_line(txtrdr)) {
+
+ /* Discard empty line */
+ while(strcspn(text[iline], "#\n") == strspn(text[iline], " \t")) ++iline;
+
+ CHK(!strncmp(txtrdr_get_line(txtrdr), text[iline], strcspn(text[iline], "#\n")));
+ CHK(txtrdr_get_line_num(txtrdr) == iline);
+ CHK(txtrdr_read_line(txtrdr) == RES_OK);
+ ++iline;
+ }
+ CHK(txtrdr_get_line_num(txtrdr) == nlines-1);
+ CHK(txtrdr_read_line(txtrdr) == RES_OK);
+ CHK(txtrdr_get_line(txtrdr) == NULL);
+ CHK(txtrdr_get_line_num(txtrdr) == nlines-1);
+ CHK(txtrdr_read_line(txtrdr) == RES_OK);
+}
+
+int
+main(int argc, char** argv)
+{
+ struct mem_allocator allocator;
+ struct txtrdr* txtrdr = NULL;
+ size_t i;
+ FILE* stream;
+ const char* filename = "test_text_reader.txt";
+ const char* stream_name = "my_stream";
+ (void)argc, (void)argv;
+
+ mem_init_proxy_allocator(&allocator, &mem_default_allocator);
+
+ /* Write the text into a stream */
+ CHK(stream = fopen(filename, "w+"));
+ FOR_EACH(i, 0, nlines) {
+ const size_t len = strlen(text[i]);
+ CHK(fwrite(text[i], 1, len, stream) == len);
+ }
+
+ rewind(stream);
+ CHK(txtrdr_stream(NULL, stream, NULL, '#', &txtrdr) == RES_OK);
+ check_text_reader(txtrdr);
+ txtrdr_ref_get(txtrdr);
+ txtrdr_ref_put(txtrdr);
+ txtrdr_ref_put(txtrdr);
+
+ rewind(stream);
+ CHK(txtrdr_stream(&allocator, stream, stream_name, '#', &txtrdr) == RES_OK);
+ CHK(!strcmp(txtrdr_get_name(txtrdr), stream_name));
+ check_text_reader(txtrdr);
+ txtrdr_ref_put(txtrdr);
+
+ CHK(txtrdr_file(&allocator, filename, '#', &txtrdr) == RES_OK);
+ CHK(!strcmp(txtrdr_get_name(txtrdr), filename));
+ check_text_reader(txtrdr);
+ txtrdr_ref_put(txtrdr);
+
+ fclose(stream);
+ check_memory_allocator(&allocator);
+ mem_shutdown_proxy_allocator(&allocator);
+ CHK(mem_allocated_size() == 0);
+ return 0;
+}
+
diff --git a/src/text_reader.c b/src/text_reader.c
@@ -0,0 +1,230 @@
+/* Copyright (C) 2013-2019 Vincent Forest (vaplv@free.fr)
+ *
+ * The RSys library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * The RSys library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the RSys library. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "dynamic_array_char.h"
+#include "mem_allocator.h"
+#include "ref_count.h"
+#include "str.h"
+#include "text_reader.h"
+
+/* #chars to add to the line buffer when there is no sufficient space to store
+ * the text */
+#define CHUNK 32
+
+struct txtrdr {
+ FILE* stream; /* Stream of the text to read */
+ struct str name; /* Stream name */
+ size_t iline; /* Line index currently read */
+ struct darray_char line; /* Buffer storing the read line */
+
+ /* String of chars from which the remaining line chars are skipped */
+ char reject[3];
+
+ /* Boolean defining if the stream is internally managed or not, i.e. if it
+ * has to be closed on text_reader release or not */
+ int manage_stream;
+
+ struct mem_allocator* allocator;
+ ref_T ref;
+};
+
+/*******************************************************************************
+ * Helper functions
+ ******************************************************************************/
+static void
+release_txtrdr(ref_T* ref)
+{
+ struct txtrdr* txtrdr = NULL;
+ ASSERT(ref);
+ txtrdr = CONTAINER_OF(ref, struct txtrdr, ref);
+ str_release(&txtrdr->name);
+ darray_char_release(&txtrdr->line);
+ if(txtrdr->stream && txtrdr->manage_stream) fclose(txtrdr->stream);
+ MEM_RM(txtrdr->allocator, txtrdr);
+}
+
+/*******************************************************************************
+ * Text reader API
+ ******************************************************************************/
+res_T
+txtrdr_stream
+ (struct mem_allocator* mem_allocator,
+ FILE* stream,
+ const char* name,
+ const char comment,
+ struct txtrdr** out_txtrdr)
+{
+ struct mem_allocator* allocator = NULL;
+ struct txtrdr* txtrdr = NULL;
+ res_T res = RES_OK;
+ ASSERT(stream && out_txtrdr);
+
+ allocator = mem_allocator ? mem_allocator : &mem_default_allocator;
+ txtrdr = MEM_CALLOC(allocator, 1, sizeof(*txtrdr));
+ if(!txtrdr) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+ ref_init(&txtrdr->ref);
+ str_init(allocator, &txtrdr->name);
+ darray_char_init(allocator, &txtrdr->line);
+ txtrdr->allocator = allocator;
+ txtrdr->stream = stream;
+ txtrdr->iline = (size_t)(-1);
+ txtrdr->reject[0] = comment;
+ txtrdr->reject[1] = '\n';
+ txtrdr->reject[2] = '\0'; /* Finalize the string */
+
+ res = str_set(&txtrdr->name, name ? name : "<null>");
+ if(res != RES_OK) goto error;
+
+ res = darray_char_resize(&txtrdr->line, CHUNK);
+ if(res != RES_OK) goto error;
+
+exit:
+ *out_txtrdr = txtrdr;
+ return res;
+error:
+ if(txtrdr) {
+ txtrdr_ref_put(txtrdr);
+ txtrdr = NULL;
+ }
+ goto exit;
+}
+
+res_T
+txtrdr_file
+ (struct mem_allocator* mem_allocator,
+ const char* filename,
+ const char comment,
+ struct txtrdr** out_txtrdr)
+{
+ FILE* fp = NULL;
+ res_T res = RES_OK;
+ ASSERT(filename);
+
+ fp = fopen(filename, "r");
+ if(!fp) { res = RES_IO_ERR; goto error; }
+
+ res = txtrdr_stream(mem_allocator, fp, filename, comment, out_txtrdr);
+ if(res != RES_OK) goto error;
+
+ (*out_txtrdr)->manage_stream = 1;
+
+exit:
+ return res;
+error:
+ if(fp) fclose(fp);
+ goto exit;
+}
+
+void
+txtrdr_ref_get(struct txtrdr* txtrdr)
+{
+ ASSERT(txtrdr);
+ ref_get(&txtrdr->ref);
+}
+
+void
+txtrdr_ref_put(struct txtrdr* txtrdr)
+{
+ ASSERT(txtrdr);
+ ref_put(&txtrdr->ref, release_txtrdr);
+}
+
+res_T
+txtrdr_read_line(struct txtrdr* txtrdr)
+{
+ char* str = NULL;
+ res_T res = RES_OK;
+
+ if(!txtrdr) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ do {
+ /* Read the line */
+ str = fgets
+ (darray_char_data_get(&txtrdr->line),
+ (int)darray_char_size_get(&txtrdr->line),
+ txtrdr->stream);
+ if(!str) {
+ darray_char_clear(&txtrdr->line);
+ break;
+ }
+
+ /* Ensure tht the whole line is read */
+ while(!strrchr(DARRAY_BUF(&txtrdr->line), '\n') && !feof(txtrdr->stream)) {
+ const size_t sz = darray_char_size_get(&txtrdr->line);
+ char* more = NULL;
+
+ /* Resize the line buffer */
+ res = darray_char_resize(&txtrdr->line, sz+CHUNK);
+ if(res != RES_OK) goto error;
+
+ /* Read the remaing chars */
+ more = darray_char_data_get(&txtrdr->line) + sz-1/*null char*/;
+ str = fgets(more, CHUNK+1/*previous null char*/, txtrdr->stream);
+ if(!str) {
+ res = RES_IO_ERR;
+ goto error;
+ }
+ }
+
+ /* Remove new line & comments */
+ str = darray_char_data_get(&txtrdr->line);
+ str[strcspn(str, txtrdr->reject)] = '\0';
+
+ ++txtrdr->iline; /* Increment the line index */
+
+ } while(strspn(str, " \t") == strlen(str));/*Keep going if the line is Empty*/
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+char*
+txtrdr_get_line(struct txtrdr* txtrdr)
+{
+ ASSERT(txtrdr);
+ return darray_char_size_get(&txtrdr->line) == 0
+ ? NULL : darray_char_data_get(&txtrdr->line);
+}
+
+const char*
+txtrdr_get_cline(const struct txtrdr* txtrdr)
+{
+ ASSERT(txtrdr);
+ return darray_char_size_get(&txtrdr->line) == 0
+ ? NULL : darray_char_cdata_get(&txtrdr->line);
+}
+
+size_t
+txtrdr_get_line_num(const struct txtrdr* txtrdr)
+{
+ ASSERT(txtrdr);
+ return txtrdr->iline;
+}
+
+const char*
+txtrdr_get_name(const struct txtrdr* txtrdr)
+{
+ ASSERT(txtrdr);
+ return str_cget(&txtrdr->name);
+}
+
diff --git a/src/text_reader.h b/src/text_reader.h
@@ -0,0 +1,73 @@
+/* Copyright (C) 2013-2019 Vincent Forest (vaplv@free.fr)
+ *
+ * The RSys library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * The RSys library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the RSys library. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef TEXT_READER_H
+#define TEXT_READER_H
+
+#include "rsys.h"
+
+struct txtrdr;
+struct mem_allocator;
+
+BEGIN_DECLS
+
+RSYS_API res_T
+txtrdr_file
+ (struct mem_allocator* allocator, /* May be NULL <=> default allocator */
+ const char* filename,
+ const char comment, /* Char preceeding a comment */
+ struct txtrdr** txtrdr);
+
+RSYS_API res_T
+txtrdr_stream
+ (struct mem_allocator* allocator, /* May be NULL <=> default allocator */
+ FILE* stream,
+ const char* name, /* Stream name. May be NULL */
+ const char comment, /* Char preceeding a comment */
+ struct txtrdr** txtrdr);
+
+RSYS_API void
+txtrdr_ref_get
+ (struct txtrdr* txtrdr);
+
+RSYS_API void
+txtrdr_ref_put
+ (struct txtrdr* txtrdr);
+
+/* Read a non empty line, i.e. line with text that is not a comment. The new
+ * line character as well as the comments are skipped from the reading */
+RSYS_API res_T
+txtrdr_read_line
+ (struct txtrdr* txtrdr);
+
+RSYS_API char*
+txtrdr_get_line
+ (struct txtrdr* txtrdr);
+
+RSYS_API const char*
+txtrdr_get_cline
+ (const struct txtrdr* txtrdr);
+
+RSYS_API size_t
+txtrdr_get_line_num
+ (const struct txtrdr* txtrdr);
+
+RSYS_API const char*
+txtrdr_get_name
+ (const struct txtrdr* txtrdr);
+
+END_DECLS
+
+#endif /* TEXT_READER_H */