gambas-source-code/main/lib/data/c_deque.c

392 lines
8.2 KiB
C
Raw Normal View History

/*
* c_deque.c - Deque, FILO and FIFO types
*
* Copyright (C) 2012/3 Tobias Boege <tobias@gambas-buch.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#define __C_DEQUE_C
#include "gambas.h"
#include "gb_common.h" /* EXTERN for gbx_type.h */
#include "gbx_type.h" /* TYPE_is_object() */
#include "c_list.h"
#include "c_deque.h"
typedef struct {
GB_VARIANT_VALUE var;
LIST list;
int prio;
} CDEQUE_ELEM;
#define get_elem(node) LIST_data(node, CDEQUE_ELEM, list)
static CDEQUE_ELEM *CDEQUE_new_elem(GB_VARIANT *variant)
{
CDEQUE_ELEM *new;
GB.Alloc((void **) &new, sizeof(*new));
new->var.type = GB_T_NULL;
LIST_init(&new->list);
GB.StoreVariant(variant, &new->var);
return new;
}
static CDEQUE_ELEM *CDEQUE_copy_elem(CDEQUE_ELEM *elem)
{
CDEQUE_ELEM *copy;
GB.Alloc((void **) &copy, sizeof(*copy));
LIST_init(&copy->list);
copy->prio = elem->prio;
memcpy(&copy->var, &elem->var, sizeof(copy->var));
if (TYPE_is_object(copy->var.type))
GB.Ref(copy->var.value._object);
return copy;
}
static void CDEQUE_destroy_elem(CDEQUE_ELEM *elem)
{
LIST_unlink(&elem->list);
GB.StoreVariant(NULL, &elem->var);
GB.Free((void **) &elem);
}
typedef struct {
GB_BASE ob;
LIST elements;
} CDEQUE;
static int CDEQUE_is_empty(CDEQUE *dq)
{
return LIST_is_empty(&dq->elements);
}
static void CDEQUE_init(CDEQUE *dq)
{
LIST_init(&dq->elements);
}
#define THIS ((CDEQUE *) _object)
BEGIN_METHOD_VOID(Deque_new)
CDEQUE_init(THIS);
END_METHOD
static CDEQUE_ELEM *CDEQUE_pop_front(CDEQUE *dq)
{
CDEQUE_ELEM *elem;
if (CDEQUE_is_empty(dq))
return NULL;
elem = get_elem(dq->elements.next);
LIST_unlink(&elem->list);
return elem;
}
static void CDEQUE_pop_and_free_all(CDEQUE *dq)
{
while (!CDEQUE_is_empty(dq))
CDEQUE_destroy_elem(CDEQUE_pop_front(dq));
}
BEGIN_METHOD_VOID(Deque_free)
CDEQUE_pop_and_free_all(THIS);
END_METHOD
static void CDEQUE_push_front(CDEQUE *dq, CDEQUE_ELEM *elem)
{
LIST_append(&dq->elements, &elem->list);
}
BEGIN_METHOD(Deque_PushFront, GB_VARIANT value)
CDEQUE_ELEM *elem;
elem = CDEQUE_new_elem(ARG(value));
CDEQUE_push_front(THIS, elem);
END_METHOD
static void CDEQUE_push_back(CDEQUE *dq, CDEQUE_ELEM *elem)
{
LIST_prepend(&dq->elements, &elem->list);
}
BEGIN_METHOD(Deque_PushBack, GB_VARIANT value)
CDEQUE_ELEM *elem;
elem = CDEQUE_new_elem(ARG(value));
CDEQUE_push_back(THIS, elem);
END_METHOD
BEGIN_METHOD_VOID(Deque_PopFront)
CDEQUE_ELEM *elem;
if (CDEQUE_is_empty(THIS)) {
GB.ReturnNull();
GB.ReturnConvVariant();
return;
}
elem = CDEQUE_pop_front(THIS);
GB.ReturnVariant(&elem->var);
GB.ReturnBorrow();
GB.StoreVariant(NULL, &elem->var);
GB.ReturnRelease();
CDEQUE_destroy_elem(elem);
END_METHOD
static CDEQUE_ELEM *CDEQUE_pop_back(CDEQUE *dq)
{
CDEQUE_ELEM *elem;
if (CDEQUE_is_empty(dq))
return NULL;
elem = get_elem(dq->elements.prev);
LIST_unlink(&elem->list);
return elem;
}
BEGIN_METHOD_VOID(Deque_PopBack)
CDEQUE_ELEM *elem;
if (CDEQUE_is_empty(THIS)) {
GB.ReturnNull();
GB.ReturnConvVariant();
return;
}
elem = CDEQUE_pop_back(THIS);
GB.ReturnVariant(&elem->var);
GB.ReturnBorrow();
GB.StoreVariant(NULL, &elem->var);
GB.ReturnRelease();
CDEQUE_destroy_elem(elem);
END_METHOD
BEGIN_METHOD_VOID(Deque_PeekFront)
CDEQUE_ELEM *elem;
if (CDEQUE_is_empty(THIS)) {
GB.ReturnNull();
GB.ReturnConvVariant();
} else {
elem = get_elem(THIS->elements.next);
GB.ReturnVariant(&elem->var);
}
END_METHOD
BEGIN_METHOD_VOID(Deque_PeekBack)
CDEQUE_ELEM *elem;
if (CDEQUE_is_empty(THIS)) {
GB.ReturnNull();
GB.ReturnConvVariant();
} else {
elem = get_elem(THIS->elements.prev);
GB.ReturnVariant(&elem->var);
}
END_METHOD
BEGIN_METHOD_VOID(Deque_Clear)
CDEQUE_pop_and_free_all(THIS);
END_METHOD
static void CDEQUE_copy(CDEQUE *src, CDEQUE *dst)
{
LIST *node;
list_for_each(node, &src->elements)
CDEQUE_push_back(dst, CDEQUE_copy_elem(get_elem(node)));
}
BEGIN_METHOD_VOID(Deque_Copy)
CDEQUE *copy;
/* This method is also used by Queue and Stack, so get the right
* class. */
copy = GB.New(GB.GetClass(THIS), NULL, NULL);
CDEQUE_copy(THIS, copy);
GB.ReturnObject(copy);
END_METHOD
BEGIN_PROPERTY(Deque_IsEmpty)
GB.ReturnBoolean(CDEQUE_is_empty(THIS));
END_PROPERTY
BEGIN_PROPERTY(Deque_Size)
size_t size;
LIST *node;
size = 0;
list_for_each(node, &THIS->elements)
size++;
GB.ReturnInteger(size);
END_PROPERTY
/*
* Double-ended queue
*/
[GB.DATA] * BUG: Fix possible bugs when updating an invalid Current. * BUG: Really erase old elements instead of just marking them as Null to prevent (potential) information leaks. * OPT: Current and the enumerators (internally) carry list global indices (LGI) which can be used as anchor points together with List.Count to speed up traversals (always taking the shortest way through the list). * OPT: Remove workaround code related to an enumerator bug fixed in #5749. * NEW: Expose List.Current as a virtual object representing the current list element which has Append() and Prepend() methods to manipulate the list not only around its head. This breaks backwards compatibility. * NEW: List.Value is what List.Current has meant formerly. * NEW: Add List.Current.Index and List.Index which are an absolute index of the current element into the list. These are guaranteed to be in bounds of [-List.Count; List.Count - 1]. Note that each non-negative index has an associated negative equivalent and vice versa. They work like the indices given to List._get(). * NEW: Add a List.AutoNormalize property which instructs the list to automatically make indices (like resulting from a calculation) fit into the List bounds preserving the sign of the given index. This effectively prevents any "Out of bounds" errors. * NEW: Add List.MoveTo() to point Current to an index. * NEW: List.Current.Is{First,Last,Valid} can be used to determine if Current is the first or last element of the list or if it is valid, respectively. git-svn-id: svn://localhost/gambas/trunk@5778 867c0c6c-44f3-4631-809d-bfa615b0a4ec
2013-08-09 20:31:20 +02:00
GB_DESC CDeque[] = {
GB_DECLARE("Deque", sizeof(CDEQUE)),
GB_METHOD("_new", NULL, Deque_new, NULL),
GB_METHOD("_free", NULL, Deque_free, NULL),
GB_METHOD("PushFront", NULL, Deque_PushFront, "(Value)v"),
GB_METHOD("PushBack", NULL, Deque_PushBack, "(Value)v"),
GB_METHOD("PopFront", "v", Deque_PopFront, NULL),
GB_METHOD("PopBack", "v", Deque_PopBack, NULL),
GB_METHOD("PeekFront", "v", Deque_PeekFront, NULL),
GB_METHOD("PeekBack", "v", Deque_PeekBack, NULL),
GB_METHOD("Clear", NULL, Deque_Clear, NULL),
GB_METHOD("Copy", "Deque", Deque_Copy, NULL),
GB_PROPERTY_READ("IsEmpty", "b", Deque_IsEmpty),
GB_PROPERTY_READ("Size", "i", Deque_Size),
GB_END_DECLARE
};
/*
[GB.DATA] * BUG: Fix possible bugs when updating an invalid Current. * BUG: Really erase old elements instead of just marking them as Null to prevent (potential) information leaks. * OPT: Current and the enumerators (internally) carry list global indices (LGI) which can be used as anchor points together with List.Count to speed up traversals (always taking the shortest way through the list). * OPT: Remove workaround code related to an enumerator bug fixed in #5749. * NEW: Expose List.Current as a virtual object representing the current list element which has Append() and Prepend() methods to manipulate the list not only around its head. This breaks backwards compatibility. * NEW: List.Value is what List.Current has meant formerly. * NEW: Add List.Current.Index and List.Index which are an absolute index of the current element into the list. These are guaranteed to be in bounds of [-List.Count; List.Count - 1]. Note that each non-negative index has an associated negative equivalent and vice versa. They work like the indices given to List._get(). * NEW: Add a List.AutoNormalize property which instructs the list to automatically make indices (like resulting from a calculation) fit into the List bounds preserving the sign of the given index. This effectively prevents any "Out of bounds" errors. * NEW: Add List.MoveTo() to point Current to an index. * NEW: List.Current.Is{First,Last,Valid} can be used to determine if Current is the first or last element of the list or if it is valid, respectively. git-svn-id: svn://localhost/gambas/trunk@5778 867c0c6c-44f3-4631-809d-bfa615b0a4ec
2013-08-09 20:31:20 +02:00
* LIFO
*/
[GB.DATA] * BUG: Fix possible bugs when updating an invalid Current. * BUG: Really erase old elements instead of just marking them as Null to prevent (potential) information leaks. * OPT: Current and the enumerators (internally) carry list global indices (LGI) which can be used as anchor points together with List.Count to speed up traversals (always taking the shortest way through the list). * OPT: Remove workaround code related to an enumerator bug fixed in #5749. * NEW: Expose List.Current as a virtual object representing the current list element which has Append() and Prepend() methods to manipulate the list not only around its head. This breaks backwards compatibility. * NEW: List.Value is what List.Current has meant formerly. * NEW: Add List.Current.Index and List.Index which are an absolute index of the current element into the list. These are guaranteed to be in bounds of [-List.Count; List.Count - 1]. Note that each non-negative index has an associated negative equivalent and vice versa. They work like the indices given to List._get(). * NEW: Add a List.AutoNormalize property which instructs the list to automatically make indices (like resulting from a calculation) fit into the List bounds preserving the sign of the given index. This effectively prevents any "Out of bounds" errors. * NEW: Add List.MoveTo() to point Current to an index. * NEW: List.Current.Is{First,Last,Valid} can be used to determine if Current is the first or last element of the list or if it is valid, respectively. git-svn-id: svn://localhost/gambas/trunk@5778 867c0c6c-44f3-4631-809d-bfa615b0a4ec
2013-08-09 20:31:20 +02:00
GB_DESC CStack[] = {
GB_DECLARE("Stack", sizeof(CDEQUE)),
GB_METHOD("_new", NULL, Deque_new, NULL),
GB_METHOD("_free", NULL, Deque_free, NULL),
GB_METHOD("Push", NULL, Deque_PushBack, "(Value)v"),
GB_METHOD("Pop", "v", Deque_PopBack, NULL),
GB_METHOD("Peek", "v", Deque_PeekBack, NULL),
GB_METHOD("Clear", NULL, Deque_Clear, NULL),
GB_METHOD("Copy", "Stack", Deque_Copy, NULL),
GB_PROPERTY_READ("IsEmpty", "b", Deque_IsEmpty),
GB_PROPERTY_READ("Size", "i", Deque_Size),
GB_END_DECLARE
};
/*
* FIFO
*/
[GB.DATA] * BUG: Fix possible bugs when updating an invalid Current. * BUG: Really erase old elements instead of just marking them as Null to prevent (potential) information leaks. * OPT: Current and the enumerators (internally) carry list global indices (LGI) which can be used as anchor points together with List.Count to speed up traversals (always taking the shortest way through the list). * OPT: Remove workaround code related to an enumerator bug fixed in #5749. * NEW: Expose List.Current as a virtual object representing the current list element which has Append() and Prepend() methods to manipulate the list not only around its head. This breaks backwards compatibility. * NEW: List.Value is what List.Current has meant formerly. * NEW: Add List.Current.Index and List.Index which are an absolute index of the current element into the list. These are guaranteed to be in bounds of [-List.Count; List.Count - 1]. Note that each non-negative index has an associated negative equivalent and vice versa. They work like the indices given to List._get(). * NEW: Add a List.AutoNormalize property which instructs the list to automatically make indices (like resulting from a calculation) fit into the List bounds preserving the sign of the given index. This effectively prevents any "Out of bounds" errors. * NEW: Add List.MoveTo() to point Current to an index. * NEW: List.Current.Is{First,Last,Valid} can be used to determine if Current is the first or last element of the list or if it is valid, respectively. git-svn-id: svn://localhost/gambas/trunk@5778 867c0c6c-44f3-4631-809d-bfa615b0a4ec
2013-08-09 20:31:20 +02:00
GB_DESC CQueue[] = {
GB_DECLARE("Queue", sizeof(CDEQUE)),
GB_METHOD("_new", NULL, Deque_new, NULL),
GB_METHOD("_free", NULL, Deque_free, NULL),
GB_METHOD("Enqueue", NULL, Deque_PushBack, "(Value)v"),
GB_METHOD("Enq", NULL, Deque_PushBack, "(Value)v"),
GB_METHOD("Dequeue", "v", Deque_PopFront, NULL),
GB_METHOD("Deq", "v", Deque_PopFront, NULL),
GB_METHOD("Peek", "v", Deque_PeekFront, NULL),
GB_METHOD("Clear", NULL, Deque_Clear, NULL),
GB_METHOD("Copy", "Queue", Deque_Copy, NULL),
GB_PROPERTY_READ("IsEmpty", "b", Deque_IsEmpty),
GB_PROPERTY_READ("Size", "i", Deque_Size),
GB_END_DECLARE
};
/*
* We keep the Priority queue sorted by a special Enqueue() function.
* The higher the priority, the closer to the beginning the value will be
* enqueued. For equal priorities, the later added element will be enqueued
* at a higher index.
*
* TODO: This is an O(n) search over a linked list. We gotta change that!
*/
static void CPRIOQ_enqueue(CDEQUE *dq, CDEQUE_ELEM *elem, int prio)
{
LIST *next;
CDEQUE_ELEM *e;
/* Just find the right node to prepend the new value to */
list_for_each(next, &dq->elements) {
e = get_elem(next);
if (prio > e->prio)
break;
}
elem->prio = prio;
LIST_prepend(next, &elem->list);
}
BEGIN_METHOD(PrioQueue_Enqueue, GB_VARIANT value; GB_INTEGER prio)
CDEQUE_ELEM *elem;
elem = CDEQUE_new_elem(ARG(value));
CPRIOQ_enqueue(THIS, elem, VARG(prio));
END_METHOD
/*
* Priority FIFO
*/
[GB.DATA] * BUG: Fix possible bugs when updating an invalid Current. * BUG: Really erase old elements instead of just marking them as Null to prevent (potential) information leaks. * OPT: Current and the enumerators (internally) carry list global indices (LGI) which can be used as anchor points together with List.Count to speed up traversals (always taking the shortest way through the list). * OPT: Remove workaround code related to an enumerator bug fixed in #5749. * NEW: Expose List.Current as a virtual object representing the current list element which has Append() and Prepend() methods to manipulate the list not only around its head. This breaks backwards compatibility. * NEW: List.Value is what List.Current has meant formerly. * NEW: Add List.Current.Index and List.Index which are an absolute index of the current element into the list. These are guaranteed to be in bounds of [-List.Count; List.Count - 1]. Note that each non-negative index has an associated negative equivalent and vice versa. They work like the indices given to List._get(). * NEW: Add a List.AutoNormalize property which instructs the list to automatically make indices (like resulting from a calculation) fit into the List bounds preserving the sign of the given index. This effectively prevents any "Out of bounds" errors. * NEW: Add List.MoveTo() to point Current to an index. * NEW: List.Current.Is{First,Last,Valid} can be used to determine if Current is the first or last element of the list or if it is valid, respectively. git-svn-id: svn://localhost/gambas/trunk@5778 867c0c6c-44f3-4631-809d-bfa615b0a4ec
2013-08-09 20:31:20 +02:00
GB_DESC CPrioQueue[] = {
GB_DECLARE("PrioQueue", sizeof(CDEQUE)),
GB_METHOD("_new", NULL, Deque_new, NULL),
GB_METHOD("_free", NULL, Deque_free, NULL),
GB_METHOD("Enqueue", NULL, PrioQueue_Enqueue, "(Value)v(Prio)i"),
GB_METHOD("Enq", NULL, PrioQueue_Enqueue, "(Value)v(Prio)i"),
GB_METHOD("Dequeue", "v", Deque_PopFront, NULL),
GB_METHOD("Deq", "v", Deque_PopFront, NULL),
GB_METHOD("Peek", "v", Deque_PeekFront, NULL),
GB_METHOD("Clear", NULL, Deque_Clear, NULL),
GB_PROPERTY_READ("IsEmpty", "b", Deque_IsEmpty),
GB_PROPERTY_READ("Size", "i", Deque_Size),
GB_END_DECLARE
};