General Kernel Hash Table Based on list_head

General Kernel Hash Table Based on list_head

Because the struct list_head in the Linux kernel has defined a prev pointer to a predecessor and a next pointer to a successor, and provides related linked list operation methods

Because the struct list_head in the Linux kernel has defined a prev pointer to a predecessor and a next pointer to a successor, and provides related linked list operation methods, in order to facilitate reuse, this article encapsulates and implements a kind of use on The universal kernel hash table glib_htable , which resolves conflicts by chain method , provides six operations of initialization, addition, lookup, deletion, emptying, and destruction. Except for initialization and destruction, other operations are synchronized, which is suitable for interrupt and process context. It is different from the general general Hash table (such as hash_map in C ++ and some generic hash tables implemented in C language):

● The object stored in glib_htable is created externally instead of internally. This object must be directly or indirectly I have combined the list_head members (indirect combination, including glib_hentry below ), and here I use the term combination in UML to emphasize that it is not an aggregation relationship.
● The semantics of the delete operation is to remove the link of the object from the hash table, but releasing the object is optional.
● The number of buckets is specified externally rather than internally.
In summary, it can be seen that glib_htable is linked to the Hash table using the existing embedded member list_head of the object. Compared with the general Hash table, each table entry saves 1 pointer space, as shown below.

Structure definition
struct glib_hentry {
     struct list_head list;
     void *data;
 };
 
 typedef unsigned int (*glib_htable_hashfun_t)(const void*,unsigned int);
 typedef int (*glib_htable_cmpfun_t)(const void*, const void*);
 typedef void (*glib_htable_cbfun_t)(struct glib_hentry*);
 typedef void (*glib_htable_freefun_t)(struct glib_hentry*);

struct glib_htable {
    struct list_head *bucket;
    unsigned int size;
    unsigned int vmalloced;
    
    rwlock_t lock;
    
    glib_htable_hashfun_t hashfun;
    glib_htable_cmpfun_t cmpfun;
    glib_htable_cbfun_t cbfun;
    glib_htable_freefun_t freefun;
};
  1. glibhentry abstracts the embedded members of the storage object, representing the Hash item, or the entire object. At this time, the embedded member is the object itself, and the member data represents any data associated with the object, which is used to calculate the hash value. When the associated data When the size is <= sizeof (void *), you can directly cast to data storage instead of the data address.
  2. glibhtable abstracts the Hash table, and size indicates the number of buckets. Considering that the size may be large and requires large blocks of memory, so in the case of failure to allocate continuous physical pages, use vmalloc to try to allocate discontinuous physical pages, so it is introduced Vmalloced indicates the allocation mode, non-zero indicates vmalloc, zero is __get_free_pages; hashfun and cmpfun are two indispensable key functions to implement the Hash table. cbfun is used to find callback processing when it is successful, such as printing, increasing reference count, etc. freefun is used to release objects. This callback interface is provided for convenience Objects can be released after being removed from the Hash table without having to be released externally, increasing flexibility.
main interface

represents the glib_htable object.
● initialization
int glib_htable_init(struct glib_htable *ht, unsigned int size, glib_htable_hashfun_t hashfun, glib_htable_cmpfun_t cmpfun);
size indicates the number of hash table buckets, hashfun is a hash function, cmpfun is a comparison function; 0 is returned on success, cbfun and freefun on the ht member are set to empty, and ENOMEM is returned on failure. Since vmalloc may be used to allocate memory, it cannot be used in an interrupt context.
● increase

void glib_htable_add(struct glib_htable *ht, struct glib_hentry *he, int num);

Add multiple objects in one synchronization, he is a pointer to the hash item of the object, and num is the number.

● Find

