diff --git a/legacy/eina/ChangeLog b/legacy/eina/ChangeLog index 6e284e3042..7d61e5a902 100644 --- a/legacy/eina/ChangeLog +++ b/legacy/eina/ChangeLog @@ -270,3 +270,7 @@ 2012-05-06 Cedric Bail * Fix a rounding issue near 1.0 for eina_f32p32_cos and eina_f32p32_sin. + +2012-05-08 Cedric Bail + + * Add eina_file_map_lines to iterate on lines of a mapped file. diff --git a/legacy/eina/NEWS b/legacy/eina/NEWS index b6f0c90c67..5d82a9de87 100644 --- a/legacy/eina/NEWS +++ b/legacy/eina/NEWS @@ -5,6 +5,7 @@ Changes since Eina 1.2.0: Additions: * Add backtrace support to Eina_Log, use EINA_LOG_BACKTRACE to enable it. + * Add an helper to iterate over line in a mapped file. Fixes: * Add missing files in the tarball. diff --git a/legacy/eina/src/include/eina_file.h b/legacy/eina/src/include/eina_file.h index 91497048dc..7dccba1b36 100644 --- a/legacy/eina/src/include/eina_file.h +++ b/legacy/eina/src/include/eina_file.h @@ -98,6 +98,12 @@ typedef struct _Eina_File_Direct_Info Eina_File_Direct_Info; */ typedef struct _Eina_Stat Eina_Stat; +/** + * @typedef Eina_File_Lines + * A typedef to #_Eina_File_Lines. + */ +typedef struct _Eina_File_Lines Eina_File_Lines; + /** * @typedef Eina_File_Dir_List_Cb * Type for a callback to be called when iterating over the files of a @@ -188,6 +194,20 @@ struct _Eina_Stat unsigned long int ctimensec; }; +/** + * @struct _Eina_File_Lines + * A structure to store information of line + * @since 1.3 + */ +struct _Eina_File_Lines +{ + struct { + const char *start; + const char *end; + } line; + unsigned long long length; +}; + /** * @def EINA_FILE_DIR_LIST_CB * @brief cast to an #Eina_File_Dir_List_Cb. @@ -470,6 +490,19 @@ EAPI void *eina_file_map_new(Eina_File *file, Eina_File_Populate rule, */ EAPI void eina_file_map_free(Eina_File *file, void *map); +/** + * @brief Map line by line in memory efficiently with an Eina_Iterator + * @param file The file to run over + * @return an Eina_Iterator that will produce @typedef Eina_File_Lines. + * + * This function return an iterator that will act like fgets without the + * useless memcpy. Be aware that once eina_iterator_next has been called, + * nothing garanty you that the memory will still be mapped. + * + * @since 1.3 + */ +EAPI Eina_Iterator *eina_file_map_lines(Eina_File *file); + /** * @brief Tell if their was an IO error during the life of a mmaped file * diff --git a/legacy/eina/src/lib/eina_file.c b/legacy/eina/src/lib/eina_file.c index 1431c050b2..a1d93c5f5b 100644 --- a/legacy/eina/src/lib/eina_file.c +++ b/legacy/eina/src/lib/eina_file.c @@ -41,6 +41,7 @@ void *alloca (size_t); #include #include #include +#include #ifdef HAVE_DIRENT_H # include #endif @@ -1127,6 +1128,139 @@ eina_file_map_all(Eina_File *file, Eina_File_Populate rule) return ret; } +typedef struct _Eina_Lines_Iterator Eina_Lines_Iterator; +struct _Eina_Lines_Iterator +{ + Eina_Iterator iterator; + + Eina_File *fp; + const char *map; + const char *end; + + int boundary; + + Eina_File_Lines current; +}; + +/* search '\r' and '\n' by preserving cache locality and page locality + in doing a search inside 4K boundary. + */ +static inline const char * +_eina_fine_eol(const char *start, int boundary, const char *end) +{ + const char *cr; + const char *lf; + unsigned long long chunk; + + while (start < end) + { + chunk = start + boundary < end ? boundary : end - start; + cr = memchr(start, '\r', chunk); + lf = memchr(start, '\n', chunk); + if (cr) + { + if (lf && lf < cr) + return lf + 1; + return cr + 1; + } + else if (lf) + return lf + 1; + + start += chunk; + boundary = 4096; + } + + return end; +} + +static Eina_Bool +_eina_file_map_lines_iterator_next(Eina_Lines_Iterator *it, void **data) +{ + const char *eol; + + if (it->current.line.end >= it->end) + return EINA_FALSE; + + while ((*it->current.line.end == '\n' || *it->current.line.end == '\r') + && it->current.line.end < it->end) + it->current.line.end++; + + if (it->current.line.end == it->end) + return EINA_FALSE; + + eol = _eina_fine_eol(it->current.line.end, + it->boundary, + it->end); + it->boundary = (uintptr_t) eol & 0x3FF; + if (it->boundary == 0) it->boundary = 4096; + + it->current.line.start = it->current.line.end; + while (*eol == '\n' || *eol == '\r') + eol--; + + it->current.line.end = eol; + it->current.length = eol - it->current.line.start - 1; + + *data = &it->current; + return EINA_TRUE; +} + +static Eina_File * +_eina_file_map_lines_iterator_container(Eina_Lines_Iterator *it) +{ + return it->fp; +} + +static void +_eina_file_map_lines_iterator_free(Eina_Lines_Iterator *it) +{ + eina_file_map_free(it->fp, (void*) it->map); + eina_file_close(it->fp); + + EINA_MAGIC_SET(&it->iterator, 0); + free(it); +} + +EAPI Eina_Iterator * +eina_file_map_lines(Eina_File *file) +{ + Eina_Lines_Iterator *it; + + EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL); + + if (file->length == 0) return NULL; + + it = calloc(1, sizeof (Eina_Lines_Iterator)); + if (!it) return NULL; + + EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR); + + it->map = eina_file_map_all(file, EINA_FILE_SEQUENTIAL); + if (!it->map) + { + free(it); + return NULL; + } + + eina_lock_take(&file->lock); + file->refcount++; + eina_lock_release(&file->lock); + + it->fp = file; + it->boundary = 4096; + it->current.line.start = it->map; + it->current.line.end = it->current.line.start; + it->current.length = 0; + it->end = it->map + it->fp->length; + + it->iterator.version = EINA_ITERATOR_VERSION; + it->iterator.next = FUNC_ITERATOR_NEXT(_eina_file_map_lines_iterator_next); + it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(_eina_file_map_lines_iterator_container); + it->iterator.free = FUNC_ITERATOR_FREE(_eina_file_map_lines_iterator_free); + + return &it->iterator; +} + EAPI void * eina_file_map_new(Eina_File *file, Eina_File_Populate rule, unsigned long int offset, unsigned long int length)