blc_tools.h 17.9 KB
Newer Older
Arnaud Blanchard's avatar
Arnaud Blanchard committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/* Basic Library for C/C++ (blclib)
 Copyright  ETIS — ENSEA, Université de Cergy-Pontoise, CNRS (2011 - 2014)

 Author: A. Blanchard

 This software is governed by the CeCILL v2.1 license under French law and abiding by the rules of distribution of free software.
 You can use, modify and/ or redistribute the software under the terms of the CeCILL v2.1 license as circulated by CEA, CNRS and INRIA at the following URL "http://www.cecill.info".
 As a counterpart to the access to the source code and  rights to copy, modify and redistribute granted by the license,
 users are provided only with a limited warranty and the software's author, the holder of the economic rights,  and the successive licensors have only limited liability.
 In this respect, the user's attention is drawn to the risks associated with loading, using, modifying and/or developing or reproducing the software by the user in light of its specific status of free software,
 that may mean  that it is complicated to manipulate, and that also therefore means that it is reserved for developers and experienced professionals having in-depth computer knowledge.
 Users are therefore encouraged to load and test the software's suitability as regards their requirements in conditions enabling the security of their systems and/or data to be ensured
 and, more generally, to use and operate it in the same conditions as regards security.
 The fact that you are presently reading this means that you have had knowledge of the CeCILL v2.1 license and that you accept its terms.
Created on: Apr 28, 2011
*/

#ifndef BLC_TOOLS_H
#define BLC_TOOLS_H

/**
22
23
24
25
 @defgroup blc_tools blc_tools
 @{
 @brief Provides many simple tools and shortcut to help programming. It stays at very low level.
 */
Arnaud Blanchard's avatar
Arnaud Blanchard committed
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

///Start a C function definition block. The function inside this block will be able to be call by C programs even if it is compiled in C++.
#ifdef __cplusplus
#define START_EXTERN_C extern "C" {
///End a C definition block.
#define END_EXTERN_C }
#else
#define START_EXTERN_C
///End a C definition block.
#define END_EXTERN_C
#endif

#include <stdio.h> /* FILE* */
#include <stdlib.h> /* size_t */
#include <stdarg.h> /* variable arguments ... */
#include <string.h> /*memset*/
#include <stdint.h> /* uint32_t */
#include <sys/param.h>
#include <arpa/inet.h> /* htonl, ... */

46
47

///Shortcut to better understand arguments of a function while 0 is used for no flag
48
49
#define NO_FLAG 0

Arnaud Blanchard's avatar
Arnaud Blanchard committed
50
51
52
53
54
55
/// Set zeros to any structures. These structures have to be static otherwise the macro cannot determine the size of the structure.
#define CLEAR(structure) memset(&(structure), 0, sizeof(structure))

/// Clip the value on the number between 0 and 255. Usefull to convert any number to a uchar.
#define CLIP_UCHAR(x) (uchar)((x) < 0 ? 0 : ((x) >= 256 ? 255 : (x)))

56
/// Clip the value on the number between 0 and 255. Usefull to convert any number to a uchar.
57
#define BLC_CLIP_FLOAT(x) ((x) < 0 ? 0.f : ((x) > 1.0f ? 1.0f : (x)))
58

59
60
/**Shorcut to do a loop from 0 to max-1 element. If it is not broken, the last iterator after the loop is max.
You should not modify the iterator yourself, in this case use a standard for(;;) */
Arnaud Blanchard's avatar
Arnaud Blanchard committed
61
62
#define FOR(iterator, max) for (iterator = 0; iterator != max; ++iterator)

63
64
/**Shorcut to do a loop from max-1 elements to 0. This is a bit faster. If it is not broken, the last iterator after the loop is -1.
You should not modify the iterator yourself, in this case use a standard for(;;) */
Arnaud Blanchard's avatar
Arnaud Blanchard committed
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#define FOR_INV(iterator, max) for (iterator = max; iterator--;)

/// Do a modulo even with negative numbers.
#define MOD(a,b) ((((a)%(b))+(abs(b)))%(b))

#ifndef HOST_NAME_MAX
#define HOST_NAME_MAX _POSIX_HOST_NAME_MAX //Darwin (APPLE) does not have HOST_NAME_MAX definition
#endif