struct glib_hentry* glib_htable_get(struct glib_htable *ht, const void *data);
struct glib_hentry* glib_htable_rget(struct glib_htable *ht, const void *data);
struct glib_hentry* glib_htable_cget(struct glib_htable *ht, const void *data, int(*cmp)(const struct glib_hentry*, void*), void *arg);
struct glib_hentry* glib_htable_crget(struct glib_htable *ht, const void *data, int(*cmp)(const struct glib_hentry*, void*), void *arg);
struct glib_hentry* glib_htable_cget_byidx(struct glib_htable *ht, unsigned int *bucket, int(*cmp)(const struct glib_hentry*, void*), void *arg);
struct glib_hentry* glib_htable_crget_byidx(struct glib_htable *ht, unsigned int *bucket, int(*cmp)(const struct glib_hentry*, void*), void *arg);

From top to bottom: forward search, reverse search, forward condition search, reverse condition search, forward condition search by bucket positioning, reverse condition search by bucket positioning, data is object-associated data, and cmp is Custom comparison function, arg is the custom parameter that cmp brings, bucket is the bucket index, and if the lookup is successful, the bucket is updated to the bucket index where the object is located. All the above operations return NULL when it fails.

● Delete

void glib_htable_del(struct glib_htable *ht, struct glib_hentry *he, int num);
void glib_htable_del_bydata(struct glib_htable *ht, const void **data, int num);

The first deletes by object hash item, and the second deletes by object-related data. Num represents the number. If the ht member freefun is not empty, the object is released.

● Empty

void glib_htable_clear(struct glib_htable *ht);

Delete all objects in one synchronization. If the ht member freefun is not empty, the objects are released.

● Destroy
void glib_htable_free(struct glib_htable *ht);
Only release the memory occupied by all buckets, which should be called after glib_htable_clear. Since it is possible to free memory with vfree, it cannot be used in interrupt context.

Interface implementation

Other interface implementation is relatively simple, skip the explanation. For the lookup interface, if a parameter is added to indicate the traversal direction, then although the total number of interfaces is halved, when using it, especially in a loop, unnecessary direction judgment is performed every time and performance is reduced, so for the forward direction And reverse traversal, each gives an interface, just like strchr and strrchr in the c library, iterator and reverse_iterator in the c ++ container, which makes it clearer. Except for different traversal directions, the other codes are the same, so in order to avoid manual coding redundancy, 3 sets of macros are used to generate.

Helper function macro generation

#define DEFINE_GLIB_HTABLE_GET_HELP(name) \
static struct glib_hentry* __glib_htable_##name(struct glib_htable *ht, unsigned int hash, const void *data)  \
{\
     struct glib_hentry *he; \
\
    glib_htable_list_##name(he,&ht->bucket[hash],list){ \
        if(ht->cmpfun(he->data,data)){ \
            if(ht->cbfun) \
                ht->cbfun(he); \
           return he; \
       } \
   } \
\
    return NULL; \
}

DEFINE_GLIB_HTABLE_GET_HELP(get)
DEFINE_GLIB_HTABLE_GET_HELP(rget)

#define DEFINE_GLIB_HTABLE_COND_GET_HELP(name) \
static struct glib_hentry* __glib_htable_c##name(struct glib_htable *ht, unsigned int hash, int(*cmp)(const struct glib_hentry*, void*), void *arg) \
{ \
    struct glib_hentry *he; \
\
    glib_htable_list_##name(he,&ht->bucket[hash],list){ \
        if(cmp(he, arg)){ \
            if(ht->cbfun) \
                ht->cbfun(he); \
            return he; \
        } \
    } \
\
    return NULL; \
}

DEFINE_GLIB_HTABLE_COND_GET_HELP(get)
DEFINE_GLIB_HTABLE_COND_GET_HELP(rget)

The generated macros are DEFINE_GLIB_HTABLE_GET_HELP and DEFINE_GLIB_HTABLE_COND_GET_HELP. After expansion, there are __glib_htable_get(rget) and __glib_htable_cget(crget). 4 unlocked functions are used to implement the corresponding lock interface. glib_htable_list_get and glib_htable_list_rget are aliases for the macros list_for_each_entry and list_for_each_entry_reverse, respectively.

