diff --git a/SConstruct b/SConstruct index 7b7f1c4..94e438b 100644 --- a/SConstruct +++ b/SConstruct @@ -42,6 +42,12 @@ AddOption('--coverage', help='Build with coverage (gcov) support' ) +AddOption('--with-assertions', + dest='assertions', + action='store_true', + help='Build with assertions enabled (Default for a debug build)' +) + AddOption('--stack-protection', dest = 'stack_protection', action='store_true', @@ -51,6 +57,7 @@ AddOption('--stack-protection', build_type = GetOption('build_type') +assertions = GetOption('assertions') test_xml = GetOption('test_xml') prefix = GetOption('prefix') if not (build_type in ['debug', 'release']): @@ -81,8 +88,9 @@ env.Append(CPPPATH = ['#include']) if build_type == 'release': env.Append(CFLAGS = ['-O2']) - #this will turn off assert() - env.Append(CPPDEFINES = ['NDEBUG']) + if not assertions: + #this will turn off assert() + env.Append(CPPDEFINES = ['NDEBUG']) elif build_type == 'debug': env.Append(CPPDEFINES = ['DEBUG']) diff --git a/include/ucore/bitvec.h b/include/ucore/bitvec.h index acbedaa..5eec670 100644 --- a/include/ucore/bitvec.h +++ b/include/ucore/bitvec.h @@ -1,5 +1,5 @@ -#ifndef BITEVEC_H_ -#define BITEVEC_H_ +#ifndef UC_BITEVEC_H_ +#define UC_BITEVEC_H_ #ifdef __cplusplus extern "C" { @@ -65,14 +65,6 @@ void uc_bv_set_all(struct UCBitVec *v); * */ void uc_bv_set_bits_from_array(struct UCBitVec *v,char *array,size_t array_len); -// The _s ("secure") versions does boundary checking and assert() if they -// try to access a bit out of bounds. - -void uc_bv_set_bit_s(struct UCBitVec *v,int b); - -void uc_bv_clear_bit_s(struct UCBitVec *v,int b); - -int uc_bv_get_bit_s(const struct UCBitVec *v,int b); #ifdef __cplusplus } diff --git a/include/ucore/buffer.h b/include/ucore/buffer.h index f49f339..87b0016 100644 --- a/include/ucore/buffer.h +++ b/include/ucore/buffer.h @@ -1,5 +1,5 @@ -#ifndef UCORE_BUFFER_H_ -#define UCORE_BUFFER_H_ +#ifndef UC_BUFFER_H_ +#define uc_BUFFER_H_ #include diff --git a/include/ucore/dbuf.h b/include/ucore/dbuf.h index c3c841c..f564c3e 100644 --- a/include/ucore/dbuf.h +++ b/include/ucore/dbuf.h @@ -1,5 +1,5 @@ -#ifndef dbuf_H_ -#define dbuf_H_ +#ifndef UC_DBUF_H_ +#define UC_DBUF_H_ #include #ifdef __cplusplus diff --git a/include/ucore/fd_utils.h b/include/ucore/fd_utils.h index 9c8d178..b696e3a 100644 --- a/include/ucore/fd_utils.h +++ b/include/ucore/fd_utils.h @@ -1,5 +1,5 @@ -#ifndef FD_UTILS_H_ -#define FD_UTILS_H_ +#ifndef UC_FD_UTILS_H_ +#define UC_FD_UTILS_H_ #ifdef __cplusplus extern "C" { #endif diff --git a/include/ucore/hash.h b/include/ucore/hash.h index 6c90469..750b446 100644 --- a/include/ucore/hash.h +++ b/include/ucore/hash.h @@ -1,5 +1,5 @@ -#ifndef UCORE_HASH_H_ -#define UCORE_HASH_H_ +#ifndef UC_HASH_H_ +#define UC_HASH_H_ #include #include diff --git a/include/ucore/heapsort.h b/include/ucore/heapsort.h index 2391e84..38947c3 100644 --- a/include/ucore/heapsort.h +++ b/include/ucore/heapsort.h @@ -1,5 +1,5 @@ -#ifndef UCORE_HEAPSORT_H_ -#define UCORE_HEAPSORT_H_ +#ifndef UC_HEAPSORT_H_ +#define UC_HEAPSORT_H_ #include diff --git a/include/ucore/htable.h b/include/ucore/htable.h new file mode 100644 index 0000000..e5dc9d1 --- /dev/null +++ b/include/ucore/htable.h @@ -0,0 +1,279 @@ +#ifndef UC_HTABLE_H_ +#define UC_HTABLE_H_ + +#include + +/** Building block for hash table. + * The hash table is stored using a chained list of buckets. + * Nodes with different hash value can be stored in the same bucket. + * + * + * The hash table manages only the hash value, not the equality + * between nodes. + * + * It's up to the user to check for duplicate values + * before adding nodes if dublicates are not desired. + * + * Finding a node must be done in a user specific way, + * normally by iterating over all nodes with the desidred + * hash, and deciding in some other way whether it is the desidred + * node. + */ + +/** Node the hashtable tracks. + * Will normally be embedded inside another struct + */ +struct UHNode { + //cached value of the hash + size_t hash; + //pointer to next in same bucket + struct UHNode *next; +}; + +/** + * The hash table struct + */ +struct UHTable { + size_t cnt_elements; + size_t cnt_buckets; + struct UHNode **buckets; +}; + +/** + * Initialize a hash table. + * @param table hash table to initialize + * @param buckets number of buckets + * @return 0 on success, errno if malloc fails or buckets == 0 + */ +int uc_htable_init(struct UHTable *table, size_t buckets); + +/** + * Free resources of the hash table. + * @param table hash table to free (does not free @table itself + */ +void uc_htable_destroy(struct UHTable *table); + + +/** + * @param table + * @param hash find the bucket from this hash value + * @return the bucket number for the given hash + */ +static inline size_t uc_htable_bucket(const struct UHTable *table, size_t hash) +{ + return hash % table->cnt_buckets; +} + +/** + * Get the first bucket node for the given hash. + * The returned node might not have the same hash, since different + * nodes can share the same bucket. + * + * @param table table + * @param hash hash + */ +static inline struct UHNode *uc_htable_first_bucket(const struct UHTable *table, size_t hash) +{ + size_t bucket = uc_htable_bucket(table, hash); + + return table->buckets[bucket]; +} + +/** + * Get the next node in the bucket. + * @return next node or NULL + */ +static inline struct UHNode *uc_htable_next_bucket(const struct UHNode *node) +{ + return node->next; +} + +/** + * Get the first node that have the given hash + * @param table table + * @param hash hash + * @return first node with the given hash or NULL + */ +struct UHNode *uc_htable_first_hash(const struct UHTable *table, size_t hash); + + +//internal helper +struct UHNode *uc_htable_next_hash_hlp(struct UHNode *next, size_t hash); + +/** + * Get the next node with the same hash + * @param node previous node + * @return next node with the same hash as node or NULL + */ +static inline struct UHNode *uc_htable_next_hash(const struct UHNode *node) +{ + return uc_htable_next_hash_hlp(node->next, node->hash); +} + + +//internal helper +struct UHNode *uc_htable_iter_hlp(struct UHTable *table, size_t bucket); + +/** Get the first node in the hash table + * + * @param table table + * @return first node or NULL + */ +static inline struct UHNode *uc_htable_first(struct UHTable *table) +{ + return uc_htable_iter_hlp(table, 0); +} + +/** Get the next node in the hash table + * + * @param table table + * @param node previous node + * @return first node or NULL + */ +struct UHNode *uc_htable_next(struct UHTable *table, struct UHNode *node); + +/** + * Swap 2 hash tables. + * + * @param a first table + * @param b second table + */ +void uc_htable_swap(struct UHTable *a, struct UHTable *b); + +/** + * Iterate over all nodes in a bucket. + * The table must not be changed while iterating. + * + * @param table the struct UHTable* + * @param node_iter struct UHNode* for the current node being iterated + * @param hash size_t hash for the bucket to iterate + */ +#define UC_HTABLE_FOREACH_BUCKET(table, node_iter, hash)\ + for ((node_iter) = uc_htable_first_bucket((table), (hash));\ + (node_iter) != NULL;\ + (node_iter) = uc_htable_next_bucket((node_iter))) + +/** + * Iterate over all nodes that has the given hash. + * The table must not be changed while iterating. + * Normally the user would check in a user specific way + * if the node_iter is equal to the node one wants to find. + * + * @param table the struct UHTable* + * @param node_iter struct UHNode* for the current node being iterated + * @param hash size_t hash for the node(s) to iterate over + */ +#define UC_HTABLE_FOREACH_HASH(table, node_iter, hash)\ + for ((node_iter) = uc_htable_first_hash((table), (hash));\ + (node_iter) != NULL;\ + (node_iter) = uc_htable_next_hash((node_iter))) + +/** + * "Safe" variant of UC_HTABLE_FOREACH_HASH, the iterated node + * (or any previously iterated nodes) can be freed while iterating + * + * @param table the struct UHTable* + * @param node_iter struct UHNode* for the current node being iterated + * @param next_iter struct UHNode* for the next node, required by the + * iteration, next_iter must not be altered while iterating. + * @param hash size_t hash for the node(s) to iterate over + */ +#define UC_HTABLE_FOREACH_HASH_SAFE(table, node_iter, next_iter, hash)\ + for ((node_iter) = uc_htable_first_hash((table), (hash));\ + (node_iter) != NULL && (((next_iter) = (node_iter->next)), 1);\ + (node_iter) = (next)) + +/** + * Iterate over all nodes in a table + * The table must not be changed while iterating. + * + * @param table the struct UHTable* + * @param node_iter struct UHNode* for the current node being iterated + */ +#define UC_HTABLE_FOREACH(table, node_iter)\ + for ((node_iter) = uc_htable_first((table));\ + (node_iter) != NULL;\ + (node_iter) = uc_htable_next((table), (node_iter))) + +/** + * "Safe" variant of UC_HTABLE_FOREACH, the iterated node + * (or any previously iterated nodes) can be freed while iterating + * + * @param table the struct UHTable* + * @param node_iter struct UHNode* for the current node being iterated + * @param next_iter struct UHNode* for the next node, required by the + * iteration, next_iter must not be altered while iterating. +*/ +#define UC_HTABLE_FOREACH_SAFE(table, node_iter, next_iter)\ + for ((node_iter) = uc_htable_first((table));\ + (node_iter) != NULL &&\ + ((next_iter) = uc_htable_next((table), (node_iter)), 1);\ + (node_iter) = (next)) + + +/** + * Check if the given node pointer exists in the hash table + * + * @param table table + * @param node node to check for + * @return 0 if the node does not exist in table non-0 if it does + */ +int uc_htable_has_node(struct UHTable *table, struct UHNode *node); + + +/** + * + * @return 0 if table is empty non-0 if it contains any nodes + */ +static inline int uc_htable_isempty(const struct UHTable *table) +{ + return table->cnt_elements == 0; +} + +/** + * Get number of elements in the hash table. + * + * @rparam table table + * @return number of elements + */ +static inline size_t uc_htable_count(const struct UHTable *table) +{ + return table->cnt_elements; +} + +/** Clears all nodes from the hash table, making it empty. + * No memory is released. + * @param table table to clear + */ +void uc_htable_clear(struct UHTable *table); + +/** Insert a new node. + * The new node pointer must exist as long as it is part of the + * hash table. The hash table does not check for duplicates in any way. + * + * @param table table to insert into + * @param node new node to insert. + * @param hash hash value of the new node + */ +void uc_htable_insert(struct UHTable *table, struct UHNode *node, size_t hash); + +/** Remove a node. + * If the node pointer is not part of the table, + * no action is taken + * + * @param table table to remove from. + * @param node node to remove. + */ +void uc_htable_remove(struct UHTable *table, struct UHNode *node); + +/** Resize the hash table. This changes the number of buckets, and re-inserts + * all nodes. + * + * @param table table to resize + * @param buckets new number of buckets, must be > 0 + * @return 0 on success, errno if malloc fails or buckets == 0 + */ +int uc_htable_resize(struct UHTable *table, size_t buckets); + + +#endif diff --git a/include/ucore/iomux.h b/include/ucore/iomux.h index 78e4ccd..afc746d 100644 --- a/include/ucore/iomux.h +++ b/include/ucore/iomux.h @@ -1,5 +1,5 @@ -#ifndef IO_MUX_H_ -#define IO_MUX_H_ +#ifndef UC_IO_MUX_H_ +#define UC_IO_MUX_H_ #include "timers.h" /** @addtogroup IOMux diff --git a/include/ucore/logging.h b/include/ucore/logging.h index 9efa2d6..bbaebe0 100644 --- a/include/ucore/logging.h +++ b/include/ucore/logging.h @@ -1,5 +1,6 @@ -#ifndef UCORE_LOGGING_H_ -#define UCORE_LOGGING_H_ +#ifndef UC_LOGGING_H_ +#define UC_LOGGING_H_ +#include "utils.h" /** Logging functions. * All logging functions except uc_log_init are thread safe, @@ -268,14 +269,16 @@ int uc_log_reopen_files(void); void uc_log_init(struct uc_log_modules *user_modules); void uc_logf(int log_level, int module, int raw, - const char *file, int line, const char *fmt, ...) - __attribute__((format(printf, 6, 7))); + const char *location, const char *fmt, ...) + __attribute__((format(printf, 5, 6))); -#ifdef DEBUG +// only compile in UC_DEBUG log calls in debug mode, but allow the user +// to override that by defining UC_DEBUG_ALWAYS +#if defined(DEBUG) || defined(UC_DEBUG_ALWAYS) # define UC_DEBUGF(mod, fmt, ...)\ - uc_logf(UC_LL_DEBUG, mod,0 , __FILE__, __LINE__, fmt, ## __VA_ARGS__) + uc_logf(UC_LL_DEBUG, mod,0 , UC_SRC_LOCATION, fmt, ## __VA_ARGS__) # define UC_DEBUGFR(mod, fmt, ...)\ - uc_logf(UC_LL_DEBUG, mod,1 , __FILE__, __LINE__, fmt, ## __VA_ARGS__) + uc_logf(UC_LL_DEBUG, mod,1 , UC_SRC_LOCATION, fmt, ## __VA_ARGS__) #else # define UC_DEBUGF(mod, fmt, ...) # define UC_DEBUGFR(mod, fmt, ...) @@ -292,14 +295,14 @@ void uc_logf(int log_level, int module, int raw, * @param ... printf style arguments */ #define UC_LOGF(lvl, mod, fmt, ...) \ - uc_logf(lvl, mod, 0, __FILE__, __LINE__, fmt, ## __VA_ARGS__) + uc_logf(lvl, mod, 0, UC_SRC_LOCATION, fmt, ## __VA_ARGS__) /** Raw logging ,Like UC_LOGF, but only prints the supplied * data, not timestamp, log level etc. */ #define UC_LOGFR(lvl, mod, fmt, ...) \ - uc_logf(lvl, mod, 1, __FILE__, __LINE__, fmt, ## __VA_ARGS__) + uc_logf(lvl, mod, 1, UC_SRC_LOCATION, fmt, ## __VA_ARGS__) #ifdef __cplusplus } diff --git a/include/ucore/math.h b/include/ucore/math.h index 8af110a..4d9d6fc 100644 --- a/include/ucore/math.h +++ b/include/ucore/math.h @@ -1,5 +1,5 @@ -#ifndef UCORE_MATH_H_ -#define UCORE_MATH_H_ +#ifndef UC_MATH_H_ +#define UC_MATH_H_ #include diff --git a/include/ucore/mbuf.h b/include/ucore/mbuf.h index 256354a..1b92b81 100644 --- a/include/ucore/mbuf.h +++ b/include/ucore/mbuf.h @@ -30,6 +30,7 @@ struct MBuf { /**Arbitary pointer user can associate with the mbuf*/ void *cookie; + /*UC_MBUF_FLAGS. The upper 16 bits can be used by the application */ uint32_t flags; /** Total no of bytes from bufstart*/ uint32_t allocated; diff --git a/include/ucore/mersenne_twister.h b/include/ucore/mersenne_twister.h index f3fb166..b2604e1 100644 --- a/include/ucore/mersenne_twister.h +++ b/include/ucore/mersenne_twister.h @@ -1,5 +1,5 @@ -#ifndef UCORE_MERSENNE_TWISTER_H -#define UCORE_MERSENNE_TWISTER_H +#ifndef UC_MERSENNE_TWISTER_H +#define UC_MERSENNE_TWISTER_H #ifdef __cplusplus extern "C" { diff --git a/include/ucore/pack.h b/include/ucore/pack.h index ed36852..9c9b742 100644 --- a/include/ucore/pack.h +++ b/include/ucore/pack.h @@ -1,5 +1,5 @@ -#ifndef UCORE_PACK_H_ -#define UCORE_PACK_H_ +#ifndef UC_PACK_H_ +#define UC_PACK_H_ #include #ifdef __GNUC__ diff --git a/include/ucore/rate_limit.h b/include/ucore/rate_limit.h new file mode 100644 index 0000000..fedfd55 --- /dev/null +++ b/include/ucore/rate_limit.h @@ -0,0 +1,106 @@ +#ifndef UC_RATE_LIMIT_H_ +#define UC_RATE_LIMIT_H_ + +/** This is a rate limiter, based on the token bucket principle. + * We have X amount of tickets(=our rate) per Y amount of time, + * + * The allowed tickets increases with X per Y amount of time. + * + * Bursts will be allowed up till the number of tickets initially + * specified. + * e.g. if we are limited to 50 tickets * per 2 second, 50 tickets + * can be immedietely handed out in the + * first second. At the 2. second, 25 more tickets become available + * handed out, at the 3. second 25 more, and so on. + * (So this is throttleed, so no more than the initial tickets, 50 in + * this case, can be handed out in any given period) + * + * Another example is if we rate 1000 tickets per 60 seconds, + * 1000 tickets can be handed out the 1. second, 16/17 more the + * next second, 16/17 more the 3. second and so on. + * + * Specify how many tickes we have available per period of time. + * when work is performed, call uc_ratelimit_cllow() with the current + * time. + * The unit of time passed to uc_ratelimit_allow must be the same as + * for uc_ratelimit_init. + * + * Example for rate limiting accepting connections: + * struct RateLimit r; + * //Limit to 20 per 1 second, which also limits us to max 20 + * per second. (1 second since we will pass in time that have 1 second resolution) + * 40 per 2 second would even out to the same rate, but allow a burst of 40 + * (if there's 40 tickets available) + * + * uc_ratelimit_init(&r,20, 1, time(NULL)); + * + * NOTE. (period + 1) * tickets must not exceed the range of the RateLimit.funds + * + * for (;;) { + * int fd = accept(..); + * if (uc_ratelimit_allow(&r, time(NULL)) { + * //handle new connection + * } else { + * //exceeded limit + * close(fd); + * } + * } + */ +struct RateLimit { + //how many tickets we want + long tickets; + //per this period of time + long period; + + //internal fields: + + //money we have to "buy" tickets + long funds; + //time of the previos period + long last_ts; +}; + +/** Static initializer for a struct RateLimit + * + * @param tickets number of tickets (bucket depth) + * @param period per this period + */ +#define UC_RATELIMIT_INITIALIZER(tickets)\ +{ tickets, period, 0, -period} + + + +/** + * Initialize a struct RateLimit, which can hand out @tickets per @period + * the perioid must be in the same units as the current_ts + * + * NOTE. 2 * (period + 1) * tickets must not exceed the range of the RateLimit.funds + * + * @param r RateLimit to initialize + * @param tickets number of tickets (bucket depth) + * @param period per this period + */ +void uc_ratelimit_init(struct RateLimit *r, long tickets, long period); + +/** + * Reset a RateLimit, filling up the tickets again. + * + * @param r RateLimit to reset + * @param current ts, if non-zero, re-sets the last_ts to the current_ts + */ +void uc_ratelimit_reset(struct RateLimit *r, long current_ts); + +/** + * Check if we have 1 ticket available, and can therefore allow + * the work. Removes 1 ticket if we can, and replenishes the tickets + * based on the elapsed time since last time the function was called. + * + * @param r the RateLimit + * @param current_ts the current timestamp. Used to calculate the + * replenisment of tichets since the previous check. + * @return 1 if we can allow, 0 if we don't. + */ +int uc_ratelimit_allow(struct RateLimit *r, long current_ts); + +#endif + diff --git a/include/ucore/rbtree.h b/include/ucore/rbtree.h index edc271d..7b6e16e 100644 --- a/include/ucore/rbtree.h +++ b/include/ucore/rbtree.h @@ -1,5 +1,5 @@ -#ifndef RBTREE_H_ -#define RBTREE_H_ +#ifndef UC_RBTREE_H_ +#define UC_RBTREE_H_ #ifdef __cplusplus extern "C" { diff --git a/include/ucore/read_file.h b/include/ucore/read_file.h index 173adc8..9e1ea10 100644 --- a/include/ucore/read_file.h +++ b/include/ucore/read_file.h @@ -1,5 +1,5 @@ -#ifndef UCORE_READ_FILE_H_ -#define UCORE_READ_FILE_H_y +#ifndef UC_READ_FILE_H_ +#define UC_READ_FILE_H_y #include diff --git a/include/ucore/salloc.h b/include/ucore/salloc.h index 4632316..d9f5d44 100644 --- a/include/ucore/salloc.h +++ b/include/ucore/salloc.h @@ -1,5 +1,5 @@ -#ifndef SALLOC_H_ -#define SALLOC_H_ +#ifndef UC_SALLOC_H_ +#define UC_SALLOC_H_ #include diff --git a/include/ucore/saturating_math.h b/include/ucore/saturating_math.h index e10539d..4ef5ff0 100644 --- a/include/ucore/saturating_math.h +++ b/include/ucore/saturating_math.h @@ -1,3 +1,5 @@ +#ifndef UC_SATMATH_H_ +#define UC_SATMATH_H_ #include /** Implementation of saturated operation on uintxx_t. @@ -79,3 +81,4 @@ static inline uint64_t uc_sat_multu64(uint64_t a, uint64_t b) return UC_SAT_MULT(a, b, uint64_t, UINT64_MAX); } +#endif diff --git a/include/ucore/seq.h b/include/ucore/seq.h index 72f33fb..7d5867b 100644 --- a/include/ucore/seq.h +++ b/include/ucore/seq.h @@ -1,5 +1,5 @@ -#ifndef SEQ_H_ -#define SEQ_H_ +#ifndef UC_SEQ_H_ +#define UC_SEQ_H_ #include // Check if a is less than b, taking care of wrap arounds diff --git a/include/ucore/string.h b/include/ucore/string.h index 9b09103..fa586bb 100644 --- a/include/ucore/string.h +++ b/include/ucore/string.h @@ -1,5 +1,5 @@ -#ifndef UCORE_STRING_H_ -#define UCORE_STRING_H_ +#ifndef UC_STRING_H_ +#define UC_STRING_H_ #include #include @@ -17,16 +17,48 @@ uc_hex_decode(const char *in, size_t maxlen,uint8_t *out); char* uc_hex_encode_delim(const uint8_t *in, size_t inlen, char *out, int outlen, char *delim); +/** + * Convert the string to lowercase, calls tolower() on each char. + * @param s string to lowercase + */ void uc_str_tolower(char *s); +/** + * Convert the string to uppercase, calls toupper() on each char. + * @param s string to uppercase + */ void uc_str_toupper(char *s); +/** Macro to calculate the number of bytes required to BCD encode + * the number of ascii digits + */ #define UC_BCD_LEN(ascii_len) ((ascii_len) / 2 + ((ascii_len) % 2)) -#define UC_ASCII_LEN(bcd_len) ((bcd_len) * 2 + 1) +/** Macro to calculate the number of bytes required to ASCII encode + * the number of BCD digits + */ +#define UC_ASCII_LEN(bcd_len) ((bcd_len) * 2) + +/** Convert the ascii digits to BCD. Digits not in 0..9 are skipped. + * + * @param ascii string to BCD encode. + * @param bcdout resulting BCD data. Must be at least UC_BCD_LEN(strlen(asciii)) big + * @param filler filler byte(not the ascii digit) to write in the last BCD nibble + * if the number of ASCII digits is odd. This would usually be 0x0 or 0xf. + * @return the number of bytes written to bcdout + */ size_t uc_ascii2bcd(const char *ascii, unsigned char *bcdout, unsigned char filler); + +/** Convert the BCD digits to ascii. + * The resulting ascii string is 0 terminated. + * + * @param bcd Buffer with BCD data to convert to ascii + * @param bcdlen number of bytes in @bcd to convert + * @param asciiout buffer to place ASCII in, must be UC_ASCII_LEN(bcdlen)+1 big + * @return the number of characters written to bcdout (this will always be UC_ASCII_LEN(bcdlen) + */ size_t uc_bcd2ascii(const unsigned char *bcd, size_t bcdlen, char *asciiout); @@ -39,10 +71,13 @@ uc_sprintbll(char *result, unsigned long long value); char* uc_sprintbs(char *result, unsigned short value); +/** Like http://swtch.com/plan9port/man/man3/getfields.html + */ int getfields(char *str, char **args, int max, int mflag, const char *set); -//ocnvert bytes to a human representable test, (i.e. 12kB, 160 MB 1.78 GB etc. +//Converts bytes to a human representable test, (i.e. 12kB, 160 MB 1.78 GB etc.) +//Conversion is done in decimal (base 10 ,1 kB == 1000 bytes) ///result should be at least of size 12, ///returns the result argument char * diff --git a/include/ucore/strvec.h b/include/ucore/strvec.h index 774ccfc..46c8f5b 100644 --- a/include/ucore/strvec.h +++ b/include/ucore/strvec.h @@ -1,8 +1,9 @@ -#include - #ifndef UC_STRVEC_H_ #define UC_STRVEC_H_ +#include + + #ifdef __cplusplus extern "C" { #endif diff --git a/include/ucore/supervisor.h b/include/ucore/supervisor.h index b33aa2b..d3e4d13 100644 --- a/include/ucore/supervisor.h +++ b/include/ucore/supervisor.h @@ -1,5 +1,5 @@ -#ifndef UCORE_SUPERVISOR_H_ -#define UCORE_SUPERVISOR_H_ +#ifndef UC_SUPERVISOR_H_ +#define UC_SUPERVISOR_H_ /** Supervises this process - restart if it dies. * diff --git a/include/ucore/threadqueue.h b/include/ucore/threadqueue.h index 383621d..c6f546e 100644 --- a/include/ucore/threadqueue.h +++ b/include/ucore/threadqueue.h @@ -1,5 +1,5 @@ -#ifndef _THREADQUEUE_H_ -#define _THREADQUEUE_H_ 1 +#ifndef UC_THREADQUEUE_H_ +#define UC_THREADQUEUE_H_ #include @@ -44,7 +44,7 @@ struct uc_threadmsg{ /** * Callback function for the walk and destroy functions. */ -typedef void (*uc_thread_queue_walk_func) (struct uc_threadmsg *msg); +typedef void (*uc_thread_queue_walk_func) (struct uc_threadmsg *msg, void *cookie); /** @@ -240,23 +240,40 @@ long uc_thread_queue_length( struct uc_threadqueue *queue ); * @param queue Pointer to the queue that should be cleaned * @param free_func pointer to function that will be called for each item. * The function must not in anyway interact with the queue. + * @param cookie pointer passed back to the uc_thread_queue_walk_func * @return 0 on success EINVAL if queue is NULL EBUSY if someone is holding any locks on the queue */ -int uc_thread_queue_destroy(struct uc_threadqueue *queue, uc_thread_queue_walk_func free_func); +int uc_thread_queue_destroy(struct uc_threadqueue *queue, uc_thread_queue_walk_func free_func, void *cookie); /** * Walk the elements of the queue, intended for debugging * * The supplied function will be called for each item in the queue, internal queue * locks are held while the function is called, so the supplied function must not - * interact with the queue, else deadlock occors. + * interact with the queue, else deadlock occurs. * * @param queue Pointer to the queue to walk * @param free_func pointer to function that will be called for each item. + * @param cookie pointer passed back to the uc_thread_queue_walk_func * The function must not in anyway interact with the queue. * @return 0 on success */ -int uc_thread_queue_walk(struct uc_threadqueue *queue, uc_thread_queue_walk_func walk_func); +int uc_thread_queue_walk(struct uc_threadqueue *queue, uc_thread_queue_walk_func walk_func, void *cookie); + + +/** + * Clear the queue. + * The free_func unless NULL, will be called for each element + * locks are held while the function is called, so the supplied function must not + * interact with the queue, else deadlock occurs. + * + * @param queue Pointer to the queue to clear + * @param free_func pointer to function that will be called for each item, intended to e.g. release memory + * @param cookie pointer passed back to the uc_thread_queue_walk_func + * @return 0 on success +*/ +int uc_thread_queue_clear(struct uc_threadqueue *queue, uc_thread_queue_walk_func free_func, void *cookie); + /** * @}*/ diff --git a/include/ucore/timers.h b/include/ucore/timers.h index 6aa34b5..c202cb0 100644 --- a/include/ucore/timers.h +++ b/include/ucore/timers.h @@ -1,5 +1,5 @@ -#ifndef UCORE_TIMERS_H_ -#define UCORE_TIMERS_H_ +#ifndef UC_TIMERS_H_ +#define UC_TIMERS_H_ #include #include diff --git a/include/ucore/utils.h b/include/ucore/utils.h index 26d706e..cc23bef 100644 --- a/include/ucore/utils.h +++ b/include/ucore/utils.h @@ -1,23 +1,16 @@ -#ifndef UCORE_UTILS_H_ -#define UCORE_UTILS_H_ +#ifndef UC_UTILS_H_ +#define UC_UTILS_H_ + #include +#include +#include +#include "backtrace.h" //Gnerate a compiler error if the compile time //constant expression fails #define UC_STATIC_ASSERT(expr) \ - do { \ - enum { assert_static__ = 1/(expr) }; \ - } while (0) + enum { assert_static__ = 1/(expr) }; -#ifdef DEBUG - #define TRACEF(fmt, ...)\ - do { \ - fprintf(stdout, "%s:%d (%s)\t" fmt, __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__);\ - fflush(stdout);\ - } while(0) -#else - #define TRACEF(fmt, ...) -#endif //MAX of a and b #define UC_MAX(a,b) \ @@ -78,22 +71,42 @@ const typeof( ((const type *)0)->member ) *__mptr = (ptr); \ #define UC_STRINGIFY(x) UC_STRINGIFY_HLP(x) #define UC_STRINGIFY_HLP(x) #x +/** Macro that expands to a string for the current file:line + */ +#define UC_SRC_LOCATION __FILE__ ":" UC_STRINGIFY(__LINE__) /** * assert() that is not affected by NDEBUG define */ -#define UC_ASSERT(expr) do {\ -if (!(expr) ) {\ - fprintf(stderr,\ - "UC_ASSERT failed '%s' %s:%d\n", UC_STRINGIFY(expr), __FILE__, __LINE__);\ - uc_backtrace_fd(3);\ - abort();\ +#define UC_ASSERT(expr)\ + do {\ + if (!(expr) ) {\ + fprintf(stderr,\ + "UC_ASSERT failed '%s' %s\n", UC_STRINGIFY(expr), UC_SRC_LOCATION);\ + uc_backtrace_fd(2);\ + abort();\ + }\ } while (0) +#ifdef DEBUG + #define TRACEF(fmt, ...)\ + do { \ + fprintf(stdout, "%s (%s)\t" fmt, UC_SRC_LOCATION, __FUNCTION__, ##__VA_ARGS__);\ + fflush(stdout);\ + } while(0) +#else + #define TRACEF(fmt, ...) +#endif + /** * Macro for asserting code that should not be reached, * terminates the program if executed */ -#define UC_NOT_REACED() abort() +#define UC_NOT_REACED()\ +do {\ + fprintf(stderr, "UC_NOT_REACED at %s\n", UC_SRC_LOCATION);\ + uc_backtrace_fd(2);\ + abort();\ +while (0) /** * Round up X / y diff --git a/include/ucore/version.h b/include/ucore/version.h index 9f9d0b2..23803a1 100644 --- a/include/ucore/version.h +++ b/include/ucore/version.h @@ -1,5 +1,5 @@ -#ifndef UCORE_VERSION_H_ -#define UCORE_VERSION_H_ +#ifndef UC_VERSION_H_ +#define UC_VERSION_H_ /** * @mainpage * @htmlonly diff --git a/src/bitvec.c b/src/bitvec.c index fff5d31..abab68b 100644 --- a/src/bitvec.c +++ b/src/bitvec.c @@ -48,7 +48,6 @@ void uc_bv_free(struct UCBitVec *v) } } - void uc_bv_clr_bit(struct UCBitVec *v,int b) { v->vec[bit_index(b)] &= ~(1UL << (bit_offset(b))); @@ -64,40 +63,6 @@ void uc_bv_set_bit(struct UCBitVec *v,int b) v->vec[bit_index(b)] |= 1UL << (bit_offset(b)); } -void uc_bv_set_bit_s(struct UCBitVec *v,int b) -{ - size_t idx = (size_t)bit_index(b); - assert(b >= 0); - assert(v->vec_len > (size_t)idx); - - if (likely(idx < v->vec_len)) - v->vec[idx] |= 1UL << (bit_offset(b)); -} - -void uc_bv_clear_bit_s(struct UCBitVec *v,int b) -{ - size_t idx = (size_t)bit_index(b); - - assert(b >= 0); - assert(v->vec_len > (size_t)idx); - - if (likely(idx < v->vec_len)) - v->vec[idx] &= ~(1UL << (bit_offset(b))); -} - -int uc_bv_get_bit_s(const struct UCBitVec *v,int b) -{ - size_t idx = (size_t)bit_index(b); - - assert(b >= 0); - assert(v->vec_len > (size_t)idx); - - if (likely(idx < v->vec_len)) - return !!(v->vec[idx] & (1UL << bit_offset(b))); - else - return 0; -} - void uc_bv_set_bits_from_array(struct UCBitVec *v,char *array,size_t array_len) { size_t i; diff --git a/src/htable.c b/src/htable.c new file mode 100644 index 0000000..02fc876 --- /dev/null +++ b/src/htable.c @@ -0,0 +1,173 @@ +#include +#include +#include +#include "ucore/htable.h" + +int uc_htable_init(struct UHTable *table, size_t buckets) +{ + if (buckets == 0) { + return EINVAL; + } + + table->buckets = calloc(buckets, sizeof *table->buckets); + if (table->buckets == NULL) { + return ENOMEM; + } + + table->cnt_buckets = buckets; + table->cnt_elements = 0; + + return 0; +} + +void uc_htable_destroy(struct UHTable *table) +{ + free(table->buckets); + table->buckets = NULL; + table->cnt_elements = table->cnt_buckets = -1; +} + +struct UHNode *uc_htable_first_hash(const struct UHTable *table, size_t hash) +{ + size_t bucket = uc_htable_bucket(table, hash); + struct UHNode *it = table->buckets[bucket]; + + while (it && it->hash != hash) { + it = it->next; + } + + return it; +} + +//given a node, find the next one in the chain with the given hash +struct UHNode *uc_htable_next_hash_hlp(struct UHNode *next, size_t hash) +{ + while (next && next->hash != hash) { + next = next->next; + } + + return next; +} + +//find a node, starting at bucket +struct UHNode *uc_htable_iter_hlp(struct UHTable *table, size_t bucket) +{ + size_t i; + struct UHNode *found = NULL; + + //search through buckets until a node is found + for (i = bucket; i < table->cnt_buckets; i++) { + found = uc_htable_first_bucket(table, i); + if (found != NULL) { + break; + } + } + return found; +} + +struct UHNode *uc_htable_next(struct UHTable *table, struct UHNode *node) +{ + struct UHNode *found; + + found = uc_htable_next_bucket(node); + if (found == NULL) { + size_t current_bucket = uc_htable_bucket(table, node->hash); + found = uc_htable_iter_hlp(table, current_bucket + 1); + } + + return found; +} + +void uc_htable_swap(struct UHTable *a, struct UHTable *b) +{ + struct UHTable tmp; + + tmp = *a; + *a = *b; + *b = tmp; +} + +int uc_htable_has_node(struct UHTable *table, struct UHNode *node) +{ + struct UHNode *iter; + + UC_HTABLE_FOREACH_BUCKET(table, iter, node->hash) { + if (node == iter) { + return 1; + } + } + + return 0; +} + +void uc_htable_clear(struct UHTable *table) +{ + memset(table->buckets, 0 , sizeof *table->buckets * table->cnt_buckets); + table->cnt_elements = 0; +} + +void uc_htable_insert(struct UHTable *table, struct UHNode *node, size_t hash) +{ + size_t bucket = uc_htable_bucket(table, hash); + + node->next = table->buckets[bucket]; + table->buckets[bucket] = node; + node->hash = hash; + + table->cnt_elements++; +} + +void uc_htable_remove(struct UHTable *table, struct UHNode *node) +{ + struct UHNode **it = &table->buckets[uc_htable_bucket(table, node->hash)]; + + while (*it) { + + if (*it == node) { + *it = (*it)->next; + table->cnt_elements--; + break; + } + + it = &(*it)->next; + } +} + +int uc_htable_resize(struct UHTable *table, size_t buckets) +{ + struct UHTable tmp_table; + int rc; + size_t h; + + if (buckets == 0) { + return EINVAL; + } + + //create new temp table + rc = uc_htable_init(&tmp_table, buckets); + if (rc != 0) { + return rc; + } + + + //move all the nodes to the new temp table + for (h = 0; h < table->cnt_buckets; h++) { + struct UHNode *next; + struct UHNode *node; + + for (node = uc_htable_first_bucket(table, h); + node != NULL; + node = next) { + next = node->next; + uc_htable_insert(&tmp_table, node, node->hash); + } + } + + //replace the existing table with the resized temp table + uc_htable_swap(&tmp_table, table); + uc_htable_destroy(&tmp_table); + + return 0; +} + + diff --git a/src/logging.c b/src/logging.c index 85146e8..248e548 100644 --- a/src/logging.c +++ b/src/logging.c @@ -42,8 +42,7 @@ struct uc_log_args { struct uc_log_destination *dest; const struct uc_log_module *module; int log_level; - const char *file; - int line; + const char *location; int raw; }; @@ -415,9 +414,9 @@ static inline void uc_vlog_file(const struct uc_log_args *args, const char *fmt, time_buf[sizeof time_buf -1] = 0; if (dest->log_location) { - fprintf(dest->type.file.file, "[%-7s] %s %s:%d [%s] : ", - uc_ll_2_str(args->log_level), time_buf, args->file, - args->line, args->module->short_name); + fprintf(dest->type.file.file, "[%-7s] %s %s [%s] : ", + uc_ll_2_str(args->log_level), time_buf, + args->location, args->module->short_name); } else { fprintf(dest->type.file.file, "[%-7s] %s [%s]: ", uc_ll_2_str(args->log_level), time_buf, args->module->short_name); @@ -446,7 +445,7 @@ static inline void uc_vlog_syslog(const struct uc_log_args *args, const char *fm buf[0] = 0; if (dest->log_location) { - rc = snprintf(buf, sizeof buf, "%s:%d ", args->file, args->line); + rc = snprintf(buf, sizeof buf, "%s ", args->location); if (rc < 0) goto log; offset += rc; @@ -543,7 +542,7 @@ void uc_log_destination_set_mask( } void uc_logf(int log_level, int module, int raw, - const char *file, int line, const char *fmt, ...) + const char *location, const char *fmt, ...) { va_list ap; struct uc_log_destination *dest; @@ -581,8 +580,7 @@ void uc_logf(int log_level, int module, int raw, .dest = dest, .module = log_module, .log_level = log_level, - .file = file, - .line = line, + .location = location, .raw = raw }; diff --git a/src/rate_limit.c b/src/rate_limit.c new file mode 100644 index 0000000..14299fe --- /dev/null +++ b/src/rate_limit.c @@ -0,0 +1,73 @@ +#include +#include +#include "ucore/rate_limit.h" + +void uc_ratelimit_init(struct RateLimit *r, long tickets, long period) +{ + r->tickets = tickets; + + r->period = period; + r->funds = 0; //we fill it on first _allow call + r->last_ts = -period; //-period ensures the first tick can start + //at >= 0 and still refill the bicket + + assert(tickets > 0); + assert(r->period > 0); +} + +void uc_ratelimit_reset(struct RateLimit *r, long current_ts) +{ + r->funds = r->period * r->tickets; + + if (current_ts) { + r->last_ts = current_ts; + } +} + +int uc_ratelimit_allow(struct RateLimit *r, long current_ts) +{ + int allowed; + long diff_period; + + if (r->funds >= r->period) { + //we have enough + r->funds -= r->period; + allowed = 1; + } else { //refill + //Find elapsed time + diff_period = current_ts - r->last_ts; + + if (diff_period > r->period + 1) { + //help prevent overflow when calculating available_tickets below + diff_period = r->period + 1; + } else if (diff_period < 0) { + //time went backwards, skip this round + diff_period = 0; + } + + //Calculate the cost of tickets that became available + r->funds += diff_period * r->tickets; + + + //throttle handing out tickets + if (r->funds > r->period * r->tickets) { + r->funds = r->period * r->tickets; + } + + //If we have enough to buy atleast one ticket, we can allow + if (r->funds >= r->period) { + r->funds -= r->period; + allowed = 1; + } else { + //not enough to buy a ticket + allowed = 0; + } + + //save this period. + r->last_ts = current_ts; + } + + + return allowed; +} + diff --git a/src/threadqueue.c b/src/threadqueue.c index 47308df..c5da95f 100644 --- a/src/threadqueue.c +++ b/src/threadqueue.c @@ -199,19 +199,21 @@ int uc_thread_queue_tryget(struct uc_threadqueue *queue, } static void uc_thread_queue_walk_unlocked(struct uc_threadqueue *queue, - uc_thread_queue_walk_func walk_func) + uc_thread_queue_walk_func walk_func, + void *cookie) { struct uc_threadmsg *p; struct uc_threadmsg *next; for(p = queue->first; p; p = next) { next = p->next; - walk_func(p); + walk_func(p, cookie); } } int uc_thread_queue_walk(struct uc_threadqueue *queue, - uc_thread_queue_walk_func walk_func) + uc_thread_queue_walk_func walk_func, + void *cookie) { if (queue == NULL || !walk_func) { return EINVAL; @@ -219,7 +221,7 @@ int uc_thread_queue_walk(struct uc_threadqueue *queue, pthread_mutex_lock(&queue->mutex); - uc_thread_queue_walk_unlocked(queue, walk_func); + uc_thread_queue_walk_unlocked(queue, walk_func, cookie); pthread_mutex_unlock(&queue->mutex); @@ -227,7 +229,8 @@ int uc_thread_queue_walk(struct uc_threadqueue *queue, } int uc_thread_queue_destroy(struct uc_threadqueue *queue, - uc_thread_queue_walk_func free_func) + uc_thread_queue_walk_func free_func, + void *cookie) { int rc; @@ -250,7 +253,7 @@ int uc_thread_queue_destroy(struct uc_threadqueue *queue, } if (free_func) { - uc_thread_queue_walk_unlocked(queue, free_func); + uc_thread_queue_walk_unlocked(queue, free_func, cookie); } pthread_mutex_unlock(&queue->mutex); @@ -276,3 +279,40 @@ long uc_thread_queue_length(struct uc_threadqueue *queue) return length; } + +int uc_thread_queue_clear(struct uc_threadqueue *queue, + uc_thread_queue_walk_func free_func, + void *cookie) +{ + if (queue == NULL) { + return -EINVAL; + } + + pthread_mutex_lock(&queue->mutex); + + //Let the caller free the data + if (free_func != NULL) { + uc_thread_queue_walk_unlocked(queue, free_func, cookie); + } + + //reset the elements + queue->first = NULL; + queue->num_elements = 0; + queue->last = &queue->first; + + //notify writes, there should be space now + if (queue->num_write_waiters == 1) { + //if there's just one thread waiting to push elements + //use _cond_signal. _cond_broadcast can be more expensive (os dependent) + pthread_cond_signal(&queue->write_cond); + } else if (queue->num_write_waiters > 1) { + //signall all threads that there's items available. + pthread_cond_broadcast(&queue->write_cond); + } + + pthread_mutex_unlock(&queue->mutex); + + return 0; +} + + diff --git a/src/timers.c b/src/timers.c index 872a21b..8cb57b3 100644 --- a/src/timers.c +++ b/src/timers.c @@ -76,6 +76,8 @@ void uc_timers_remove(struct UCTimers *timers, struct UCTimer *timer) timer->ready_entry.tqe_prev = (struct UCTimer **)105; return; } + assert(timer->state == UC_TIMER_ACTIVE || + timer->state == UC_TIMER_PENDING); uc_rb_remove(&timers->timer_tree, &timer->rb_node); //remove it, safe for the //callback to re_add it diff --git a/test/rate_limit.c b/test/rate_limit.c new file mode 100644 index 0000000..d3d8bea --- /dev/null +++ b/test/rate_limit.c @@ -0,0 +1,26 @@ +#include +#include +#include +#include "ucore/rate_limit.h" + + +int main() +{ + int i; + struct RateLimit r; + time_t start = time(NULL); + + uc_ratelimit_init(&r, 30, 2); + + for(i = 0; i < 120; i++) { + int k; + long allowed = 0; + sleep(1); + time_t now = time(NULL); + for (k = 0; k < 5000; k++) + allowed += uc_ratelimit_allow(&r, now); + printf("%3ld of 500 can pass i=%3d, time = %3ld\n", allowed,i , (long)(now - start)); + } + + return 0; +} diff --git a/test/test_bcd.c b/test/test_bcd.c index 82b7598..96dc6fb 100644 --- a/test/test_bcd.c +++ b/test/test_bcd.c @@ -40,6 +40,7 @@ START_TEST (test_2bcd_2_filler) fail_if(len != 2, "len is %zu", len); fail_if(bcd[0] != 0x21,"was 0x%x",bcd[0]); fail_if(bcd[1] != 0xf3,"was 0x%x",bcd[1]); + fail_if(UC_BCD_LEN(strlen(ascii)) != len); END_TEST START_TEST (test_2bcd_3_filler) @@ -98,6 +99,7 @@ START_TEST (test_2hex_1) fail_if(ascii[2] != '3',"2 was %c",ascii[2]); fail_if(ascii[3] != '4',"3 was %c",ascii[3]); fail_if(ascii[4] != 0, "4 was %c",ascii[4]); + fail_if(UC_ASCII_LEN(sizeof bcd) != len); END_TEST START_TEST (test_2hex_2) diff --git a/test/test_htable.c b/test/test_htable.c new file mode 100644 index 0000000..61a6889 --- /dev/null +++ b/test/test_htable.c @@ -0,0 +1,379 @@ +#include +#include +#include +#include + +struct MyString { + char str[32]; + struct UHNode node; +}; + +size_t hash_str(const char *str) +{ + size_t hash = 31; + while (*str) { + hash *= *str++; + } + + return hash; +} + +size_t bad_hash(const char *str) +{ + return str[0]; +} + +struct MyString *my_find(struct UHTable *table, const char *str, size_t hash) +{ + struct UHNode *it; + + UC_HTABLE_FOREACH_HASH(table, it, hash) { + struct MyString *my_str = UC_CONTAINER_OF(it, struct MyString, node); + if (strcmp(my_str->str, str) == 0) { + return my_str; + } + } + + return NULL; +} + + + +START_TEST (test_htable1) + struct MyString str[5] = { + {"One", {0,NULL}}, + {"Two", {0,NULL}}, + {"Three",{0,NULL}}, + {"Four", {0,NULL}}, + {"Five", {0,NULL}} + }; + struct UHTable table; + int i; + + uc_htable_init(&table, 3); + + for (i = 0; i < 5; i++) { + uc_htable_insert(&table, &str[i].node, hash_str(str[i].str)); + } + + ck_assert_int_eq(uc_htable_count(&table), 5); + + fail_if(my_find(&table, "One", hash_str("One")) != &str[0]); + fail_if(my_find(&table, "Two", hash_str("Two")) != &str[1]); + fail_if(my_find(&table, "Three",hash_str("Three")) != &str[2]); + fail_if(my_find(&table, "Four", hash_str("Four")) != &str[3]); + fail_if(my_find(&table, "Five", hash_str("Five")) != &str[4]); + fail_if(my_find(&table, "NotHere", hash_str("NotHere")) != NULL); + + uc_htable_destroy(&table); + +END_TEST + +START_TEST (test_htable_hash_collision) + //same as test1 but with a bad hash to force equal hash + struct MyString str[5] = { + {"...", {0,NULL}}, + {"", {0,NULL}}, + {".", {0,NULL}}, + {"..", {0,NULL}}, + {"....", {0,NULL}} + }; + struct UHTable table; + int i; + + uc_htable_init(&table, 1); + + for (i = 0; i < 5; i++) { + uc_htable_insert(&table, &str[i].node, bad_hash(str[i].str)); + } + + ck_assert_int_eq(uc_htable_count(&table), 5); + + fail_if(my_find(&table, "....", bad_hash("....")) != &str[4]); + fail_if(my_find(&table, ".", bad_hash(".")) != &str[2]); + fail_if(my_find(&table, "", bad_hash("")) != &str[1]); + fail_if(my_find(&table, "..", bad_hash("..")) != &str[3]); + fail_if(my_find(&table, "...", bad_hash("...")) != &str[0]); + + uc_htable_destroy(&table); + +END_TEST + +START_TEST (test_htable_resize) + struct MyString str[5] = { + {"One", {0,NULL}}, + {"Two", {0,NULL}}, + {"Three",{0,NULL}}, + {"Four", {0,NULL}}, + {"Five", {0,NULL}} + }; + struct UHTable table; + int i; + int rc; + + rc = uc_htable_init(&table, 3); + ck_assert_int_eq(rc, 0); + + for (i = 0; i < 5; i++) { + uc_htable_insert(&table, &str[i].node, hash_str(str[i].str)); + } + ck_assert_int_eq(uc_htable_count(&table), 5); + + rc = uc_htable_resize(&table, 1024); + ck_assert_int_eq(rc, 0); + + ck_assert_int_eq(uc_htable_count(&table), 5); + + fail_if(my_find(&table, "One", hash_str("One")) != &str[0]); + fail_if(my_find(&table, "Two", hash_str("Two")) != &str[1]); + fail_if(my_find(&table, "Three",hash_str("Three")) != &str[2]); + fail_if(my_find(&table, "Four", hash_str("Four")) != &str[3]); + fail_if(my_find(&table, "Five", hash_str("Five")) != &str[4]); + + uc_htable_destroy(&table); + +END_TEST + +START_TEST (test_htable_fail_init_resize) + int rc; + struct UHTable table; + rc = uc_htable_init(&table, 0); + ck_assert_int_ne(rc, 0); + + rc = uc_htable_init(&table, 1); + ck_assert_int_eq(rc, 0); + + rc = uc_htable_resize(&table, 0); + ck_assert_int_ne(rc, 0); + + uc_htable_destroy(&table); + +END_TEST + +START_TEST (test_htable_has_node) + struct MyString str1 = {"One", {0,NULL}}; + struct MyString str2 = {"Two", {0,NULL}}; + + struct UHTable table; + int rc; + + rc = uc_htable_init(&table, 3); + ck_assert_int_eq(rc, 0); + + uc_htable_insert(&table, &str1.node, hash_str(str1.str)); + ck_assert_int_eq(uc_htable_count(&table), 1); + + rc = uc_htable_has_node(&table, &str1.node); + ck_assert_int_ne(rc, 0); + + rc = uc_htable_has_node(&table, &str2.node); + ck_assert_int_eq(rc, 0); + + uc_htable_destroy(&table); + +END_TEST + +START_TEST (test_htable_remove) + struct MyString str1 = {"One", {0,NULL}}; + struct MyString str2 = {"One", {0,NULL}}; + struct MyString str3 = {"Other", {0,NULL}}; + + struct UHTable table; + int rc; + + rc = uc_htable_init(&table, 1); + ck_assert_int_eq(rc, 0); + + uc_htable_insert(&table, &str1.node, hash_str(str1.str)); + uc_htable_insert(&table, &str2.node, hash_str(str2.str)); + uc_htable_insert(&table, &str3.node, hash_str(str3.str)); + ck_assert_int_eq(uc_htable_count(&table), 3); + + uc_htable_remove(&table, &str2.node); + ck_assert_int_eq(uc_htable_count(&table), 2); + + //this one should still be here + fail_if(my_find(&table, "One", hash_str("One")) != &str1); + // + //thisone should be gone + rc = uc_htable_has_node(&table, &str2.node); + ck_assert_int_eq(rc, 0); + + + uc_htable_remove(&table, &str1.node); + ck_assert_int_eq(uc_htable_count(&table), 1); + + //now this one should be gone + rc = uc_htable_has_node(&table, &str1.node); + ck_assert_int_eq(rc, 0); + + //double removal should be ok + uc_htable_remove(&table, &str1.node); + ck_assert_int_eq(uc_htable_count(&table), 1); + + //this one should still be here + fail_if(my_find(&table, "Other", hash_str("Other")) != &str3); + + uc_htable_destroy(&table); + +END_TEST + +START_TEST (test_htable_remove_foreach) + struct MyString str1 = {"One", {0,NULL}}; + struct MyString str2 = {"One", {0,NULL}}; + struct MyString str3 = {"Other", {0,NULL}}; + + struct UHTable table; + struct UHNode *it; + struct UHNode *next; + int rc; + int removed = 0; + + rc = uc_htable_init(&table, 1); + ck_assert_int_eq(rc, 0); + + uc_htable_insert(&table, &str1.node, hash_str(str1.str)); + uc_htable_insert(&table, &str2.node, hash_str(str2.str)); + uc_htable_insert(&table, &str3.node, hash_str(str3.str)); + ck_assert_int_eq(uc_htable_count(&table), 3); + + fail_if(my_find(&table, "One", hash_str("One")) == NULL); + + UC_HTABLE_FOREACH_HASH_SAFE(&table, it, next, hash_str("One")) { + uc_htable_remove(&table, it); + memset(it, 0xff, sizeof *it); + removed++; + + } + + ck_assert_int_eq(removed, 2); + + fail_if(my_find(&table, "One", hash_str("One")) != NULL); + fail_if(my_find(&table, "Other", hash_str("Other")) != &str3); + ck_assert_int_eq(uc_htable_count(&table), 1); + + uc_htable_destroy(&table); + +END_TEST + +START_TEST (test_htable_clear) + struct MyString str1 = {"One", {0,NULL}}; + + struct UHTable table; + int rc; + + rc = uc_htable_init(&table, 100); + ck_assert_int_eq(rc, 0); + + uc_htable_insert(&table, &str1.node, hash_str(str1.str)); + ck_assert_int_eq(uc_htable_count(&table), 1); + + fail_if(my_find(&table, "One", hash_str("One")) != &str1); + + uc_htable_clear(&table); + ck_assert_int_eq(uc_htable_count(&table), 0); + fail_if(my_find(&table, "One", hash_str("One")) != NULL); + + uc_htable_destroy(&table); + +END_TEST + +START_TEST (test_htable_foreach) + struct MyString str1 = {"One", {0,NULL}}; + struct MyString str2 = {"One", {0,NULL}}; + struct MyString str3 = {"Other",{0,NULL}}; + + struct UHTable table; + struct UHNode *it; + int rc; + int found1 = 0; + int found2 = 0; + int found3 = 0; + + rc = uc_htable_init(&table, 1000); + ck_assert_int_eq(rc, 0); + + uc_htable_insert(&table, &str1.node, hash_str(str1.str)); + uc_htable_insert(&table, &str2.node, hash_str(str2.str)); + uc_htable_insert(&table, &str3.node, hash_str(str3.str)); + ck_assert_int_eq(uc_htable_count(&table), 3); + + UC_HTABLE_FOREACH(&table, it) { + struct MyString *my_str = UC_CONTAINER_OF(it, struct MyString, node); + if (my_str == &str1) { + found1++; + } + if (my_str == &str2) { + found2++; + } + if (my_str == &str3) { + found3++; + } + } + + ck_assert_int_eq(found1, 1); + ck_assert_int_eq(found2, 1); + ck_assert_int_eq(found3, 1); + + + uc_htable_destroy(&table); + +END_TEST + +START_TEST (test_htable_foreach_safe) + struct MyString str1 = {"One", {0,NULL}}; + struct MyString str2 = {"One", {0,NULL}}; + struct MyString str3 = {"Other",{0,NULL}}; + + struct UHTable table; + struct UHNode *it; + struct UHNode *next; + int rc; + + rc = uc_htable_init(&table, 1000); + ck_assert_int_eq(rc, 0); + + uc_htable_insert(&table, &str1.node, hash_str(str1.str)); + uc_htable_insert(&table, &str2.node, hash_str(str2.str)); + uc_htable_insert(&table, &str3.node, hash_str(str3.str)); + + fail_if(my_find(&table, "Other", hash_str("Other")) != &str3); + + ck_assert_int_eq(uc_htable_count(&table), 3); + + UC_HTABLE_FOREACH_SAFE(&table, it, next) { + uc_htable_remove(&table, it); + memset(it, 0xff, sizeof *it); + } + + fail_if(my_find(&table, "One", hash_str("One")) != NULL); + fail_if(my_find(&table, "Other", hash_str("Other")) != NULL); + + ck_assert_int_eq(uc_htable_count(&table), 0); + + uc_htable_destroy(&table); + +END_TEST + +Suite *htable_suite(void) +{ + Suite *s = suite_create("htable"); + TCase *tc = tcase_create("htable tests"); + + tcase_add_test(tc, test_htable1); + tcase_add_test(tc, test_htable_hash_collision); + tcase_add_test(tc, test_htable_resize); + tcase_add_test(tc, test_htable_fail_init_resize); + tcase_add_test(tc, test_htable_has_node); + tcase_add_test(tc, test_htable_remove); + tcase_add_test(tc, test_htable_remove_foreach); + tcase_add_test(tc, test_htable_clear); + tcase_add_test(tc, test_htable_foreach); + tcase_add_test(tc, test_htable_foreach_safe); + + + suite_add_tcase(s, tc); + + return s; +} + diff --git a/test/test_ratelimit.c b/test/test_ratelimit.c new file mode 100644 index 0000000..0b09753 --- /dev/null +++ b/test/test_ratelimit.c @@ -0,0 +1,148 @@ +#include +#include + + +START_TEST (test_ratelimit_1) + struct RateLimit r; + long now = 10; + int i; + + uc_ratelimit_init(&r, 30, 2); + for (i = 0; i < 30; i++) { + fail_if(!uc_ratelimit_allow(&r, now), "i = %d", i); + } + fail_if(uc_ratelimit_allow(&r, now)); +END_TEST + +START_TEST (test_ratelimit_2) + struct RateLimit r; + long now = 10; + + uc_ratelimit_init(&r, 1, 1); + fail_if(!uc_ratelimit_allow(&r, now)); + fail_if(uc_ratelimit_allow(&r, now)); + + now += 1; + + fail_if(!uc_ratelimit_allow(&r, now)); + fail_if(uc_ratelimit_allow(&r, now)); + +END_TEST + +START_TEST (test_ratelimit_3) + struct RateLimit r; + long now = 10; + int i; + + uc_ratelimit_init(&r, 10, 5); + + for (i = 0; i < 10; i++) { + fail_if(!uc_ratelimit_allow(&r, now)); + } + + now += 1; + + for (i = 0; i < 2; i++) { + fail_if(!uc_ratelimit_allow(&r, now)); + } + + fail_if(uc_ratelimit_allow(&r, now)); + + now += 1; + + for (i = 0; i < 2; i++) { + fail_if(!uc_ratelimit_allow(&r, now)); + } + fail_if(uc_ratelimit_allow(&r, now)); + + now += 1; + + for (i = 0; i < 2; i++) { + fail_if(!uc_ratelimit_allow(&r, now)); + } + fail_if(uc_ratelimit_allow(&r, now)); + + +END_TEST + +START_TEST (test_ratelimit_4) + struct RateLimit r; + long now = 10; + int i; + + uc_ratelimit_init(&r, 10, 5); + + for (i = 0; i < 10; i++) { + fail_if(!uc_ratelimit_allow(&r, now)); + } + + + fail_if(uc_ratelimit_allow(&r, now)); + + uc_ratelimit_reset(&r, now); + + for (i = 0; i < 10; i++) { + fail_if(!uc_ratelimit_allow(&r, now)); + } + fail_if(uc_ratelimit_allow(&r, now)); + +END_TEST + +START_TEST (test_ratelimit_5) + struct RateLimit r; + long now = 1400000000; + + uc_ratelimit_init(&r, 1, 10); + + fail_if(!uc_ratelimit_allow(&r, now)); + fail_if(uc_ratelimit_allow(&r, now)); + + now += 5; + + fail_if(uc_ratelimit_allow(&r, now)); + + now += 5; + + fail_if(!uc_ratelimit_allow(&r, now)); + fail_if(uc_ratelimit_allow(&r, now)); + + now += 200; + + fail_if(!uc_ratelimit_allow(&r, now)); + fail_if(uc_ratelimit_allow(&r, now)); + +END_TEST + +START_TEST (test_ratelimit_time_backward) + struct RateLimit r; + long now = 1400000000; + + uc_ratelimit_init(&r, 1, 10); + + fail_if(!uc_ratelimit_allow(&r, now)); + fail_if(uc_ratelimit_allow(&r, now)); + + now -= 5; + + fail_if(uc_ratelimit_allow(&r, now)); + +END_TEST + + +Suite *ratelimit_suite(void) +{ + Suite *s = suite_create("ratelimit"); + TCase *tc = tcase_create("ratelimit tests"); + + tcase_add_test(tc, test_ratelimit_1); + tcase_add_test(tc, test_ratelimit_2); + tcase_add_test(tc, test_ratelimit_3); + tcase_add_test(tc, test_ratelimit_4); + tcase_add_test(tc, test_ratelimit_5); + tcase_add_test(tc, test_ratelimit_time_backward); + + suite_add_tcase(s, tc); + + return s; +} + diff --git a/test/test_runner.c b/test/test_runner.c index 73cf4c1..c485f6f 100644 --- a/test/test_runner.c +++ b/test/test_runner.c @@ -24,6 +24,8 @@ extern Suite *human_bytesz_suite(void); extern Suite *dbuf_suite(void); extern Suite *sprintb_suite(void); extern Suite *strv_suite(void); +extern Suite *ratelimit_suite(void); +extern Suite *htable_suite(void); static suite_func suites[] = { bitvec_suite, @@ -42,6 +44,8 @@ static suite_func suites[] = { dbuf_suite, sprintb_suite, strv_suite, + ratelimit_suite, + htable_suite, clock_suite, threadqueue_suite }; @@ -58,6 +62,8 @@ main (int argc, char *argv[]) if (xml_output) srunner_set_xml(sr, "result.xml"); + srunner_set_fork_status(sr, CK_FORK_GETENV); + for(i = 0; i < sizeof suites/sizeof suites[0]; i++) { srunner_add_suite(sr, suites[i]()); diff --git a/test/test_threadqueue.c b/test/test_threadqueue.c index 933645a..2cd7d6d 100644 --- a/test/test_threadqueue.c +++ b/test/test_threadqueue.c @@ -50,17 +50,44 @@ START_TEST (test_thread_queue_one_thread) fail_if(my_msg->a != i); free(my_msg); } - fail_if(uc_thread_queue_destroy(&queue, NULL) != 0); + fail_if(uc_thread_queue_destroy(&queue, NULL, NULL) != 0); + +END_TEST + +START_TEST (test_thread_queue_priority_msg) + + struct uc_threadqueue queue; + int i; + + struct uc_threadmsg msg[3]; + memset(&msg, 0, sizeof msg); + + fail_if(uc_thread_queue_init(&queue, 3) != 0); + + + for (i = 0; i < 3; i++) { + msg[i].msgtype = -1; //negative == add at head + fail_if(uc_thread_queue_add(&queue, &msg[i]) != 0); + } + + for (i = 2; i >= 0; i--) { + struct uc_threadmsg *my_msg; + fail_if(uc_thread_queue_get(&queue, &my_msg) != 0); + + fail_if(my_msg != &msg[i]); + } + + fail_if(uc_thread_queue_destroy(&queue, NULL, NULL) != 0); END_TEST - //pretty ugly.. static int test_thread_queue_walk_global; -static void test_thread_queue_walk_func(struct uc_threadmsg *msg) +static void test_thread_queue_walk_func(struct uc_threadmsg *msg, void *cookie) { + (void)cookie; struct thr_msg *my_msg = (struct thr_msg *)msg; fail_if(my_msg->a != test_thread_queue_walk_global); test_thread_queue_walk_global++; @@ -82,7 +109,7 @@ START_TEST (test_thread_queue_walk) test_thread_queue_walk_global = 0; - uc_thread_queue_walk(&queue, test_thread_queue_walk_func); + uc_thread_queue_walk(&queue, test_thread_queue_walk_func, NULL); END_TEST @@ -125,7 +152,7 @@ START_TEST (test_thread_queue_reader_writer) pthread_join(tid, NULL); - fail_if(uc_thread_queue_destroy(&queue, NULL) != 0); + fail_if(uc_thread_queue_destroy(&queue, NULL, NULL) != 0); END_TEST @@ -216,7 +243,7 @@ START_TEST (test_thread_queue_timeout1) fail_if(uc_thread_queue_init(&queue, 100) != 0); fail_if(uc_thread_queue_tryget(&queue, &timeout, &msg) != ETIMEDOUT); - uc_thread_queue_destroy(&queue, NULL); + uc_thread_queue_destroy(&queue, NULL, NULL); END_TEST @@ -251,7 +278,7 @@ START_TEST (test_thread_queue_timeout2) pthread_join(tid1, NULL); - uc_thread_queue_destroy(&queue, NULL); + uc_thread_queue_destroy(&queue, NULL, NULL); END_TEST @@ -270,7 +297,7 @@ START_TEST (test_thread_queue_tryadd_timeout_0_0) fail_if(uc_thread_queue_tryadd(&queue, &timeout, &msg[2].msg) != ETIMEDOUT); - fail_if(uc_thread_queue_destroy(&queue, NULL) != 0); + fail_if(uc_thread_queue_destroy(&queue, NULL, NULL) != 0); END_TEST @@ -289,7 +316,7 @@ START_TEST (test_thread_queue_tryadd_timeout_0_100) fail_if(uc_thread_queue_tryadd(&queue, &timeout, &msg[2].msg) != ETIMEDOUT); - fail_if(uc_thread_queue_destroy(&queue, NULL) != 0); + fail_if(uc_thread_queue_destroy(&queue, NULL, NULL) != 0); END_TEST @@ -308,7 +335,36 @@ START_TEST (test_thread_queue_tryadd_no_timeout_0_100) fail_if(uc_thread_queue_tryadd(&queue, &timeout, &msg[2].msg) != 0); - fail_if(uc_thread_queue_destroy(&queue, NULL) != 0); + fail_if(uc_thread_queue_destroy(&queue, NULL, NULL) != 0); + +END_TEST + +void clear_cb(struct uc_threadmsg *msg, void *cookie) +{ + int *i = cookie; + (void)msg; + ++*i; +} + +START_TEST (test_thread_queue_clear) + + struct uc_threadqueue queue; + int i; + struct thr_msg msg[3]; + memset(msg, 0, sizeof msg); + int cnt = 0; + + fail_if(uc_thread_queue_init(&queue, 3) != 0); + for (i = 0; i < 3; i++) { + fail_if(uc_thread_queue_add(&queue, &msg[i].msg) != 0); + } + + fail_if(uc_thread_queue_length(&queue) != 3); + fail_if(uc_thread_queue_clear(&queue, clear_cb, &cnt)); + ck_assert_int_eq(cnt , 3); + fail_if(uc_thread_queue_length(&queue) != 0); + + fail_if(uc_thread_queue_destroy(&queue, NULL, NULL) != 0); END_TEST @@ -320,6 +376,7 @@ Suite *threadqueue_suite(void) tcase_add_test(tc1, test_thread_queue_length); tcase_add_test(tc1, test_thread_queue_one_thread); + tcase_add_test(tc1, test_thread_queue_priority_msg); tcase_add_test(tc1, test_thread_queue_walk); tcase_add_test(tc1, test_thread_queue_reader_writer); tcase_add_test(tc1, test_thread_queue_reader_writer_len_1); @@ -330,6 +387,7 @@ Suite *threadqueue_suite(void) tcase_add_test(tc1, test_thread_queue_tryadd_timeout_0_0); tcase_add_test(tc1, test_thread_queue_tryadd_timeout_0_100); tcase_add_test(tc1, test_thread_queue_tryadd_no_timeout_0_100); + tcase_add_test(tc1, test_thread_queue_clear); //lets start with this: tcase_set_timeout(tc1, 15);