diff --git a/CMakeLists.txt b/CMakeLists.txt index 78b9f73..0797385 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,13 +5,12 @@ cmake_minimum_required(VERSION 3.0) add_subdirectory(src) include_directories(include) - find_package(Python REQUIRED COMPONENTS Interpreter Development) include_directories(${Python_INCLUDE_DIRS}) include_directories(/usr/include/python3.8) -add_library(_structura SHARED src/ringbuffer.c src/structura.c src/stack.c) +add_library(_structura SHARED src/ringbuffer.c src/structura.c src/stack.c src/linked.c) set_target_properties( structura diff --git a/README.rst b/README.rst index e9e62f8..b330aed 100644 --- a/README.rst +++ b/README.rst @@ -41,11 +41,16 @@ To build `structura` from source, you should clone the repo and run the followin What's New ---------- -Ring buffers have been added. +- 0.1.2 + - RingBuffer +- 0.2.0 + - Stack + - LinkedList + What's Coming ------------- -Linked Lists, Queues, Stacks, Hash Tables, Trees. +Queues, Hash Tables, Trees, iterables. Proposals for enhancement ------------------------- diff --git a/include/linked.h b/include/linked.h new file mode 100644 index 0000000..983da51 --- /dev/null +++ b/include/linked.h @@ -0,0 +1,24 @@ +#ifndef LINKED_H +#define LINKED_H + +#include + +extern PyTypeObject LinkedType; + +// double linked list +typedef struct _LinkedNode +{ + PyObject *data; + struct _LinkedNode *prev; + struct _LinkedNode *next; +} LinkedNode; + +typedef struct +{ + PyObject_HEAD; + long size; + LinkedNode *head; + LinkedNode *tail; +} Linked; + +#endif /* LINKED_H */ \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 940a552..1682ec0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -11,6 +11,7 @@ set(SOURCES structura.c ringbuffer.c stack.c + linked.c ) # add headers from include directory diff --git a/src/linked.c b/src/linked.c new file mode 100644 index 0000000..772c9ef --- /dev/null +++ b/src/linked.c @@ -0,0 +1,266 @@ +#define PY_SSIZE_T_CLEAN +#include +#include + +#include "../include/linked.h" + +static PyObject * +Linked_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + Linked *self; + + self = (Linked *)type->tp_alloc(type, 0); + if (self != NULL) + { + self->size = 0; + self->head = NULL; + self->tail = NULL; + } + + return (PyObject *)self; +} + +static void +Linked_dealloc(Linked *self) +{ + Py_TYPE(self)->tp_free((PyObject *)self); +} + +static int +Linked_init(Linked *self, PyObject *args, PyObject *kwds) +{ + return 0; +} + +static PyObject * +Linked_append(Linked *self, PyObject *args) +{ + PyObject *data; + LinkedNode *node; + + if (!PyArg_ParseTuple(args, "O", &data)) + return NULL; + + // TODO: CHECK IF THIS IS NEEDED OR NOT + Py_IncRef(data); + + node = (LinkedNode *)PyMem_Malloc(sizeof(LinkedNode)); + node->data = data; + node->prev = self->tail; + node->next = NULL; + + if (self->tail != NULL) + self->tail->next = node; + self->tail = node; + + if (self->head == NULL) + self->head = node; + + self->size++; + + Py_RETURN_NONE; +} + +// append but at the beginning +static PyObject * +Linked_prepend(Linked *self, PyObject *args) +{ + PyObject *data; + LinkedNode *node; + + if (!PyArg_ParseTuple(args, "O", &data)) + return NULL; + + // TODO: CHECK IF THIS IS NEEDED OR NOT + Py_IncRef(data); + + node = (LinkedNode *)PyMem_Malloc(sizeof(LinkedNode)); + node->data = data; + node->prev = NULL; + + if (self->head != NULL) + self->head->prev = node; + + node->next = self->head; + self->head = node; + + if (self->tail == NULL) + self->tail = node; + + self->size++; + + Py_RETURN_NONE; +} + +static PyObject * +Linked_pop(Linked *self, PyObject *args) +{ + PyObject *data; + LinkedNode *node; + + if (self->size == 0) + { + PyErr_SetString(PyExc_IndexError, "pop from empty list"); + return NULL; + } + + node = self->tail; + data = node->data; + + self->tail = node->prev; + if (self->tail != NULL) + self->tail->next = NULL; + + PyMem_Free(node); + + self->size--; + + return data; +} + +static PyObject * +Linked_pop_first(Linked *self, PyObject *args) +{ + LinkedNode *node; + + if (self->size == 0) + { + PyErr_SetString(PyExc_IndexError, "pop from empty list"); + return NULL; + } + + node = self->head; + + self->head = node->next; + if (self->head != NULL) + self->head->prev = NULL; + + PyMem_Free(node); + + self->size--; + + Py_RETURN_NONE; +} + +static PyObject * +Linked_delete_at(Linked *self, PyObject *args) +{ + PyObject *index_obj; + LinkedNode *node; + + if (!PyArg_ParseTuple(args, "O", &index_obj)) + return NULL; + + Py_IncRef(index_obj); + long index = PyLong_AsLong(index_obj); + + if (index < 0 || index >= self->size) + { + PyErr_SetString(PyExc_IndexError, "index out of range"); + return NULL; + } + + node = self->head; + + for (long i = 0; i < index; i++) + node = node->next; + + if (node->prev != NULL) + node->prev->next = node->next; + if (node->next != NULL) + node->next->prev = node->prev; + + // PyMem_Free(node); + + self->size--; + + Py_RETURN_NONE; +} + +static PyObject * +Linked_find(Linked *self, PyObject *args) +{ + PyObject *data; + LinkedNode *node; + + if (!PyArg_ParseTuple(args, "O", &data)) + return NULL; + + // TODO: CHECK IF THIS IS NEEDED OR NOT + Py_IncRef(data); + + node = self->head; + + long index = 0; + while (node != NULL) + { + if (PyObject_RichCompareBool(node->data, data, Py_EQ) == 1) + return PyLong_FromLong(index); + + node = node->next; + index++; + } + + Py_RETURN_NONE; +} + +static PyObject * +Linked_size(Linked *self, PyObject *args) +{ + return PyLong_FromLong(self->size); +} + +static PyObject * +Linked_head(Linked *self, PyObject *args) +{ + if (self->head == NULL) + Py_RETURN_NONE; + + PyObject *head = self->head->data; + Py_IncRef(head); + + return head; +} + +static PyObject * +Linked_tail(Linked *self, PyObject *args) +{ + if (self->tail == NULL) + Py_RETURN_NONE; + + PyObject *tail = self->tail->data; + Py_IncRef(tail); + + return tail; +} + +static PyMethodDef Linked_methods[] = { + {"append", (PyCFunction)Linked_append, METH_VARARGS, "Append an item to the end of the list"}, + {"prepend", (PyCFunction)Linked_prepend, METH_VARARGS, "Append an item to the beginning of the list"}, + {"pop", (PyCFunction)Linked_pop, METH_NOARGS, "Remove and return the last item"}, + {"pop_first", (PyCFunction)Linked_pop_first, METH_NOARGS, "Remove and return the first item"}, + {"delete_at", (PyCFunction)Linked_delete_at, METH_VARARGS, "Delete an item at a given index"}, + {"find", (PyCFunction)Linked_find, METH_VARARGS, "Find first occurence of an item and return its index, None otherwise"}, + {"size", (PyCFunction)Linked_size, METH_NOARGS, "Return the size of the list"}, + {"head", (PyCFunction)Linked_head, METH_NOARGS, "Return the first item"}, + {"tail", (PyCFunction)Linked_tail, METH_NOARGS, "Return the last item"}, + {NULL} /* Sentinel */ +}; + +static PyMemberDef Linked_members[] = { + {NULL} /* Sentinel */ +}; + +PyTypeObject LinkedType = { + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "LinkedList", + .tp_doc = "LinkedList data structure", + .tp_basicsize = sizeof(Linked), + .tp_itemsize = 0, + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + .tp_new = Linked_new, + .tp_init = (initproc)Linked_init, + .tp_dealloc = (destructor)Linked_dealloc, + .tp_members = Linked_members, + .tp_methods = Linked_methods, +}; diff --git a/src/ringbuffer.c b/src/ringbuffer.c index 5d18fa5..890451b 100644 --- a/src/ringbuffer.c +++ b/src/ringbuffer.c @@ -122,6 +122,9 @@ RingBuffer_dequeue(RingBuffer *self) return item; } +// TODO: FIX THE SEGMENTATION FAULT +// PROBABLY THE PROBLEM IS WE ARE STORING PYTHON OBJECTS +// WE ARE HOLDING REFERENCES TO THEM INSTEAD OF COPYING THEM static PyObject * RingBuffer_peek(RingBuffer const *self) { diff --git a/src/structura.c b/src/structura.c index cd6156b..5c1aada 100644 --- a/src/structura.c +++ b/src/structura.c @@ -1,8 +1,9 @@ #define PY_SSIZE_T_CLEAN #include -extern PyTypeObject RingBufferType; extern PyTypeObject StackType; +extern PyTypeObject LinkedType; +extern PyTypeObject RingBufferType; static PyObject * method_fputs(PyObject *self, PyObject *args) @@ -64,5 +65,12 @@ PyMODINIT_FUNC PyInit_structura(void) Py_INCREF(&StackType); PyModule_AddObject(module, "Stack", (PyObject *)&StackType); + /* Add LinkedList type */ + if (PyType_Ready(&LinkedType) < 0) + return NULL; + + Py_INCREF(&LinkedType); + PyModule_AddObject(module, "LinkedList", (PyObject *)&LinkedType); + return module; } \ No newline at end of file diff --git a/test/linked.py b/test/linked.py new file mode 100644 index 0000000..20cff08 --- /dev/null +++ b/test/linked.py @@ -0,0 +1,14 @@ +from structura import LinkedList + +l = LinkedList() + +for i in range(100): + l.append(f"q{i}") + print(l.head()) # this generates error + print(l.tail()) # no error + +for i in range(100): + l.delete_at(0) + +print(31) +