/* * BRLTTY - A background process providing access to the console screen (when in * text mode) for a blind person using a refreshable braille display. * * Copyright (C) 1995-2021 by The BRLTTY Developers. * * BRLTTY comes with ABSOLUTELY NO WARRANTY. * * This is free software, placed under the terms of the * GNU Lesser General Public License, as published by the Free Software * Foundation; either version 2.1 of the License, or (at your option) any * later version. Please see the file LICENSE-LGPL for details. * * Web Page: http://brltty.app/ * * This software is maintained by Dave Mielke . */ #include "prologue.h" #include "log.h" #include "queue.h" #include "lock.h" #include "program.h" static Element *discardedElements = NULL; static LockDescriptor * getDiscardedElementsLock (void) { static LockDescriptor *lock = NULL; return getLockDescriptor(&lock, "queue-discarded-elements"); } static void lockDiscardedElements (void) { obtainExclusiveLock(getDiscardedElementsLock()); } static void unlockDiscardedElements (void) { releaseLock(getDiscardedElementsLock()); } struct QueueStruct { Element *head; unsigned int size; void *data; ItemDeallocator *deallocateItem; ItemComparator *compareItems; }; struct ElementStruct { Element *next; Element *previous; Queue *queue; int identifier; void *item; }; static void addElement (Queue *queue, Element *element) { { static int identifier = 0; element->identifier = ++identifier; } element->queue = queue; queue->size += 1; } static void removeElement (Element *element) { element->queue->size -= 1; element->queue = NULL; element->identifier = 0; } static void removeItem (Element *element) { if (element->item) { Queue *queue = element->queue; ItemDeallocator *deallocateItem = queue->deallocateItem; if (deallocateItem) deallocateItem(element->item, queue->data); element->item = NULL; } } static void discardElement (Element *element) { removeItem(element); removeElement(element); lockDiscardedElements(); element->next = discardedElements; discardedElements = element; unlockDiscardedElements(); } static Element * retrieveElement (void) { Element *element; lockDiscardedElements(); if ((element = discardedElements)) { discardedElements = element->next; element->next = NULL; } unlockDiscardedElements(); return element; } static Element * newElement (Queue *queue, void *item) { Element *element; if (!(element = retrieveElement())) { if (!(element = malloc(sizeof(*element)))) { logMallocError(); return NULL; } element->previous = element->next = NULL; } addElement(queue, element); element->item = item; return element; } static void linkFirstElement (Element *element) { element->queue->head = element->previous = element->next = element; } static void linkAdditionalElement (Element *reference, Element *element) { element->next = reference; element->previous = reference->previous; element->next->previous = element; element->previous->next = element; } static void unlinkElement (Element *element) { Queue *queue = element->queue; if (element == element->next) { queue->head = NULL; } else { if (element == queue->head) queue->head = element->next; element->next->previous = element->previous; element->previous->next = element->next; } element->previous = element->next = NULL; } void deleteElement (Element *element) { unlinkElement(element); discardElement(element); } typedef struct { Queue *queue; void *item; } FindReferenceElementData; static int findReferenceElement (const void *item, void *data) { const FindReferenceElementData *fre = data; return fre->queue->compareItems(fre->item, item, fre->queue->data); } static void linkElement (Element *element) { Queue *queue = element->queue; if (queue->head) { Element *reference; int isNewHead = 0; if (queue->compareItems) { FindReferenceElementData fre = { .queue = queue, .item = element->item }; if (!(reference = findElement(queue, findReferenceElement, &fre))) { reference = queue->head; } else if (reference == queue->head) { isNewHead = 1; } } else { reference = queue->head; } linkAdditionalElement(reference, element); if (isNewHead) queue->head = element; } else { linkFirstElement(element); } } Element * enqueueItem (Queue *queue, void *item) { Element *element = newElement(queue, item); if (element) linkElement(element); return element; } void requeueElement (Element *element) { unlinkElement(element); linkElement(element); } void moveElement (Element *element, Queue *queue) { unlinkElement(element); removeElement(element); addElement(queue, element); linkElement(element); } void * dequeueItem (Queue *queue) { void *item; Element *element; if (!(element = queue->head)) return NULL; item = element->item; element->item = NULL; deleteElement(element); return item; } Queue * getElementQueue (const Element *element) { return element->queue; } int getElementIdentifier (const Element *element) { return element->identifier; } void * getElementItem (const Element *element) { return element->item; } static int queueInitialized = 0; static void exitQueue (void *data) { lockDiscardedElements(); while (discardedElements) { Element *element = discardedElements; discardedElements = element->next; free(element); } unlockDiscardedElements(); queueInitialized = 0; } Queue * newQueue (ItemDeallocator *deallocateItem, ItemComparator *compareItems) { Queue *queue; if (!queueInitialized) { queueInitialized = 1; onProgramExit("queue", exitQueue, NULL); } if ((queue = malloc(sizeof(*queue)))) { queue->head = NULL; queue->size = 0; queue->data = NULL; queue->deallocateItem = deallocateItem; queue->compareItems = compareItems; return queue; } else { logMallocError(); } return NULL; } void deleteElements (Queue *queue) { while (queue->head) deleteElement(queue->head); } void deallocateQueue (Queue *queue) { deleteElements(queue); free(queue); } static void exitProgramQueue (void *data) { Queue **queue = data; if (*queue) { deallocateQueue(*queue); *queue = NULL; } } Queue * getProgramQueue ( Queue **queue, const char *name, int create, QueueCreator *createQueue, void *data ) { if (!*queue && create) { if ((*queue = createQueue(data))) { onProgramExit(name, exitProgramQueue, queue); } } return *queue; } int getQueueSize (const Queue *queue) { return queue->size; } void * getQueueData (const Queue *queue) { return queue->data; } void * setQueueData (Queue *queue, void *data) { void *previous = queue->data; queue->data = data; return previous; } Element * getQueueHead (const Queue *queue) { return queue->head; } Element * getStackHead (const Queue *queue) { Element *head = queue->head; return head? head->previous: NULL; } static Element * getElementByIndex (const Queue *queue, unsigned int index, int fromTail) { if (index < queue->size) { Element *element = queue->head; { int i = queue->size - 1 - index; if (i < index) { index = i; fromTail = !fromTail; } } if (fromTail) element = element->previous; while (index > 0) { element = fromTail? element->previous: element->next; index -= 1; } return element; } return 0; } Element * getQueueElement (const Queue *queue, unsigned int index) { return getElementByIndex(queue, index, 0); } Element * getStackElement (const Queue *queue, unsigned int index) { return getElementByIndex(queue, index, 1); } Element * findElement (const Queue *queue, ItemTester *testItem, void *data) { if (queue->head) { Element *element = queue->head; do { if (testItem(element->item, data)) return element; } while ((element = element->next) != queue->head); } return NULL; } void * findItem (const Queue *queue, ItemTester *testItem, void *data) { Element *element = findElement(queue, testItem, data); if (element) return element->item; return NULL; } static int testElementHasItem (const void *item, void *data) { return item == data; } Element * findElementWithItem (const Queue *queue, void *item) { return findElement(queue, testElementHasItem, item); } Element * processQueue (Queue *queue, ItemProcessor *processItem, void *data) { Element *element = queue->head; while (element) { Element *next = element->next; if (next == queue->head) next = NULL; if (processItem(element->item, data)) return element; element = next; } return NULL; } static int testItemAddress (const void *item, void *data) { return item == data; } int deleteItem (Queue *queue, void *item) { Element *element = findElement(queue, testItemAddress, item); if (!element) return 0; element->item = NULL; deleteElement(element); return 1; }