///definition of qsort_r in the with the BSD syntax even on gnuC. Be careful the compar function passed in argument has to be adapted as well. The void *arg is expected as a third argument on gnuC and it is as a first on Darwin
#ifdef BSD
#define QSORT_R qsort_r
#else
#define QSORT_R(base, elements_nb, width, arg, compar) (base, elements_nb, width, compar, arg);
#endif

///Convert a define in string e.g. STRINGIFY(EXIT_FAILURE) -> "EXIT_FAILURE"
#define STRINGIFY(string) #string

///Convert the content of a definition in string e.g. STRINGIFY_CONTENT(EXIT_FAILURE) -> "1"  Utile pour afficher les parametres de compilations.
#define STRINGIFY_CONTENT(variable) STRINGIFY(variable)

/**Convert a string of 4 chars to a uint32_t number. Usefull for FOURCC pixel format. e.g. you can campare pixel_format with 'YUV2' or 'JPEG' or 'RGB3'.
@code {.c}
if ('UIN8' == STRING_TO_UINT32("UIN8")) ...
@endcode*/
#define STRING_TO_UINT32(x) ntohl(*(uint32_t*)x)

/**Convert a uint32_t number to a 4 chars string. Usefull for displaying. It needs an additiional uint32_t to store the transformed data.
@code {.c}
uint32_t value_str;
printf("type: %.4s", UINT32_TO_STRING(value_str, 'UIN8'));
@endcode*/
98
99
100
101
102
103
#ifdef __cplusplus
#define UINT32_TO_STRING(uint32_str, x) ((char*)&(uint32_str=htonl(x)))
#else
char* blc_uint32_to_string(uint32_t *string, uint32_t x);
#define UINT32_TO_STRING(uint32_str, x) blc_uint32_to_string(&uint32_str, x)
#endif
Arnaud Blanchard's avatar
Arnaud Blanchard committed
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213

///Shorcut to use in formated string to define a maximal size of the receiving string.
#define SCAN_CONV(size_max, format)  "%" STRINGIFY_CONTENT(size_max)format

///Print a red error message with the file, line and position of the caller with the message in parameter with printf format. Then raise SIGABRT signal (usefull for debugging) and exit with code EXIT_FAILURE.
#define EXIT_ON_ERROR(...) fatal_error(__FILE__, __FUNCTION__, __LINE__, __VA_ARGS__)

///Print an orange warning message with the file, line and position of the caller with the message in parameter with printf format. Then return.
#define PRINT_WARNING(...) print_warning(__FILE__, __FUNCTION__, __LINE__, __VA_ARGS__)

///Like @ref EXIT_ON_ERROR butprints also the system error message corresponding to the last system error (errno).
#define EXIT_ON_SYSTEM_ERROR(...) fatal_system_error(__FILE__, __FUNCTION__, __LINE__, __VA_ARGS__)

///Like @ref PRINT_WARNING but print also the system error message corresponding to the last system error (errno).
#define PRINT_SYSTEM_ERROR(...) print_system_error(__FILE__, __FUNCTION__, __LINE__, __VA_ARGS__)

/**
@code{.c}
char *filename="wrong_name";
FILE* file;
SYSTEM_ERROR_CHECK(file=fopen(filename, "r"), NULL, "Problem opening the file '%s'", filename);
@endcode
if file is NULL this exit with a error message showing the position of the error, the executed command 'file=fopen(filename, "r")', the system error message and your personnal formated message*/
#define SYSTEM_ERROR_CHECK(command, bad_value, ...) do{if ((command) == bad_value) fatal_command_system_error(__FILE__, __FUNCTION__, __LINE__, STRINGIFY(command),__VA_ARGS__);}while(0)

///Same as SYSTEM_ERROR_CHECK but cheking success instead of error.
#define SYSTEM_SUCCESS_CHECK(command, good_value, ...) do{if ((command) != good_value) fatal_command_system_error(__FILE__, __FUNCTION__, __LINE__, STRINGIFY(command),__VA_ARGS__);}while(0)

