/
priority_queue.c
365 lines (305 loc) · 11.2 KB
/
priority_queue.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
/*
* File: priority_queue.c
* Authors: Jeremy Stormo & Daniel Ward
*
* This library provides priority queues. It can serve many queues, it is
* defaulted to be able to provide 1024 priority queues.
*
* The priority queues are created as a doubly linked list of nodes
*/
#include "priority_queue.h"
#include "stdio.h"
/************ INTERNAL DEFINES ************/
#define MAXIMUM_NUMBER_OF_QUEUES 1024
#define MAXIMUM_NUMBER_OF_ELEMENTS_IN_A_QUEUE 1024
#define INDEX_OFFSET 0x1221 //Value for obfuscating index in queue_ticket
#define START_NONCE 0x0502 //Start value for nonce used in queue_ticket
#define TRUE 1
#define FALSE 0
typedef char boolean;
/********** END INTERNAL DEFINES **********/
/************ INTERNAL STRUCTS ************/
//a node for the linked list inside of the queue
typedef struct node
{
struct node* pNext;
struct node* pPrev;
ELEMENT item;
} NODE;
//a queue as a doubly linked list with a pointer to the head and tail
typedef struct priority_queue
{
NODE* head;
NODE* tail;
uint size;
QUEUE_TICKET ticket;
} PRIORITY_QUEUE;
//a manager to hold an array of pointers to queues
//also holds the number of active queues
typedef struct queue_manager
{
PRIORITY_QUEUE* queues[MAXIMUM_NUMBER_OF_QUEUES];
uint size;
}QUEUE_MANAGER;
/************ END INTERNAL STRUCTS ************/
/************ INTERNAL PROTOTYPES ************/
static int find_open_slot();
static QUEUE_TICKET create_ticket(uint index);
static void free_queue(PRIORITY_QUEUE* pQueue);
static int decrypt_ticket(QUEUE_TICKET ticket);
static RESULT set_result(int resultCode, char* message);
static PRIORITY_QUEUE* redeem_ticket(QUEUE_TICKET ticket);
static ELEMENT remove_one_element(PRIORITY_QUEUE* pQueue);
static void insert_node(PRIORITY_QUEUE* pQueue, NODE* pNode);
/********** END INTERNAL PROTOTYPES ************/
/************ INTERNAL GLOBALS *****************/
static uint nonce = START_NONCE; //Nonce counter for queue ticket creation
static QUEUE_MANAGER queue_guard;//Guard for queue, obfuscates the location of the queue
/********** END INTERNAL GLOBALS ***************/
static RESULT set_result(int resultCode, char* message)
{//Helper function for setting the members of the result struct
RESULT outcome;
outcome.code = resultCode;
snprintf(outcome.message,sizeof(outcome.message),"%s",message);
return outcome;
}
static QUEUE_TICKET create_ticket(uint index)
{//Create queue_ticket given an index
QUEUE_TICKET ticket = 0;
uint high = 0; //Obfuscated index value to put into high 16 bits
uint low = 0; //Nonce value to put into low 16 bits
while(low == 0){ //Assign nonce such that it's non-trivial, try again if it is
low = nonce & 0xffff; //Cut off upper 16 bits of nonce
nonce = nonce + 1;
}
high = (index + INDEX_OFFSET) & 0x7fff; //Obfuscate index, force positive and cuts to 16 bits
if(high == index + INDEX_OFFSET) //Check that real index didn't get trashed by obfuscation
ticket = (high << 16) | low; //Create ticket by combining obfuscated index and nonce
return ticket; //return valid ticket, NULL if failed
//Should only ever fail by giving a bad index,
//one which will overflow when combined with the INDEX_OFFSET.
//So it's dependent on MAXIMUM_NUMBER_OF_QUEUES and INDEX_OFFSET not
//adding together to exceed 16 bits
//If it does, then there will be indices in the array for which tickets cannot be created
}
static int decrypt_ticket(QUEUE_TICKET ticket)
{//Pulls the index value out of a ticket
int index = ticket >> 16; //Pull off top 16 bits of ticket
return (index - INDEX_OFFSET); //Remove obfuscation
}
static PRIORITY_QUEUE* redeem_ticket(QUEUE_TICKET ticket)
{//Given ticket, returns pointer to queue requested
int index = decrypt_ticket(ticket); //Get index from the ticket
PRIORITY_QUEUE* pQueue = NULL;
if(index < MAXIMUM_NUMBER_OF_QUEUES //If index is in valid queue range
&& index >= 0)
pQueue = queue_guard.queues[index]; //Get queue at index from manager
if(pQueue != NULL
&& pQueue->ticket != ticket) //If queue wasn't found or ticket didn't match queue's copy
pQueue = NULL; //Setup to return NULL
return pQueue;
}
WELCOME_PACKET create_queue()
{//Create a new queue in the queue manager returning its ticket
//TODO:refactor
WELCOME_PACKET outcome;
QUEUE_TICKET ticket = 0;
outcome.result = set_result(SUCCESS,"");
PRIORITY_QUEUE* pQueue = NULL;
int indexFound = find_open_slot();
if(indexFound == -1)
outcome.result = set_result(QUEUE_CANNOT_BE_CREATED, "Exceeded maximum number of queues");
if(outcome.result.code == SUCCESS) //Using outcome's error flag to avoid nested if-else
{//Create a ticket
ticket = create_ticket(indexFound);
if(ticket == 0)
outcome.result = set_result(QUEUE_CANNOT_BE_CREATED, "Ticket creation failed");
}
if(outcome.result.code == SUCCESS)
{//Malloc new queue
pQueue = (PRIORITY_QUEUE*) malloc(sizeof(PRIORITY_QUEUE));
if(pQueue == NULL)
outcome.result = set_result(OUT_OF_MEMORY, "Failed to allocate memory");
}
if(outcome.result.code == SUCCESS)
{//Initialize new queue
pQueue->ticket = ticket;
pQueue->size = 0;
pQueue->head = NULL;
pQueue->tail = NULL;
queue_guard.queues[indexFound] = pQueue; //Give queue manager the new queue
queue_guard.size++; //Increment queue manager's size
}
outcome.ticket = ticket;
return outcome;
}
RESULT delete_queue(QUEUE_TICKET ticket)
{//Free a queue struct and it's associated memory
RESULT outcome = set_result(SUCCESS,"");
PRIORITY_QUEUE* pQueue = redeem_ticket(ticket);
//Ticket returned a valid queue, delete it
if(pQueue != NULL)
{
queue_guard.queues[decrypt_ticket(ticket)] = NULL; //remove refernce in the array of queues
queue_guard.size = queue_guard.size - 1; //decrement the size
free_queue(pQueue);
}
else
outcome = set_result(TICKET_INVALID,"Provided an invalid queue ticket");
return outcome;
}
SIZE_RESULT get_size(QUEUE_TICKET ticket)
{//Returns the current size of the queue requested
SIZE_RESULT outcome;
PRIORITY_QUEUE* pQueue = redeem_ticket(ticket);
if(pQueue != NULL)
{//Found the queue, get size
outcome.size = pQueue->size;
outcome.result = set_result(SUCCESS,"");
}
else
{//Invalid ticket
outcome.size = 0;
outcome.result = set_result(TICKET_INVALID, "Provided an invalid queue ticket");
}
return outcome;
}
static void insert_node(PRIORITY_QUEUE* pQueue, NODE* pNode)
{//Insert node into sorted linked list
//TODO: refactor
boolean finished = FALSE;
if(pQueue->tail == NULL)
{//Insert into empty list
pQueue->tail = pNode;
pQueue->head = pNode;
pNode->pNext = NULL;
pNode->pPrev = NULL;
finished = TRUE;
}
NODE* curNode = pQueue->tail;
while(finished == FALSE
&& curNode != NULL)
{//Insertion sort the node into list
if(curNode->item.priority < pNode->item.priority) //curNode's priority is lower than new one
curNode=curNode->pNext; //Iterate to next node
else
{//Insert new node before curNode
//Update new node's links
pNode->pPrev = curNode->pPrev;
pNode->pNext = curNode;
if(pNode->pPrev == NULL) //Adding to tail of queue
pQueue->tail = pNode; //Update tail
else //Adding inbetween nodes
pNode->pPrev->pNext = pNode; //Update prior node's links
//Update curNode's links
curNode->pPrev = pNode;
finished = TRUE;
}
}
if(finished == FALSE)
{//Hit end of list, add to head of queue
curNode = pQueue->head; //Add after the curNode
//Update new node's links
pNode->pPrev = curNode;
pNode->pNext = NULL;
//Update curNode's links
curNode->pNext = pNode;
//Update head link
pQueue->head = pNode;
}
pQueue->size = pQueue->size+1;
}
RESULT enqueue(ELEMENT item, QUEUE_TICKET ticket)
{//Takes an element and add it to the ticket's queue
PRIORITY_QUEUE* pQueue = redeem_ticket(ticket);
NODE* pNode;
RESULT outcome = set_result(SUCCESS,"");
if(pQueue == NULL)
outcome = set_result(TICKET_INVALID,"Provided an invalid queue ticket");
else if(item.priority > 10)
outcome = set_result(ITEM_INVALID,"Priority outside of legal range");
else if(pQueue->size >= MAXIMUM_NUMBER_OF_ELEMENTS_IN_A_QUEUE)
outcome = set_result(QUEUE_IS_FULL,"Cannot add to full queue");
else
{//Found queue, create a new node for it
pNode = (NODE*)malloc(sizeof(NODE));
if(pNode == NULL)
outcome = set_result(OUT_OF_MEMORY, "Failed to allocate memory");
}
if(outcome.code == SUCCESS)
{//Succeeded up to this point, give item to node and insert node
pNode->item = item;
insert_node(pQueue, pNode); //Insert node into queue
}
return outcome;
}
ELEMENT_RESULT dequeue(QUEUE_TICKET ticket)
{//Removes one element from the front of the queue returning it
ELEMENT_RESULT outcome; //the result of the dequeue
snprintf(outcome.element.item,MAX_STRING_LENGTH,"");
outcome.element.priority = 0;
outcome.result = set_result(SUCCESS,"");
//get the queue represented by the ticket
PRIORITY_QUEUE* pQueue = redeem_ticket(ticket);
if(pQueue == NULL) //if NULL the mark it as an error
outcome.result = set_result(TICKET_INVALID,"Provided an invalid queue ticket");
else if(pQueue->size == 0) //if the size is 0 then mark an error
outcome.result = set_result(QUEUE_IS_EMPTY,"Cannot dequeue from an empty queue");
else
//if the ticket is valid and the size > 0 then remove one element
outcome.element = remove_one_element(pQueue);
return outcome;
}
RESULT is_full(QUEUE_TICKET ticket)
{//Returns if queue is full or not
SIZE_RESULT size = get_size(ticket);
if(size.result.code == SUCCESS)
{//Found queue, check it's size vs the max
if(size.size >= MAXIMUM_NUMBER_OF_ELEMENTS_IN_A_QUEUE)
size.result = set_result(QUEUE_IS_FULL,"Queue is full");
else
size.result = set_result(QUEUE_IS_NOT_FULL,"Queue is not full");
}
return size.result;
}
static int find_open_slot()
{//Find the first open array index in queue manager
int result = -1;
int index;
//Walk array looking for an opening
for(index = 0; index < MAXIMUM_NUMBER_OF_QUEUES && result == -1; index++)
if(queue_guard.queues[index] == NULL)
result = index;
return result;
}
static ELEMENT remove_one_element(PRIORITY_QUEUE* pQueue)
{//Remove a node from the front of the linked list
ELEMENT result = pQueue->head->item;
NODE* temp_node = pQueue->head;
//Set head to previous node
pQueue->head = pQueue->head->pPrev;
if(pQueue->head != NULL) //List isn't empty
pQueue->head->pNext = NULL;//Set new head's next to NULL
else
pQueue->tail = NULL; //Empty list, so set tail to NULL too
free(temp_node); //Free the removed node
pQueue->size = pQueue->size - 1; //Decrement size
return result;
}
static void free_queue(PRIORITY_QUEUE* pQueue)
{//Free the queue and associated linked list
NODE* pNext;
NODE* pCur = pQueue->tail;
//Traverse linked list freeing each node
while (pCur != NULL)
{
//set the next node to traverse to
pNext = pCur->pNext;
//free the current node
free(pCur);
//swap the current with the next node
pCur = pNext;
}//end while
//free the queue
free(pQueue);
}