Normal find macro generation
#define DEFINE_GLIB_HTABLE_GET(name) \
struct glib_hentry* glib_htable_##name(struct glib_htable *ht, const void *data) \
{ \
    struct glib_hentry *he; \
    unsigned int h = ht->hashfun(data,ht->size); \
\
    read_lock_bh(&ht->lock); \
    he = __glib_htable_##name(ht, h, data); \
     read_unlock_bh(&ht->lock); \
\
    return he; \
}

DEFINE_GLIB_HTABLE_GET(get)
DEFINE_GLIB_HTABLE_GET(rget)

The auxiliary function __glib_htable_get(rget) is called to implement, and the generated macro is DEFINE_GLIB_HTABLE_GET. After expansion, there is the glib_htable_get(rget) interface.

Conditional search macro generation
#define DEFINE_GLIB_HTABLE_COND_GET(name) \
struct glib_hentry* glib_htable_c##name(struct glib_htable *ht, const void *data, int(*cmp)(const struct glib_hentry*, void*), void *arg) \
{ \
    struct glib_hentry *he;    \
    unsigned int h = ht->hashfun(data,ht->size); \
\
    read_lock_bh(&ht->lock); \
    he = __glib_htable_c##name(ht, h, cmp, arg); \
    read_unlock_bh(&ht->lock); \
\
    return he; \
}

DEFINE_GLIB_HTABLE_COND_GET(get)
DEFINE_GLIB_HTABLE_COND_GET(rget)

#define DEFINE_GLIB_HTABLE_COND_GET_BYIDX(name) \
struct glib_hentry* glib_htable_c##name##_byidx(struct glib_htable *ht, unsigned int *bucket, int(*cmp)(const struct glib_hentry*, void*), void *arg) \
{ \
    unsigned int h; \
    struct glib_hentry *he = NULL; \
\
    read_lock_bh(&ht->lock); \
\
    for (h = *bucket; h < ht->size; h = (*bucket)++){ \
        he = __glib_htable_c##name(ht, h, cmp, arg); \
        if(he) \
            break; \
    } \
\
    read_unlock_bh(&ht->lock); \
\
    return he; \
}

DEFINE_GLIB_HTABLE_COND_GET_BYIDX(get)
DEFINE_GLIB_HTABLE_COND_GET_BYIDX(rget)
The former calls the helper function `__glib_htable_cget(rget)`, and the generated macro is DEFINE_GLIB_HTABLE_COND_GET. After expansion, there is the glib_htable_cget(rget) interface; the latter calls the helper function __glib_htable_cget(rget) _byidx, and the macro is DEFINE_GLIB_HTABLE_COND. After expansion, it is expanded glib_htable_cget(rget) _byidx interface.

Full source download: glib_hash , including glib_htable.h and glib_htable.c files.

C/C++ vs. Rust: A developer’s perspective

C/C++ vs. Rust: A developer’s perspective

In this post, you'll see the difference between Rust and C/C++ in a developer’s perspective

C++ is an incredibly fast and efficient programming language. Its versatility knows no bounds and its maturity ensures support and reliability are second to none. Code developed in C++ is also extremely portable, all major operating systems support it. Many developers begin their coding journey with the language, and this is no coincidence. Being object-oriented means it does a very good job of teaching concepts like classes, inheritance, abstraction, encapsulation and polymorphism. Its concepts and syntax can be found in modern languages like C#, Java and Rust. It provides a great foundation that serves as a high speed on ramp to the more popular, easier to use and modern alternatives.

Now it’s not all rosy. C++ has a very steep learning curve and requires developers to apply best practices to the letter or risk ending up with unsafe and/or poor performing code. The small footprint of the standard library, while most times considered a benefit, also adds to the level of difficulty. This means successfully using C++ to create useful complex libraries and applications can be challenging. There is also very little offered in terms of memory management, developers must do this themselves. Novice programmers could end up with debugging nightmares as their lack of experience leads to memory corruption and other sticky situations. This last point has lead many companies to explore fast performing, safe and equally powerful alternatives to C++. For today’s Microsoft that means Rust.

