Add comments to explain what we do
Always use the prefixed version of M*LIB
This commit is contained in:
1 parent
55b745b916
commit
bf6c1341b3
1 file changed
+63
-46
+63
-46
@@ -10,24 +10,34 @@
|
|||||||
#include "m-string.h"
|
#include "m-string.h"
|
||||||
#include "m-atomic.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)
|
static inline int atomic_uint_cmp(const atomic_uint *a, const atomic_uint *b)
|
||||||
{
|
{
|
||||||
unsigned ai = atomic_load(a), bi = atomic_load(b);
|
unsigned ai = atomic_load(a), bi = atomic_load(b);
|
||||||
return ai > bi ? -1 : ai < bi;
|
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 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
|
/* Define a directory as a tree where each node contains:
|
||||||
and the name of the directory
|
* the number of lines of the files in this directory,
|
||||||
(Only sort with the number value by disable the sort compare for the string).
|
* 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))))
|
M_TUPLE_DEF2( dir, (nlines, atomic_uint), (name, m_string_t, M_OPEXTEND(M_STRING_OPLIST, CMP(0))))
|
||||||
#define M_OPL_dir_t() TUPLE_OPLIST(dir, M_OPL_atomic_uint(), M_STRING_OPLIST)
|
/* 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)
|
M_TREE_DEF(tree, dir_t)
|
||||||
|
|
||||||
|
|
||||||
@@ -39,17 +49,20 @@ M_TREE_DEF(tree, dir_t)
|
|||||||
|
|
||||||
|
|
||||||
/* Define the pool of worker threads */
|
/* Define the pool of worker threads */
|
||||||
worker_t workers;
|
m_worker_t workers;
|
||||||
|
|
||||||
/* Global synchronous block for all threads */
|
/* Global synchronous block for all threads */
|
||||||
m_worker_sync_t workers_block;
|
m_worker_sync_t workers_block;
|
||||||
|
|
||||||
/* Add a specialization of the spawn function for scan_file */
|
/* Add a specialization of the spawn function for the scan_file function.
|
||||||
M_WORKER_SPAWN_DEF2(scan_file, (parent, tree_it_t, M_POD_OPLIST), (filename, string_t) )
|
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
|
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)) {
|
if (tree_end_p(parent)) {
|
||||||
return tree_emplace_root( tree_tree(parent), 0, dirname);
|
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 */
|
/* count the number of end of line character in the buffer */
|
||||||
static unsigned
|
static unsigned
|
||||||
count_eol(const char buffer[], size_t num)
|
count_eol(size_t num, const char buffer[num])
|
||||||
{
|
{
|
||||||
unsigned count = 0;
|
unsigned count = 0;
|
||||||
for(size_t i = 0; i < num; i++) {
|
for(size_t i = 0; i < num; i++) {
|
||||||
@@ -69,23 +82,23 @@ count_eol(const char buffer[], size_t num)
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Test if a filename suffix ends with a C++ or C suffix */
|
||||||
static bool
|
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")
|
return (m_string_end_with_str_p(filename, ".c")
|
||||||
|| string_end_with_str_p(filename, ".h")
|
|| m_string_end_with_str_p(filename, ".h")
|
||||||
|| string_end_with_str_p(filename, ".cpp")
|
|| m_string_end_with_str_p(filename, ".cpp")
|
||||||
|| string_end_with_str_p(filename, ".hpp"));
|
|| m_string_end_with_str_p(filename, ".hpp"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Scan the file and count its number of lines */
|
||||||
static void
|
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(m_string_get_cstr(filename), "rt");
|
||||||
|
|
||||||
FILE *f = fopen(string_get_cstr(filename), "rt");
|
|
||||||
if (f == NULL) {
|
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);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,25 +108,27 @@ scan_file(tree_it_t parent, string_t filename)
|
|||||||
size_t num;
|
size_t num;
|
||||||
do {
|
do {
|
||||||
num = fread (buffer, 1, MAX_READ_BUFFER, f);
|
num = fread (buffer, 1, MAX_READ_BUFFER, f);
|
||||||
count += count_eol(buffer, num);
|
count += count_eol(num, buffer);
|
||||||
} while (num == MAX_READ_BUFFER);
|
} while (num == MAX_READ_BUFFER);
|
||||||
fclose(f);
|
fclose(f);
|
||||||
|
|
||||||
/* Adding the number of lines to the parent directory */
|
/* Adding the number of lines to the parent directory */
|
||||||
dir_t *d = tree_ref(parent);
|
dir_t *d = tree_ref(parent);
|
||||||
|
/* atomic add because the scan of files is done in parallel */
|
||||||
atomic_fetch_add( &(*d)->nlines, count );
|
atomic_fetch_add( &(*d)->nlines, count );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Scan the directories recursively for all files */
|
||||||
static void
|
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);
|
tree_it_t it = add_directory(parent, dirname);
|
||||||
|
|
||||||
string_t filename;
|
m_string_t filename;
|
||||||
string_init(filename);
|
m_string_init(filename);
|
||||||
DIR *dir = opendir(string_get_cstr(dirname));
|
DIR *dir = opendir(m_string_get_cstr(dirname));
|
||||||
if (dir == NULL) {
|
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);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,20 +137,20 @@ scan_directories(tree_it_t parent, const string_t dirname)
|
|||||||
while (entry) {
|
while (entry) {
|
||||||
// Ignore hidden entries (including '.' and '..')
|
// Ignore hidden entries (including '.' and '..')
|
||||||
if (entry->d_name[0] != '.') {
|
if (entry->d_name[0] != '.') {
|
||||||
// Construct the path to the filename
|
// Construct the path to the filename by concatenating dirname, "/" and d_name
|
||||||
string_sets(filename, dirname, "/", entry->d_name);
|
m_string_sets(filename, dirname, "/", entry->d_name);
|
||||||
// Get the property of this filename
|
// Get the property of this filename
|
||||||
struct stat buf;
|
struct stat buf;
|
||||||
int err = stat(string_get_cstr(filename), &buf);
|
int err = stat(m_string_get_cstr(filename), &buf);
|
||||||
if (err != 0) {
|
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);
|
exit(2);
|
||||||
}
|
}
|
||||||
// Further scanning in function of the kind of entry
|
// Further scanning in function of the kind of entry
|
||||||
if (S_ISDIR(buf.st_mode)) {
|
if (S_ISDIR(buf.st_mode)) {
|
||||||
scan_directories(it, filename);
|
scan_directories(it, filename);
|
||||||
} else {
|
} else if (is_a_c_file(filename)) {
|
||||||
// Spawn a scan of file to available workers
|
// Spawn a scan of file to available workers or run it ourselves
|
||||||
m_worker_spawn_scan_file(workers_block, scan_file, it, filename);
|
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);
|
closedir(dir);
|
||||||
string_clear(filename);
|
m_string_clear(filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Consolidate all lines number to the parent directory */
|
||||||
static void
|
static void
|
||||||
consolidate_directories(tree_t directories)
|
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) ) {
|
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 *parent = tree_up_ref(it);
|
||||||
dir_t *myself = tree_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
|
// Add the child number of lines to the parent
|
||||||
atomic_fetch_add_explicit(&(*parent)->nlines, (*myself)->nlines, memory_order_relaxed);
|
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);
|
tree_sort_child(it);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Print the consolidated results on stdout */
|
||||||
static void
|
static void
|
||||||
print_result(tree_t directories)
|
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) ) {
|
for(tree_it_t it = tree_it(directories) ; !tree_end_p(it) ; tree_next(&it) ) {
|
||||||
int i, depth = tree_depth(it);
|
int i, depth = tree_depth(it);
|
||||||
for(i = 0; i < depth; i++) printf("+");
|
for(i = 0; i < depth; i++) printf("+");
|
||||||
for( ; i < 8; i++) printf(" ");
|
for( ; i < 8; i++) printf(" ");
|
||||||
const dir_t *d = tree_cref(it);
|
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[])
|
int main(int argc, const char *argv[])
|
||||||
{
|
{
|
||||||
tree_t directories;
|
tree_t directories;
|
||||||
string_t str;
|
m_string_t str;
|
||||||
|
|
||||||
printf("Count the number of C/C++ lines of code\n");
|
printf("Count the number of C/C++ lines of code\n");
|
||||||
if (argc < 2) {
|
if (argc < 2) {
|
||||||
@@ -188,7 +205,7 @@ int main(int argc, const char *argv[])
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Initialize the worker threads */
|
/* Initialize the worker threads */
|
||||||
worker_init(workers);
|
m_worker_init(workers);
|
||||||
|
|
||||||
/* Init the tree, reserve for at least the number of max directory
|
/* Init the tree, reserve for at least the number of max directory
|
||||||
and prevent any further increase of allocation:
|
and prevent any further increase of allocation:
|
||||||
@@ -196,20 +213,20 @@ int main(int argc, const char *argv[])
|
|||||||
tree_init(directories);
|
tree_init(directories);
|
||||||
tree_reserve(directories, MAX_DIRECTORY);
|
tree_reserve(directories, MAX_DIRECTORY);
|
||||||
tree_lock(directories, true);
|
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);
|
tree_it_t it = tree_it_end(directories);
|
||||||
|
|
||||||
/* Main processing */
|
/* Main processing */
|
||||||
worker_start(workers_block, workers);
|
m_worker_start(workers_block, workers);
|
||||||
scan_directories(it, str);
|
scan_directories(it, str);
|
||||||
worker_sync(workers_block);
|
worker_sync(workers_block);
|
||||||
consolidate_directories(directories);
|
consolidate_directories(directories);
|
||||||
print_result(directories);
|
print_result(directories);
|
||||||
|
|
||||||
/* Clear everything & quit */
|
/* Clear everything & quit */
|
||||||
string_clear(str);
|
m_string_clear(str);
|
||||||
tree_clear(directories);
|
tree_clear(directories);
|
||||||
worker_clear(workers);
|
m_worker_clear(workers);
|
||||||
|
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user