/**Like SYSTEM_ERROR_CHECK but the command is reexecuted if the errno is errno_accepted (Usually in case of interruption EINTR).*/
#define SYSTEM_ERROR_RETRY_ON_SPECIFIC_ERRNO(command, bad_value, errno_accepted, ...) while((command) == bad_value) { if (errno!=(errno_accepted)) fatal_command_system_error(__FILE__, __FUNCTION__, __LINE__, STRINGIFY(command),__VA_ARGS__);}

/**Do a malloc big enough to contain 'type', checks if it succeeds and return the memory casted as 'type*'.
@code{.c}
struct toto *toto_pointer;
toto_pointer=ALLOCATION(struct toto);
@endcode*/
#define ALLOCATION(type) (type*)secure_malloc(__FILE__, __FUNCTION__,__LINE__, sizeof(type))

///Like @ref ALLOCATION but allocates 'numbers' elements of 'type'.
#define MANY_ALLOCATIONS(numbers, type) (type*)secure_malloc(__FILE__,  __FUNCTION__, __LINE__, (numbers)*sizeof(type))

/**Like @ref ALLOCATION but reallocates 'pointer' to contain 'numbers' element of 'type'. The content of pointer may be changed if needed. If pointer is NULL, this macro has the same effect than MANY_ALLOACTIONS. @return Nothing as the 'pointer' is directly modified.
@code{.c}
int *values;
values=MANY_ALLOCATIONS(5, int);
//values is an array of 5 int
MANY_REALLOCATIONS(&values, 7);
//now values is an array of 7 int. The values address may change but the 5 first values are the same.
@endcode*/
#define MANY_REALLOCATIONS(array_pt, numbers) secure_realloc(__FILE__,  __FUNCTION__, __LINE__, (void**)array_pt, (numbers)*sizeof(**(array_pt)))

/// Free the content of the pointer and set it to NULL. Useful to be sure that a freed pointer will not be used again.
#define FREE(pointer) do{ free((void*)pointer); pointer=NULL; }while(0)

/**
 Append the content of the object pointed by new_element to the array pointed by pointer. The size of the array will increase and the pointeris changed acordingly. The number of element is incremented. The new element is returned. The size of the element is determined by the type of pointer.
 @code{.c}
 type_item item0, item1;
 type_item *items=NULL;
 int items_nb=0;
 //Add the content (copy) of item in the end of the array
 APPEND_ITEM(&items, &items_nb, &item0);
 //Items is a pointer to a memory of sizeof(type_item), and items_nb=1
 @endcode */
#define APPEND_ITEM(array_pt, numbers_pt, new_element_pt) append_item(__FILE__,  __FUNCTION__, __LINE__, (void**)array_pt, sizeof(**(array_pt)), numbers_pt, sizeof(*(new_element_pt)), (void*)new_element_pt)

/** Return the pointer toward a new allocation the end of the array*/
#define APPEND_ALLOCATION(array_pt, numbers_pt, type) (type*)append_allocation(__FILE__,  __FUNCTION__, __LINE__, (void**)array_pt, sizeof(**(array_pt)), numbers_pt, sizeof(type))

/**Insert the content of the object pointed by new_element to the array pointed by pointer. The size of the array will increase and the pointer will be changed acordingly. To insert the data, a part of the array is copied, which may not be optimal. The number of elements is incremented. The size of the element is determined by the type of pointer.
 @code{.c}
 //Insert the content of item in the end of the array
 INSERT_ITEM(&items, &items_nb, &item1, 0);
 //Items is a pointer to a memory of 2*sizeof(type_item), and items_nb=2, the first item is item1, the second item is item0.
 @endcode */
#define INSERT_ITEM(array_pt, numbers_pt, new_element_pt, position) insert_item(__FILE__,  __FUNCTION__, __LINE__, (void**)array_pt, sizeof(**(array_pt)), numbers_pt, sizeof(*(new_element_pt)), (void*)new_element_pt, position)

/**Process each item of an array
 @code{.c}
 type_item *iterator;
 FOR_EACH(iterator, items, items_nb) printf("item name '%s'", iterator->name);
 @endcode */
#define FOR_EACH(iterator, array, length) for(iterator=array; iterator!=(array)+(length); iterator++) // May not be optimal

///Same as FOR_EACH in reverse order
#define FOR_EACH_INV(iterator, array, length) for (iterator=(array+length); array!=iterator--;)

