├── data ├── categories.txt ├── database.txt ├── orders.txt └── finalreport.txt ├── pa5 ├── data │ ├── categories.txt │ ├── database.txt │ ├── orders.txt │ └── finalreport.txt ├── Makefile ├── src │ ├── order.h │ ├── pseudo │ ├── category.h │ ├── customer.h │ ├── main.c │ └── utlist.h └── README.md ├── book_order.pdf ├── .gitignore ├── Makefile ├── src ├── order.h ├── pseudo ├── category.h ├── customer.h ├── main.c ├── utlist.h └── uthash.h └── README.md /data/categories.txt: -------------------------------------------------------------------------------- 1 | SPORTS01 2 | HOUSING01 3 | POLITICS01 4 | -------------------------------------------------------------------------------- /pa5/data/categories.txt: -------------------------------------------------------------------------------- 1 | SPORTS01 2 | HOUSING01 3 | POLITICS01 4 | -------------------------------------------------------------------------------- /book_order.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/i/ORDERDIS/master/book_order.pdf -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Order binary 2 | order 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | 8 | # Libraries 9 | *.lib 10 | *.a 11 | 12 | # Shared objects (inc. Windows DLLs) 13 | *.dll 14 | *.so 15 | *.so.* 16 | *.dylib 17 | 18 | # Executables 19 | *.exe 20 | *.out 21 | *.app 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC=gcc 2 | HARDORE=-pthread -pedantic -ansi -Wall -Werror -pthread 3 | CFILES=./src/main.c 4 | 5 | 6 | all: 7 | $(CC) $(CFILES) $(HARDCORE) -o order 8 | 9 | debug: 10 | $(CC) -g -pthread $(CFILES) $(CFLAGS) -o order 11 | 12 | clean: 13 | rm order 14 | -------------------------------------------------------------------------------- /pa5/Makefile: -------------------------------------------------------------------------------- 1 | CC=gcc 2 | HARDORE=-pthread -pedantic -ansi -Wall -Werror -pthread 3 | CFILES=./src/main.c 4 | 5 | 6 | all: 7 | $(CC) $(CFILES) $(HARDCORE) -o order 8 | 9 | debug: 10 | $(CC) -g -pthread $(CFILES) $(CFLAGS) -o order 11 | 12 | clean: 13 | rm order 14 | -------------------------------------------------------------------------------- /data/database.txt: -------------------------------------------------------------------------------- 1 | "Brian Russell"| 1| 500.00| "110 Frelinghuysen Rd., Piscataway"| "New Jersey"| "08854" 2 | "Ying Zhan"| 2| 65.00| "3100 Hamilton Blvd, South Plainfield"| "New Jersey"| "07080" 3 | "Sejong Yoon"| 3| 10.00| "200 Promenade Boulevard, Bridgewater"| "New Jersey"| "08807" 4 | "Yuanzhen Gu"| 4| 80.00| "45 Kilmer Road, Edison"| "New Jersey"| "08817" 5 | "Zi Yan"| 5| 100.00| "100 Middlesex Essex Turnpike, Iselin"| "New Jersey"| "08830" -------------------------------------------------------------------------------- /pa5/data/database.txt: -------------------------------------------------------------------------------- 1 | "Brian Russell"| 1| 500.00| "110 Frelinghuysen Rd., Piscataway"| "New Jersey"| "08854" 2 | "Ying Zhan"| 2| 65.00| "3100 Hamilton Blvd, South Plainfield"| "New Jersey"| "07080" 3 | "Sejong Yoon"| 3| 10.00| "200 Promenade Boulevard, Bridgewater"| "New Jersey"| "08807" 4 | "Yuanzhen Gu"| 4| 80.00| "45 Kilmer Road, Edison"| "New Jersey"| "08817" 5 | "Zi Yan"| 5| 100.00| "100 Middlesex Essex Turnpike, Iselin"| "New Jersey"| "08830" -------------------------------------------------------------------------------- /src/order.h: -------------------------------------------------------------------------------- 1 | #ifndef ORDER_H 2 | #define ORDER_H 3 | 4 | #include 5 | #include 6 | 7 | #include "uthash.h" 8 | 9 | 10 | /* Order types */ 11 | enum order_types { FAILURE, SUCCESS, PENDING }; 12 | 13 | /* Order struct */ 14 | typedef struct order { 15 | char title[300]; 16 | float price; 17 | float remaining_balance; 18 | int customer_id; 19 | char category[50]; /* hash key for queues */ 20 | int success; /* 1: successful, -1: failed, 0: pending */ 21 | struct order *next; 22 | 23 | } order; 24 | 25 | /* Global hashtable for customers */ 26 | order *orders = NULL; 27 | 28 | order *new_order(int customer_id, char *title, float price) { 29 | 30 | order *o = malloc(sizeof(order)); 31 | o->customer_id = customer_id; /* Customer ID */ 32 | strcpy(o->title, title); /* Book title */ 33 | o->price = price; /* Price of book */ 34 | o->remaining_balance = 0; /* Remaining balance of customer after successful purchase */ 35 | o->success = PENDING; /* Success of order */ 36 | o->next = NULL; 37 | 38 | return o; 39 | } 40 | 41 | 42 | 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /pa5/src/order.h: -------------------------------------------------------------------------------- 1 | #ifndef ORDER_H 2 | #define ORDER_H 3 | 4 | #include 5 | #include 6 | 7 | #include "uthash.h" 8 | 9 | 10 | /* Order types */ 11 | enum order_types { FAILURE, SUCCESS, PENDING }; 12 | 13 | /* Order struct */ 14 | typedef struct order { 15 | char title[300]; 16 | float price; 17 | float remaining_balance; 18 | int customer_id; 19 | char category[50]; /* hash key for queues */ 20 | int success; /* 1: successful, -1: failed, 0: pending */ 21 | struct order *next; 22 | 23 | } order; 24 | 25 | /* Global hashtable for customers */ 26 | order *orders = NULL; 27 | 28 | order *new_order(int customer_id, char *title, float price) { 29 | 30 | order *o = malloc(sizeof(order)); 31 | o->customer_id = customer_id; /* Customer ID */ 32 | strcpy(o->title, title); /* Book title */ 33 | o->price = price; /* Price of book */ 34 | o->remaining_balance = 0; /* Remaining balance of customer after successful purchase */ 35 | o->success = PENDING; /* Success of order */ 36 | o->next = NULL; 37 | 38 | return o; 39 | } 40 | 41 | 42 | 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ORDERDIS 2 | ======== 3 | CS 214 Assignment 5 -- Book Order System 4 | ======== 5 | Ian Lozinski (iml22), Edward Zaneski(epz5) 6 | 7 | 8 | Usage: ```make; ./order path/to/database path/to/orders``` 9 | 10 | The goal of this assignment was to produce a multithreaded book order system 11 | consisting of the producer-consumer model. 12 | 13 | 14 | The program takes in a "database" file consisting of customers and an orders file 15 | consisting of orders. 16 | 17 | Each book order is handled by a separate book order processor thread 18 | (the consumers). We opted to put the book orders into separate queues, one for 19 | each category and created and stored in a hashtable which uses the category name 20 | as the hash key. Each consumer thread has exclusive access to its specialized 21 | queue. The threads are dynamically created as new categories are seen in the 22 | input file. 23 | 24 | ###Architecture: 25 | A thread is spawned and assigned a category with a queue. If the queue is empty, 26 | the thread checks if the producer is done taking in orders. If it is, the thread 27 | dies. If the producer is still processing orders, the thread goes into a conditional 28 | wait. Every time the producer adds an item to the queue, it sends a wakeup signal 29 | to the thread. The producer then joins on each consumer thread while they work to 30 | prevent it from ending early. 31 | 32 | 33 | 34 | ###Complexity analysis: 35 | Inserting customer into database O(1) time. 36 | Inserting order into database O(1/q) where q is the number of queues. 37 | -------------------------------------------------------------------------------- /pa5/README.md: -------------------------------------------------------------------------------- 1 | ORDERDIS 2 | ======== 3 | CS 214 Assignment 5 -- Book Order System 4 | ======== 5 | Ian Lozinski (iml22), Edward Zaneski(epz5) 6 | 7 | 8 | Usage: ```make; ./order path/to/database path/to/orders``` 9 | 10 | The goal of this assignment was to produce a multithreaded book order system 11 | consisting of the producer-consumer model. 12 | 13 | 14 | The program takes in a "database" file consisting of customers and an orders file 15 | consisting of orders. 16 | 17 | Each book order is handled by a separate book order processor thread 18 | (the consumers). We opted to put the book orders into separate queues, one for 19 | each category and created and stored in a hashtable which uses the category name 20 | as the hash key. Each consumer thread has exclusive access to its specialized 21 | queue. The threads are dynamically created as new categories are seen in the 22 | input file. 23 | 24 | ###Architecture: 25 | A thread is spawned and assigned a category with a queue. If the queue is empty, 26 | the thread checks if the producer is done taking in orders. If it is, the thread 27 | dies. If the producer is still processing orders, the thread goes into a conditional 28 | wait. Every time the producer adds an item to the queue, it sends a wakeup signal 29 | to the thread. The producer then joins on each consumer thread while they work to 30 | prevent it from ending early. 31 | 32 | 33 | 34 | ###Complexity analysis: 35 | Inserting customer into database O(1) time. 36 | Inserting order into database O(1/q) where q is the number of queues. 37 | -------------------------------------------------------------------------------- /src/pseudo: -------------------------------------------------------------------------------- 1 | 2 | int main() { 3 | 4 | read customer database; database.txt 5 | 6 | store all the customers in hash table; 7 | 8 | read all categories from categories.txt; 9 | 10 | ==== 11 | for each category 12 | create a new queue category; // a queue contains a list of orders that need to be processed 13 | create a thread for category; // thread will be responsible for processing orders from queue 14 | === 15 | 16 | read in the orders.txt 17 | for each order: 18 | place order in MAIN queue; 19 | 20 | 21 | After main has read all orders and placed them in MAIN QUEUE 22 | main will pthread_join each thread; 23 | Write out database to finalreport 24 | } 25 | 26 | 27 | void worker_thread() { 28 | if (can get order item from Q) { 29 | order = Q.pop 30 | customer = find_customer(order.customer_id); /* get customer from hash table */ 31 | if customer can afford book { 32 | decrement customer balance 33 | add order to customer.successful_orders 34 | } else { 35 | order status is unsuccessful 36 | add order to customer.unsuccessful_orders 37 | } 38 | } 39 | 40 | } 41 | 42 | // ian = new_customer(1, "ian", 450.0, "426 rosemont ringoes rd", "new jersey", "08559"); 43 | // add_customer(ian); /* add ian to hashtable */ 44 | // order = new_order(5, "the land before time", 5.5); /* Create new order object */ 45 | // LL_PREPEND(ian->orders, new_lnode(order)); /* Add order to the list of Ian's orders */ 46 | 47 | // remove_customer(ian); 48 | // free(ian); 49 | -------------------------------------------------------------------------------- /pa5/src/pseudo: -------------------------------------------------------------------------------- 1 | 2 | int main() { 3 | 4 | read customer database; database.txt 5 | 6 | store all the customers in hash table; 7 | 8 | read all categories from categories.txt; 9 | 10 | ==== 11 | for each category 12 | create a new queue category; // a queue contains a list of orders that need to be processed 13 | create a thread for category; // thread will be responsible for processing orders from queue 14 | === 15 | 16 | read in the orders.txt 17 | for each order: 18 | place order in MAIN queue; 19 | 20 | 21 | After main has read all orders and placed them in MAIN QUEUE 22 | main will pthread_join each thread; 23 | Write out database to finalreport 24 | } 25 | 26 | 27 | void worker_thread() { 28 | if (can get order item from Q) { 29 | order = Q.pop 30 | customer = find_customer(order.customer_id); /* get customer from hash table */ 31 | if customer can afford book { 32 | decrement customer balance 33 | add order to customer.successful_orders 34 | } else { 35 | order status is unsuccessful 36 | add order to customer.unsuccessful_orders 37 | } 38 | } 39 | 40 | } 41 | 42 | // ian = new_customer(1, "ian", 450.0, "426 rosemont ringoes rd", "new jersey", "08559"); 43 | // add_customer(ian); /* add ian to hashtable */ 44 | // order = new_order(5, "the land before time", 5.5); /* Create new order object */ 45 | // LL_PREPEND(ian->orders, new_lnode(order)); /* Add order to the list of Ian's orders */ 46 | 47 | // remove_customer(ian); 48 | // free(ian); 49 | -------------------------------------------------------------------------------- /src/category.h: -------------------------------------------------------------------------------- 1 | #ifndef CATEGORY_H 2 | #define CATEGORY_H 3 | 4 | #include "utlist.h" 5 | #include "uthash.h" 6 | 7 | typedef struct category_{ 8 | char cat[50]; 9 | void *val; 10 | struct category_ *next; 11 | struct category_ *prev; 12 | 13 | int producing; 14 | pthread_mutex_t lock; 15 | pthread_cond_t waiter; 16 | UT_hash_handle hh; /* makes this structure hashable */ 17 | } category; 18 | 19 | /* Returns a new category queue node */ 20 | category *new_category (char *cat, void *val) { 21 | category *q = malloc(sizeof(category)); 22 | 23 | strcat(q->cat, cat); 24 | q->val = val; 25 | q->prev = NULL; 26 | q->next = NULL; 27 | return q; 28 | } 29 | 30 | /* Global hashtable for queues */ 31 | category *queues = NULL; 32 | 33 | /* Add category to hashtable */ 34 | void add_queue(category *q) { 35 | q->producing = 1; 36 | pthread_mutex_init(&q->lock, NULL); 37 | pthread_cond_init(&q->waiter, NULL); 38 | HASH_ADD_KEYPTR(hh, queues, q->cat, strlen(q->cat), q); /* name: name of key field */ 39 | } 40 | 41 | /* Get queue from hashtable */ 42 | /* q can be null on failure */ 43 | category *find_queue(char *name) { 44 | category *q; 45 | 46 | HASH_FIND_STR(queues, name, q); 47 | return q; 48 | } 49 | 50 | void enqueue(category *head, category *add) { 51 | pthread_mutex_lock(&(head->lock)); 52 | LL_APPEND(head, add); 53 | pthread_mutex_unlock(&(head->lock)); 54 | /* pthread_cond_signal(&head->waiter); */ 55 | } 56 | 57 | 58 | /* Gets an order object and deletes it from front of queue */ 59 | order *dequeue(char *category_name) { 60 | order *o; 61 | category *r, *q = find_queue(category_name); 62 | 63 | /* Sanity checks */ 64 | if (q != NULL) { 65 | if(q->next != NULL && q->next->val != NULL) { 66 | o = (order *)q->next->val; 67 | r = (q->next); 68 | LL_DELETE(q, q->next); 69 | free(r); 70 | return o; 71 | } 72 | else { 73 | printf("Found queue, but nothing in it.\n"); 74 | return NULL; 75 | } 76 | } else { 77 | fprintf(stderr, "Couldn't find queue %s\n", category_name); 78 | return NULL; 79 | } 80 | } 81 | 82 | #endif 83 | -------------------------------------------------------------------------------- /pa5/src/category.h: -------------------------------------------------------------------------------- 1 | #ifndef CATEGORY_H 2 | #define CATEGORY_H 3 | 4 | #include "utlist.h" 5 | #include "uthash.h" 6 | 7 | typedef struct category_{ 8 | char cat[50]; 9 | void *val; 10 | struct category_ *next; 11 | struct category_ *prev; 12 | 13 | int producing; 14 | pthread_mutex_t lock; 15 | pthread_cond_t waiter; 16 | UT_hash_handle hh; /* makes this structure hashable */ 17 | } category; 18 | 19 | /* Returns a new category queue node */ 20 | category *new_category (char *cat, void *val) { 21 | category *q = malloc(sizeof(category)); 22 | 23 | strcat(q->cat, cat); 24 | q->val = val; 25 | q->prev = NULL; 26 | q->next = NULL; 27 | return q; 28 | } 29 | 30 | /* Global hashtable for queues */ 31 | category *queues = NULL; 32 | 33 | /* Add category to hashtable */ 34 | void add_queue(category *q) { 35 | q->producing = 1; 36 | pthread_mutex_init(&q->lock, NULL); 37 | pthread_cond_init(&q->waiter, NULL); 38 | HASH_ADD_KEYPTR(hh, queues, q->cat, strlen(q->cat), q); /* name: name of key field */ 39 | } 40 | 41 | /* Get queue from hashtable */ 42 | /* q can be null on failure */ 43 | category *find_queue(char *name) { 44 | category *q; 45 | 46 | HASH_FIND_STR(queues, name, q); 47 | return q; 48 | } 49 | 50 | void enqueue(category *head, category *add) { 51 | pthread_mutex_lock(&(head->lock)); 52 | LL_APPEND(head, add); 53 | pthread_mutex_unlock(&(head->lock)); 54 | /* pthread_cond_signal(&head->waiter); */ 55 | } 56 | 57 | 58 | /* Gets an order object and deletes it from front of queue */ 59 | order *dequeue(char *category_name) { 60 | order *o; 61 | category *r, *q = find_queue(category_name); 62 | 63 | /* Sanity checks */ 64 | if (q != NULL) { 65 | if(q->next != NULL && q->next->val != NULL) { 66 | o = (order *)q->next->val; 67 | r = (q->next); 68 | LL_DELETE(q, q->next); 69 | free(r); 70 | return o; 71 | } 72 | else { 73 | printf("Found queue, but nothing in it.\n"); 74 | return NULL; 75 | } 76 | } else { 77 | fprintf(stderr, "Couldn't find queue %s\n", category_name); 78 | return NULL; 79 | } 80 | } 81 | 82 | #endif 83 | -------------------------------------------------------------------------------- /data/orders.txt: -------------------------------------------------------------------------------- 1 | "I Could Pee on This: And Other Poems by Cats"| 7.49| 1| HOUSING01 2 | "Across Islands and Oceans"| 1.99| 1| SPORTS01 3 | "Soul Surfer"| 7.99| 3| SPORTS01 4 | "The Immortal Life of Henrietta Lacks"| 8.91| 4| POLITICS01 5 | "The Handbuilt Home: 34 Simple Stylish and Budget-Friendly Woodworking Projects for Every Room"| 15.63| 1| HOUSING01 6 | "The History of Surfing"| 31.5| 2| SPORTS01 7 | "Following Atticus (P.S.)"| 2.99| 4| HOUSING01 8 | "Stop Living In This Land. Go To The Everlasting World Of Happiness. Live There Forever."| 7.19| 4| POLITICS01 9 | "Fifty Places to Dive Before You Die: Diving Experts Share the World's Greatest Destinations"| 16.47| 1| SPORTS01 10 | "The Greatest Hockey Stories Ever Told: The Finest Writers on Ice"| 9.71| 1| SPORTS01 11 | "SuperFreakonomics"| 3.79| 1| POLITICS01 12 | "How to Create a Mind: The Secret of Human Thought Revealed"| 16.4| 2| POLITICS01 13 | "The Endurance: Shackleton's Legendary Antarctic Expedition"| 21.29| 5| SPORTS01 14 | "The California Surf Project"| 22.16| 1| SPORTS01 15 | "The Joy of Hate: How to Triumph over Whiners in the Age of Phony Outrage"| 15.6| 2| POLITICS01 16 | "A Letter to My Dog: Notes to Our Best Friends"| 16.47| 1| HOUSING01 17 | "How Children Succeed: Grit, Curiosity, and the Hidden Power of Character"| 9.45| 1| POLITICS01 18 | "Underwater Dogs"| 11.6| 1| HOUSING01 19 | "365 Cats 2013 Page-A-Day Calendar"| 9.66| 4| HOUSING01 20 | "Soul Surfer: A True Story of Faith, Family, and Fighting to Get Back on the Board"| 7.99| 2| SPORTS01 21 | "My Year in Meals"| 16.91| 1| HOUSING01 22 | "Star Wars: The Ultimate Action Figure Collection"| 21.38| 1| HOUSING01 23 | "Thomas Jefferson: The Art of Power"| 18.98| 4| POLITICS01 24 | "Origami Fun Kit for Beginners (Dover Fun Kit)"| 10.17| 4| HOUSING01 25 | "Swimming Studies"| 19.8| 2| SPORTS01 26 | "Damaged: The Heartbreaking True Story of a Forgotten Child"| 0.99| 1| POLITICS01 27 | "The Outpost: An Untold Story of American Valor"| 12.99| 4| POLITICS01 28 | "Hockey; The Math of the Game (Sports Illustrated Kids: Sports Math)"| 7.95| 3| SPORTS01 29 | "Outliers: The Story of Success"| 9.83| 1| POLITICS01 30 | "Tasting Beer: An Insider's Guide to the World's Greatest Drink"| 11.31| 5| HOUSING01 31 | -------------------------------------------------------------------------------- /pa5/data/orders.txt: -------------------------------------------------------------------------------- 1 | "I Could Pee on This: And Other Poems by Cats"| 7.49| 1| HOUSING01 2 | "Across Islands and Oceans"| 1.99| 1| SPORTS01 3 | "Soul Surfer"| 7.99| 3| SPORTS01 4 | "The Immortal Life of Henrietta Lacks"| 8.91| 4| POLITICS01 5 | "The Handbuilt Home: 34 Simple Stylish and Budget-Friendly Woodworking Projects for Every Room"| 15.63| 1| HOUSING01 6 | "The History of Surfing"| 31.5| 2| SPORTS01 7 | "Following Atticus (P.S.)"| 2.99| 4| HOUSING01 8 | "Stop Living In This Land. Go To The Everlasting World Of Happiness. Live There Forever."| 7.19| 4| POLITICS01 9 | "Fifty Places to Dive Before You Die: Diving Experts Share the World's Greatest Destinations"| 16.47| 1| SPORTS01 10 | "The Greatest Hockey Stories Ever Told: The Finest Writers on Ice"| 9.71| 1| SPORTS01 11 | "SuperFreakonomics"| 3.79| 1| POLITICS01 12 | "How to Create a Mind: The Secret of Human Thought Revealed"| 16.4| 2| POLITICS01 13 | "The Endurance: Shackleton's Legendary Antarctic Expedition"| 21.29| 5| SPORTS01 14 | "The California Surf Project"| 22.16| 1| SPORTS01 15 | "The Joy of Hate: How to Triumph over Whiners in the Age of Phony Outrage"| 15.6| 2| POLITICS01 16 | "A Letter to My Dog: Notes to Our Best Friends"| 16.47| 1| HOUSING01 17 | "How Children Succeed: Grit, Curiosity, and the Hidden Power of Character"| 9.45| 1| POLITICS01 18 | "Underwater Dogs"| 11.6| 1| HOUSING01 19 | "365 Cats 2013 Page-A-Day Calendar"| 9.66| 4| HOUSING01 20 | "Soul Surfer: A True Story of Faith, Family, and Fighting to Get Back on the Board"| 7.99| 2| SPORTS01 21 | "My Year in Meals"| 16.91| 1| HOUSING01 22 | "Star Wars: The Ultimate Action Figure Collection"| 21.38| 1| HOUSING01 23 | "Thomas Jefferson: The Art of Power"| 18.98| 4| POLITICS01 24 | "Origami Fun Kit for Beginners (Dover Fun Kit)"| 10.17| 4| HOUSING01 25 | "Swimming Studies"| 19.8| 2| SPORTS01 26 | "Damaged: The Heartbreaking True Story of a Forgotten Child"| 0.99| 1| POLITICS01 27 | "The Outpost: An Untold Story of American Valor"| 12.99| 4| POLITICS01 28 | "Hockey; The Math of the Game (Sports Illustrated Kids: Sports Math)"| 7.95| 3| SPORTS01 29 | "Outliers: The Story of Success"| 9.83| 1| POLITICS01 30 | "Tasting Beer: An Insider's Guide to the World's Greatest Drink"| 11.31| 5| HOUSING01 31 | -------------------------------------------------------------------------------- /data/finalreport.txt: -------------------------------------------------------------------------------- 1 | === BEGIN CUSTOMER INFO === 2 | ### BALANCE ### 3 | Customer name: Brian Russell 4 | Customer ID number: 1 5 | Remaining credit balance after all purchases (a dollar amount): 336.13 6 | ### SUCCESSFUL ORDERS ### 7 | "I Could Pee on This: And Other Poems by Cats"| 7.49| 492.51 8 | "Across Islands and Oceans"| 1.99| 490.52 9 | "The Handbuilt Home: 34 Simple Stylish and Budget-Friendly Woodworking Projects for Every Room"| 15.63| 474.89 10 | "Fifty Places to Dive Before You Die: Diving Experts Share the World's Greatest Destinations"| 16.47| 458.42 11 | "The Greatest Hockey Stories Ever Told: The Finest Writers on Ice"| 9.71| 448.71 12 | "SuperFreakonomics"| 3.79| 444.92 13 | "The California Surf Project"| 22.16| 422.76 14 | "A Letter to My Dog: Notes to Our Best Friends"| 16.47| 406.29 15 | "How Children Succeed: Grit, Curiosity, and the Hidden Power of Character"| 9.45| 396.84 16 | "Underwater Dogs"| 11.6| 385.24 17 | "My Year in Meals"| 16.91| 368.33 18 | "Star Wars: The Ultimate Action Figure Collection"| 21.38| 346.95 19 | "Damaged: The Heartbreaking True Story of a Forgotten Child"| 0.99| 345.96 20 | "Outliers: The Story of Success| 9.83| 336.13 21 | ### REJECTED ORDERS ### 22 | === END CUSTOMER INFO === 23 | 24 | === BEGIN CUSTOMER INFO === 25 | ### BALANCE ### 26 | Customer name: Ying Zhan 27 | Customer ID number: 2 28 | Remaining credit balance after all purchases (a dollar amount): 1.5 29 | ### SUCCESSFUL ORDERS ### 30 | "The History of Surfing"| 31.5| 33.5 31 | "How to Create a Mind: The Secret of Human Thought Revealed"| 16.4| 17.1 32 | "The Joy of Hate: How to Triumph over Whiners in the Age of Phony Outrage"| 15.6| 1.5 33 | ### REJECTED ORDERS ### 34 | "Soul Surfer: A True Story of Faith, Family, and Fighting to Get Back on the Board"| 7.99 35 | "Swimming Studies"| 19.8 36 | === END CUSTOMER INFO === 37 | 38 | === BEGIN CUSTOMER INFO === 39 | ### BALANCE ### 40 | Customer name: Sejong Yoon 41 | Customer ID number: 3 42 | Remaining credit balance after all purchases (a dollar amount): 2.01 43 | ### SUCCESSFUL ORDERS ### 44 | "Soul Surfer"| 7.99| 2.01 45 | ### REJECTED ORDERS ### 46 | "Hockey; The Math of the Game (Sports Illustrated Kids: Sports Math)"| 7.95 47 | === END CUSTOMER INFO === 48 | 49 | === BEGIN CUSTOMER INFO === 50 | ### BALANCE ### 51 | Customer name: Yuanzhen Gu 52 | Customer ID number: 4 53 | Remaining credit balance after all purchases (a dollar amount): 9.11 54 | ### SUCCESSFUL ORDERS ### 55 | "The Immortal Life of Henrietta Lacks"| 8.91| 71.09 56 | "Following Atticus (P.S.)"| 2.99| 68.1 57 | "Stop Living In This Land. Go To The Everlasting World Of Happiness. Live There Forever."| 7.19| 60.91 58 | "365 Cats 2013 Page-A-Day Calendar"| 9.66| 51.25 59 | "Thomas Jefferson: The Art of Power"| 18.98| 32.27 60 | "Origami Fun Kit for Beginners (Dover Fun Kit)"| 10.17| 22.1 61 | "The Outpost: An Untold Story of American Valor"| 12.99| 9.11 62 | ### REJECTED ORDERS ### 63 | === END CUSTOMER INFO === 64 | 65 | === BEGIN CUSTOMER INFO === 66 | ### BALANCE ### 67 | Customer name: Zi Yan 68 | Customer ID number: 5 69 | Remaining credit balance after all purchases (a dollar amount): 67.4 70 | ### SUCCESSFUL ORDERS ### 71 | "The Endurance: Shackleton's Legendary Antarctic Expedition"| 21.29| 78.71 72 | "Tasting Beer: An Insider's Guide to the World's Greatest Drink"| 11.31| 67.4 73 | ### REJECTED ORDERS ### 74 | === END CUSTOMER INFO === -------------------------------------------------------------------------------- /pa5/data/finalreport.txt: -------------------------------------------------------------------------------- 1 | === BEGIN CUSTOMER INFO === 2 | ### BALANCE ### 3 | Customer name: Brian Russell 4 | Customer ID number: 1 5 | Remaining credit balance after all purchases (a dollar amount): 336.13 6 | ### SUCCESSFUL ORDERS ### 7 | "I Could Pee on This: And Other Poems by Cats"| 7.49| 492.51 8 | "Across Islands and Oceans"| 1.99| 490.52 9 | "The Handbuilt Home: 34 Simple Stylish and Budget-Friendly Woodworking Projects for Every Room"| 15.63| 474.89 10 | "Fifty Places to Dive Before You Die: Diving Experts Share the World's Greatest Destinations"| 16.47| 458.42 11 | "The Greatest Hockey Stories Ever Told: The Finest Writers on Ice"| 9.71| 448.71 12 | "SuperFreakonomics"| 3.79| 444.92 13 | "The California Surf Project"| 22.16| 422.76 14 | "A Letter to My Dog: Notes to Our Best Friends"| 16.47| 406.29 15 | "How Children Succeed: Grit, Curiosity, and the Hidden Power of Character"| 9.45| 396.84 16 | "Underwater Dogs"| 11.6| 385.24 17 | "My Year in Meals"| 16.91| 368.33 18 | "Star Wars: The Ultimate Action Figure Collection"| 21.38| 346.95 19 | "Damaged: The Heartbreaking True Story of a Forgotten Child"| 0.99| 345.96 20 | "Outliers: The Story of Success| 9.83| 336.13 21 | ### REJECTED ORDERS ### 22 | === END CUSTOMER INFO === 23 | 24 | === BEGIN CUSTOMER INFO === 25 | ### BALANCE ### 26 | Customer name: Ying Zhan 27 | Customer ID number: 2 28 | Remaining credit balance after all purchases (a dollar amount): 1.5 29 | ### SUCCESSFUL ORDERS ### 30 | "The History of Surfing"| 31.5| 33.5 31 | "How to Create a Mind: The Secret of Human Thought Revealed"| 16.4| 17.1 32 | "The Joy of Hate: How to Triumph over Whiners in the Age of Phony Outrage"| 15.6| 1.5 33 | ### REJECTED ORDERS ### 34 | "Soul Surfer: A True Story of Faith, Family, and Fighting to Get Back on the Board"| 7.99 35 | "Swimming Studies"| 19.8 36 | === END CUSTOMER INFO === 37 | 38 | === BEGIN CUSTOMER INFO === 39 | ### BALANCE ### 40 | Customer name: Sejong Yoon 41 | Customer ID number: 3 42 | Remaining credit balance after all purchases (a dollar amount): 2.01 43 | ### SUCCESSFUL ORDERS ### 44 | "Soul Surfer"| 7.99| 2.01 45 | ### REJECTED ORDERS ### 46 | "Hockey; The Math of the Game (Sports Illustrated Kids: Sports Math)"| 7.95 47 | === END CUSTOMER INFO === 48 | 49 | === BEGIN CUSTOMER INFO === 50 | ### BALANCE ### 51 | Customer name: Yuanzhen Gu 52 | Customer ID number: 4 53 | Remaining credit balance after all purchases (a dollar amount): 9.11 54 | ### SUCCESSFUL ORDERS ### 55 | "The Immortal Life of Henrietta Lacks"| 8.91| 71.09 56 | "Following Atticus (P.S.)"| 2.99| 68.1 57 | "Stop Living In This Land. Go To The Everlasting World Of Happiness. Live There Forever."| 7.19| 60.91 58 | "365 Cats 2013 Page-A-Day Calendar"| 9.66| 51.25 59 | "Thomas Jefferson: The Art of Power"| 18.98| 32.27 60 | "Origami Fun Kit for Beginners (Dover Fun Kit)"| 10.17| 22.1 61 | "The Outpost: An Untold Story of American Valor"| 12.99| 9.11 62 | ### REJECTED ORDERS ### 63 | === END CUSTOMER INFO === 64 | 65 | === BEGIN CUSTOMER INFO === 66 | ### BALANCE ### 67 | Customer name: Zi Yan 68 | Customer ID number: 5 69 | Remaining credit balance after all purchases (a dollar amount): 67.4 70 | ### SUCCESSFUL ORDERS ### 71 | "The Endurance: Shackleton's Legendary Antarctic Expedition"| 21.29| 78.71 72 | "Tasting Beer: An Insider's Guide to the World's Greatest Drink"| 11.31| 67.4 73 | ### REJECTED ORDERS ### 74 | === END CUSTOMER INFO === -------------------------------------------------------------------------------- /src/customer.h: -------------------------------------------------------------------------------- 1 | #ifndef CUSTOMER_H 2 | #define CUSTOMER_H 3 | 4 | #include 5 | #include 6 | 7 | #include "order.h" 8 | #include "uthash.h" 9 | #include "utlist.h" 10 | #include "category.h" 11 | 12 | 13 | /* Customer struct */ 14 | typedef struct { 15 | int id; /* key */ 16 | char name[20]; 17 | float balance; 18 | char address[20]; 19 | char state[20]; 20 | char zip[10]; 21 | order *successful_orders; 22 | order *failed_orders; 23 | 24 | pthread_mutex_t lock; 25 | UT_hash_handle hh; /* makes this structure hashable */ 26 | } customer; 27 | 28 | /* Global hashtable for customers */ 29 | customer *customers = NULL; 30 | 31 | /* Add order to customer's list of orders */ 32 | void add_order(customer *c, order *o) { 33 | if (o->success == SUCCESS) { 34 | if (c->successful_orders == NULL) { 35 | c->successful_orders = o; 36 | o->next = NULL; 37 | } 38 | else { 39 | o->next = c->successful_orders; 40 | c->successful_orders = o; 41 | } 42 | } 43 | else if (o->success == FAILURE) { 44 | if (c->failed_orders == NULL) { 45 | c->failed_orders = o; 46 | o->next = NULL; 47 | } 48 | else { 49 | o->next = c->failed_orders; 50 | c->failed_orders = o; 51 | } 52 | } else { 53 | fprintf(stderr, "tried adding incomplete order to %s's order list\n", c->name); 54 | } 55 | } 56 | 57 | /* Add customer to hashtable */ 58 | void add_customer(customer *c) { 59 | HASH_ADD_INT( customers, id, c ); /* id: name of key field */ 60 | } 61 | 62 | /* Get customer from hashtable */ 63 | /* s can be null on failure */ 64 | customer *find_customer(int cust_id) { 65 | customer *s; 66 | 67 | HASH_FIND_INT( customers, &cust_id, s); 68 | return s; 69 | } 70 | 71 | /* Remove customer from hashtable */ 72 | void remove_customer(customer *c) { 73 | HASH_DEL( customers, c); 74 | } 75 | 76 | 77 | void print_summary(customer *c) { 78 | order *o; 79 | float subtotal, total = 0;; 80 | 81 | /* Print out order summary for each user */ 82 | for(c = customers; c != NULL; c = c->hh.next) { 83 | subtotal = 0; 84 | printf("=== BEGIN CUSTOMER INFO ===\n"); 85 | printf("### BALANCE ###\n"); 86 | printf("Customer name: %s\n", c->name); 87 | printf("Customer ID number: %d\n", c->id); 88 | printf("Remaining credit balance after all purchases (a dollar amount): $%'.2f\n", c->balance); 89 | printf("### SUCCESSFUL ORDERS ###\n"); 90 | for (o = c->successful_orders; o != NULL; o = o->next) { 91 | printf("\"%s\"| %'.2f| %'.2f\n", o->title, o->price, o->remaining_balance); 92 | subtotal += o->price; 93 | } 94 | printf("Total amount spent by customer: $%'.2f\n", subtotal); 95 | total += subtotal; 96 | printf("### REJECTED ORDERS ###\n"); 97 | for (o = c->failed_orders; o != NULL; o = o->next) { 98 | printf("\"%s\"| %'.2f\n", o->title, o->price); 99 | } 100 | printf("### END CUSTOMER INFO ###\n"); 101 | } 102 | printf("Total amount spent by all customer: $%'.2f\n", total); 103 | } 104 | 105 | 106 | void destroy_db() { 107 | customer *c, *d; 108 | order *o, *p; 109 | category *q, *r; 110 | 111 | /* Destroy all CUSTOMERS and their ORDERS */ 112 | c = customers; 113 | while(c != NULL) { 114 | o = c->successful_orders; 115 | while (o != NULL) { 116 | p = o->next; 117 | free(o); 118 | o = p; 119 | } 120 | o = c->failed_orders; 121 | while (o != NULL) { 122 | p = o->next; 123 | free(o); 124 | o = p; 125 | } 126 | HASH_DEL(customers, c); 127 | d = c; 128 | c = c->hh.next; 129 | free(d); 130 | } 131 | 132 | /* Destroy all queues */ 133 | q = queues; 134 | while (q != NULL) { 135 | HASH_DEL(queues, q); 136 | r = q; 137 | free(q->val); 138 | q = q->hh.next; 139 | free(r); 140 | } 141 | 142 | } 143 | 144 | #endif 145 | -------------------------------------------------------------------------------- /pa5/src/customer.h: -------------------------------------------------------------------------------- 1 | #ifndef CUSTOMER_H 2 | #define CUSTOMER_H 3 | 4 | #include 5 | #include 6 | 7 | #include "order.h" 8 | #include "uthash.h" 9 | #include "utlist.h" 10 | #include "category.h" 11 | 12 | 13 | /* Customer struct */ 14 | typedef struct { 15 | int id; /* key */ 16 | char name[20]; 17 | float balance; 18 | char address[20]; 19 | char state[20]; 20 | char zip[10]; 21 | order *successful_orders; 22 | order *failed_orders; 23 | 24 | pthread_mutex_t lock; 25 | UT_hash_handle hh; /* makes this structure hashable */ 26 | } customer; 27 | 28 | /* Global hashtable for customers */ 29 | customer *customers = NULL; 30 | 31 | /* Add order to customer's list of orders */ 32 | void add_order(customer *c, order *o) { 33 | if (o->success == SUCCESS) { 34 | if (c->successful_orders == NULL) { 35 | c->successful_orders = o; 36 | o->next = NULL; 37 | } 38 | else { 39 | o->next = c->successful_orders; 40 | c->successful_orders = o; 41 | } 42 | } 43 | else if (o->success == FAILURE) { 44 | if (c->failed_orders == NULL) { 45 | c->failed_orders = o; 46 | o->next = NULL; 47 | } 48 | else { 49 | o->next = c->failed_orders; 50 | c->failed_orders = o; 51 | } 52 | } else { 53 | fprintf(stderr, "tried adding incomplete order to %s's order list\n", c->name); 54 | } 55 | } 56 | 57 | /* Add customer to hashtable */ 58 | void add_customer(customer *c) { 59 | HASH_ADD_INT( customers, id, c ); /* id: name of key field */ 60 | } 61 | 62 | /* Get customer from hashtable */ 63 | /* s can be null on failure */ 64 | customer *find_customer(int cust_id) { 65 | customer *s; 66 | 67 | HASH_FIND_INT( customers, &cust_id, s); 68 | return s; 69 | } 70 | 71 | /* Remove customer from hashtable */ 72 | void remove_customer(customer *c) { 73 | HASH_DEL( customers, c); 74 | } 75 | 76 | 77 | void print_summary(customer *c) { 78 | order *o; 79 | float subtotal, total = 0;; 80 | 81 | /* Print out order summary for each user */ 82 | for(c = customers; c != NULL; c = c->hh.next) { 83 | subtotal = 0; 84 | printf("=== BEGIN CUSTOMER INFO ===\n"); 85 | printf("### BALANCE ###\n"); 86 | printf("Customer name: %s\n", c->name); 87 | printf("Customer ID number: %d\n", c->id); 88 | printf("Remaining credit balance after all purchases (a dollar amount): $%'.2f\n", c->balance); 89 | printf("### SUCCESSFUL ORDERS ###\n"); 90 | for (o = c->successful_orders; o != NULL; o = o->next) { 91 | printf("\"%s\"| %'.2f| %'.2f\n", o->title, o->price, o->remaining_balance); 92 | subtotal += o->price; 93 | } 94 | printf("Total amount spent by customer: $%'.2f\n", subtotal); 95 | total += subtotal; 96 | printf("### REJECTED ORDERS ###\n"); 97 | for (o = c->failed_orders; o != NULL; o = o->next) { 98 | printf("\"%s\"| %'.2f\n", o->title, o->price); 99 | } 100 | printf("### END CUSTOMER INFO ###\n"); 101 | } 102 | printf("Total amount spent by all customer: $%'.2f\n", total); 103 | } 104 | 105 | 106 | void destroy_db() { 107 | customer *c, *d; 108 | order *o, *p; 109 | category *q, *r; 110 | 111 | /* Destroy all CUSTOMERS and their ORDERS */ 112 | c = customers; 113 | while(c != NULL) { 114 | o = c->successful_orders; 115 | while (o != NULL) { 116 | p = o->next; 117 | free(o); 118 | o = p; 119 | } 120 | o = c->failed_orders; 121 | while (o != NULL) { 122 | p = o->next; 123 | free(o); 124 | o = p; 125 | } 126 | HASH_DEL(customers, c); 127 | d = c; 128 | c = c->hh.next; 129 | free(d); 130 | } 131 | 132 | /* Destroy all queues */ 133 | q = queues; 134 | while (q != NULL) { 135 | HASH_DEL(queues, q); 136 | r = q; 137 | free(q->val); 138 | q = q->hh.next; 139 | free(r); 140 | } 141 | 142 | } 143 | 144 | #endif 145 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "order.h" 5 | #include "customer.h" 6 | #include "category.h" 7 | 8 | 9 | void *worker(void *queue) { 10 | customer *c; 11 | order *o; 12 | int err; 13 | category *q = (category*)queue; 14 | 15 | while(1) { 16 | /* Get lock */ 17 | pthread_mutex_lock(&q->lock); 18 | 19 | /* check to see if queue is empty */ 20 | if (q->next != NULL) { 21 | /* if queue not empty, get first item, remove from queue */ 22 | o = dequeue(q->cat); 23 | 24 | /* get customer from database using find_customer */ 25 | c = find_customer(o->customer_id); 26 | 27 | /* validate customer ID */ 28 | if (c == NULL) { 29 | fprintf(stderr, "invalid customer id %d\n", o->customer_id); 30 | continue; 31 | } 32 | 33 | pthread_mutex_lock(&(c->lock)); // LOCK CUSTOMER 34 | 35 | /* check customer balance */ 36 | 37 | if (c->balance >= o->price) { /* if balance is > transaction amount */ 38 | c->balance -= o->price; /* remove amount from customer balance */ 39 | o->success = SUCCESS; /* set order status to successful */ 40 | o->remaining_balance = c->balance; /* show customer's balance on order */ 41 | } else { 42 | o->success = FAILURE; /* set order status to failure if order doesn't succeed */ 43 | } 44 | add_order(c, o); /* add order to customer's proper order list */ 45 | err = pthread_mutex_unlock(&(c->lock)); // UNLOCK CUSTOMER 46 | pthread_mutex_unlock(&q->lock); 47 | } 48 | else if (q->producing) { 49 | /* Wait for signal */ 50 | pthread_cond_wait(&(q->waiter), &(q->lock)); // WAITS, BLOCKS 51 | /* Got signal */ 52 | pthread_mutex_unlock(&(q->lock)); 53 | } 54 | else { 55 | /* Done consuming this queue */ 56 | pthread_mutex_unlock(&q->lock); 57 | return NULL; 58 | } 59 | } 60 | return NULL; 61 | } 62 | 63 | int main(int argc, char **argv) { 64 | customer *c; 65 | category *q; 66 | order *o; 67 | FILE *f; 68 | char line[300]; 69 | int err; 70 | pthread_t *tid; 71 | 72 | if (argc != 3) { 73 | printf("Usage: ./order path/to/database path/to/orders\n"); 74 | exit(1); 75 | } 76 | 77 | 78 | f = fopen(argv[1], "r"); 79 | if (f == NULL) { 80 | fprintf(stderr, "Customer file not found\n"); 81 | exit(1); 82 | } 83 | 84 | /* Add customers to database */ 85 | while (fgets(line, 300, f)) { 86 | c = malloc(sizeof(customer)); 87 | strcpy(c->name, strtok(line, "\"")); /* Assign name */ 88 | c->id = atoi(strtok(NULL, "|")); /* Assign ID */ 89 | c->balance = atof(strtok(NULL, "|")); /* Assign balance */ 90 | strcpy(c->address, strtok(NULL, "|")); /* Assign address */ 91 | strcpy(c->state, strtok(NULL, "|")); /* Assign state */ 92 | strcpy(c->zip, strtok(NULL, "|")); /* Assign zipcode */ 93 | pthread_mutex_init(&c->lock, NULL); /* Initializes the lock */ 94 | add_customer(c); 95 | } 96 | 97 | fclose(f); /* Close database.txt */ 98 | 99 | f = fopen(argv[2], "r"); 100 | if (f == NULL) { 101 | fprintf(stderr, "Order file not found\n"); 102 | exit(1); 103 | } 104 | 105 | while (fgets(line, 300, f)) { 106 | o = malloc(sizeof(order)); 107 | strcpy(o->title, strtok(line, "\"")); /* Assign title */ 108 | o->price = atof(strtok(NULL, "|")); 109 | o->customer_id = atoi(strtok(NULL, "|")); 110 | strcpy(o->category, strtok(NULL, " \r\n")); 111 | o->success = PENDING; 112 | 113 | /* If it's a new category, add new dummy node queue to hashtable */ 114 | if (!(q = find_queue(o->category))) { 115 | 116 | q = new_category(o->category, NULL); 117 | tid = malloc(sizeof(pthread_t)); 118 | add_queue(q); 119 | err = pthread_create(tid, NULL, &worker, q); 120 | q->val = tid; 121 | if (err != 0) { 122 | fprintf(stderr, "Thread could not be created!\n"); 123 | exit(1); 124 | } 125 | } 126 | 127 | enqueue(q, new_category(o->category, o)); 128 | } 129 | fclose(f); 130 | 131 | /* Send wakeup call to queues */ 132 | for(q = queues; q != NULL; q = q->hh.next) { 133 | pthread_mutex_lock(&q->lock); 134 | q->producing = 0; 135 | pthread_cond_signal(&q->waiter); 136 | pthread_mutex_unlock(&q->lock); 137 | } 138 | 139 | for(q = queues; q != NULL; q = q->hh.next) { 140 | pthread_join(*(pthread_t*)q->val, NULL); 141 | } 142 | 143 | print_summary(c); 144 | 145 | destroy_db(); 146 | 147 | return 0; 148 | } 149 | -------------------------------------------------------------------------------- /pa5/src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "order.h" 5 | #include "customer.h" 6 | #include "category.h" 7 | 8 | 9 | void *worker(void *queue) { 10 | customer *c; 11 | order *o; 12 | int err; 13 | category *q = (category*)queue; 14 | 15 | while(1) { 16 | /* Get lock */ 17 | pthread_mutex_lock(&q->lock); 18 | 19 | /* check to see if queue is empty */ 20 | if (q->next != NULL) { 21 | /* if queue not empty, get first item, remove from queue */ 22 | o = dequeue(q->cat); 23 | 24 | /* get customer from database using find_customer */ 25 | c = find_customer(o->customer_id); 26 | 27 | /* validate customer ID */ 28 | if (c == NULL) { 29 | fprintf(stderr, "invalid customer id %d\n", o->customer_id); 30 | continue; 31 | } 32 | 33 | pthread_mutex_lock(&(c->lock)); // LOCK CUSTOMER 34 | 35 | /* check customer balance */ 36 | 37 | if (c->balance >= o->price) { /* if balance is > transaction amount */ 38 | c->balance -= o->price; /* remove amount from customer balance */ 39 | o->success = SUCCESS; /* set order status to successful */ 40 | o->remaining_balance = c->balance; /* show customer's balance on order */ 41 | } else { 42 | o->success = FAILURE; /* set order status to failure if order doesn't succeed */ 43 | } 44 | add_order(c, o); /* add order to customer's proper order list */ 45 | err = pthread_mutex_unlock(&(c->lock)); // UNLOCK CUSTOMER 46 | pthread_mutex_unlock(&q->lock); 47 | } 48 | else if (q->producing) { 49 | /* Wait for signal */ 50 | pthread_cond_wait(&(q->waiter), &(q->lock)); // WAITS, BLOCKS 51 | /* Got signal */ 52 | pthread_mutex_unlock(&(q->lock)); 53 | } 54 | else { 55 | /* Done consuming this queue */ 56 | pthread_mutex_unlock(&q->lock); 57 | return NULL; 58 | } 59 | } 60 | return NULL; 61 | } 62 | 63 | int main(int argc, char **argv) { 64 | customer *c; 65 | category *q; 66 | order *o; 67 | FILE *f; 68 | char line[300]; 69 | int err; 70 | pthread_t *tid; 71 | 72 | if (argc != 3) { 73 | printf("Usage: ./order path/to/database path/to/orders\n"); 74 | exit(1); 75 | } 76 | 77 | 78 | f = fopen(argv[1], "r"); 79 | if (f == NULL) { 80 | fprintf(stderr, "Customer file not found\n"); 81 | exit(1); 82 | } 83 | 84 | /* Add customers to database */ 85 | while (fgets(line, 300, f)) { 86 | c = malloc(sizeof(customer)); 87 | strcpy(c->name, strtok(line, "\"")); /* Assign name */ 88 | c->id = atoi(strtok(NULL, "|")); /* Assign ID */ 89 | c->balance = atof(strtok(NULL, "|")); /* Assign balance */ 90 | strcpy(c->address, strtok(NULL, "|")); /* Assign address */ 91 | strcpy(c->state, strtok(NULL, "|")); /* Assign state */ 92 | strcpy(c->zip, strtok(NULL, "|")); /* Assign zipcode */ 93 | pthread_mutex_init(&c->lock, NULL); /* Initializes the lock */ 94 | add_customer(c); 95 | } 96 | 97 | fclose(f); /* Close database.txt */ 98 | 99 | f = fopen(argv[2], "r"); 100 | if (f == NULL) { 101 | fprintf(stderr, "Order file not found\n"); 102 | exit(1); 103 | } 104 | 105 | while (fgets(line, 300, f)) { 106 | o = malloc(sizeof(order)); 107 | strcpy(o->title, strtok(line, "\"")); /* Assign title */ 108 | o->price = atof(strtok(NULL, "|")); 109 | o->customer_id = atoi(strtok(NULL, "|")); 110 | strcpy(o->category, strtok(NULL, " \r\n")); 111 | o->success = PENDING; 112 | 113 | /* If it's a new category, add new dummy node queue to hashtable */ 114 | if (!(q = find_queue(o->category))) { 115 | 116 | q = new_category(o->category, NULL); 117 | tid = malloc(sizeof(pthread_t)); 118 | add_queue(q); 119 | err = pthread_create(tid, NULL, &worker, q); 120 | q->val = tid; 121 | if (err != 0) { 122 | fprintf(stderr, "Thread could not be created!\n"); 123 | exit(1); 124 | } 125 | } 126 | 127 | enqueue(q, new_category(o->category, o)); 128 | } 129 | fclose(f); 130 | 131 | /* Send wakeup call to queues */ 132 | for(q = queues; q != NULL; q = q->hh.next) { 133 | pthread_mutex_lock(&q->lock); 134 | q->producing = 0; 135 | pthread_cond_signal(&q->waiter); 136 | pthread_mutex_unlock(&q->lock); 137 | } 138 | 139 | for(q = queues; q != NULL; q = q->hh.next) { 140 | pthread_join(*(pthread_t*)q->val, NULL); 141 | } 142 | 143 | print_summary(c); 144 | 145 | destroy_db(); 146 | 147 | return 0; 148 | } 149 | -------------------------------------------------------------------------------- /src/utlist.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2007-2013, Troy D. Hanson http://troydhanson.github.com/uthash/ 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 12 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 13 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 14 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 15 | OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 16 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 17 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 18 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 19 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 20 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 21 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | */ 23 | 24 | #ifndef UTLIST_H 25 | #define UTLIST_H 26 | 27 | #define UTLIST_VERSION 1.9.8 28 | 29 | #include 30 | 31 | /* 32 | * This file contains macros to manipulate singly and doubly-linked lists. 33 | * 34 | * 1. LL_ macros: singly-linked lists. 35 | * 2. DL_ macros: doubly-linked lists. 36 | * 3. CDL_ macros: circular doubly-linked lists. 37 | * 38 | * To use singly-linked lists, your structure must have a "next" pointer. 39 | * To use doubly-linked lists, your structure must "prev" and "next" pointers. 40 | * Either way, the pointer to the head of the list must be initialized to NULL. 41 | * 42 | * ----------------.EXAMPLE ------------------------- 43 | * struct item { 44 | * int id; 45 | * struct item *prev, *next; 46 | * } 47 | * 48 | * struct item *list = NULL: 49 | * 50 | * int main() { 51 | * struct item *item; 52 | * ... allocate and populate item ... 53 | * DL_APPEND(list, item); 54 | * } 55 | * -------------------------------------------------- 56 | * 57 | * For doubly-linked lists, the append and delete macros are O(1) 58 | * For singly-linked lists, append and delete are O(n) but prepend is O(1) 59 | * The sort macro is O(n log(n)) for all types of single/double/circular lists. 60 | */ 61 | 62 | /* These macros use decltype or the earlier __typeof GNU extension. 63 | As decltype is only available in newer compilers (VS2010 or gcc 4.3+ 64 | when compiling c++ code), this code uses whatever method is needed 65 | or, for VS2008 where neither is available, uses casting workarounds. */ 66 | #ifdef _MSC_VER /* MS compiler */ 67 | #if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ 68 | #define LDECLTYPE(x) decltype(x) 69 | #else /* VS2008 or older (or VS2010 in C mode) */ 70 | #define NO_DECLTYPE 71 | #define LDECLTYPE(x) char* 72 | #endif 73 | #elif defined(__ICCARM__) 74 | #define NO_DECLTYPE 75 | #define LDECLTYPE(x) char* 76 | #else /* GNU, Sun and other compilers */ 77 | #define LDECLTYPE(x) __typeof(x) 78 | #endif 79 | 80 | /* for VS2008 we use some workarounds to get around the lack of decltype, 81 | * namely, we always reassign our tmp variable to the list head if we need 82 | * to dereference its prev/next pointers, and save/restore the real head.*/ 83 | #ifdef NO_DECLTYPE 84 | #define _SV(elt,list) _tmp = (char*)(list); {char **_alias = (char**)&(list); *_alias = (elt); } 85 | #define _NEXT(elt,list,next) ((char*)((list)->next)) 86 | #define _NEXTASGN(elt,list,to,next) { char **_alias = (char**)&((list)->next); *_alias=(char*)(to); } 87 | /* #define _PREV(elt,list,prev) ((char*)((list)->prev)) */ 88 | #define _PREVASGN(elt,list,to,prev) { char **_alias = (char**)&((list)->prev); *_alias=(char*)(to); } 89 | #define _RS(list) { char **_alias = (char**)&(list); *_alias=_tmp; } 90 | #define _CASTASGN(a,b) { char **_alias = (char**)&(a); *_alias=(char*)(b); } 91 | #else 92 | #define _SV(elt,list) 93 | #define _NEXT(elt,list,next) ((elt)->next) 94 | #define _NEXTASGN(elt,list,to,next) ((elt)->next)=(to) 95 | /* #define _PREV(elt,list,prev) ((elt)->prev) */ 96 | #define _PREVASGN(elt,list,to,prev) ((elt)->prev)=(to) 97 | #define _RS(list) 98 | #define _CASTASGN(a,b) (a)=(b) 99 | #endif 100 | 101 | /****************************************************************************** 102 | * The sort macro is an adaptation of Simon Tatham's O(n log(n)) mergesort * 103 | * Unwieldy variable names used here to avoid shadowing passed-in variables. * 104 | *****************************************************************************/ 105 | #define LL_SORT(list, cmp) \ 106 | LL_SORT2(list, cmp, next) 107 | 108 | #define LL_SORT2(list, cmp, next) \ 109 | do { \ 110 | LDECLTYPE(list) _ls_p; \ 111 | LDECLTYPE(list) _ls_q; \ 112 | LDECLTYPE(list) _ls_e; \ 113 | LDECLTYPE(list) _ls_tail; \ 114 | int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ 115 | if (list) { \ 116 | _ls_insize = 1; \ 117 | _ls_looping = 1; \ 118 | while (_ls_looping) { \ 119 | _CASTASGN(_ls_p,list); \ 120 | list = NULL; \ 121 | _ls_tail = NULL; \ 122 | _ls_nmerges = 0; \ 123 | while (_ls_p) { \ 124 | _ls_nmerges++; \ 125 | _ls_q = _ls_p; \ 126 | _ls_psize = 0; \ 127 | for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ 128 | _ls_psize++; \ 129 | _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list,next); _RS(list); \ 130 | if (!_ls_q) break; \ 131 | } \ 132 | _ls_qsize = _ls_insize; \ 133 | while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ 134 | if (_ls_psize == 0) { \ 135 | _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \ 136 | _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \ 137 | } else if (_ls_qsize == 0 || !_ls_q) { \ 138 | _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \ 139 | _NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \ 140 | } else if (cmp(_ls_p,_ls_q) <= 0) { \ 141 | _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \ 142 | _NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \ 143 | } else { \ 144 | _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \ 145 | _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \ 146 | } \ 147 | if (_ls_tail) { \ 148 | _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e,next); _RS(list); \ 149 | } else { \ 150 | _CASTASGN(list,_ls_e); \ 151 | } \ 152 | _ls_tail = _ls_e; \ 153 | } \ 154 | _ls_p = _ls_q; \ 155 | } \ 156 | if (_ls_tail) { \ 157 | _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,NULL,next); _RS(list); \ 158 | } \ 159 | if (_ls_nmerges <= 1) { \ 160 | _ls_looping=0; \ 161 | } \ 162 | _ls_insize *= 2; \ 163 | } \ 164 | } \ 165 | } while (0) 166 | 167 | 168 | #define DL_SORT(list, cmp) \ 169 | DL_SORT2(list, cmp, prev, next) 170 | 171 | #define DL_SORT2(list, cmp, prev, next) \ 172 | do { \ 173 | LDECLTYPE(list) _ls_p; \ 174 | LDECLTYPE(list) _ls_q; \ 175 | LDECLTYPE(list) _ls_e; \ 176 | LDECLTYPE(list) _ls_tail; \ 177 | int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ 178 | if (list) { \ 179 | _ls_insize = 1; \ 180 | _ls_looping = 1; \ 181 | while (_ls_looping) { \ 182 | _CASTASGN(_ls_p,list); \ 183 | list = NULL; \ 184 | _ls_tail = NULL; \ 185 | _ls_nmerges = 0; \ 186 | while (_ls_p) { \ 187 | _ls_nmerges++; \ 188 | _ls_q = _ls_p; \ 189 | _ls_psize = 0; \ 190 | for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ 191 | _ls_psize++; \ 192 | _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list,next); _RS(list); \ 193 | if (!_ls_q) break; \ 194 | } \ 195 | _ls_qsize = _ls_insize; \ 196 | while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ 197 | if (_ls_psize == 0) { \ 198 | _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \ 199 | _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \ 200 | } else if (_ls_qsize == 0 || !_ls_q) { \ 201 | _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \ 202 | _NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \ 203 | } else if (cmp(_ls_p,_ls_q) <= 0) { \ 204 | _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \ 205 | _NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \ 206 | } else { \ 207 | _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \ 208 | _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \ 209 | } \ 210 | if (_ls_tail) { \ 211 | _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e,next); _RS(list); \ 212 | } else { \ 213 | _CASTASGN(list,_ls_e); \ 214 | } \ 215 | _SV(_ls_e,list); _PREVASGN(_ls_e,list,_ls_tail,prev); _RS(list); \ 216 | _ls_tail = _ls_e; \ 217 | } \ 218 | _ls_p = _ls_q; \ 219 | } \ 220 | _CASTASGN(list->prev, _ls_tail); \ 221 | _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,NULL,next); _RS(list); \ 222 | if (_ls_nmerges <= 1) { \ 223 | _ls_looping=0; \ 224 | } \ 225 | _ls_insize *= 2; \ 226 | } \ 227 | } \ 228 | } while (0) 229 | 230 | #define CDL_SORT(list, cmp) \ 231 | CDL_SORT2(list, cmp, prev, next) 232 | 233 | #define CDL_SORT2(list, cmp, prev, next) \ 234 | do { \ 235 | LDECLTYPE(list) _ls_p; \ 236 | LDECLTYPE(list) _ls_q; \ 237 | LDECLTYPE(list) _ls_e; \ 238 | LDECLTYPE(list) _ls_tail; \ 239 | LDECLTYPE(list) _ls_oldhead; \ 240 | LDECLTYPE(list) _tmp; \ 241 | int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ 242 | if (list) { \ 243 | _ls_insize = 1; \ 244 | _ls_looping = 1; \ 245 | while (_ls_looping) { \ 246 | _CASTASGN(_ls_p,list); \ 247 | _CASTASGN(_ls_oldhead,list); \ 248 | list = NULL; \ 249 | _ls_tail = NULL; \ 250 | _ls_nmerges = 0; \ 251 | while (_ls_p) { \ 252 | _ls_nmerges++; \ 253 | _ls_q = _ls_p; \ 254 | _ls_psize = 0; \ 255 | for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ 256 | _ls_psize++; \ 257 | _SV(_ls_q,list); \ 258 | if (_NEXT(_ls_q,list,next) == _ls_oldhead) { \ 259 | _ls_q = NULL; \ 260 | } else { \ 261 | _ls_q = _NEXT(_ls_q,list,next); \ 262 | } \ 263 | _RS(list); \ 264 | if (!_ls_q) break; \ 265 | } \ 266 | _ls_qsize = _ls_insize; \ 267 | while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ 268 | if (_ls_psize == 0) { \ 269 | _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \ 270 | _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \ 271 | if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \ 272 | } else if (_ls_qsize == 0 || !_ls_q) { \ 273 | _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \ 274 | _NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \ 275 | if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \ 276 | } else if (cmp(_ls_p,_ls_q) <= 0) { \ 277 | _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \ 278 | _NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \ 279 | if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \ 280 | } else { \ 281 | _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \ 282 | _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \ 283 | if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \ 284 | } \ 285 | if (_ls_tail) { \ 286 | _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e,next); _RS(list); \ 287 | } else { \ 288 | _CASTASGN(list,_ls_e); \ 289 | } \ 290 | _SV(_ls_e,list); _PREVASGN(_ls_e,list,_ls_tail,prev); _RS(list); \ 291 | _ls_tail = _ls_e; \ 292 | } \ 293 | _ls_p = _ls_q; \ 294 | } \ 295 | _CASTASGN(list->prev,_ls_tail); \ 296 | _CASTASGN(_tmp,list); \ 297 | _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_tmp,next); _RS(list); \ 298 | if (_ls_nmerges <= 1) { \ 299 | _ls_looping=0; \ 300 | } \ 301 | _ls_insize *= 2; \ 302 | } \ 303 | } \ 304 | } while (0) 305 | 306 | /****************************************************************************** 307 | * singly linked list macros (non-circular) * 308 | *****************************************************************************/ 309 | #define LL_PREPEND(head,add) \ 310 | LL_PREPEND2(head,add,next) 311 | 312 | #define LL_PREPEND2(head,add,next) \ 313 | do { \ 314 | (add)->next = head; \ 315 | head = add; \ 316 | } while (0) 317 | 318 | #define LL_CONCAT(head1,head2) \ 319 | LL_CONCAT2(head1,head2,next) 320 | 321 | #define LL_CONCAT2(head1,head2,next) \ 322 | do { \ 323 | LDECLTYPE(head1) _tmp; \ 324 | if (head1) { \ 325 | _tmp = head1; \ 326 | while (_tmp->next) { _tmp = _tmp->next; } \ 327 | _tmp->next=(head2); \ 328 | } else { \ 329 | (head1)=(head2); \ 330 | } \ 331 | } while (0) 332 | 333 | #define LL_APPEND(head,add) \ 334 | LL_APPEND2(head,add,next) 335 | 336 | #define LL_APPEND2(head,add,next) \ 337 | do { \ 338 | LDECLTYPE(head) _tmp; \ 339 | (add)->next=NULL; \ 340 | if (head) { \ 341 | _tmp = head; \ 342 | while (_tmp->next) { _tmp = _tmp->next; } \ 343 | _tmp->next=(add); \ 344 | } else { \ 345 | (head)=(add); \ 346 | } \ 347 | } while (0) 348 | 349 | #define LL_DELETE(head,del) \ 350 | LL_DELETE2(head,del,next) 351 | 352 | #define LL_DELETE2(head,del,next) \ 353 | do { \ 354 | LDECLTYPE(head) _tmp; \ 355 | if ((head) == (del)) { \ 356 | (head)=(head)->next; \ 357 | } else { \ 358 | _tmp = head; \ 359 | while (_tmp->next && (_tmp->next != (del))) { \ 360 | _tmp = _tmp->next; \ 361 | } \ 362 | if (_tmp->next) { \ 363 | _tmp->next = ((del)->next); \ 364 | } \ 365 | } \ 366 | } while (0) 367 | 368 | /* Here are VS2008 replacements for LL_APPEND and LL_DELETE */ 369 | #define LL_APPEND_VS2008(head,add) \ 370 | LL_APPEND2_VS2008(head,add,next) 371 | 372 | #define LL_APPEND2_VS2008(head,add,next) \ 373 | do { \ 374 | if (head) { \ 375 | (add)->next = head; /* use add->next as a temp variable */ \ 376 | while ((add)->next->next) { (add)->next = (add)->next->next; } \ 377 | (add)->next->next=(add); \ 378 | } else { \ 379 | (head)=(add); \ 380 | } \ 381 | (add)->next=NULL; \ 382 | } while (0) 383 | 384 | #define LL_DELETE_VS2008(head,del) \ 385 | LL_DELETE2_VS2008(head,del,next) 386 | 387 | #define LL_DELETE2_VS2008(head,del,next) \ 388 | do { \ 389 | if ((head) == (del)) { \ 390 | (head)=(head)->next; \ 391 | } else { \ 392 | char *_tmp = (char*)(head); \ 393 | while ((head)->next && ((head)->next != (del))) { \ 394 | head = (head)->next; \ 395 | } \ 396 | if ((head)->next) { \ 397 | (head)->next = ((del)->next); \ 398 | } \ 399 | { \ 400 | char **_head_alias = (char**)&(head); \ 401 | *_head_alias = _tmp; \ 402 | } \ 403 | } \ 404 | } while (0) 405 | #ifdef NO_DECLTYPE 406 | #undef LL_APPEND 407 | #define LL_APPEND LL_APPEND_VS2008 408 | #undef LL_DELETE 409 | #define LL_DELETE LL_DELETE_VS2008 410 | #undef LL_DELETE2 411 | #define LL_DELETE2 LL_DELETE2_VS2008 412 | #undef LL_APPEND2 413 | #define LL_APPEND2 LL_APPEND2_VS2008 414 | #undef LL_CONCAT /* no LL_CONCAT_VS2008 */ 415 | #undef DL_CONCAT /* no DL_CONCAT_VS2008 */ 416 | #endif 417 | /* end VS2008 replacements */ 418 | 419 | #define LL_COUNT(head,el,counter) \ 420 | LL_COUNT2(head,el,counter,next) \ 421 | 422 | #define LL_COUNT2(head,el,counter,next) \ 423 | { \ 424 | counter = 0; \ 425 | LL_FOREACH2(head,el,next){ ++counter; } \ 426 | } 427 | 428 | #define LL_FOREACH(head,el) \ 429 | LL_FOREACH2(head,el,next) 430 | 431 | #define LL_FOREACH2(head,el,next) \ 432 | for(el=head;el;el=(el)->next) 433 | 434 | #define LL_FOREACH_SAFE(head,el,tmp) \ 435 | LL_FOREACH_SAFE2(head,el,tmp,next) 436 | 437 | #define LL_FOREACH_SAFE2(head,el,tmp,next) \ 438 | for((el)=(head);(el) && (tmp = (el)->next, 1); (el) = tmp) 439 | 440 | #define LL_SEARCH_SCALAR(head,out,field,val) \ 441 | LL_SEARCH_SCALAR2(head,out,field,val,next) 442 | 443 | #define LL_SEARCH_SCALAR2(head,out,field,val,next) \ 444 | do { \ 445 | LL_FOREACH2(head,out,next) { \ 446 | if ((out)->field == (val)) break; \ 447 | } \ 448 | } while(0) 449 | 450 | #define LL_SEARCH(head,out,elt,cmp) \ 451 | LL_SEARCH2(head,out,elt,cmp,next) 452 | 453 | #define LL_SEARCH2(head,out,elt,cmp,next) \ 454 | do { \ 455 | LL_FOREACH2(head,out,next) { \ 456 | if ((cmp(out,elt))==0) break; \ 457 | } \ 458 | } while(0) 459 | 460 | #define LL_REPLACE_ELEM(head, el, add) \ 461 | do { \ 462 | LDECLTYPE(head) _tmp; \ 463 | assert(head != NULL); \ 464 | assert(el != NULL); \ 465 | assert(add != NULL); \ 466 | (add)->next = (el)->next; \ 467 | if ((head) == (el)) { \ 468 | (head) = (add); \ 469 | } else { \ 470 | _tmp = head; \ 471 | while (_tmp->next && (_tmp->next != (el))) { \ 472 | _tmp = _tmp->next; \ 473 | } \ 474 | if (_tmp->next) { \ 475 | _tmp->next = (add); \ 476 | } \ 477 | } \ 478 | } while (0) 479 | 480 | #define LL_PREPEND_ELEM(head, el, add) \ 481 | do { \ 482 | LDECLTYPE(head) _tmp; \ 483 | assert(head != NULL); \ 484 | assert(el != NULL); \ 485 | assert(add != NULL); \ 486 | (add)->next = (el); \ 487 | if ((head) == (el)) { \ 488 | (head) = (add); \ 489 | } else { \ 490 | _tmp = head; \ 491 | while (_tmp->next && (_tmp->next != (el))) { \ 492 | _tmp = _tmp->next; \ 493 | } \ 494 | if (_tmp->next) { \ 495 | _tmp->next = (add); \ 496 | } \ 497 | } \ 498 | } while (0) \ 499 | 500 | 501 | /****************************************************************************** 502 | * doubly linked list macros (non-circular) * 503 | *****************************************************************************/ 504 | #define DL_PREPEND(head,add) \ 505 | DL_PREPEND2(head,add,prev,next) 506 | 507 | #define DL_PREPEND2(head,add,prev,next) \ 508 | do { \ 509 | (add)->next = head; \ 510 | if (head) { \ 511 | (add)->prev = (head)->prev; \ 512 | (head)->prev = (add); \ 513 | } else { \ 514 | (add)->prev = (add); \ 515 | } \ 516 | (head) = (add); \ 517 | } while (0) 518 | 519 | #define DL_APPEND(head,add) \ 520 | DL_APPEND2(head,add,prev,next) 521 | 522 | #define DL_APPEND2(head,add,prev,next) \ 523 | do { \ 524 | if (head) { \ 525 | (add)->prev = (head)->prev; \ 526 | (head)->prev->next = (add); \ 527 | (head)->prev = (add); \ 528 | (add)->next = NULL; \ 529 | } else { \ 530 | (head)=(add); \ 531 | (head)->prev = (head); \ 532 | (head)->next = NULL; \ 533 | } \ 534 | } while (0) 535 | 536 | #define DL_CONCAT(head1,head2) \ 537 | DL_CONCAT2(head1,head2,prev,next) 538 | 539 | #define DL_CONCAT2(head1,head2,prev,next) \ 540 | do { \ 541 | LDECLTYPE(head1) _tmp; \ 542 | if (head2) { \ 543 | if (head1) { \ 544 | _tmp = (head2)->prev; \ 545 | (head2)->prev = (head1)->prev; \ 546 | (head1)->prev->next = (head2); \ 547 | (head1)->prev = _tmp; \ 548 | } else { \ 549 | (head1)=(head2); \ 550 | } \ 551 | } \ 552 | } while (0) 553 | 554 | #define DL_DELETE(head,del) \ 555 | DL_DELETE2(head,del,prev,next) 556 | 557 | #define DL_DELETE2(head,del,prev,next) \ 558 | do { \ 559 | assert((del)->prev != NULL); \ 560 | if ((del)->prev == (del)) { \ 561 | (head)=NULL; \ 562 | } else if ((del)==(head)) { \ 563 | (del)->next->prev = (del)->prev; \ 564 | (head) = (del)->next; \ 565 | } else { \ 566 | (del)->prev->next = (del)->next; \ 567 | if ((del)->next) { \ 568 | (del)->next->prev = (del)->prev; \ 569 | } else { \ 570 | (head)->prev = (del)->prev; \ 571 | } \ 572 | } \ 573 | } while (0) 574 | 575 | #define DL_COUNT(head,el,counter) \ 576 | DL_COUNT2(head,el,counter,next) \ 577 | 578 | #define DL_COUNT2(head,el,counter,next) \ 579 | { \ 580 | counter = 0; \ 581 | DL_FOREACH2(head,el,next){ ++counter; } \ 582 | } 583 | 584 | #define DL_FOREACH(head,el) \ 585 | DL_FOREACH2(head,el,next) 586 | 587 | #define DL_FOREACH2(head,el,next) \ 588 | for(el=head;el;el=(el)->next) 589 | 590 | /* this version is safe for deleting the elements during iteration */ 591 | #define DL_FOREACH_SAFE(head,el,tmp) \ 592 | DL_FOREACH_SAFE2(head,el,tmp,next) 593 | 594 | #define DL_FOREACH_SAFE2(head,el,tmp,next) \ 595 | for((el)=(head);(el) && (tmp = (el)->next, 1); (el) = tmp) 596 | 597 | /* these are identical to their singly-linked list counterparts */ 598 | #define DL_SEARCH_SCALAR LL_SEARCH_SCALAR 599 | #define DL_SEARCH LL_SEARCH 600 | #define DL_SEARCH_SCALAR2 LL_SEARCH_SCALAR2 601 | #define DL_SEARCH2 LL_SEARCH2 602 | 603 | #define DL_REPLACE_ELEM(head, el, add) \ 604 | do { \ 605 | assert(head != NULL); \ 606 | assert(el != NULL); \ 607 | assert(add != NULL); \ 608 | if ((head) == (el)) { \ 609 | (head) = (add); \ 610 | (add)->next = (el)->next; \ 611 | if ((el)->next == NULL) { \ 612 | (add)->prev = (add); \ 613 | } else { \ 614 | (add)->prev = (el)->prev; \ 615 | (add)->next->prev = (add); \ 616 | } \ 617 | } else { \ 618 | (add)->next = (el)->next; \ 619 | (add)->prev = (el)->prev; \ 620 | (add)->prev->next = (add); \ 621 | if ((el)->next == NULL) { \ 622 | (head)->prev = (add); \ 623 | } else { \ 624 | (add)->next->prev = (add); \ 625 | } \ 626 | } \ 627 | } while (0) 628 | 629 | #define DL_PREPEND_ELEM(head, el, add) \ 630 | do { \ 631 | assert(head != NULL); \ 632 | assert(el != NULL); \ 633 | assert(add != NULL); \ 634 | (add)->next = (el); \ 635 | (add)->prev = (el)->prev; \ 636 | (el)->prev = (add); \ 637 | if ((head) == (el)) { \ 638 | (head) = (add); \ 639 | } else { \ 640 | (add)->prev->next = (add); \ 641 | } \ 642 | } while (0) \ 643 | 644 | 645 | /****************************************************************************** 646 | * circular doubly linked list macros * 647 | *****************************************************************************/ 648 | #define CDL_PREPEND(head,add) \ 649 | CDL_PREPEND2(head,add,prev,next) 650 | 651 | #define CDL_PREPEND2(head,add,prev,next) \ 652 | do { \ 653 | if (head) { \ 654 | (add)->prev = (head)->prev; \ 655 | (add)->next = (head); \ 656 | (head)->prev = (add); \ 657 | (add)->prev->next = (add); \ 658 | } else { \ 659 | (add)->prev = (add); \ 660 | (add)->next = (add); \ 661 | } \ 662 | (head)=(add); \ 663 | } while (0) 664 | 665 | #define CDL_DELETE(head,del) \ 666 | CDL_DELETE2(head,del,prev,next) 667 | 668 | #define CDL_DELETE2(head,del,prev,next) \ 669 | do { \ 670 | if ( ((head)==(del)) && ((head)->next == (head))) { \ 671 | (head) = 0L; \ 672 | } else { \ 673 | (del)->next->prev = (del)->prev; \ 674 | (del)->prev->next = (del)->next; \ 675 | if ((del) == (head)) (head)=(del)->next; \ 676 | } \ 677 | } while (0) 678 | 679 | #define CDL_COUNT(head,el,counter) \ 680 | CDL_COUNT2(head,el,counter,next) \ 681 | 682 | #define CDL_COUNT2(head, el, counter,next) \ 683 | { \ 684 | counter = 0; \ 685 | CDL_FOREACH2(head,el,next){ ++counter; } \ 686 | } 687 | 688 | #define CDL_FOREACH(head,el) \ 689 | CDL_FOREACH2(head,el,next) 690 | 691 | #define CDL_FOREACH2(head,el,next) \ 692 | for(el=head;el;el=((el)->next==head ? 0L : (el)->next)) 693 | 694 | #define CDL_FOREACH_SAFE(head,el,tmp1,tmp2) \ 695 | CDL_FOREACH_SAFE2(head,el,tmp1,tmp2,prev,next) 696 | 697 | #define CDL_FOREACH_SAFE2(head,el,tmp1,tmp2,prev,next) \ 698 | for((el)=(head), ((tmp1)=(head)?((head)->prev):NULL); \ 699 | (el) && ((tmp2)=(el)->next, 1); \ 700 | ((el) = (((el)==(tmp1)) ? 0L : (tmp2)))) 701 | 702 | #define CDL_SEARCH_SCALAR(head,out,field,val) \ 703 | CDL_SEARCH_SCALAR2(head,out,field,val,next) 704 | 705 | #define CDL_SEARCH_SCALAR2(head,out,field,val,next) \ 706 | do { \ 707 | CDL_FOREACH2(head,out,next) { \ 708 | if ((out)->field == (val)) break; \ 709 | } \ 710 | } while(0) 711 | 712 | #define CDL_SEARCH(head,out,elt,cmp) \ 713 | CDL_SEARCH2(head,out,elt,cmp,next) 714 | 715 | #define CDL_SEARCH2(head,out,elt,cmp,next) \ 716 | do { \ 717 | CDL_FOREACH2(head,out,next) { \ 718 | if ((cmp(out,elt))==0) break; \ 719 | } \ 720 | } while(0) 721 | 722 | #define CDL_REPLACE_ELEM(head, el, add) \ 723 | do { \ 724 | assert(head != NULL); \ 725 | assert(el != NULL); \ 726 | assert(add != NULL); \ 727 | if ((el)->next == (el)) { \ 728 | (add)->next = (add); \ 729 | (add)->prev = (add); \ 730 | (head) = (add); \ 731 | } else { \ 732 | (add)->next = (el)->next; \ 733 | (add)->prev = (el)->prev; \ 734 | (add)->next->prev = (add); \ 735 | (add)->prev->next = (add); \ 736 | if ((head) == (el)) { \ 737 | (head) = (add); \ 738 | } \ 739 | } \ 740 | } while (0) 741 | 742 | #define CDL_PREPEND_ELEM(head, el, add) \ 743 | do { \ 744 | assert(head != NULL); \ 745 | assert(el != NULL); \ 746 | assert(add != NULL); \ 747 | (add)->next = (el); \ 748 | (add)->prev = (el)->prev; \ 749 | (el)->prev = (add); \ 750 | (add)->prev->next = (add); \ 751 | if ((head) == (el)) { \ 752 | (head) = (add); \ 753 | } \ 754 | } while (0) \ 755 | 756 | #endif /* UTLIST_H */ 757 | 758 | -------------------------------------------------------------------------------- /pa5/src/utlist.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2007-2013, Troy D. Hanson http://troydhanson.github.com/uthash/ 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 12 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 13 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 14 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 15 | OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 16 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 17 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 18 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 19 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 20 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 21 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | */ 23 | 24 | #ifndef UTLIST_H 25 | #define UTLIST_H 26 | 27 | #define UTLIST_VERSION 1.9.8 28 | 29 | #include 30 | 31 | /* 32 | * This file contains macros to manipulate singly and doubly-linked lists. 33 | * 34 | * 1. LL_ macros: singly-linked lists. 35 | * 2. DL_ macros: doubly-linked lists. 36 | * 3. CDL_ macros: circular doubly-linked lists. 37 | * 38 | * To use singly-linked lists, your structure must have a "next" pointer. 39 | * To use doubly-linked lists, your structure must "prev" and "next" pointers. 40 | * Either way, the pointer to the head of the list must be initialized to NULL. 41 | * 42 | * ----------------.EXAMPLE ------------------------- 43 | * struct item { 44 | * int id; 45 | * struct item *prev, *next; 46 | * } 47 | * 48 | * struct item *list = NULL: 49 | * 50 | * int main() { 51 | * struct item *item; 52 | * ... allocate and populate item ... 53 | * DL_APPEND(list, item); 54 | * } 55 | * -------------------------------------------------- 56 | * 57 | * For doubly-linked lists, the append and delete macros are O(1) 58 | * For singly-linked lists, append and delete are O(n) but prepend is O(1) 59 | * The sort macro is O(n log(n)) for all types of single/double/circular lists. 60 | */ 61 | 62 | /* These macros use decltype or the earlier __typeof GNU extension. 63 | As decltype is only available in newer compilers (VS2010 or gcc 4.3+ 64 | when compiling c++ code), this code uses whatever method is needed 65 | or, for VS2008 where neither is available, uses casting workarounds. */ 66 | #ifdef _MSC_VER /* MS compiler */ 67 | #if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ 68 | #define LDECLTYPE(x) decltype(x) 69 | #else /* VS2008 or older (or VS2010 in C mode) */ 70 | #define NO_DECLTYPE 71 | #define LDECLTYPE(x) char* 72 | #endif 73 | #elif defined(__ICCARM__) 74 | #define NO_DECLTYPE 75 | #define LDECLTYPE(x) char* 76 | #else /* GNU, Sun and other compilers */ 77 | #define LDECLTYPE(x) __typeof(x) 78 | #endif 79 | 80 | /* for VS2008 we use some workarounds to get around the lack of decltype, 81 | * namely, we always reassign our tmp variable to the list head if we need 82 | * to dereference its prev/next pointers, and save/restore the real head.*/ 83 | #ifdef NO_DECLTYPE 84 | #define _SV(elt,list) _tmp = (char*)(list); {char **_alias = (char**)&(list); *_alias = (elt); } 85 | #define _NEXT(elt,list,next) ((char*)((list)->next)) 86 | #define _NEXTASGN(elt,list,to,next) { char **_alias = (char**)&((list)->next); *_alias=(char*)(to); } 87 | /* #define _PREV(elt,list,prev) ((char*)((list)->prev)) */ 88 | #define _PREVASGN(elt,list,to,prev) { char **_alias = (char**)&((list)->prev); *_alias=(char*)(to); } 89 | #define _RS(list) { char **_alias = (char**)&(list); *_alias=_tmp; } 90 | #define _CASTASGN(a,b) { char **_alias = (char**)&(a); *_alias=(char*)(b); } 91 | #else 92 | #define _SV(elt,list) 93 | #define _NEXT(elt,list,next) ((elt)->next) 94 | #define _NEXTASGN(elt,list,to,next) ((elt)->next)=(to) 95 | /* #define _PREV(elt,list,prev) ((elt)->prev) */ 96 | #define _PREVASGN(elt,list,to,prev) ((elt)->prev)=(to) 97 | #define _RS(list) 98 | #define _CASTASGN(a,b) (a)=(b) 99 | #endif 100 | 101 | /****************************************************************************** 102 | * The sort macro is an adaptation of Simon Tatham's O(n log(n)) mergesort * 103 | * Unwieldy variable names used here to avoid shadowing passed-in variables. * 104 | *****************************************************************************/ 105 | #define LL_SORT(list, cmp) \ 106 | LL_SORT2(list, cmp, next) 107 | 108 | #define LL_SORT2(list, cmp, next) \ 109 | do { \ 110 | LDECLTYPE(list) _ls_p; \ 111 | LDECLTYPE(list) _ls_q; \ 112 | LDECLTYPE(list) _ls_e; \ 113 | LDECLTYPE(list) _ls_tail; \ 114 | int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ 115 | if (list) { \ 116 | _ls_insize = 1; \ 117 | _ls_looping = 1; \ 118 | while (_ls_looping) { \ 119 | _CASTASGN(_ls_p,list); \ 120 | list = NULL; \ 121 | _ls_tail = NULL; \ 122 | _ls_nmerges = 0; \ 123 | while (_ls_p) { \ 124 | _ls_nmerges++; \ 125 | _ls_q = _ls_p; \ 126 | _ls_psize = 0; \ 127 | for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ 128 | _ls_psize++; \ 129 | _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list,next); _RS(list); \ 130 | if (!_ls_q) break; \ 131 | } \ 132 | _ls_qsize = _ls_insize; \ 133 | while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ 134 | if (_ls_psize == 0) { \ 135 | _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \ 136 | _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \ 137 | } else if (_ls_qsize == 0 || !_ls_q) { \ 138 | _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \ 139 | _NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \ 140 | } else if (cmp(_ls_p,_ls_q) <= 0) { \ 141 | _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \ 142 | _NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \ 143 | } else { \ 144 | _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \ 145 | _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \ 146 | } \ 147 | if (_ls_tail) { \ 148 | _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e,next); _RS(list); \ 149 | } else { \ 150 | _CASTASGN(list,_ls_e); \ 151 | } \ 152 | _ls_tail = _ls_e; \ 153 | } \ 154 | _ls_p = _ls_q; \ 155 | } \ 156 | if (_ls_tail) { \ 157 | _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,NULL,next); _RS(list); \ 158 | } \ 159 | if (_ls_nmerges <= 1) { \ 160 | _ls_looping=0; \ 161 | } \ 162 | _ls_insize *= 2; \ 163 | } \ 164 | } \ 165 | } while (0) 166 | 167 | 168 | #define DL_SORT(list, cmp) \ 169 | DL_SORT2(list, cmp, prev, next) 170 | 171 | #define DL_SORT2(list, cmp, prev, next) \ 172 | do { \ 173 | LDECLTYPE(list) _ls_p; \ 174 | LDECLTYPE(list) _ls_q; \ 175 | LDECLTYPE(list) _ls_e; \ 176 | LDECLTYPE(list) _ls_tail; \ 177 | int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ 178 | if (list) { \ 179 | _ls_insize = 1; \ 180 | _ls_looping = 1; \ 181 | while (_ls_looping) { \ 182 | _CASTASGN(_ls_p,list); \ 183 | list = NULL; \ 184 | _ls_tail = NULL; \ 185 | _ls_nmerges = 0; \ 186 | while (_ls_p) { \ 187 | _ls_nmerges++; \ 188 | _ls_q = _ls_p; \ 189 | _ls_psize = 0; \ 190 | for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ 191 | _ls_psize++; \ 192 | _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list,next); _RS(list); \ 193 | if (!_ls_q) break; \ 194 | } \ 195 | _ls_qsize = _ls_insize; \ 196 | while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ 197 | if (_ls_psize == 0) { \ 198 | _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \ 199 | _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \ 200 | } else if (_ls_qsize == 0 || !_ls_q) { \ 201 | _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \ 202 | _NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \ 203 | } else if (cmp(_ls_p,_ls_q) <= 0) { \ 204 | _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \ 205 | _NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \ 206 | } else { \ 207 | _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \ 208 | _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \ 209 | } \ 210 | if (_ls_tail) { \ 211 | _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e,next); _RS(list); \ 212 | } else { \ 213 | _CASTASGN(list,_ls_e); \ 214 | } \ 215 | _SV(_ls_e,list); _PREVASGN(_ls_e,list,_ls_tail,prev); _RS(list); \ 216 | _ls_tail = _ls_e; \ 217 | } \ 218 | _ls_p = _ls_q; \ 219 | } \ 220 | _CASTASGN(list->prev, _ls_tail); \ 221 | _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,NULL,next); _RS(list); \ 222 | if (_ls_nmerges <= 1) { \ 223 | _ls_looping=0; \ 224 | } \ 225 | _ls_insize *= 2; \ 226 | } \ 227 | } \ 228 | } while (0) 229 | 230 | #define CDL_SORT(list, cmp) \ 231 | CDL_SORT2(list, cmp, prev, next) 232 | 233 | #define CDL_SORT2(list, cmp, prev, next) \ 234 | do { \ 235 | LDECLTYPE(list) _ls_p; \ 236 | LDECLTYPE(list) _ls_q; \ 237 | LDECLTYPE(list) _ls_e; \ 238 | LDECLTYPE(list) _ls_tail; \ 239 | LDECLTYPE(list) _ls_oldhead; \ 240 | LDECLTYPE(list) _tmp; \ 241 | int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ 242 | if (list) { \ 243 | _ls_insize = 1; \ 244 | _ls_looping = 1; \ 245 | while (_ls_looping) { \ 246 | _CASTASGN(_ls_p,list); \ 247 | _CASTASGN(_ls_oldhead,list); \ 248 | list = NULL; \ 249 | _ls_tail = NULL; \ 250 | _ls_nmerges = 0; \ 251 | while (_ls_p) { \ 252 | _ls_nmerges++; \ 253 | _ls_q = _ls_p; \ 254 | _ls_psize = 0; \ 255 | for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ 256 | _ls_psize++; \ 257 | _SV(_ls_q,list); \ 258 | if (_NEXT(_ls_q,list,next) == _ls_oldhead) { \ 259 | _ls_q = NULL; \ 260 | } else { \ 261 | _ls_q = _NEXT(_ls_q,list,next); \ 262 | } \ 263 | _RS(list); \ 264 | if (!_ls_q) break; \ 265 | } \ 266 | _ls_qsize = _ls_insize; \ 267 | while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ 268 | if (_ls_psize == 0) { \ 269 | _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \ 270 | _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \ 271 | if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \ 272 | } else if (_ls_qsize == 0 || !_ls_q) { \ 273 | _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \ 274 | _NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \ 275 | if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \ 276 | } else if (cmp(_ls_p,_ls_q) <= 0) { \ 277 | _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \ 278 | _NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \ 279 | if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \ 280 | } else { \ 281 | _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \ 282 | _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \ 283 | if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \ 284 | } \ 285 | if (_ls_tail) { \ 286 | _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e,next); _RS(list); \ 287 | } else { \ 288 | _CASTASGN(list,_ls_e); \ 289 | } \ 290 | _SV(_ls_e,list); _PREVASGN(_ls_e,list,_ls_tail,prev); _RS(list); \ 291 | _ls_tail = _ls_e; \ 292 | } \ 293 | _ls_p = _ls_q; \ 294 | } \ 295 | _CASTASGN(list->prev,_ls_tail); \ 296 | _CASTASGN(_tmp,list); \ 297 | _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_tmp,next); _RS(list); \ 298 | if (_ls_nmerges <= 1) { \ 299 | _ls_looping=0; \ 300 | } \ 301 | _ls_insize *= 2; \ 302 | } \ 303 | } \ 304 | } while (0) 305 | 306 | /****************************************************************************** 307 | * singly linked list macros (non-circular) * 308 | *****************************************************************************/ 309 | #define LL_PREPEND(head,add) \ 310 | LL_PREPEND2(head,add,next) 311 | 312 | #define LL_PREPEND2(head,add,next) \ 313 | do { \ 314 | (add)->next = head; \ 315 | head = add; \ 316 | } while (0) 317 | 318 | #define LL_CONCAT(head1,head2) \ 319 | LL_CONCAT2(head1,head2,next) 320 | 321 | #define LL_CONCAT2(head1,head2,next) \ 322 | do { \ 323 | LDECLTYPE(head1) _tmp; \ 324 | if (head1) { \ 325 | _tmp = head1; \ 326 | while (_tmp->next) { _tmp = _tmp->next; } \ 327 | _tmp->next=(head2); \ 328 | } else { \ 329 | (head1)=(head2); \ 330 | } \ 331 | } while (0) 332 | 333 | #define LL_APPEND(head,add) \ 334 | LL_APPEND2(head,add,next) 335 | 336 | #define LL_APPEND2(head,add,next) \ 337 | do { \ 338 | LDECLTYPE(head) _tmp; \ 339 | (add)->next=NULL; \ 340 | if (head) { \ 341 | _tmp = head; \ 342 | while (_tmp->next) { _tmp = _tmp->next; } \ 343 | _tmp->next=(add); \ 344 | } else { \ 345 | (head)=(add); \ 346 | } \ 347 | } while (0) 348 | 349 | #define LL_DELETE(head,del) \ 350 | LL_DELETE2(head,del,next) 351 | 352 | #define LL_DELETE2(head,del,next) \ 353 | do { \ 354 | LDECLTYPE(head) _tmp; \ 355 | if ((head) == (del)) { \ 356 | (head)=(head)->next; \ 357 | } else { \ 358 | _tmp = head; \ 359 | while (_tmp->next && (_tmp->next != (del))) { \ 360 | _tmp = _tmp->next; \ 361 | } \ 362 | if (_tmp->next) { \ 363 | _tmp->next = ((del)->next); \ 364 | } \ 365 | } \ 366 | } while (0) 367 | 368 | /* Here are VS2008 replacements for LL_APPEND and LL_DELETE */ 369 | #define LL_APPEND_VS2008(head,add) \ 370 | LL_APPEND2_VS2008(head,add,next) 371 | 372 | #define LL_APPEND2_VS2008(head,add,next) \ 373 | do { \ 374 | if (head) { \ 375 | (add)->next = head; /* use add->next as a temp variable */ \ 376 | while ((add)->next->next) { (add)->next = (add)->next->next; } \ 377 | (add)->next->next=(add); \ 378 | } else { \ 379 | (head)=(add); \ 380 | } \ 381 | (add)->next=NULL; \ 382 | } while (0) 383 | 384 | #define LL_DELETE_VS2008(head,del) \ 385 | LL_DELETE2_VS2008(head,del,next) 386 | 387 | #define LL_DELETE2_VS2008(head,del,next) \ 388 | do { \ 389 | if ((head) == (del)) { \ 390 | (head)=(head)->next; \ 391 | } else { \ 392 | char *_tmp = (char*)(head); \ 393 | while ((head)->next && ((head)->next != (del))) { \ 394 | head = (head)->next; \ 395 | } \ 396 | if ((head)->next) { \ 397 | (head)->next = ((del)->next); \ 398 | } \ 399 | { \ 400 | char **_head_alias = (char**)&(head); \ 401 | *_head_alias = _tmp; \ 402 | } \ 403 | } \ 404 | } while (0) 405 | #ifdef NO_DECLTYPE 406 | #undef LL_APPEND 407 | #define LL_APPEND LL_APPEND_VS2008 408 | #undef LL_DELETE 409 | #define LL_DELETE LL_DELETE_VS2008 410 | #undef LL_DELETE2 411 | #define LL_DELETE2 LL_DELETE2_VS2008 412 | #undef LL_APPEND2 413 | #define LL_APPEND2 LL_APPEND2_VS2008 414 | #undef LL_CONCAT /* no LL_CONCAT_VS2008 */ 415 | #undef DL_CONCAT /* no DL_CONCAT_VS2008 */ 416 | #endif 417 | /* end VS2008 replacements */ 418 | 419 | #define LL_COUNT(head,el,counter) \ 420 | LL_COUNT2(head,el,counter,next) \ 421 | 422 | #define LL_COUNT2(head,el,counter,next) \ 423 | { \ 424 | counter = 0; \ 425 | LL_FOREACH2(head,el,next){ ++counter; } \ 426 | } 427 | 428 | #define LL_FOREACH(head,el) \ 429 | LL_FOREACH2(head,el,next) 430 | 431 | #define LL_FOREACH2(head,el,next) \ 432 | for(el=head;el;el=(el)->next) 433 | 434 | #define LL_FOREACH_SAFE(head,el,tmp) \ 435 | LL_FOREACH_SAFE2(head,el,tmp,next) 436 | 437 | #define LL_FOREACH_SAFE2(head,el,tmp,next) \ 438 | for((el)=(head);(el) && (tmp = (el)->next, 1); (el) = tmp) 439 | 440 | #define LL_SEARCH_SCALAR(head,out,field,val) \ 441 | LL_SEARCH_SCALAR2(head,out,field,val,next) 442 | 443 | #define LL_SEARCH_SCALAR2(head,out,field,val,next) \ 444 | do { \ 445 | LL_FOREACH2(head,out,next) { \ 446 | if ((out)->field == (val)) break; \ 447 | } \ 448 | } while(0) 449 | 450 | #define LL_SEARCH(head,out,elt,cmp) \ 451 | LL_SEARCH2(head,out,elt,cmp,next) 452 | 453 | #define LL_SEARCH2(head,out,elt,cmp,next) \ 454 | do { \ 455 | LL_FOREACH2(head,out,next) { \ 456 | if ((cmp(out,elt))==0) break; \ 457 | } \ 458 | } while(0) 459 | 460 | #define LL_REPLACE_ELEM(head, el, add) \ 461 | do { \ 462 | LDECLTYPE(head) _tmp; \ 463 | assert(head != NULL); \ 464 | assert(el != NULL); \ 465 | assert(add != NULL); \ 466 | (add)->next = (el)->next; \ 467 | if ((head) == (el)) { \ 468 | (head) = (add); \ 469 | } else { \ 470 | _tmp = head; \ 471 | while (_tmp->next && (_tmp->next != (el))) { \ 472 | _tmp = _tmp->next; \ 473 | } \ 474 | if (_tmp->next) { \ 475 | _tmp->next = (add); \ 476 | } \ 477 | } \ 478 | } while (0) 479 | 480 | #define LL_PREPEND_ELEM(head, el, add) \ 481 | do { \ 482 | LDECLTYPE(head) _tmp; \ 483 | assert(head != NULL); \ 484 | assert(el != NULL); \ 485 | assert(add != NULL); \ 486 | (add)->next = (el); \ 487 | if ((head) == (el)) { \ 488 | (head) = (add); \ 489 | } else { \ 490 | _tmp = head; \ 491 | while (_tmp->next && (_tmp->next != (el))) { \ 492 | _tmp = _tmp->next; \ 493 | } \ 494 | if (_tmp->next) { \ 495 | _tmp->next = (add); \ 496 | } \ 497 | } \ 498 | } while (0) \ 499 | 500 | 501 | /****************************************************************************** 502 | * doubly linked list macros (non-circular) * 503 | *****************************************************************************/ 504 | #define DL_PREPEND(head,add) \ 505 | DL_PREPEND2(head,add,prev,next) 506 | 507 | #define DL_PREPEND2(head,add,prev,next) \ 508 | do { \ 509 | (add)->next = head; \ 510 | if (head) { \ 511 | (add)->prev = (head)->prev; \ 512 | (head)->prev = (add); \ 513 | } else { \ 514 | (add)->prev = (add); \ 515 | } \ 516 | (head) = (add); \ 517 | } while (0) 518 | 519 | #define DL_APPEND(head,add) \ 520 | DL_APPEND2(head,add,prev,next) 521 | 522 | #define DL_APPEND2(head,add,prev,next) \ 523 | do { \ 524 | if (head) { \ 525 | (add)->prev = (head)->prev; \ 526 | (head)->prev->next = (add); \ 527 | (head)->prev = (add); \ 528 | (add)->next = NULL; \ 529 | } else { \ 530 | (head)=(add); \ 531 | (head)->prev = (head); \ 532 | (head)->next = NULL; \ 533 | } \ 534 | } while (0) 535 | 536 | #define DL_CONCAT(head1,head2) \ 537 | DL_CONCAT2(head1,head2,prev,next) 538 | 539 | #define DL_CONCAT2(head1,head2,prev,next) \ 540 | do { \ 541 | LDECLTYPE(head1) _tmp; \ 542 | if (head2) { \ 543 | if (head1) { \ 544 | _tmp = (head2)->prev; \ 545 | (head2)->prev = (head1)->prev; \ 546 | (head1)->prev->next = (head2); \ 547 | (head1)->prev = _tmp; \ 548 | } else { \ 549 | (head1)=(head2); \ 550 | } \ 551 | } \ 552 | } while (0) 553 | 554 | #define DL_DELETE(head,del) \ 555 | DL_DELETE2(head,del,prev,next) 556 | 557 | #define DL_DELETE2(head,del,prev,next) \ 558 | do { \ 559 | assert((del)->prev != NULL); \ 560 | if ((del)->prev == (del)) { \ 561 | (head)=NULL; \ 562 | } else if ((del)==(head)) { \ 563 | (del)->next->prev = (del)->prev; \ 564 | (head) = (del)->next; \ 565 | } else { \ 566 | (del)->prev->next = (del)->next; \ 567 | if ((del)->next) { \ 568 | (del)->next->prev = (del)->prev; \ 569 | } else { \ 570 | (head)->prev = (del)->prev; \ 571 | } \ 572 | } \ 573 | } while (0) 574 | 575 | #define DL_COUNT(head,el,counter) \ 576 | DL_COUNT2(head,el,counter,next) \ 577 | 578 | #define DL_COUNT2(head,el,counter,next) \ 579 | { \ 580 | counter = 0; \ 581 | DL_FOREACH2(head,el,next){ ++counter; } \ 582 | } 583 | 584 | #define DL_FOREACH(head,el) \ 585 | DL_FOREACH2(head,el,next) 586 | 587 | #define DL_FOREACH2(head,el,next) \ 588 | for(el=head;el;el=(el)->next) 589 | 590 | /* this version is safe for deleting the elements during iteration */ 591 | #define DL_FOREACH_SAFE(head,el,tmp) \ 592 | DL_FOREACH_SAFE2(head,el,tmp,next) 593 | 594 | #define DL_FOREACH_SAFE2(head,el,tmp,next) \ 595 | for((el)=(head);(el) && (tmp = (el)->next, 1); (el) = tmp) 596 | 597 | /* these are identical to their singly-linked list counterparts */ 598 | #define DL_SEARCH_SCALAR LL_SEARCH_SCALAR 599 | #define DL_SEARCH LL_SEARCH 600 | #define DL_SEARCH_SCALAR2 LL_SEARCH_SCALAR2 601 | #define DL_SEARCH2 LL_SEARCH2 602 | 603 | #define DL_REPLACE_ELEM(head, el, add) \ 604 | do { \ 605 | assert(head != NULL); \ 606 | assert(el != NULL); \ 607 | assert(add != NULL); \ 608 | if ((head) == (el)) { \ 609 | (head) = (add); \ 610 | (add)->next = (el)->next; \ 611 | if ((el)->next == NULL) { \ 612 | (add)->prev = (add); \ 613 | } else { \ 614 | (add)->prev = (el)->prev; \ 615 | (add)->next->prev = (add); \ 616 | } \ 617 | } else { \ 618 | (add)->next = (el)->next; \ 619 | (add)->prev = (el)->prev; \ 620 | (add)->prev->next = (add); \ 621 | if ((el)->next == NULL) { \ 622 | (head)->prev = (add); \ 623 | } else { \ 624 | (add)->next->prev = (add); \ 625 | } \ 626 | } \ 627 | } while (0) 628 | 629 | #define DL_PREPEND_ELEM(head, el, add) \ 630 | do { \ 631 | assert(head != NULL); \ 632 | assert(el != NULL); \ 633 | assert(add != NULL); \ 634 | (add)->next = (el); \ 635 | (add)->prev = (el)->prev; \ 636 | (el)->prev = (add); \ 637 | if ((head) == (el)) { \ 638 | (head) = (add); \ 639 | } else { \ 640 | (add)->prev->next = (add); \ 641 | } \ 642 | } while (0) \ 643 | 644 | 645 | /****************************************************************************** 646 | * circular doubly linked list macros * 647 | *****************************************************************************/ 648 | #define CDL_PREPEND(head,add) \ 649 | CDL_PREPEND2(head,add,prev,next) 650 | 651 | #define CDL_PREPEND2(head,add,prev,next) \ 652 | do { \ 653 | if (head) { \ 654 | (add)->prev = (head)->prev; \ 655 | (add)->next = (head); \ 656 | (head)->prev = (add); \ 657 | (add)->prev->next = (add); \ 658 | } else { \ 659 | (add)->prev = (add); \ 660 | (add)->next = (add); \ 661 | } \ 662 | (head)=(add); \ 663 | } while (0) 664 | 665 | #define CDL_DELETE(head,del) \ 666 | CDL_DELETE2(head,del,prev,next) 667 | 668 | #define CDL_DELETE2(head,del,prev,next) \ 669 | do { \ 670 | if ( ((head)==(del)) && ((head)->next == (head))) { \ 671 | (head) = 0L; \ 672 | } else { \ 673 | (del)->next->prev = (del)->prev; \ 674 | (del)->prev->next = (del)->next; \ 675 | if ((del) == (head)) (head)=(del)->next; \ 676 | } \ 677 | } while (0) 678 | 679 | #define CDL_COUNT(head,el,counter) \ 680 | CDL_COUNT2(head,el,counter,next) \ 681 | 682 | #define CDL_COUNT2(head, el, counter,next) \ 683 | { \ 684 | counter = 0; \ 685 | CDL_FOREACH2(head,el,next){ ++counter; } \ 686 | } 687 | 688 | #define CDL_FOREACH(head,el) \ 689 | CDL_FOREACH2(head,el,next) 690 | 691 | #define CDL_FOREACH2(head,el,next) \ 692 | for(el=head;el;el=((el)->next==head ? 0L : (el)->next)) 693 | 694 | #define CDL_FOREACH_SAFE(head,el,tmp1,tmp2) \ 695 | CDL_FOREACH_SAFE2(head,el,tmp1,tmp2,prev,next) 696 | 697 | #define CDL_FOREACH_SAFE2(head,el,tmp1,tmp2,prev,next) \ 698 | for((el)=(head), ((tmp1)=(head)?((head)->prev):NULL); \ 699 | (el) && ((tmp2)=(el)->next, 1); \ 700 | ((el) = (((el)==(tmp1)) ? 0L : (tmp2)))) 701 | 702 | #define CDL_SEARCH_SCALAR(head,out,field,val) \ 703 | CDL_SEARCH_SCALAR2(head,out,field,val,next) 704 | 705 | #define CDL_SEARCH_SCALAR2(head,out,field,val,next) \ 706 | do { \ 707 | CDL_FOREACH2(head,out,next) { \ 708 | if ((out)->field == (val)) break; \ 709 | } \ 710 | } while(0) 711 | 712 | #define CDL_SEARCH(head,out,elt,cmp) \ 713 | CDL_SEARCH2(head,out,elt,cmp,next) 714 | 715 | #define CDL_SEARCH2(head,out,elt,cmp,next) \ 716 | do { \ 717 | CDL_FOREACH2(head,out,next) { \ 718 | if ((cmp(out,elt))==0) break; \ 719 | } \ 720 | } while(0) 721 | 722 | #define CDL_REPLACE_ELEM(head, el, add) \ 723 | do { \ 724 | assert(head != NULL); \ 725 | assert(el != NULL); \ 726 | assert(add != NULL); \ 727 | if ((el)->next == (el)) { \ 728 | (add)->next = (add); \ 729 | (add)->prev = (add); \ 730 | (head) = (add); \ 731 | } else { \ 732 | (add)->next = (el)->next; \ 733 | (add)->prev = (el)->prev; \ 734 | (add)->next->prev = (add); \ 735 | (add)->prev->next = (add); \ 736 | if ((head) == (el)) { \ 737 | (head) = (add); \ 738 | } \ 739 | } \ 740 | } while (0) 741 | 742 | #define CDL_PREPEND_ELEM(head, el, add) \ 743 | do { \ 744 | assert(head != NULL); \ 745 | assert(el != NULL); \ 746 | assert(add != NULL); \ 747 | (add)->next = (el); \ 748 | (add)->prev = (el)->prev; \ 749 | (el)->prev = (add); \ 750 | (add)->prev->next = (add); \ 751 | if ((head) == (el)) { \ 752 | (head) = (add); \ 753 | } \ 754 | } while (0) \ 755 | 756 | #endif /* UTLIST_H */ 757 | 758 | -------------------------------------------------------------------------------- /src/uthash.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2003-2013, Troy D. Hanson http://troydhanson.github.com/uthash/ 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 12 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 13 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 14 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 15 | OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 16 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 17 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 18 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 19 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 20 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 21 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | */ 23 | 24 | #ifndef UTHASH_H 25 | #define UTHASH_H 26 | 27 | #include /* memcmp,strlen */ 28 | #include /* ptrdiff_t */ 29 | #include /* exit() */ 30 | 31 | /* These macros use decltype or the earlier __typeof GNU extension. 32 | As decltype is only available in newer compilers (VS2010 or gcc 4.3+ 33 | when compiling c++ source) this code uses whatever method is needed 34 | or, for VS2008 where neither is available, uses casting workarounds. */ 35 | #ifdef _MSC_VER /* MS compiler */ 36 | #if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ 37 | #define DECLTYPE(x) (decltype(x)) 38 | #else /* VS2008 or older (or VS2010 in C mode) */ 39 | #define NO_DECLTYPE 40 | #define DECLTYPE(x) 41 | #endif 42 | #else /* GNU, Sun and other compilers */ 43 | #define DECLTYPE(x) (__typeof(x)) 44 | #endif 45 | 46 | #ifdef NO_DECLTYPE 47 | #define DECLTYPE_ASSIGN(dst,src) \ 48 | do { \ 49 | char **_da_dst = (char**)(&(dst)); \ 50 | *_da_dst = (char*)(src); \ 51 | } while(0) 52 | #else 53 | #define DECLTYPE_ASSIGN(dst,src) \ 54 | do { \ 55 | (dst) = DECLTYPE(dst)(src); \ 56 | } while(0) 57 | #endif 58 | 59 | /* a number of the hash function use uint32_t which isn't defined on win32 */ 60 | #ifdef _MSC_VER 61 | typedef unsigned int uint32_t; 62 | typedef unsigned char uint8_t; 63 | #else 64 | #include /* uint32_t */ 65 | #endif 66 | 67 | #define UTHASH_VERSION 1.9.8 68 | 69 | #ifndef uthash_fatal 70 | #define uthash_fatal(msg) exit(-1) /* fatal error (out of memory,etc) */ 71 | #endif 72 | #ifndef uthash_malloc 73 | #define uthash_malloc(sz) malloc(sz) /* malloc fcn */ 74 | #endif 75 | #ifndef uthash_free 76 | #define uthash_free(ptr,sz) free(ptr) /* free fcn */ 77 | #endif 78 | 79 | #ifndef uthash_noexpand_fyi 80 | #define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */ 81 | #endif 82 | #ifndef uthash_expand_fyi 83 | #define uthash_expand_fyi(tbl) /* can be defined to log expands */ 84 | #endif 85 | 86 | /* initial number of buckets */ 87 | #define HASH_INITIAL_NUM_BUCKETS 32 /* initial number of buckets */ 88 | #define HASH_INITIAL_NUM_BUCKETS_LOG2 5 /* lg2 of initial number of buckets */ 89 | #define HASH_BKT_CAPACITY_THRESH 10 /* expand when bucket count reaches */ 90 | 91 | /* calculate the element whose hash handle address is hhe */ 92 | #define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho))) 93 | 94 | #define HASH_FIND(hh,head,keyptr,keylen,out) \ 95 | do { \ 96 | unsigned _hf_bkt,_hf_hashv; \ 97 | out=NULL; \ 98 | if (head) { \ 99 | HASH_FCN(keyptr,keylen, (head)->hh.tbl->num_buckets, _hf_hashv, _hf_bkt); \ 100 | if (HASH_BLOOM_TEST((head)->hh.tbl, _hf_hashv)) { \ 101 | HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], \ 102 | keyptr,keylen,out); \ 103 | } \ 104 | } \ 105 | } while (0) 106 | 107 | #ifdef HASH_BLOOM 108 | #define HASH_BLOOM_BITLEN (1ULL << HASH_BLOOM) 109 | #define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8) + ((HASH_BLOOM_BITLEN%8) ? 1:0) 110 | #define HASH_BLOOM_MAKE(tbl) \ 111 | do { \ 112 | (tbl)->bloom_nbits = HASH_BLOOM; \ 113 | (tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN); \ 114 | if (!((tbl)->bloom_bv)) { uthash_fatal( "out of memory"); } \ 115 | memset((tbl)->bloom_bv, 0, HASH_BLOOM_BYTELEN); \ 116 | (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE; \ 117 | } while (0) 118 | 119 | #define HASH_BLOOM_FREE(tbl) \ 120 | do { \ 121 | uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \ 122 | } while (0) 123 | 124 | #define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8] |= (1U << ((idx)%8))) 125 | #define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8] & (1U << ((idx)%8))) 126 | 127 | #define HASH_BLOOM_ADD(tbl,hashv) \ 128 | HASH_BLOOM_BITSET((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1))) 129 | 130 | #define HASH_BLOOM_TEST(tbl,hashv) \ 131 | HASH_BLOOM_BITTEST((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1))) 132 | 133 | #else 134 | #define HASH_BLOOM_MAKE(tbl) 135 | #define HASH_BLOOM_FREE(tbl) 136 | #define HASH_BLOOM_ADD(tbl,hashv) 137 | #define HASH_BLOOM_TEST(tbl,hashv) (1) 138 | #define HASH_BLOOM_BYTELEN 0 139 | #endif 140 | 141 | #define HASH_MAKE_TABLE(hh,head) \ 142 | do { \ 143 | (head)->hh.tbl = (UT_hash_table*)uthash_malloc( \ 144 | sizeof(UT_hash_table)); \ 145 | if (!((head)->hh.tbl)) { uthash_fatal( "out of memory"); } \ 146 | memset((head)->hh.tbl, 0, sizeof(UT_hash_table)); \ 147 | (head)->hh.tbl->tail = &((head)->hh); \ 148 | (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \ 149 | (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \ 150 | (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \ 151 | (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc( \ 152 | HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ 153 | if (! (head)->hh.tbl->buckets) { uthash_fatal( "out of memory"); } \ 154 | memset((head)->hh.tbl->buckets, 0, \ 155 | HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ 156 | HASH_BLOOM_MAKE((head)->hh.tbl); \ 157 | (head)->hh.tbl->signature = HASH_SIGNATURE; \ 158 | } while(0) 159 | 160 | #define HASH_ADD(hh,head,fieldname,keylen_in,add) \ 161 | HASH_ADD_KEYPTR(hh,head,&((add)->fieldname),keylen_in,add) 162 | 163 | #define HASH_REPLACE(hh,head,fieldname,keylen_in,add,replaced) \ 164 | do { \ 165 | replaced=NULL; \ 166 | HASH_FIND(hh,head,&((add)->fieldname),keylen_in,replaced); \ 167 | if (replaced!=NULL) { \ 168 | HASH_DELETE(hh,head,replaced); \ 169 | }; \ 170 | HASH_ADD(hh,head,fieldname,keylen_in,add); \ 171 | } while(0) 172 | 173 | #define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \ 174 | do { \ 175 | unsigned _ha_bkt; \ 176 | (add)->hh.next = NULL; \ 177 | (add)->hh.key = (char*)(keyptr); \ 178 | (add)->hh.keylen = (unsigned)(keylen_in); \ 179 | if (!(head)) { \ 180 | head = (add); \ 181 | (head)->hh.prev = NULL; \ 182 | HASH_MAKE_TABLE(hh,head); \ 183 | } else { \ 184 | (head)->hh.tbl->tail->next = (add); \ 185 | (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \ 186 | (head)->hh.tbl->tail = &((add)->hh); \ 187 | } \ 188 | (head)->hh.tbl->num_items++; \ 189 | (add)->hh.tbl = (head)->hh.tbl; \ 190 | HASH_FCN(keyptr,keylen_in, (head)->hh.tbl->num_buckets, \ 191 | (add)->hh.hashv, _ha_bkt); \ 192 | HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt],&(add)->hh); \ 193 | HASH_BLOOM_ADD((head)->hh.tbl,(add)->hh.hashv); \ 194 | HASH_EMIT_KEY(hh,head,keyptr,keylen_in); \ 195 | HASH_FSCK(hh,head); \ 196 | } while(0) 197 | 198 | #define HASH_TO_BKT( hashv, num_bkts, bkt ) \ 199 | do { \ 200 | bkt = ((hashv) & ((num_bkts) - 1)); \ 201 | } while(0) 202 | 203 | /* delete "delptr" from the hash table. 204 | * "the usual" patch-up process for the app-order doubly-linked-list. 205 | * The use of _hd_hh_del below deserves special explanation. 206 | * These used to be expressed using (delptr) but that led to a bug 207 | * if someone used the same symbol for the head and deletee, like 208 | * HASH_DELETE(hh,users,users); 209 | * We want that to work, but by changing the head (users) below 210 | * we were forfeiting our ability to further refer to the deletee (users) 211 | * in the patch-up process. Solution: use scratch space to 212 | * copy the deletee pointer, then the latter references are via that 213 | * scratch pointer rather than through the repointed (users) symbol. 214 | */ 215 | #define HASH_DELETE(hh,head,delptr) \ 216 | do { \ 217 | unsigned _hd_bkt; \ 218 | struct UT_hash_handle *_hd_hh_del; \ 219 | if ( ((delptr)->hh.prev == NULL) && ((delptr)->hh.next == NULL) ) { \ 220 | uthash_free((head)->hh.tbl->buckets, \ 221 | (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \ 222 | HASH_BLOOM_FREE((head)->hh.tbl); \ 223 | uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ 224 | head = NULL; \ 225 | } else { \ 226 | _hd_hh_del = &((delptr)->hh); \ 227 | if ((delptr) == ELMT_FROM_HH((head)->hh.tbl,(head)->hh.tbl->tail)) { \ 228 | (head)->hh.tbl->tail = \ 229 | (UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) + \ 230 | (head)->hh.tbl->hho); \ 231 | } \ 232 | if ((delptr)->hh.prev) { \ 233 | ((UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) + \ 234 | (head)->hh.tbl->hho))->next = (delptr)->hh.next; \ 235 | } else { \ 236 | DECLTYPE_ASSIGN(head,(delptr)->hh.next); \ 237 | } \ 238 | if (_hd_hh_del->next) { \ 239 | ((UT_hash_handle*)((ptrdiff_t)_hd_hh_del->next + \ 240 | (head)->hh.tbl->hho))->prev = \ 241 | _hd_hh_del->prev; \ 242 | } \ 243 | HASH_TO_BKT( _hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ 244 | HASH_DEL_IN_BKT(hh,(head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \ 245 | (head)->hh.tbl->num_items--; \ 246 | } \ 247 | HASH_FSCK(hh,head); \ 248 | } while (0) 249 | 250 | 251 | /* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */ 252 | #define HASH_FIND_STR(head,findstr,out) \ 253 | HASH_FIND(hh,head,findstr,strlen(findstr),out) 254 | #define HASH_ADD_STR(head,strfield,add) \ 255 | HASH_ADD(hh,head,strfield,strlen(add->strfield),add) 256 | #define HASH_REPLACE_STR(head,strfield,add,replaced) \ 257 | HASH_REPLACE(hh,head,strfield,strlen(add->strfield),add,replaced) 258 | #define HASH_FIND_INT(head,findint,out) \ 259 | HASH_FIND(hh,head,findint,sizeof(int),out) 260 | #define HASH_ADD_INT(head,intfield,add) \ 261 | HASH_ADD(hh,head,intfield,sizeof(int),add) 262 | #define HASH_REPLACE_INT(head,intfield,add,replaced) \ 263 | HASH_REPLACE(hh,head,intfield,sizeof(int),add,replaced) 264 | #define HASH_FIND_PTR(head,findptr,out) \ 265 | HASH_FIND(hh,head,findptr,sizeof(void *),out) 266 | #define HASH_ADD_PTR(head,ptrfield,add) \ 267 | HASH_ADD(hh,head,ptrfield,sizeof(void *),add) 268 | #define HASH_REPLACE_PTR(head,ptrfield,add) \ 269 | HASH_REPLACE(hh,head,ptrfield,sizeof(void *),add,replaced) 270 | #define HASH_DEL(head,delptr) \ 271 | HASH_DELETE(hh,head,delptr) 272 | 273 | /* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined. 274 | * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined. 275 | */ 276 | #ifdef HASH_DEBUG 277 | #define HASH_OOPS(...) do { fprintf(stderr,__VA_ARGS__); exit(-1); } while (0) 278 | #define HASH_FSCK(hh,head) \ 279 | do { \ 280 | unsigned _bkt_i; \ 281 | unsigned _count, _bkt_count; \ 282 | char *_prev; \ 283 | struct UT_hash_handle *_thh; \ 284 | if (head) { \ 285 | _count = 0; \ 286 | for( _bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; _bkt_i++) { \ 287 | _bkt_count = 0; \ 288 | _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \ 289 | _prev = NULL; \ 290 | while (_thh) { \ 291 | if (_prev != (char*)(_thh->hh_prev)) { \ 292 | HASH_OOPS("invalid hh_prev %p, actual %p\n", \ 293 | _thh->hh_prev, _prev ); \ 294 | } \ 295 | _bkt_count++; \ 296 | _prev = (char*)(_thh); \ 297 | _thh = _thh->hh_next; \ 298 | } \ 299 | _count += _bkt_count; \ 300 | if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \ 301 | HASH_OOPS("invalid bucket count %d, actual %d\n", \ 302 | (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \ 303 | } \ 304 | } \ 305 | if (_count != (head)->hh.tbl->num_items) { \ 306 | HASH_OOPS("invalid hh item count %d, actual %d\n", \ 307 | (head)->hh.tbl->num_items, _count ); \ 308 | } \ 309 | /* traverse hh in app order; check next/prev integrity, count */ \ 310 | _count = 0; \ 311 | _prev = NULL; \ 312 | _thh = &(head)->hh; \ 313 | while (_thh) { \ 314 | _count++; \ 315 | if (_prev !=(char*)(_thh->prev)) { \ 316 | HASH_OOPS("invalid prev %p, actual %p\n", \ 317 | _thh->prev, _prev ); \ 318 | } \ 319 | _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \ 320 | _thh = ( _thh->next ? (UT_hash_handle*)((char*)(_thh->next) + \ 321 | (head)->hh.tbl->hho) : NULL ); \ 322 | } \ 323 | if (_count != (head)->hh.tbl->num_items) { \ 324 | HASH_OOPS("invalid app item count %d, actual %d\n", \ 325 | (head)->hh.tbl->num_items, _count ); \ 326 | } \ 327 | } \ 328 | } while (0) 329 | #else 330 | #define HASH_FSCK(hh,head) 331 | #endif 332 | 333 | /* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to 334 | * the descriptor to which this macro is defined for tuning the hash function. 335 | * The app can #include to get the prototype for write(2). */ 336 | #ifdef HASH_EMIT_KEYS 337 | #define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) \ 338 | do { \ 339 | unsigned _klen = fieldlen; \ 340 | write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \ 341 | write(HASH_EMIT_KEYS, keyptr, fieldlen); \ 342 | } while (0) 343 | #else 344 | #define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) 345 | #endif 346 | 347 | /* default to Jenkin's hash unless overridden e.g. DHASH_FUNCTION=HASH_SAX */ 348 | #ifdef HASH_FUNCTION 349 | #define HASH_FCN HASH_FUNCTION 350 | #else 351 | #define HASH_FCN HASH_JEN 352 | #endif 353 | 354 | /* The Bernstein hash function, used in Perl prior to v5.6 */ 355 | #define HASH_BER(key,keylen,num_bkts,hashv,bkt) \ 356 | do { \ 357 | unsigned _hb_keylen=keylen; \ 358 | char *_hb_key=(char*)(key); \ 359 | (hashv) = 0; \ 360 | while (_hb_keylen--) { (hashv) = ((hashv) * 33) + *_hb_key++; } \ 361 | bkt = (hashv) & (num_bkts-1); \ 362 | } while (0) 363 | 364 | 365 | /* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at 366 | * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx */ 367 | #define HASH_SAX(key,keylen,num_bkts,hashv,bkt) \ 368 | do { \ 369 | unsigned _sx_i; \ 370 | char *_hs_key=(char*)(key); \ 371 | hashv = 0; \ 372 | for(_sx_i=0; _sx_i < keylen; _sx_i++) \ 373 | hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \ 374 | bkt = hashv & (num_bkts-1); \ 375 | } while (0) 376 | 377 | #define HASH_FNV(key,keylen,num_bkts,hashv,bkt) \ 378 | do { \ 379 | unsigned _fn_i; \ 380 | char *_hf_key=(char*)(key); \ 381 | hashv = 2166136261UL; \ 382 | for(_fn_i=0; _fn_i < keylen; _fn_i++) \ 383 | hashv = (hashv * 16777619) ^ _hf_key[_fn_i]; \ 384 | bkt = hashv & (num_bkts-1); \ 385 | } while(0) 386 | 387 | #define HASH_OAT(key,keylen,num_bkts,hashv,bkt) \ 388 | do { \ 389 | unsigned _ho_i; \ 390 | char *_ho_key=(char*)(key); \ 391 | hashv = 0; \ 392 | for(_ho_i=0; _ho_i < keylen; _ho_i++) { \ 393 | hashv += _ho_key[_ho_i]; \ 394 | hashv += (hashv << 10); \ 395 | hashv ^= (hashv >> 6); \ 396 | } \ 397 | hashv += (hashv << 3); \ 398 | hashv ^= (hashv >> 11); \ 399 | hashv += (hashv << 15); \ 400 | bkt = hashv & (num_bkts-1); \ 401 | } while(0) 402 | 403 | #define HASH_JEN_MIX(a,b,c) \ 404 | do { \ 405 | a -= b; a -= c; a ^= ( c >> 13 ); \ 406 | b -= c; b -= a; b ^= ( a << 8 ); \ 407 | c -= a; c -= b; c ^= ( b >> 13 ); \ 408 | a -= b; a -= c; a ^= ( c >> 12 ); \ 409 | b -= c; b -= a; b ^= ( a << 16 ); \ 410 | c -= a; c -= b; c ^= ( b >> 5 ); \ 411 | a -= b; a -= c; a ^= ( c >> 3 ); \ 412 | b -= c; b -= a; b ^= ( a << 10 ); \ 413 | c -= a; c -= b; c ^= ( b >> 15 ); \ 414 | } while (0) 415 | 416 | #define HASH_JEN(key,keylen,num_bkts,hashv,bkt) \ 417 | do { \ 418 | unsigned _hj_i,_hj_j,_hj_k; \ 419 | unsigned char *_hj_key=(unsigned char*)(key); \ 420 | hashv = 0xfeedbeef; \ 421 | _hj_i = _hj_j = 0x9e3779b9; \ 422 | _hj_k = (unsigned)(keylen); \ 423 | while (_hj_k >= 12) { \ 424 | _hj_i += (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 ) \ 425 | + ( (unsigned)_hj_key[2] << 16 ) \ 426 | + ( (unsigned)_hj_key[3] << 24 ) ); \ 427 | _hj_j += (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 ) \ 428 | + ( (unsigned)_hj_key[6] << 16 ) \ 429 | + ( (unsigned)_hj_key[7] << 24 ) ); \ 430 | hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 ) \ 431 | + ( (unsigned)_hj_key[10] << 16 ) \ 432 | + ( (unsigned)_hj_key[11] << 24 ) ); \ 433 | \ 434 | HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ 435 | \ 436 | _hj_key += 12; \ 437 | _hj_k -= 12; \ 438 | } \ 439 | hashv += keylen; \ 440 | switch ( _hj_k ) { \ 441 | case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); \ 442 | case 10: hashv += ( (unsigned)_hj_key[9] << 16 ); \ 443 | case 9: hashv += ( (unsigned)_hj_key[8] << 8 ); \ 444 | case 8: _hj_j += ( (unsigned)_hj_key[7] << 24 ); \ 445 | case 7: _hj_j += ( (unsigned)_hj_key[6] << 16 ); \ 446 | case 6: _hj_j += ( (unsigned)_hj_key[5] << 8 ); \ 447 | case 5: _hj_j += _hj_key[4]; \ 448 | case 4: _hj_i += ( (unsigned)_hj_key[3] << 24 ); \ 449 | case 3: _hj_i += ( (unsigned)_hj_key[2] << 16 ); \ 450 | case 2: _hj_i += ( (unsigned)_hj_key[1] << 8 ); \ 451 | case 1: _hj_i += _hj_key[0]; \ 452 | } \ 453 | HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ 454 | bkt = hashv & (num_bkts-1); \ 455 | } while(0) 456 | 457 | /* The Paul Hsieh hash function */ 458 | #undef get16bits 459 | #if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ 460 | || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__) 461 | #define get16bits(d) (*((const uint16_t *) (d))) 462 | #endif 463 | 464 | #if !defined (get16bits) 465 | #define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \ 466 | +(uint32_t)(((const uint8_t *)(d))[0]) ) 467 | #endif 468 | #define HASH_SFH(key,keylen,num_bkts,hashv,bkt) \ 469 | do { \ 470 | unsigned char *_sfh_key=(unsigned char*)(key); \ 471 | uint32_t _sfh_tmp, _sfh_len = keylen; \ 472 | \ 473 | int _sfh_rem = _sfh_len & 3; \ 474 | _sfh_len >>= 2; \ 475 | hashv = 0xcafebabe; \ 476 | \ 477 | /* Main loop */ \ 478 | for (;_sfh_len > 0; _sfh_len--) { \ 479 | hashv += get16bits (_sfh_key); \ 480 | _sfh_tmp = (uint32_t)(get16bits (_sfh_key+2)) << 11 ^ hashv; \ 481 | hashv = (hashv << 16) ^ _sfh_tmp; \ 482 | _sfh_key += 2*sizeof (uint16_t); \ 483 | hashv += hashv >> 11; \ 484 | } \ 485 | \ 486 | /* Handle end cases */ \ 487 | switch (_sfh_rem) { \ 488 | case 3: hashv += get16bits (_sfh_key); \ 489 | hashv ^= hashv << 16; \ 490 | hashv ^= (uint32_t)(_sfh_key[sizeof (uint16_t)] << 18); \ 491 | hashv += hashv >> 11; \ 492 | break; \ 493 | case 2: hashv += get16bits (_sfh_key); \ 494 | hashv ^= hashv << 11; \ 495 | hashv += hashv >> 17; \ 496 | break; \ 497 | case 1: hashv += *_sfh_key; \ 498 | hashv ^= hashv << 10; \ 499 | hashv += hashv >> 1; \ 500 | } \ 501 | \ 502 | /* Force "avalanching" of final 127 bits */ \ 503 | hashv ^= hashv << 3; \ 504 | hashv += hashv >> 5; \ 505 | hashv ^= hashv << 4; \ 506 | hashv += hashv >> 17; \ 507 | hashv ^= hashv << 25; \ 508 | hashv += hashv >> 6; \ 509 | bkt = hashv & (num_bkts-1); \ 510 | } while(0) 511 | 512 | #ifdef HASH_USING_NO_STRICT_ALIASING 513 | /* The MurmurHash exploits some CPU's (x86,x86_64) tolerance for unaligned reads. 514 | * For other types of CPU's (e.g. Sparc) an unaligned read causes a bus error. 515 | * MurmurHash uses the faster approach only on CPU's where we know it's safe. 516 | * 517 | * Note the preprocessor built-in defines can be emitted using: 518 | * 519 | * gcc -m64 -dM -E - < /dev/null (on gcc) 520 | * cc -## a.c (where a.c is a simple test file) (Sun Studio) 521 | */ 522 | #if (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86)) 523 | #define MUR_GETBLOCK(p,i) p[i] 524 | #else /* non intel */ 525 | #define MUR_PLUS0_ALIGNED(p) (((unsigned long)p & 0x3) == 0) 526 | #define MUR_PLUS1_ALIGNED(p) (((unsigned long)p & 0x3) == 1) 527 | #define MUR_PLUS2_ALIGNED(p) (((unsigned long)p & 0x3) == 2) 528 | #define MUR_PLUS3_ALIGNED(p) (((unsigned long)p & 0x3) == 3) 529 | #define WP(p) ((uint32_t*)((unsigned long)(p) & ~3UL)) 530 | #if (defined(__BIG_ENDIAN__) || defined(SPARC) || defined(__ppc__) || defined(__ppc64__)) 531 | #define MUR_THREE_ONE(p) ((((*WP(p))&0x00ffffff) << 8) | (((*(WP(p)+1))&0xff000000) >> 24)) 532 | #define MUR_TWO_TWO(p) ((((*WP(p))&0x0000ffff) <<16) | (((*(WP(p)+1))&0xffff0000) >> 16)) 533 | #define MUR_ONE_THREE(p) ((((*WP(p))&0x000000ff) <<24) | (((*(WP(p)+1))&0xffffff00) >> 8)) 534 | #else /* assume little endian non-intel */ 535 | #define MUR_THREE_ONE(p) ((((*WP(p))&0xffffff00) >> 8) | (((*(WP(p)+1))&0x000000ff) << 24)) 536 | #define MUR_TWO_TWO(p) ((((*WP(p))&0xffff0000) >>16) | (((*(WP(p)+1))&0x0000ffff) << 16)) 537 | #define MUR_ONE_THREE(p) ((((*WP(p))&0xff000000) >>24) | (((*(WP(p)+1))&0x00ffffff) << 8)) 538 | #endif 539 | #define MUR_GETBLOCK(p,i) (MUR_PLUS0_ALIGNED(p) ? ((p)[i]) : \ 540 | (MUR_PLUS1_ALIGNED(p) ? MUR_THREE_ONE(p) : \ 541 | (MUR_PLUS2_ALIGNED(p) ? MUR_TWO_TWO(p) : \ 542 | MUR_ONE_THREE(p)))) 543 | #endif 544 | #define MUR_ROTL32(x,r) (((x) << (r)) | ((x) >> (32 - (r)))) 545 | #define MUR_FMIX(_h) \ 546 | do { \ 547 | _h ^= _h >> 16; \ 548 | _h *= 0x85ebca6b; \ 549 | _h ^= _h >> 13; \ 550 | _h *= 0xc2b2ae35l; \ 551 | _h ^= _h >> 16; \ 552 | } while(0) 553 | 554 | #define HASH_MUR(key,keylen,num_bkts,hashv,bkt) \ 555 | do { \ 556 | const uint8_t *_mur_data = (const uint8_t*)(key); \ 557 | const int _mur_nblocks = (keylen) / 4; \ 558 | uint32_t _mur_h1 = 0xf88D5353; \ 559 | uint32_t _mur_c1 = 0xcc9e2d51; \ 560 | uint32_t _mur_c2 = 0x1b873593; \ 561 | uint32_t _mur_k1 = 0; \ 562 | const uint8_t *_mur_tail; \ 563 | const uint32_t *_mur_blocks = (const uint32_t*)(_mur_data+_mur_nblocks*4); \ 564 | int _mur_i; \ 565 | for(_mur_i = -_mur_nblocks; _mur_i; _mur_i++) { \ 566 | _mur_k1 = MUR_GETBLOCK(_mur_blocks,_mur_i); \ 567 | _mur_k1 *= _mur_c1; \ 568 | _mur_k1 = MUR_ROTL32(_mur_k1,15); \ 569 | _mur_k1 *= _mur_c2; \ 570 | \ 571 | _mur_h1 ^= _mur_k1; \ 572 | _mur_h1 = MUR_ROTL32(_mur_h1,13); \ 573 | _mur_h1 = _mur_h1*5+0xe6546b64; \ 574 | } \ 575 | _mur_tail = (const uint8_t*)(_mur_data + _mur_nblocks*4); \ 576 | _mur_k1=0; \ 577 | switch((keylen) & 3) { \ 578 | case 3: _mur_k1 ^= _mur_tail[2] << 16; \ 579 | case 2: _mur_k1 ^= _mur_tail[1] << 8; \ 580 | case 1: _mur_k1 ^= _mur_tail[0]; \ 581 | _mur_k1 *= _mur_c1; \ 582 | _mur_k1 = MUR_ROTL32(_mur_k1,15); \ 583 | _mur_k1 *= _mur_c2; \ 584 | _mur_h1 ^= _mur_k1; \ 585 | } \ 586 | _mur_h1 ^= (keylen); \ 587 | MUR_FMIX(_mur_h1); \ 588 | hashv = _mur_h1; \ 589 | bkt = hashv & (num_bkts-1); \ 590 | } while(0) 591 | #endif /* HASH_USING_NO_STRICT_ALIASING */ 592 | 593 | /* key comparison function; return 0 if keys equal */ 594 | #define HASH_KEYCMP(a,b,len) memcmp(a,b,len) 595 | 596 | /* iterate over items in a known bucket to find desired item */ 597 | #define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,out) \ 598 | do { \ 599 | if (head.hh_head) DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,head.hh_head)); \ 600 | else out=NULL; \ 601 | while (out) { \ 602 | if ((out)->hh.keylen == keylen_in) { \ 603 | if ((HASH_KEYCMP((out)->hh.key,keyptr,keylen_in)) == 0) break; \ 604 | } \ 605 | if ((out)->hh.hh_next) DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,(out)->hh.hh_next)); \ 606 | else out = NULL; \ 607 | } \ 608 | } while(0) 609 | 610 | /* add an item to a bucket */ 611 | #define HASH_ADD_TO_BKT(head,addhh) \ 612 | do { \ 613 | head.count++; \ 614 | (addhh)->hh_next = head.hh_head; \ 615 | (addhh)->hh_prev = NULL; \ 616 | if (head.hh_head) { (head).hh_head->hh_prev = (addhh); } \ 617 | (head).hh_head=addhh; \ 618 | if (head.count >= ((head.expand_mult+1) * HASH_BKT_CAPACITY_THRESH) \ 619 | && (addhh)->tbl->noexpand != 1) { \ 620 | HASH_EXPAND_BUCKETS((addhh)->tbl); \ 621 | } \ 622 | } while(0) 623 | 624 | /* remove an item from a given bucket */ 625 | #define HASH_DEL_IN_BKT(hh,head,hh_del) \ 626 | (head).count--; \ 627 | if ((head).hh_head == hh_del) { \ 628 | (head).hh_head = hh_del->hh_next; \ 629 | } \ 630 | if (hh_del->hh_prev) { \ 631 | hh_del->hh_prev->hh_next = hh_del->hh_next; \ 632 | } \ 633 | if (hh_del->hh_next) { \ 634 | hh_del->hh_next->hh_prev = hh_del->hh_prev; \ 635 | } 636 | 637 | /* Bucket expansion has the effect of doubling the number of buckets 638 | * and redistributing the items into the new buckets. Ideally the 639 | * items will distribute more or less evenly into the new buckets 640 | * (the extent to which this is true is a measure of the quality of 641 | * the hash function as it applies to the key domain). 642 | * 643 | * With the items distributed into more buckets, the chain length 644 | * (item count) in each bucket is reduced. Thus by expanding buckets 645 | * the hash keeps a bound on the chain length. This bounded chain 646 | * length is the essence of how a hash provides constant time lookup. 647 | * 648 | * The calculation of tbl->ideal_chain_maxlen below deserves some 649 | * explanation. First, keep in mind that we're calculating the ideal 650 | * maximum chain length based on the *new* (doubled) bucket count. 651 | * In fractions this is just n/b (n=number of items,b=new num buckets). 652 | * Since the ideal chain length is an integer, we want to calculate 653 | * ceil(n/b). We don't depend on floating point arithmetic in this 654 | * hash, so to calculate ceil(n/b) with integers we could write 655 | * 656 | * ceil(n/b) = (n/b) + ((n%b)?1:0) 657 | * 658 | * and in fact a previous version of this hash did just that. 659 | * But now we have improved things a bit by recognizing that b is 660 | * always a power of two. We keep its base 2 log handy (call it lb), 661 | * so now we can write this with a bit shift and logical AND: 662 | * 663 | * ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0) 664 | * 665 | */ 666 | #define HASH_EXPAND_BUCKETS(tbl) \ 667 | do { \ 668 | unsigned _he_bkt; \ 669 | unsigned _he_bkt_i; \ 670 | struct UT_hash_handle *_he_thh, *_he_hh_nxt; \ 671 | UT_hash_bucket *_he_new_buckets, *_he_newbkt; \ 672 | _he_new_buckets = (UT_hash_bucket*)uthash_malloc( \ 673 | 2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ 674 | if (!_he_new_buckets) { uthash_fatal( "out of memory"); } \ 675 | memset(_he_new_buckets, 0, \ 676 | 2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ 677 | tbl->ideal_chain_maxlen = \ 678 | (tbl->num_items >> (tbl->log2_num_buckets+1)) + \ 679 | ((tbl->num_items & ((tbl->num_buckets*2)-1)) ? 1 : 0); \ 680 | tbl->nonideal_items = 0; \ 681 | for(_he_bkt_i = 0; _he_bkt_i < tbl->num_buckets; _he_bkt_i++) \ 682 | { \ 683 | _he_thh = tbl->buckets[ _he_bkt_i ].hh_head; \ 684 | while (_he_thh) { \ 685 | _he_hh_nxt = _he_thh->hh_next; \ 686 | HASH_TO_BKT( _he_thh->hashv, tbl->num_buckets*2, _he_bkt); \ 687 | _he_newbkt = &(_he_new_buckets[ _he_bkt ]); \ 688 | if (++(_he_newbkt->count) > tbl->ideal_chain_maxlen) { \ 689 | tbl->nonideal_items++; \ 690 | _he_newbkt->expand_mult = _he_newbkt->count / \ 691 | tbl->ideal_chain_maxlen; \ 692 | } \ 693 | _he_thh->hh_prev = NULL; \ 694 | _he_thh->hh_next = _he_newbkt->hh_head; \ 695 | if (_he_newbkt->hh_head) _he_newbkt->hh_head->hh_prev = \ 696 | _he_thh; \ 697 | _he_newbkt->hh_head = _he_thh; \ 698 | _he_thh = _he_hh_nxt; \ 699 | } \ 700 | } \ 701 | uthash_free( tbl->buckets, tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \ 702 | tbl->num_buckets *= 2; \ 703 | tbl->log2_num_buckets++; \ 704 | tbl->buckets = _he_new_buckets; \ 705 | tbl->ineff_expands = (tbl->nonideal_items > (tbl->num_items >> 1)) ? \ 706 | (tbl->ineff_expands+1) : 0; \ 707 | if (tbl->ineff_expands > 1) { \ 708 | tbl->noexpand=1; \ 709 | uthash_noexpand_fyi(tbl); \ 710 | } \ 711 | uthash_expand_fyi(tbl); \ 712 | } while(0) 713 | 714 | 715 | /* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */ 716 | /* Note that HASH_SORT assumes the hash handle name to be hh. 717 | * HASH_SRT was added to allow the hash handle name to be passed in. */ 718 | #define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn) 719 | #define HASH_SRT(hh,head,cmpfcn) \ 720 | do { \ 721 | unsigned _hs_i; \ 722 | unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize; \ 723 | struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \ 724 | if (head) { \ 725 | _hs_insize = 1; \ 726 | _hs_looping = 1; \ 727 | _hs_list = &((head)->hh); \ 728 | while (_hs_looping) { \ 729 | _hs_p = _hs_list; \ 730 | _hs_list = NULL; \ 731 | _hs_tail = NULL; \ 732 | _hs_nmerges = 0; \ 733 | while (_hs_p) { \ 734 | _hs_nmerges++; \ 735 | _hs_q = _hs_p; \ 736 | _hs_psize = 0; \ 737 | for ( _hs_i = 0; _hs_i < _hs_insize; _hs_i++ ) { \ 738 | _hs_psize++; \ 739 | _hs_q = (UT_hash_handle*)((_hs_q->next) ? \ 740 | ((void*)((char*)(_hs_q->next) + \ 741 | (head)->hh.tbl->hho)) : NULL); \ 742 | if (! (_hs_q) ) break; \ 743 | } \ 744 | _hs_qsize = _hs_insize; \ 745 | while ((_hs_psize > 0) || ((_hs_qsize > 0) && _hs_q )) { \ 746 | if (_hs_psize == 0) { \ 747 | _hs_e = _hs_q; \ 748 | _hs_q = (UT_hash_handle*)((_hs_q->next) ? \ 749 | ((void*)((char*)(_hs_q->next) + \ 750 | (head)->hh.tbl->hho)) : NULL); \ 751 | _hs_qsize--; \ 752 | } else if ( (_hs_qsize == 0) || !(_hs_q) ) { \ 753 | _hs_e = _hs_p; \ 754 | if (_hs_p){ \ 755 | _hs_p = (UT_hash_handle*)((_hs_p->next) ? \ 756 | ((void*)((char*)(_hs_p->next) + \ 757 | (head)->hh.tbl->hho)) : NULL); \ 758 | } \ 759 | _hs_psize--; \ 760 | } else if (( \ 761 | cmpfcn(DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_p)), \ 762 | DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_q))) \ 763 | ) <= 0) { \ 764 | _hs_e = _hs_p; \ 765 | if (_hs_p){ \ 766 | _hs_p = (UT_hash_handle*)((_hs_p->next) ? \ 767 | ((void*)((char*)(_hs_p->next) + \ 768 | (head)->hh.tbl->hho)) : NULL); \ 769 | } \ 770 | _hs_psize--; \ 771 | } else { \ 772 | _hs_e = _hs_q; \ 773 | _hs_q = (UT_hash_handle*)((_hs_q->next) ? \ 774 | ((void*)((char*)(_hs_q->next) + \ 775 | (head)->hh.tbl->hho)) : NULL); \ 776 | _hs_qsize--; \ 777 | } \ 778 | if ( _hs_tail ) { \ 779 | _hs_tail->next = ((_hs_e) ? \ 780 | ELMT_FROM_HH((head)->hh.tbl,_hs_e) : NULL); \ 781 | } else { \ 782 | _hs_list = _hs_e; \ 783 | } \ 784 | if (_hs_e) { \ 785 | _hs_e->prev = ((_hs_tail) ? \ 786 | ELMT_FROM_HH((head)->hh.tbl,_hs_tail) : NULL); \ 787 | } \ 788 | _hs_tail = _hs_e; \ 789 | } \ 790 | _hs_p = _hs_q; \ 791 | } \ 792 | if (_hs_tail){ \ 793 | _hs_tail->next = NULL; \ 794 | } \ 795 | if ( _hs_nmerges <= 1 ) { \ 796 | _hs_looping=0; \ 797 | (head)->hh.tbl->tail = _hs_tail; \ 798 | DECLTYPE_ASSIGN(head,ELMT_FROM_HH((head)->hh.tbl, _hs_list)); \ 799 | } \ 800 | _hs_insize *= 2; \ 801 | } \ 802 | HASH_FSCK(hh,head); \ 803 | } \ 804 | } while (0) 805 | 806 | /* This function selects items from one hash into another hash. 807 | * The end result is that the selected items have dual presence 808 | * in both hashes. There is no copy of the items made; rather 809 | * they are added into the new hash through a secondary hash 810 | * hash handle that must be present in the structure. */ 811 | #define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \ 812 | do { \ 813 | unsigned _src_bkt, _dst_bkt; \ 814 | void *_last_elt=NULL, *_elt; \ 815 | UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL; \ 816 | ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst)); \ 817 | if (src) { \ 818 | for(_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \ 819 | for(_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \ 820 | _src_hh; \ 821 | _src_hh = _src_hh->hh_next) { \ 822 | _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \ 823 | if (cond(_elt)) { \ 824 | _dst_hh = (UT_hash_handle*)(((char*)_elt) + _dst_hho); \ 825 | _dst_hh->key = _src_hh->key; \ 826 | _dst_hh->keylen = _src_hh->keylen; \ 827 | _dst_hh->hashv = _src_hh->hashv; \ 828 | _dst_hh->prev = _last_elt; \ 829 | _dst_hh->next = NULL; \ 830 | if (_last_elt_hh) { _last_elt_hh->next = _elt; } \ 831 | if (!dst) { \ 832 | DECLTYPE_ASSIGN(dst,_elt); \ 833 | HASH_MAKE_TABLE(hh_dst,dst); \ 834 | } else { \ 835 | _dst_hh->tbl = (dst)->hh_dst.tbl; \ 836 | } \ 837 | HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \ 838 | HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt],_dst_hh); \ 839 | (dst)->hh_dst.tbl->num_items++; \ 840 | _last_elt = _elt; \ 841 | _last_elt_hh = _dst_hh; \ 842 | } \ 843 | } \ 844 | } \ 845 | } \ 846 | HASH_FSCK(hh_dst,dst); \ 847 | } while (0) 848 | 849 | #define HASH_CLEAR(hh,head) \ 850 | do { \ 851 | if (head) { \ 852 | uthash_free((head)->hh.tbl->buckets, \ 853 | (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket)); \ 854 | HASH_BLOOM_FREE((head)->hh.tbl); \ 855 | uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ 856 | (head)=NULL; \ 857 | } \ 858 | } while(0) 859 | 860 | #define HASH_OVERHEAD(hh,head) \ 861 | (size_t)((((head)->hh.tbl->num_items * sizeof(UT_hash_handle)) + \ 862 | ((head)->hh.tbl->num_buckets * sizeof(UT_hash_bucket)) + \ 863 | (sizeof(UT_hash_table)) + \ 864 | (HASH_BLOOM_BYTELEN))) 865 | 866 | #ifdef NO_DECLTYPE 867 | #define HASH_ITER(hh,head,el,tmp) \ 868 | for((el)=(head), (*(char**)(&(tmp)))=(char*)((head)?(head)->hh.next:NULL); \ 869 | el; (el)=(tmp),(*(char**)(&(tmp)))=(char*)((tmp)?(tmp)->hh.next:NULL)) 870 | #else 871 | #define HASH_ITER(hh,head,el,tmp) \ 872 | for((el)=(head),(tmp)=DECLTYPE(el)((head)?(head)->hh.next:NULL); \ 873 | el; (el)=(tmp),(tmp)=DECLTYPE(el)((tmp)?(tmp)->hh.next:NULL)) 874 | #endif 875 | 876 | /* obtain a count of items in the hash */ 877 | #define HASH_COUNT(head) HASH_CNT(hh,head) 878 | #define HASH_CNT(hh,head) ((head)?((head)->hh.tbl->num_items):0) 879 | 880 | typedef struct UT_hash_bucket { 881 | struct UT_hash_handle *hh_head; 882 | unsigned count; 883 | 884 | /* expand_mult is normally set to 0. In this situation, the max chain length 885 | * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If 886 | * the bucket's chain exceeds this length, bucket expansion is triggered). 887 | * However, setting expand_mult to a non-zero value delays bucket expansion 888 | * (that would be triggered by additions to this particular bucket) 889 | * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH. 890 | * (The multiplier is simply expand_mult+1). The whole idea of this 891 | * multiplier is to reduce bucket expansions, since they are expensive, in 892 | * situations where we know that a particular bucket tends to be overused. 893 | * It is better to let its chain length grow to a longer yet-still-bounded 894 | * value, than to do an O(n) bucket expansion too often. 895 | */ 896 | unsigned expand_mult; 897 | 898 | } UT_hash_bucket; 899 | 900 | /* random signature used only to find hash tables in external analysis */ 901 | #define HASH_SIGNATURE 0xa0111fe1 902 | #define HASH_BLOOM_SIGNATURE 0xb12220f2 903 | 904 | typedef struct UT_hash_table { 905 | UT_hash_bucket *buckets; 906 | unsigned num_buckets, log2_num_buckets; 907 | unsigned num_items; 908 | struct UT_hash_handle *tail; /* tail hh in app order, for fast append */ 909 | ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */ 910 | 911 | /* in an ideal situation (all buckets used equally), no bucket would have 912 | * more than ceil(#items/#buckets) items. that's the ideal chain length. */ 913 | unsigned ideal_chain_maxlen; 914 | 915 | /* nonideal_items is the number of items in the hash whose chain position 916 | * exceeds the ideal chain maxlen. these items pay the penalty for an uneven 917 | * hash distribution; reaching them in a chain traversal takes >ideal steps */ 918 | unsigned nonideal_items; 919 | 920 | /* ineffective expands occur when a bucket doubling was performed, but 921 | * afterward, more than half the items in the hash had nonideal chain 922 | * positions. If this happens on two consecutive expansions we inhibit any 923 | * further expansion, as it's not helping; this happens when the hash 924 | * function isn't a good fit for the key domain. When expansion is inhibited 925 | * the hash will still work, albeit no longer in constant time. */ 926 | unsigned ineff_expands, noexpand; 927 | 928 | uint32_t signature; /* used only to find hash tables in external analysis */ 929 | #ifdef HASH_BLOOM 930 | uint32_t bloom_sig; /* used only to test bloom exists in external analysis */ 931 | uint8_t *bloom_bv; 932 | char bloom_nbits; 933 | #endif 934 | 935 | } UT_hash_table; 936 | 937 | typedef struct UT_hash_handle { 938 | struct UT_hash_table *tbl; 939 | void *prev; /* prev element in app order */ 940 | void *next; /* next element in app order */ 941 | struct UT_hash_handle *hh_prev; /* previous hh in bucket order */ 942 | struct UT_hash_handle *hh_next; /* next hh in bucket order */ 943 | void *key; /* ptr to enclosing struct's key */ 944 | unsigned keylen; /* enclosing struct's key len */ 945 | unsigned hashv; /* result of hash-fcn(key) */ 946 | } UT_hash_handle; 947 | 948 | #endif /* UTHASH_H */ 949 | --------------------------------------------------------------------------------