Linux kernel doubly linked-list implementation
#ifndef _LIST_H
#define _LIST_H
#ifndef container_of
#define container_of(ptr, type, member) \
({ (type *)((char *)ptr - (char *)&((type *)0)->member)})
#endif
#ifndef __container_of
#define __container_of(ptr, type, member) \
({ (typeof(type))((char *)ptr - (char *)&((typeof(type))0)->member); })
#endif
struct list {
struct list* prev;
struct list* next;
};
#define LIST_HEAD(name) struct list name = { &(name), &(name) }
static inline void list_init(struct list* head)
{
head->prev = head->next = head;
}
static inline void _list_add(struct list* new,
struct list* prev,
struct list* next)
{
prev->next = new;
new->prev = prev;
new->next = next;
next->prev = new;
}
static inline void list_add(struct list* new, struct list* head)
{
_list_add(new, head, head->next);
}
static inline void list_add_tail(struct list* new, struct list* head)
{
_list_add(new, head->prev, head);
}
static inline void list_del(struct list* node)
{
struct list* prev = node->prev, *next = node->next;
prev->next = next;
next->prev = prev;
}
static inline int list_empty(struct list* head)
{
return (head->next == head);
}
static inline void list_move(struct list* list, struct list* head)
{
list_del(list);
list_add(list, head);
}
static inline void list_move_tail(struct list* list, struct list* head)
{
list_del(list);
list_add_tail(list, head);
}
#define list_entry(ptr, type, member) container_of(ptr, type, member)
#define list_for_each(pos, head, member) \
for (pos = __container_of((head)->next, pos, member); \
&pos->member != head; \
pos = __container_of(pos->member.next, pos, member))
#define list_for_each_reverse(pos, head, member) \
for (pos = __container_of((head)->prev, pos, member); \
&pos->member != head; \
pos = __container_of(pos->member.prev, pos, member))
#endif
#define EMPLOYE_ADD_TAIL
#include <stdio.h>
#include <stdlib.h>
#include <string.h> /* strcmp() */
#include "list.h"
struct employee {
char* name;
char* city;
char* country;
unsigned age;
struct list list;
};
static int employee_add(char* name, char* city, char* country, unsigned age,
struct list* head)
{
struct employee* employee;
if (!(employee = calloc(1, sizeof(*employee)))) {
return -1;
}
employee->name = name;
employee->city = city;
employee->country = country;
employee->age = age;
list_add(&employee->list, head);
return 0;
}
static int employee_add_tail(char* name, char* city, char* country,
unsigned age,
struct list* head)
{
struct employee* employee;
if (!(employee = calloc(1, sizeof(*employee)))) {
return -1;
}
employee->name = name;
employee->city = city;
employee->country = country;
employee->age = age;
list_add_tail(&employee->list, head);
return 0;
}
static void employee_travel(struct list* head)
{
struct employee* pos;
list_for_each(pos, head, list) {
printf("Name: %s\n"
"Age : %d\n"
"From: %s, %s\n\n",
pos->name, pos->age, pos->city, pos->country);
}
}
static void employee_travel_reverse(struct list* head)
{
struct employee* pos;
list_for_each_reverse(pos, head, list) {
printf("Name: %s\n"
"Age : %d\n"
"From: %s, %s\n\n",
pos->name, pos->age, pos->city, pos->country);
}
}
static void employee_move_by_name(char* name, struct list* head)
{
struct employee* pos;
list_for_each(pos, head, list) {
if (!strcmp(pos->name, name)) {
list_move(&pos->list, head);
}
}
}
static void employee_move_tail_by_name(char* name, struct list* head)
{
struct employee* pos;
list_for_each(pos, head, list) {
if (!strcmp(pos->name, name)) {
list_move_tail(&pos->list, head);
}
}
}
static void employee_del_all(struct list* head)
{
struct employee* pos;
list_for_each(pos, head, list) {
list_del(&pos->list);
free(pos);
}
}
int main(int argc, char* argv[])
{
LIST_HEAD(employee_head);
#ifndef EMPLOYE_ADD_TAIL
employee_add("Linus Torvalds", "Helsinki", "Finland", 46, &employee_head);
employee_add("Hiroyuki Sawano", "Tokyo", "Japan", 35, &employee_head);
employee_add("Yang Zhang", "Sichuan", "China", 22, &employee_head);
#else
employee_add_tail("Linus Torvalds", "Helsinki", "Finland", 46,
&employee_head);
employee_add_tail("Hiroyuki Sawano", "Tokyo", "Japan", 35,
&employee_head);
employee_add_tail("Yang Zhang", "Sichuan", "China", 22, &employee_head);
#endif
printf("--------- travel the list ---------\n");
employee_travel(&employee_head);
printf("--------- travel the list in reverse order ---------\n");
employee_travel_reverse(&employee_head);
printf("--------- move Yang Zhang to the head ---------\n");
employee_move_by_name("Yang Zhang", &employee_head);
employee_travel(&employee_head);
printf("--------- move Hiroyuki Sawano to the tail ---------\n");
employee_move_tail_by_name("Hiroyuki Sawano", &employee_head);
employee_travel(&employee_head);
printf("--------- empty the list -------------\n");
employee_del_all(&employee_head);
printf("list is empty: %s\n",
list_empty(&employee_head) ? "true" : "false");
return 0;
}
CC = gcc
CFLAGS = -Wall
PROGS = list_test
all: $(PROGS)
%: %.c list.h
$(CC) -o $@ $^ $(CFLAGS)
clean:
$(RM) $(PROGS) $(wildcard *.h.gch)