/**Return the position of the searched_item_pt or -1 if it is not found
 @code {.c}
 GET_ITEM_POSITION(items, items_nb, &item0);
 //1 is returned as item0 is at the position 1
 @endcode
 */
#define GET_ITEM_POSITION(array, numbers, searched_item_pt) get_item_position(__FILE__,  __FUNCTION__, __LINE__, (void**)array, sizeof(*(array)), numbers, sizeof(*(searched_item_pt)), (void*)searched_item_pt)

/**Remove item by replacing the item at position by the last item and liberating the memory. This change the order of the element in the array. If you use a loop, do not forget to reiterate the current position as it as now a new item ( the one that was at teh end of the array ).
 @code {.c}
 REMOVE_ITEM_ID(&items, &items_nb, 0)
 @endcode
 We are now like before INSERT_ITEM, item0 is at the first place. And the memory of item1 as been removed.
 The last item is now at the position of the current removed item !!! The order is not kept !!!
 */
#define REMOVE_ITEM(array_pt, numbers_pt, position) remove_item_position(__FILE__,  __FUNCTION__, __LINE__, (void**)array_pt, sizeof(**(array_pt)), numbers_pt, position)

/* Not usually useful. Log the data in the file log_file. log_file has to be managed (open/close), by the caller. You can have many different log file depending on the caller. It can also be stdout or stderr. In this case declare log_file = stdout/stderr. It is not yet stabilized.*/
#define BLC_LOG(...) printf_log("blc",__FILE__, __FUNCTION__,__LINE__, __VA_ARGS__)

///like sprintf but check that the buffer is big enough to contain the string. It only works with static buffer. It does not return the string size.
#define SPRINTF(buffer, ...) do{ if (snprintf(buffer, sizeof(buffer), __VA_ARGS__) >= (int)sizeof(buffer))  EXIT_ON_ERROR("The string is too long for the buffer of size '%ld'.", sizeof(buffer));}while(0)

214
215
///like strcpy but check that the buffer is big enough to contain the string. ATTENTION: It only works with static buffer !
#define STRCPY(buffer, source) do {if (source==NULL) EXIT_ON_ERROR("source must not be NULL"); else  if (buffer==NULL) EXIT_ON_ERROR("destination must not be NULL"); if (strlen(source)+1 > (int)sizeof(buffer)) EXIT_ON_ERROR("The length ('%d') of the your source ('%s') is longer than the receiving buffer: '%ld'.\nREMARQ: if the first argument is not a static buffer, this test is WRONG. The size of the pointer and not the size of the buffer is considered ! Use strncpy and make yourself the verification.", strlen(source)+1, source, sizeof(buffer)); else strcpy(buffer, source);}while(0) //tricks to not have problem in a if
Arnaud Blanchard's avatar
Arnaud Blanchard committed
216
217

/// Do a fscanf checking than the number of interpreted arguments corresponds to the first parameter.
218
#define FSCANF(fields_nb, file, ... ) do{if (fscanf(file,  __VA_ARGS__) != fields_nb) EXIT_ON_ERROR("Failed reading %d fields.", fields_nb);}while(0)
Arnaud Blanchard's avatar
Arnaud Blanchard committed
219
220

/// Do a fscanf checking than the number of interpreted arguments corresponds to the first parameter.
221
#define SSCANF(fields_nb, string, ... ) do{if (sscanf(string,  __VA_ARGS__) != fields_nb) EXIT_ON_ERROR("Failed reading %d fields in '%s'.", fields_nb, string);}while(0)
Arnaud Blanchard's avatar
Arnaud Blanchard committed
222
223

/// Like FSCANF but generates only a warning in case of error.
224
#define TRY_FSCANF(fields_nb, file, ... ) do{if (fscanf(file, __VA_ARGS__) != fields_nb) PRINT_WARNING("Failed trying reading %d fields in '%s'.", fields_nb);}while(0)
Arnaud Blanchard's avatar
Arnaud Blanchard committed
225
226

#ifndef BSD
227
///Only BSD defines this shortcut
Arnaud Blanchard's avatar
Arnaud Blanchard committed
228
#define HTONL(x) x=htonl(x)
229
///Only BSD defines this shortcut
Arnaud Blanchard's avatar
Arnaud Blanchard committed
230
231
232
233
234
235
#define NTOHL(x) x=ntohl(x)
#endif