The majority of vulnerabilities fixed and with a CVE [Common Vulnerabilities and Exposures] assigned are caused by developers inadvertently inserting memory corruption bugs into their C and C++ code - Gavin Thomas, Microsoft Security Response Center
Rust began as a personal project by a Mozilla employee named Graydon Hoare sometime in 2006. This ambitious project was in pre-release development for almost a decade, finally launching version 1.0 in May 2015. In what seems to be the blink of an eye it has stolen the hearts of hordes of developers going as far as being voted the most loved language four years straight since 2016 in the Stack Overflow Developer Survey.

The hard work has definitely paid off. The end result is very efficient language which is characteristically object oriented. The fact that it was designed to be syntactically similar to C++ makes it very easy to approach. But unlike the aforementioned it was also designed to be memory safe while also employing a form of memory management without the explicit use of garbage collection.

The ugly truth is software development is very much a trial and error endeavor. With that said Rust has gone above and beyond to help us debug our code. The compiler produces extremely intuitive and user friendly error messages along with great direct linking to relevant documentation to aid with troubleshooting. This means if the problem is not evident, most times the answer is a click away. I’ve found myself rarely having to fire up my browser to look for solutions outside of what the Rust compiler offers in terms of explanation and documentation.

Rust does not have a garbage collector but most times still allocates and release memory for you. It’s also designed to be memory safe, unlike C++ which very easily lets you get into trouble with dangling pointers and data races. In contrast Rust employs concepts which help you prevent and avoid such issues.

There are many other factors which have steered me away from C++ and onto Rust. But to be honest it has nothing to do with all the great stuff we’ve just explored. I came to Rust on a journey that began with WebAssembly. What started with me looking for a more efficient alternative to JavaScript for the web turned into figuring out just how powerful Rust turns out to be. From its seamless interop…

Automatically generate binding code between Rust, WebAssembly, and JavaScript APIs. Take advantage of libraries like web-sys that provide pre-packaged bindings for the entire web platform. – Rust website
To how fast and predictable its performance is. Everything in our lives evolves. Our smartphones, our cars, our home appliances, our own bodies. C++ while still incredibly powerful, fast and versatile can only take us so far. There is no harm in exploring alternatives, especially one as exceptional and with as much promise as Rust.

What do you guys think? Have you or would you give Rust a try? Let us know your thoughts in the comments section below.

Thanks for reading

If you liked this post, share it with all of your programming buddies!

Follow us on Facebook | Twitter

Further reading

Why you should move from Node.js to Rust in 2019

Rust Vs. Haskell: Which Language is Best for API Design?

7 reasons why you should learn Rust programming language in 2019

An introduction to Web Development with Rust for Node.js Developers

Efficient one algorithm of these two algorithms

Both of these algorithms are giving same output but the first one takes nearly double time (&gt;.67) compared to second one (.36). How is this possible? Can you tell me the time complexity of both algorithms? If they're the same, why is the time different?

Both of these algorithms are giving same output but the first one takes nearly double time (>.67) compared to second one (.36). How is this possible? Can you tell me the time complexity of both algorithms? If they're the same, why is the time different?

1st algorithm:

 for (int i =0 ;i<n;i++){
        cin>>p[i];
        if(i>0){
            if(p[i-1]>p[i]){
                cout<<p[i]<<" ";
            }
            else{
                cout<<"-1"<<" ";
            }
        }
    }

2nd algorithm:

for (int i =0 ;i<n;i++){
        cin>>p[i];
}
for (int i =0 ; i&lt;n-1;i++){
   if(p[i]&gt;p[i+1]){
            cout&lt;&lt;p[i]&lt;&lt;" ";
        }
        else{
            cout&lt;&lt;"-1"&lt;&lt;" ";
        } 
}