diff --git a/example/ex11-count-lines.c b/example/ex11-count-lines.c index 439104dd..0ec6e496 100644 --- a/example/ex11-count-lines.c +++ b/example/ex11-count-lines.c @@ -10,24 +10,34 @@ #include "m-string.h" #include "m-atomic.h" -/* Sort the atomic unsigned int with the biggest value first. */ + +/* Define the comparison function for atomic_uint type + to sort the atomic unsigned int with the biggest value first. */ static inline int atomic_uint_cmp(const atomic_uint *a, const atomic_uint *b) { unsigned ai = atomic_load(a), bi = atomic_load(b); return ai > bi ? -1 : ai < bi; } -/* Disable HASH for atomic as the default hash method won't work */ + +/* Define the oplist for atomic_uint and register it globally. + This oplist extends the basic oplist by: + * disabling HASH for atomic as the default hash method won't work. + * providing the methods for CMP, INIT_SET and SET operators (with their API adaptator) */ #define M_OPL_atomic_uint() M_OPEXTEND(M_BASIC_OPLIST, HASH(0), CMP(API_6(atomic_uint_cmp)), INIT_SET(API_2(atomic_store)), SET(API_2(atomic_store))) -/* Define a directory as the number of lines of the files in this directory - and the name of the directory - (Only sort with the number value by disable the sort compare for the string). +/* Define a directory as a tree where each node contains: + * the number of lines of the files in this directory, + * and the name of the directory/ + Since we want to sort with the number of lines only, we extend the oplist of a string by disabling the compare method. + So first, let's define the node as a tuple of such elements: */ -TUPLE_DEF2( dir, (nlines, atomic_uint), (name, string_t, M_OPEXTEND(STRING_OPLIST, CMP(0)))) -#define M_OPL_dir_t() TUPLE_OPLIST(dir, M_OPL_atomic_uint(), M_STRING_OPLIST) +M_TUPLE_DEF2( dir, (nlines, atomic_uint), (name, m_string_t, M_OPEXTEND(M_STRING_OPLIST, CMP(0)))) +/* Register globally the oplist of this tuple */ +#define M_OPL_dir_t() M_TUPLE_OPLIST(dir, M_OPL_atomic_uint(), M_STRING_OPLIST) -/* A hierarchical tree of directory */ +/* Define a hierarchical tree of directory. + Note that it will use the globally registered oplist associated to dir_t to use it */ M_TREE_DEF(tree, dir_t) @@ -39,17 +49,20 @@ M_TREE_DEF(tree, dir_t) /* Define the pool of worker threads */ -worker_t workers; +m_worker_t workers; /* Global synchronous block for all threads */ m_worker_sync_t workers_block; -/* Add a specialization of the spawn function for scan_file */ -M_WORKER_SPAWN_DEF2(scan_file, (parent, tree_it_t, M_POD_OPLIST), (filename, string_t) ) +/* Add a specialization of the spawn function for the scan_file function. + This functions takes as input an iterator of a tree (treated as a POD data), + and a file name */ +M_WORKER_SPAWN_DEF2(scan_file, (parent, tree_it_t, M_POD_OPLIST), (filename, m_string_t) ) +/* Add either the root node or a child to parent in function of the existence of parent */ static tree_it_t -add_directory(tree_it_t parent, const string_t dirname) +add_directory(tree_it_t parent, const m_string_t dirname) { if (tree_end_p(parent)) { return tree_emplace_root( tree_tree(parent), 0, dirname); @@ -60,7 +73,7 @@ add_directory(tree_it_t parent, const string_t dirname) /* count the number of end of line character in the buffer */ static unsigned -count_eol(const char buffer[], size_t num) +count_eol(size_t num, const char buffer[num]) { unsigned count = 0; for(size_t i = 0; i < num; i++) { @@ -69,23 +82,23 @@ count_eol(const char buffer[], size_t num) return count; } +/* Test if a filename suffix ends with a C++ or C suffix */ static bool -is_a_c_file(const string_t filename) +is_a_c_file(const m_string_t filename) { - return (string_end_with_str_p(filename, ".c") - || string_end_with_str_p(filename, ".h") - || string_end_with_str_p(filename, ".cpp") - || string_end_with_str_p(filename, ".hpp")); + return (m_string_end_with_str_p(filename, ".c") + || m_string_end_with_str_p(filename, ".h") + || m_string_end_with_str_p(filename, ".cpp") + || m_string_end_with_str_p(filename, ".hpp")); } +/* Scan the file and count its number of lines */ static void -scan_file(tree_it_t parent, string_t filename) +scan_file(tree_it_t parent, m_string_t filename) { - if (!is_a_c_file(filename)) return; - - FILE *f = fopen(string_get_cstr(filename), "rt"); + FILE *f = fopen(m_string_get_cstr(filename), "rt"); if (f == NULL) { - fprintf(stderr, "ERROR: Cannot open %s as a text file.\n", string_get_cstr(filename) ); + fprintf(stderr, "ERROR: Cannot open %s as a text file.\n", m_string_get_cstr(filename) ); exit(1); } @@ -95,25 +108,27 @@ scan_file(tree_it_t parent, string_t filename) size_t num; do { num = fread (buffer, 1, MAX_READ_BUFFER, f); - count += count_eol(buffer, num); + count += count_eol(num, buffer); } while (num == MAX_READ_BUFFER); fclose(f); /* Adding the number of lines to the parent directory */ dir_t *d = tree_ref(parent); + /* atomic add because the scan of files is done in parallel */ atomic_fetch_add( &(*d)->nlines, count ); } +/* Scan the directories recursively for all files */ static void -scan_directories(tree_it_t parent, const string_t dirname) +scan_directories(tree_it_t parent, const m_string_t dirname) { tree_it_t it = add_directory(parent, dirname); - string_t filename; - string_init(filename); - DIR *dir = opendir(string_get_cstr(dirname)); + m_string_t filename; + m_string_init(filename); + DIR *dir = opendir(m_string_get_cstr(dirname)); if (dir == NULL) { - fprintf(stderr, "ERROR: Cannot open %s as a directory.\n", string_get_cstr(dirname) ); + fprintf(stderr, "ERROR: Cannot open %s as a directory.\n", m_string_get_cstr(dirname) ); exit(1); } @@ -122,20 +137,20 @@ scan_directories(tree_it_t parent, const string_t dirname) while (entry) { // Ignore hidden entries (including '.' and '..') if (entry->d_name[0] != '.') { - // Construct the path to the filename - string_sets(filename, dirname, "/", entry->d_name); + // Construct the path to the filename by concatenating dirname, "/" and d_name + m_string_sets(filename, dirname, "/", entry->d_name); // Get the property of this filename struct stat buf; - int err = stat(string_get_cstr(filename), &buf); + int err = stat(m_string_get_cstr(filename), &buf); if (err != 0) { - fprintf(stderr, "ERROR: Cannot stat %s\n", string_get_cstr(filename)); + fprintf(stderr, "ERROR: Cannot stat %s\n", m_string_get_cstr(filename)); exit(2); } // Further scanning in function of the kind of entry if (S_ISDIR(buf.st_mode)) { scan_directories(it, filename); - } else { - // Spawn a scan of file to available workers + } else if (is_a_c_file(filename)) { + // Spawn a scan of file to available workers or run it ourselves m_worker_spawn_scan_file(workers_block, scan_file, it, filename); } } @@ -144,13 +159,14 @@ scan_directories(tree_it_t parent, const string_t dirname) } closedir(dir); - string_clear(filename); + m_string_clear(filename); } +/* Consolidate all lines number to the parent directory */ static void consolidate_directories(tree_t directories) { - // Scan all entries, first the childreen, then the parent + // Scan all entries, first the children, then the parent for(tree_it_t it = tree_it_post(directories) ; !tree_end_p(it) ; tree_next_post(&it) ) { dir_t *parent = tree_up_ref(it); dir_t *myself = tree_ref(it); @@ -158,28 +174,29 @@ consolidate_directories(tree_t directories) // Add the child number of lines to the parent atomic_fetch_add_explicit(&(*parent)->nlines, (*myself)->nlines, memory_order_relaxed); } - // Sort the data of the childreen by the number of lines + // Sort the data of the children by the number of lines tree_sort_child(it); } } +/* Print the consolidated results on stdout */ static void print_result(tree_t directories) { - // Scan all entries, first the parent, then the childreen + // Scan all entries, first the parent, then the children for(tree_it_t it = tree_it(directories) ; !tree_end_p(it) ; tree_next(&it) ) { int i, depth = tree_depth(it); for(i = 0; i < depth; i++) printf("+"); for( ; i < 8; i++) printf(" "); const dir_t *d = tree_cref(it); - printf("%6u %s\n", (unsigned) (*d)->nlines, string_get_cstr( (*d)->name ) ); + printf("%6u %s\n", (unsigned) (*d)->nlines, m_string_get_cstr( (*d)->name ) ); } } int main(int argc, const char *argv[]) { tree_t directories; - string_t str; + m_string_t str; printf("Count the number of C/C++ lines of code\n"); if (argc < 2) { @@ -188,7 +205,7 @@ int main(int argc, const char *argv[]) } /* Initialize the worker threads */ - worker_init(workers); + m_worker_init(workers); /* Init the tree, reserve for at least the number of max directory and prevent any further increase of allocation: @@ -196,20 +213,20 @@ int main(int argc, const char *argv[]) tree_init(directories); tree_reserve(directories, MAX_DIRECTORY); tree_lock(directories, true); - string_init_set_str(str, argv[1]); + m_string_init_set_cstr(str, argv[1]); tree_it_t it = tree_it_end(directories); /* Main processing */ - worker_start(workers_block, workers); + m_worker_start(workers_block, workers); scan_directories(it, str); worker_sync(workers_block); consolidate_directories(directories); print_result(directories); /* Clear everything & quit */ - string_clear(str); + m_string_clear(str); tree_clear(directories); - worker_clear(workers); + m_worker_clear(workers); exit(0); }