//Usefull for casting user_data in callback
#define USER_DATA( type, variable, pointer) type variable=(type)pointer

236
///x is in [0.5f/256, 1-0.5f/256] and go to [0, 255]
237
238
#define BLC_NORMED_FLOAT_TO_UCHAR(x) (x*256-0.5f)

239
///x is in [0, 255] and go to [0.5f/256, 1-0.5f/256] it is the exact inverse of BLC_NORMED_FLOAT_TO_UCHAR
240
241
242
#define BLC_UCHAR_TO_NORMED_FLOAT(x) ((x+0.5f)/256);


Arnaud Blanchard's avatar
Arnaud Blanchard committed
243
// If it has been initialise it points toward argv[0]. Useful to debug different program in parallel.
244
245
246
247
248
extern char const *blc_program_name;

//blc_program_name<pid>
extern char const *blc_program_id;

Arnaud Blanchard's avatar
Arnaud Blanchard committed
249
250
251
252
253
254

extern FILE *blc_log_file;
typedef unsigned char uchar;


START_EXTERN_C
255
/// Return the size in bytes of the type ('UIN8', 'INT8', 'FL32', ...)
Arnaud Blanchard's avatar
Arnaud Blanchard committed
256
257
size_t blc_get_type_size(uint32_t type);

258
259
260
261
262
263
264
265
266
267
/** Return a random char. The seed as to be set before and the code speed may be not be optimal but it does not have the usual error of personal methods.*/
char blc_rand_char();

/**Close both part pipe[0] (input) and pipe[1] (output) of a pipe*/
void blc_close_pipe(int *pipe);

/**Get a pointer to the extension (text after last '.') of **filename** */
char const *blc_get_filename_extension(char const *filename);

//They have to be called by macros, you usually do not need them
Arnaud Blanchard's avatar
Arnaud Blanchard committed
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
void print_warning(const char *name_of_file, const char* name_of_function, int numero_of_line, const char *message, ...);
void print_system_error(const char *name_of_file, const char* name_of_function, int numero_of_line, const char *message, ...);
void fatal_error(const char *name_of_file, const char* name_of_function, int numero_of_line, const char *message, ...);
void fatal_system_error(const char *name_of_file, const char* name_of_function, int numero_of_line, const char *message, ...);
void fatal_command_system_error(const char *name_of_file, const char* name_of_function, int numero_of_line, char const *command, const char *message, ...);
void printf_log(const char *log_name, char const *file, char const *function,  int line_id, ...);
void *secure_malloc(const char *file , const char *function,  int line, size_t size);
void secure_realloc(char const *file , char const *function, int line, void** pointer, size_t size);
void *append_allocation(const char *file, const char *function, int line, void** pointer, size_t pointer_content_size, int *items_nb, size_t item_size);

void *append_item(const char *file, const char *function, int line, void** pointer, size_t pointer_content_size, int *items_nb, size_t item_size, void* new_item);
void *insert_item(const char *file, const char *function, int line, void** pointer, size_t pointer_content_size, int *items_nb, size_t item_size, void* new_item, int position);
void remove_item_position(const char *file, const char *function, int line, void** pointer, size_t pointer_content_size, int *items_nb, int position);
int get_item_position(const char *file, const char *function, int line, void const* const* array_pt, size_t pointer_content_size, int items_nb, size_t item_size, void* searched_item_pt);

283
284
285
286
287
288
289
290
291
292
293
294

///Add an argument to the list argv of arguments. Add eventually a value as well.
void blc_add_arg( int *argc, char ***argv, char const *arg, char const *value);

///Allocate and fill a command line with the array of argument. The array must be terminated by a NULL argument and must the returned command line me be freed after use.
///It does not handle spaces in argument !!!
char *blc_create_command_line_from_argv(char const *const *argv);

///Allocate and fill an array of argument by spiting the command_line. The array is terminated by a NULL argument and it must be freed after used.
///It does not handle spaces in argument !!!
char * const*blc_create_argv_from_command_line(char const *command_line);

Arnaud Blanchard's avatar
Arnaud Blanchard committed
295
296
297
298
END_EXTERN_C
///@}
#endif /* BLC_TOOLS_H */