aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/algo.c230
-rw-r--r--src/buffer.c278
-rw-r--r--src/file_null.c275
-rw-r--r--src/file_posix.c253
-rw-r--r--src/file_win.c195
-rw-r--r--src/hashmap.c199
-rw-r--r--src/io.c186
-rw-r--r--src/queue.c135
-rw-r--r--src/thread.c152
-rw-r--r--src/thread_null.c280
-rw-r--r--src/thread_posix.c84
-rw-r--r--src/thread_win.c164
12 files changed, 2266 insertions, 165 deletions
diff --git a/src/algo.c b/src/algo.c
index 7f3287d..2d2d0ac 100644
--- a/src/algo.c
+++ b/src/algo.c
@@ -1,34 +1,72 @@
#include <bh/algo.h>
#include <string.h>
-#include <stdio.h>
-void bh_swap(void *lhs,
- void *rhs,
+/**
+ * \defgroup algo Algorithms
+ *
+ * Common algorithms (search, swap, heap, etc)
+ * \{
+ */
+
+/**
+ * Swaps \a size bytes between the object pointed to by \a src and object
+ * pointed to by \a dest.
+ *
+ * If the objects overlap, the behavior is undefined.
+ *
+ * If either \a src or \a dest is an invalid or null pointer, the behavior is
+ * undefined.
+ *
+ * \param dest Pointer to the memory location to swap to
+ * \param src Pointer to the memory location to swap from
+ * \param size Number of bytes to swap
+ */
+void bh_swap(void *dest,
+ void *src,
size_t size)
{
int tmp;
- /* Swap values in int sized chunks */
+ /* Swap bytes in int-sized chunks */
while (size >= sizeof(tmp))
{
- memmove(&tmp, lhs, sizeof(tmp));
- memmove(lhs, rhs, sizeof(tmp));
- memmove(rhs, &tmp, sizeof(tmp));
+ memmove(&tmp, dest, sizeof(tmp));
+ memmove(dest, src, sizeof(tmp));
+ memmove(src, &tmp, sizeof(tmp));
- lhs = (char *)lhs + sizeof(tmp);
- rhs = (char *)rhs + sizeof(tmp);
+ dest = (char *)dest + sizeof(tmp);
+ src = (char *)src + sizeof(tmp);
size -= sizeof(tmp);
}
- /* Swap the rest */
+ /* Swap the remaining size */
if (size)
{
- memmove(&tmp, lhs, size);
- memmove(lhs, rhs, size);
- memmove(rhs, &tmp, size);
+ memmove(&tmp, dest, size);
+ memmove(dest, src, size);
+ memmove(src, &tmp, size);
}
}
+/**
+ * Partitions the \a array of \a size elements of \a element bytes, relative to
+ * the specified \a pivot element. Function pointed by \a equal is used for
+ * comparison.
+ *
+ * If the \a pivot element is part of the partitioned \a array, function will
+ * behaive the same way, as if \a pivot element wasn't part of the array.
+ *
+ * If \a equal indicates two elements as equivalent, their order in the
+ * resulting partitioned array is unspecified.
+ *
+ * \param pivot Pointer to the pivot element
+ * \param array Pointer to the array to partition
+ * \param size Number of elements in the array
+ * \param element Size of each element in the array in bytes
+ * \param equal Comparision function
+ *
+ * \return Pointer to the first element of the second partition.
+ */
void *bh_partition(void *pivot,
void *array,
size_t size,
@@ -74,6 +112,17 @@ void *bh_partition(void *pivot,
return j + element;
}
+/**
+ * Sorts the \a array of \a size elements of \a element bytes. Function pointed
+ * by \a equal is used for comparision.
+ *
+ * This sorting does not make any gurantees about time or space complexity.
+ *
+ * \param array Pointer to the array to sort
+ * \param size Number of elements in the array
+ * \param element Size of each element in the array in bytes
+ * \param equal Comparision function
+ */
void bh_sort(void *array,
size_t size,
size_t element,
@@ -83,6 +132,23 @@ void bh_sort(void *array,
bh_sort_intro(array, size, element, equal);
}
+
+/**
+ * Sorts the \a array of \a size elements of \a element bytes with insertion
+ * sort. Function pointed by \a equal is used for comparision.
+ *
+ * This sorting has following time complexity:
+ * - best case O(n)
+ * - average case O(n^2)
+ * - worst case O(n^2)
+ *
+ * This sorting has O(1) space complexity.
+ *
+ * \param array Pointer to the array to sort
+ * \param size Number of elements in the array
+ * \param element Size of each element in the array in bytes
+ * \param equal Comparision function
+ */
void bh_sort_insert(void *array,
size_t size,
size_t element,
@@ -109,6 +175,20 @@ void bh_sort_insert(void *array,
}
+/**
+ * Sorts the \a array of \a size elements of \a element bytes with shell sort.
+ * Function pointed by \a equal is used for comparision.
+ *
+ * This sorting has the O(n log n) best cast time complexity. Average and worst
+ * cases are implementation dependant.
+ *
+ * This sorting has O(1) space complexity.
+ *
+ * \param array Pointer to the array to sort
+ * \param size Number of elements in the array
+ * \param element Size of each element in the array in bytes
+ * \param equal Comparision function
+ */
void bh_sort_shell(void *array,
size_t size,
size_t element,
@@ -118,7 +198,7 @@ void bh_sort_shell(void *array,
const size_t *gap;
size_t i, j;
- /* Shell sort with A102549 sequence*/
+ /* Shell sort with A102549 sequence */
for (gap = gaps; gap < gaps + sizeof(gaps) / sizeof(*gaps); gap++)
{
for (i = *gap; i < size; i++)
@@ -201,6 +281,22 @@ static void bh_sort_intro_r(void *array,
}
}
+/**
+ * Sorts the \a array of \a size elements of \a element bytes with insertion
+ * sort. Function pointed by \a equal is used for comparision.
+ *
+ * This sorting has following time complexity:
+ * - best case O(n log n)
+ * - average case O(n log n)
+ * - worst case O(n log n)
+ *
+ * This sorting has O(log n) space complexity.
+ *
+ * \param array Pointer to the array to sort
+ * \param size Number of elements in the array
+ * \param element Size of each element in the array in bytes
+ * \param equal Comparision function
+ */
void bh_sort_intro(void *array,
size_t size,
size_t element,
@@ -221,6 +317,22 @@ void bh_sort_intro(void *array,
bh_sort_intro_r(array, size, element, equal, depth);
}
+/**
+ * Sorts the \a array of \a size elements of \a element bytes with insertion
+ * sort. Function pointed by \a equal is used for comparision.
+ *
+ * This sorting has following time complexity:
+ * - best case O(n)
+ * - average case O(n log n)
+ * - worst case O(n log n)
+ *
+ * This sorting has O(1) space complexity.
+ *
+ * \param array Pointer to the array to sort
+ * \param size Number of elements in the array
+ * \param element Size of each element in the array in bytes
+ * \param equal Comparision function
+ */
void bh_sort_heap(void *array,
size_t size,
size_t element,
@@ -232,7 +344,15 @@ void bh_sort_heap(void *array,
bh_heap_remove(array, size--, element, equal);
}
-
+/**
+ * Heapifes the \a array of \a size elements of \a element bytes. Function
+ * pointed by \a equal is used for comparision.
+ *
+ * \param array Pointer to the array to heapify
+ * \param size Number of elements in the array
+ * \param element Size of each element in the array in bytes
+ * \param equal Comparision function
+ */
void bh_heap_make(void *array,
size_t size,
size_t element,
@@ -279,6 +399,15 @@ void bh_heap_make(void *array,
}
}
+/**
+ * Removes top element from heapified \a array of \a size elements of \a
+ * elements bytes. Function pointed by \a equal is used for comparasion.
+ *
+ * \param array Pointer to the array to remove element from
+ * \param size Number of elements in the array
+ * \param element Size of each element in the array in bytes
+ * \param equal Comparasion function
+ */
void bh_heap_remove(void *array,
size_t size,
size_t element,
@@ -323,6 +452,16 @@ void bh_heap_remove(void *array,
}
}
+/**
+ * Inserts new element into heapified \a array of \a size elements of \a
+ * elements bytes. Function pointed by \a equal is used for comparasion.
+ *
+ * \param value Pointer to the value to be inserted
+ * \param array Pointer to the array to remove element from
+ * \param size Number of elements in the array
+ * \param element Size of each element in the array in bytes
+ * \param equal Comparasion function
+ */
void bh_heap_insert(void *value,
void *array,
size_t size,
@@ -358,3 +497,64 @@ void bh_heap_insert(void *value,
break;
}
}
+
+/**
+ * Replaces top element of the heapified \a array of \a size elements of \a
+ * elements bytes. Function pointed by \a equal is used for comparasion.
+ *
+ * This function is equal to two consecutive operations of remove and insert.
+ *
+ * \param value Pointer to the value to be inserted
+ * \param array Pointer to the array to remove element from
+ * \param size Number of elements in the array
+ * \param element Size of each element in the array in bytes
+ * \param equal Comparasion function
+ */
+void bh_heap_replace(void *value,
+ void *array,
+ size_t size,
+ size_t element,
+ bh_equal_cb_t equal)
+{
+ char *start, *end, *current, *left, *right;
+
+ if (size <= 1)
+ return;
+
+ /* Calculate start, end and children pointers */
+ start = (char *)array;
+ end = start + size * element;
+ current = start;
+ left = start + (current - start) * 2 + element;
+ right = left + element;
+
+ /* Copy supplied element to the first (top) position */
+ memmove(current, value, element);
+
+ /* Iterate until we reach the end */
+ while (left < end)
+ {
+ char *biggest;
+
+ /* Determine biggest child */
+ biggest = left;
+ if (right < end && equal(left, right) < 0)
+ biggest = right;
+
+ /* Compare biggest child with current */
+ if (equal(current, biggest) < 0)
+ {
+ /* Swap content and recalculate children pointers */
+ bh_swap(current, biggest, element);
+ current = biggest;
+ left = start + (current - start) * 2 + element;
+ right = left + element;
+ }
+ else
+ break;
+ }
+}
+
+/**
+ * \}
+ */ \ No newline at end of file
diff --git a/src/buffer.c b/src/buffer.c
new file mode 100644
index 0000000..c66c6ae
--- /dev/null
+++ b/src/buffer.c
@@ -0,0 +1,278 @@
+#include <bh/internal/buffer.h>
+#include <stdlib.h>
+#include <string.h>
+
+static const bh_io_table_t bh_buffer_table = {
+ (int (*)(struct bh_io_s *, int)) bh_buffer_open_base,
+ (void (*)(struct bh_io_s *)) bh_buffer_close_base,
+ (int (*)(struct bh_io_s *)) bh_buffer_is_open_base,
+ (size_t (*)(struct bh_io_s *, char *, size_t)) bh_buffer_read_base,
+ (size_t (*)(struct bh_io_s *, const char *, size_t)) bh_buffer_write_base,
+ (void (*)(struct bh_io_s *)) bh_buffer_flush_base,
+ (int (*)(struct bh_io_s *, bh_off_t, int)) bh_buffer_seek_base,
+ (bh_off_t (*)(struct bh_io_s *)) bh_buffer_size_base,
+ (bh_off_t (*)(struct bh_io_s *)) bh_buffer_tell_base,
+ (bh_off_t (*)(struct bh_io_s *)) bh_buffer_available_base,
+ (void (*)(struct bh_io_s *)) bh_buffer_clear_base,
+ (void (*)(struct bh_io_s *)) bh_buffer_destroy
+};
+
+bh_buffer_t *bh_buffer_new(void)
+{
+ bh_buffer_t *result;
+
+ /* Allocate and initialize buffer structure */
+ result = malloc(sizeof(*result));
+ if (result && bh_buffer_init(result))
+ {
+ /* Something went wrong - free allocated memory */
+ free(result);
+ result = NULL;
+ }
+
+ return result;
+}
+
+void bh_buffer_free(bh_buffer_t *buffer)
+{
+ bh_buffer_destroy(buffer);
+ free(buffer);
+}
+
+int bh_buffer_init(bh_buffer_t *buffer)
+{
+ bh_io_init(&buffer->base, &bh_buffer_table);
+
+ buffer->data = NULL;
+ buffer->capacity = 0;
+ buffer->size = 0;
+ buffer->at = 0;
+ buffer->mode = 0;
+
+ return BH_OK;
+}
+
+void bh_buffer_destroy(bh_buffer_t *buffer)
+{
+ bh_buffer_close(buffer);
+ if (buffer->data)
+ free(buffer->data);
+}
+
+const char *bh_buffer_data(bh_buffer_t *buffer)
+{
+ return buffer->data;
+}
+
+void bh_buffer_set_data(bh_buffer_t *buffer,
+ const char *data,
+ size_t size)
+{
+ buffer->size = 0;
+ bh_buffer_write_base(buffer, data, size);
+}
+
+size_t bh_buffer_capacity(bh_buffer_t *buffer)
+{
+ return buffer->capacity;
+}
+
+int bh_buffer_reserve(bh_buffer_t *buffer,
+ size_t size)
+{
+ char *data = NULL;
+
+ /* New capacity can't be less then current buffer size */
+ if (size < buffer->size)
+ size = buffer->size;
+
+ /* Prevent same size reallocation */
+ if (buffer->capacity == size)
+ return BH_OK;
+
+ /* Allocate new memory for the buffer */
+ if (size)
+ {
+ data = malloc(size);
+ if (!data)
+ return BH_OOM;
+
+ /* Copy data */
+ if (buffer->size)
+ memmove(data, buffer->data, buffer->size);
+ }
+
+ /* Free previosly allocated memory */
+ if (buffer->data)
+ free(buffer->data);
+
+ /* Update buffer fields */
+ buffer->data = data;
+ buffer->capacity = size;
+
+ return BH_OK;
+}
+
+int bh_buffer_open_base(bh_buffer_t *buffer,
+ int mode)
+{
+ /* Check if buffer is already open */
+ if (buffer->mode != 0)
+ return BH_OK;
+
+ /* Update buffer mode field */
+ buffer->mode = mode;
+
+ /* Determine open mode */
+ switch (mode & BH_IO_MASK)
+ {
+ case BH_IO_OPEN:
+ case BH_IO_CREATE:
+ case BH_IO_APPEND:
+ break;
+
+ case BH_IO_TRUNCATE:
+ buffer->size = 0;
+ break;
+
+ default:
+ return BH_NO_IMPL;
+ }
+
+ return BH_OK;
+}
+
+void bh_buffer_close_base(bh_buffer_t *buffer)
+{
+ buffer->mode = 0;
+}
+
+int bh_buffer_is_open_base(bh_buffer_t *buffer)
+{
+ return buffer->mode != 0;
+}
+
+size_t bh_buffer_read_base(bh_buffer_t *buffer,
+ char *data,
+ size_t size)
+{
+ /* Check if buffer openned in read mode */
+ if (!(buffer->mode & BH_IO_READ))
+ {
+ buffer->base.flags |= BH_IO_ERROR;
+ return 0;
+ }
+
+ /* Calculate maximum available size for reading */
+ if (size > buffer->size - buffer->at)
+ size = buffer->size - buffer->at;
+
+ /* Perform reading */
+ if (size)
+ {
+ memmove(data, buffer->data + buffer->at, size);
+ buffer->at += size;
+ }
+
+ /* Check for end of file */
+ if (!size)
+ buffer->base.flags |= BH_IO_EOF;
+ else
+ buffer->base.flags &= ~BH_IO_EOF;
+
+ return size;
+}
+
+size_t bh_buffer_write_base(bh_buffer_t *buffer,
+ const char *data,
+ size_t size)
+{
+ size_t capacity = 0;
+
+ /* Check if buffer is openned in write mode */
+ if (!(buffer->mode & BH_IO_WRITE))
+ {
+ buffer->base.flags |= BH_IO_ERROR;
+ return 0;
+ }
+
+ /* Adjust at position */
+ if ((buffer->mode & BH_IO_MASK) == BH_IO_APPEND)
+ buffer->at = buffer->size;
+
+ /* Calculate required capacity and check for overflow */
+ capacity = buffer->at + size;
+ if (capacity < size)
+ {
+ buffer->base.flags |= BH_IO_ERROR;
+ return 0;
+ }
+
+ /* Try to grow buffer */
+ if (buffer->capacity < capacity)
+ bh_buffer_reserve(buffer, capacity);
+
+ /* Calculate maximum write size */
+ if (size > buffer->capacity - buffer->at)
+ size = buffer->capacity - buffer->at;
+
+ /* Perform writing */
+ if (size)
+ {
+ memmove(buffer->data + buffer->at, data, size);
+ buffer->at += size;
+ buffer->size += size;
+ }
+
+ /* Check for end of file */
+ if (!size)
+ buffer->base.flags |= BH_IO_EOF;
+ else
+ buffer->base.flags &= ~BH_IO_EOF;
+
+ return size;
+}
+
+void bh_buffer_flush_base(bh_buffer_t *buffer)
+{
+ (void)buffer;
+}
+
+int bh_buffer_seek_base(bh_buffer_t *buffer,
+ bh_off_t pos,
+ int dir)
+{
+ switch (dir)
+ {
+ case BH_IO_SET: buffer->at = pos; break;
+ case BH_IO_CURRENT: buffer->at += pos; break;
+ case BH_IO_END: buffer->at = buffer->size - pos; break;
+ default: return BH_NO_IMPL;
+ }
+
+ if (buffer->at > buffer->size)
+ buffer->at = buffer->size;
+
+ return BH_OK;
+}
+
+bh_off_t bh_buffer_size_base(bh_buffer_t *buffer)
+{
+ return buffer->size;
+}
+
+bh_off_t bh_buffer_tell_base(bh_buffer_t *buffer)
+{
+ return buffer->at;
+}
+
+bh_off_t bh_buffer_available_base(bh_buffer_t *buffer)
+{
+ return buffer->size - buffer->at;
+}
+
+void bh_buffer_clear_base(bh_buffer_t *buffer)
+{
+ buffer->base.flags &= ~BH_IO_ERROR;
+}
+
diff --git a/src/file_null.c b/src/file_null.c
new file mode 100644
index 0000000..75684d8
--- /dev/null
+++ b/src/file_null.c
@@ -0,0 +1,275 @@
+#include <bh/internal/file.h>
+
+/**
+ * \defgroup file File IO
+ *
+ * File input/output API
+ * \{
+ */
+
+/**
+ * Creates new file object with specified \a path to file.
+ *
+ * \param path Path to the file
+ *
+ * \return On success, returns new file object.
+ * \return On failure, returns null pointer.
+ */
+bh_file_t *bh_file_new(const char *path)
+{
+ (void)path;
+ return NULL;
+}
+
+/**
+ * Frees the \a file object.
+ *
+ * Before freeing the file object, this function ensures that underlying file
+ * was closed.
+ *
+ * \param file Pointer to the file object
+ */
+void bh_file_free(bh_file_t *file)
+{
+ (void)file;
+}
+
+/**
+ * Initializes the \a file object with specified \a path to file.
+ *
+ * \param file Pointer to the file object
+ * \param path Path to the file
+ *
+ * \return On success, returns zero.
+ * \return On failure, returns error code.
+ */
+int bh_file_init(bh_file_t *file,
+ const char *path)
+{
+ (void)file;
+ (void)path;
+ return BH_NO_IMPL;
+}
+
+/**
+ * Destroyes the \a file object.
+ *
+ * Before destroying the file object, this function ensures that underlying
+ * file was closed.
+ *
+ * \param file Pointer to the file object
+ */
+void bh_file_destroy(bh_file_t *file)
+{
+ (void)file;
+}
+
+/**
+ * Opens the \a file object for specified \a mode of operation.
+ *
+ * \param file Pointer to the file object
+ * \param mode Bitmask determining access mode
+ *
+ * \return On success, returns zero.
+ * \return On failure, returns error code.
+ *
+ * \warning This function should be used in context of implementing child
+ * io objects (files, sockets, streaming compression, etc).
+ */
+int bh_file_open_base(bh_file_t *file,
+ int mode)
+{
+ (void)file;
+ (void)mode;
+ return BH_NO_IMPL;
+}
+
+/**
+ * Closes the \a file object.
+ *
+ * \param file Pointer to the file object
+ *
+ * \warning This function should be used in context of implementing child
+ * io objects (files, sockets, streaming compression, etc).
+ */
+void bh_file_close_base(bh_file_t *file)
+{
+ (void)file;
+}
+
+/**
+ * Checks if the \a file is open.
+ *
+ * \param file Pointer to the file object
+ *
+ * \return If file object is open - returns non-zero.
+ * \return If file object is closed - returns zero.
+ *
+ * \warning This function should be used in context of implementing child
+ * io objects (files, sockets, streaming compression, etc).
+ */
+int bh_file_is_open_base(bh_file_t *file)
+{
+ (void)file;
+ return 0;
+}
+
+/**
+ * Reads up to \a size amount of bytes from the \a file object into memory
+ * buffer pointed by \a data pointer.
+ *
+ * \param file Pointer to the file object
+ * \param data Pointer to the memory buffer
+ * \param size Maximum number of bytes to be read
+ *
+ * \return On success, returns number of bytes successfuly read.
+ * \return On failure, returns zero.
+ *
+ * \note To check for end-of-file or error see bh_io_eof and bh_io_error.
+ *
+ * \warning This function should be used in context of implementing child
+ * io objects (files, sockets, streaming compression, etc).
+ */
+size_t bh_file_read_base(bh_file_t *file,
+ char *data,
+ size_t size)
+{
+ (void)file;
+ (void)data;
+ (void)size;
+ return 0;
+}
+
+/**
+ * Writes up to \a size amount of bytes to the \a file object from memory
+ * buffer pointed by \a data pointer.
+ *
+ * \param file Pointer to the file object
+ * \param data Pointer to the memory buffer
+ * \param size Maximum number of bytes to be read
+ *
+ * \return On success, returns number of bytes successfuly written.
+ * \return On failure, returns zero.
+ *
+ * \note To check for error see bh_io_error.
+ *
+ * \warning This function should be used in context of implementing child
+ * io objects (files, sockets, streaming compression, etc).
+ */
+size_t bh_file_write_base(bh_file_t *file,
+ const char *data,
+ size_t size)
+{
+ (void)file;
+ (void)data;
+ (void)size;
+ return 0;
+}
+
+/**
+ * Synchronizes the \a file object (if possible).
+ *
+ * In most cases, this function causes any unwritten/buffered data to be
+ * written.
+ *
+ * \param file Pointer to the file object
+ *
+ * \warning This function should be used in context of implementing child
+ * io objects (files, sockets, streaming compression, etc).
+ */
+void bh_file_flush_base(bh_file_t *file)
+{
+ (void)file;
+}
+
+/**
+ * Seeks the \a file object in the specified direction \a dir and \a offset
+ * (if possible).
+ *
+ * \param file Pointer to the file object
+ * \param offset Number of bytes to seek in specified direciton
+ * \param dir Seeking direction
+ *
+ * \return On success, returns zero.
+ * \return On failure, returns error code.
+ *
+ * \warning This function should be used in context of implementing child
+ * io objects (files, sockets, streaming compression, etc).
+ */
+int bh_file_seek_base(bh_file_t *file,
+ bh_off_t off,
+ int dir)
+{
+ (void)file;
+ (void)off;
+ (void)dir;
+ return BH_NO_IMPL;
+}
+
+/**
+ * Returns total size of the \a file object (if possible)
+ *
+ * \param file Pointer to the file object
+ *
+ * \return On success, returns total size of the file object.
+ * \return On failure, returns -1.
+ *
+ * \warning This function should be used in context of implementing child
+ * io objects (files, sockets, streaming compression, etc).
+ */
+bh_off_t bh_file_size_base(bh_file_t *file)
+{
+ (void)file;
+ return -1;
+}
+
+/**
+ * Returns current position in the \a file object (if possible).
+ *
+ * \param file Pointer to the io object
+ *
+ * \return On success, returns current position in the file object.
+ * \return On failure, returns -1.
+ *
+ * \warning This function should be used in context of implementing child
+ * io objects (files, sockets, streaming compression, etc).
+ */
+bh_off_t bh_file_tell_base(bh_file_t *file)
+{
+ (void)file;
+ return -1;
+}
+
+/**
+ * Returns available number of bytes to be read from the \a file object.
+ *
+ * \param file Pointer to the file object
+ *
+ * \return On success, returns number of available bytes to be read.
+ * \return On failure, returns zero.
+ *
+ * \warning This function should be used in context of implementing child
+ * io objects (files, sockets, streaming compression, etc).
+ */
+bh_off_t bh_file_available_base(bh_file_t *file)
+{
+ (void)file;
+ return 0;
+}
+
+/**
+ * Clears error of the \a file object.
+ *
+ * \param file Pointer to the file object
+ *
+ * \warning This function should be used in context of implementing child
+ * io objects (files, sockets, streaming compression, etc).
+ */
+void bh_file_clear_base(bh_file_t *file)
+{
+ (void)file;
+}
+
+/**
+ * \}
+ */
diff --git a/src/file_posix.c b/src/file_posix.c
index e69de29..0ffbf5e 100644
--- a/src/file_posix.c
+++ b/src/file_posix.c
@@ -0,0 +1,253 @@
+#include <bh/internal/file.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+
+static const bh_io_table_t bh_file_table = {
+ (int (*)(struct bh_io_s *, int)) bh_file_open_base,
+ (void (*)(struct bh_io_s *)) bh_file_close_base,
+ (int (*)(struct bh_io_s *)) bh_file_is_open_base,
+ (size_t (*)(struct bh_io_s *, char *, size_t)) bh_file_read_base,
+ (size_t (*)(struct bh_io_s *, const char *, size_t)) bh_file_write_base,
+ (void (*)(struct bh_io_s *)) bh_file_flush_base,
+ (int (*)(struct bh_io_s *, bh_off_t, int)) bh_file_seek_base,
+ (bh_off_t (*)(struct bh_io_s *)) bh_file_size_base,
+ (bh_off_t (*)(struct bh_io_s *)) bh_file_tell_base,
+ (bh_off_t (*)(struct bh_io_s *)) bh_file_available_base,
+ (void (*)(struct bh_io_s *)) bh_file_clear_base,
+ (void (*)(struct bh_io_s *)) bh_file_destroy
+};
+
+bh_file_t *bh_file_new(const char *path)
+{
+ bh_file_t *result;
+
+ /* Allocate and initialize file structure */
+ result = malloc(sizeof(*result));
+ if (result && bh_file_init(result, path))
+ {
+ /* Something went wrong - free allocated memory */
+ free(result);
+ result = NULL;
+ }
+
+ return result;
+}
+
+void bh_file_free(bh_file_t *file)
+{
+ bh_file_destroy(file);
+ free(file);
+}
+
+int bh_file_init(bh_file_t *file,
+ const char *path)
+{
+ /* Ensure that path is not empty */
+ if (!path)
+ return BH_INVALID;
+
+ /* Initialize base io object */
+ bh_io_init(&file->base, &bh_file_table);
+
+ /* Initialize file io object */
+ file->handle = -1;
+ file->mode = 0;
+ file->path = strdup(path);
+
+ return BH_OK;
+}
+
+void bh_file_destroy(bh_file_t *file)
+{
+ /* Close the file */
+ bh_file_close(file);
+ free(file->path);
+}
+
+int bh_file_open_base(bh_file_t *file,
+ int mode)
+{
+ /* Set open mode for 0666 permisions */
+ mode_t open_mode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
+ int open_flags = 0;
+
+ /* Check if file is already opened */
+ if (file->handle != -1)
+ return BH_OK;
+
+ /* Determine read/write flags */
+ if (mode & BH_IO_READ_WRITE)
+ open_flags |= O_RDWR;
+ else if (mode & BH_IO_READ)
+ open_flags |= O_RDONLY;
+ else if (mode & BH_IO_WRITE)
+ open_flags |= O_WRONLY;
+ else
+ return BH_INVALID;
+
+ /* Determine open mode */
+ switch (mode & BH_IO_MASK)
+ {
+ case BH_IO_OPEN: break;
+ case BH_IO_CREATE: open_flags |= O_CREAT | O_EXCL; break;
+ case BH_IO_APPEND: open_flags |= O_CREAT | O_APPEND; break;
+ case BH_IO_TRUNCATE: open_flags |= O_CREAT | O_TRUNC; break;
+
+ default:
+ return BH_NO_IMPL;
+ }
+
+ /* Open file */
+ file->handle = open(file->path, open_flags, open_mode);
+
+ /* Check for errors */
+ if (file->handle == -1)
+ return BH_ERROR;
+
+ return BH_OK;
+}
+
+void bh_file_close_base(bh_file_t *file)
+{
+ /* Close file if it's open */
+ if (file->handle != -1)
+ close(file->handle);
+
+ /* Set handle to invalid value */
+ file->handle = -1;
+}
+
+int bh_file_is_open_base(bh_file_t *file)
+{
+ /* If handle is not -1 - then file is open */
+ return file->handle != -1;
+}
+
+size_t bh_file_read_base(bh_file_t *file,
+ char *data,
+ size_t size)
+{
+ ssize_t readed;
+
+ /* Check if file is open */
+ if (file->handle == -1)
+ {
+ /* Error occured - set error bit */
+ file->base.flags |= BH_IO_ERROR;
+ return 0;
+ }
+
+ /* Read data from file */
+ readed = read(file->handle, data, size);
+ if (readed < 0)
+ {
+ /* Error occured - set error bit */
+ file->base.flags |= BH_IO_ERROR;
+ return 0;
+ }
+
+ /* Check for end of file */
+ if (!readed)
+ file->base.flags |= BH_IO_EOF;
+ else
+ file->base.flags &= ~BH_IO_EOF;
+
+ /* Return number of readed bytes */
+ return readed;
+}
+
+size_t bh_file_write_base(bh_file_t *file,
+ const char *data,
+ size_t size)
+{
+ ssize_t written;
+
+ /* Check if file is open */
+ if (file->handle == -1)
+ {
+ /* Error occured - set error bit */
+ file->base.flags |= BH_IO_ERROR;
+ return 0;
+ }
+
+ /* Write data to file */
+ written = write(file->handle, data, size);
+ if (written < 0)
+ {
+ /* Error occured - set error bit */
+ file->base.flags |= BH_IO_ERROR;
+ return 0;
+ }
+
+ /* Check for end of file */
+ if (!written)
+ file->base.flags |= BH_IO_EOF;
+ else
+ file->base.flags &= ~BH_IO_EOF;
+
+ /* Return number of written bytes */
+ return written;
+}
+
+void bh_file_flush_base(bh_file_t *file)
+{
+ /* Check if file is open */
+ if (file->handle == -1)
+ return;
+
+ /* Signal OS to flush data from the internal buffers */
+ fsync(file->handle);
+}
+
+int bh_file_seek_base(bh_file_t *file,
+ bh_off_t pos,
+ int dir)
+{
+ /* Check if file is open */
+ if (file->handle == -1)
+ return BH_ERROR;
+
+ /* Seek to desired location */
+ if (lseek(file->handle, pos, dir) == -1);
+ return BH_ERROR;
+
+ return BH_OK;
+}
+
+bh_off_t bh_file_size_base(bh_file_t *file)
+{
+ struct stat sb;
+
+ /* Check if file is open */
+ if (file->handle == -1)
+ return -1;
+
+ /* Get file size from the OS */
+ if (fstat(file->handle, &sb))
+ return -1;
+
+ return sb.st_size;
+}
+
+bh_off_t bh_file_tell_base(bh_file_t *file)
+{
+ /* Check if file is open */
+ if (file->handle == -1)
+ return -1;
+
+ /* Get current file position */
+ return lseek(file->handle, 0, SEEK_CUR);
+}
+
+bh_off_t bh_file_available_base(bh_file_t *file)
+{
+ /* Get available bytes for reading */
+ return bh_file_size_base(file) - bh_file_tell_base(file);
+}
+
+void bh_file_clear_base(bh_file_t *file)
+{
+ file->base.flags &= ~BH_IO_ERROR;
+}
diff --git a/src/file_win.c b/src/file_win.c
index e69de29..e31e0de 100644
--- a/src/file_win.c
+++ b/src/file_win.c
@@ -0,0 +1,195 @@
+#include <bh/internal/file.h>
+
+static const bh_io_table_t bh_file_table = {
+ (int (*)(struct bh_io_s *, int)) bh_file_open_base,
+ (void (*)(struct bh_io_s *)) bh_file_close_base,
+ (int (*)(struct bh_io_s *)) bh_file_is_open_base,
+ (size_t (*)(struct bh_io_s *, char *, size_t)) bh_file_read_base,
+ (size_t (*)(struct bh_io_s *, const char *, size_t)) bh_file_write_base,
+ (void (*)(struct bh_io_s *)) bh_file_flush_base,
+ (int (*)(struct bh_io_s *, bh_off_t, int)) bh_file_seek_base,
+ (bh_off_t (*)(struct bh_io_s *)) bh_file_size_base,
+ (bh_off_t (*)(struct bh_io_s *)) bh_file_tell_base,
+ (bh_off_t (*)(struct bh_io_s *)) bh_file_available_base,
+ (void (*)(struct bh_io_s *)) bh_file_clear_base,
+ (void (*)(struct bh_io_s *)) bh_file_destroy
+};
+
+bh_file_t *bh_file_new(const char *path)
+{
+ bh_file_t *result;
+
+ result = malloc(sizeof(*result));
+ if (result && bh_file_init(result, path))
+ {
+ free(result);
+ result = NULL;
+ }
+
+ return result;
+}
+
+void bh_file_free(bh_file_t *file)
+{
+ bh_file_destroy(file);
+ free(file);
+}
+
+int bh_file_init(bh_file_t *file,
+ const char *path)
+{
+ if (!path)
+ return BH_INVALID;
+
+ file->handle = INVALID_HANDLE_VALUE;
+ file->mode = 0;
+ file->path = strdup(path);
+ file->base.table = &bh_file_table;
+
+ return BH_OK;
+}
+
+int bh_file_open_base(bh_file_t *file,
+ int mode)
+{
+ DWORD access = 0, how = 0;
+
+ if (mode & BH_IO_READ)
+ access |= GENERIC_READ;
+ if (mode & BH_IO_WRITE)
+ access |= GENERIC_WRITE;
+
+ switch (mode & BH_IO_MASK)
+ {
+ case BH_IO_OPEN: how = OPEN_EXISTING; break;
+ case BH_IO_CREATE: how = CREATE_ALWAYS; break;
+ case BH_IO_APPEND: how = OPEN_ALWAYS; break;
+
+ default:
+ case BH_IO_TRUNCATE:
+ return BH_NO_IMPL;
+ }
+
+ file->mode = mode;
+ file->handle = CreateFileA(file->path, access, FILE_SHARE_READ, NULL, how, FILE_ATTRIBUTE_NORMAL, NULL);
+
+ if (file->handle == INVALID_HANDLE_VALUE)
+ {
+ /* Error handling */
+ switch (GetLastError())
+ {
+ case ERROR_FILE_EXISTS: return BH_FOUND;
+ case ERROR_FILE_NOT_FOUND: return BH_NOT_FOUND;
+ default: return BH_ERROR;
+ }
+ }
+
+ return BH_OK;
+}
+
+void bh_file_close_base(bh_file_t *file)
+{
+ if (file->handle != INVALID_HANDLE_VALUE)
+ CloseHandle(file->handle);
+
+ file->handle = INVALID_HANDLE_VALUE;
+}
+
+int bh_file_is_open_base(bh_file_t *file)
+{
+ return file->handle != INVALID_HANDLE_VALUE;
+}
+
+void bh_file_destroy(bh_file_t *file)
+{
+ bh_file_close(file);
+ free(file->path);
+}
+
+size_t bh_file_read_base(bh_file_t *file,
+ char *data,
+ size_t size)
+{
+ DWORD readed;
+
+ if (!ReadFile(file->handle, data, (DWORD)size, &readed, NULL))
+ {
+ file->base.flags |= BH_IO_ERROR;
+ return 0;
+ }
+
+ if (!readed)
+ file->base.flags |= BH_IO_EOF;
+ else
+ file->base.flags &= ~BH_IO_EOF;
+
+ return readed;
+}
+
+size_t bh_file_write_base(bh_file_t *file,
+ const char *data,
+ size_t size)
+{
+ DWORD written;
+
+ if ((file->mode & BH_IO_MASK) == BH_IO_APPEND)
+ bh_file_seek(file, 0, BH_IO_END);
+
+ if (!WriteFile(file->handle, data, (DWORD)size, &written, NULL))
+ {
+ file->base.flags |= BH_IO_ERROR;
+ return 0;
+ }
+
+ if (!written)
+ file->base.flags |= BH_IO_EOF;
+ else
+ file->base.flags &= ~BH_IO_EOF;
+
+ return written;
+}
+
+void bh_file_flush_base(bh_file_t *file)
+{
+ FlushFileBuffers(file->handle);
+}
+
+int bh_file_seek_base(bh_file_t *file,
+ bh_off_t pos,
+ int dir)
+{
+ LARGE_INTEGER position;
+
+ position.QuadPart = pos;
+ if (SetFilePointerEx(file->handle, position, NULL, dir))
+ return BH_OK;
+
+ return BH_ERROR;
+}
+
+bh_off_t bh_file_size_base(bh_file_t *file)
+{
+ return -1;
+}
+
+bh_off_t bh_file_tell_base(bh_file_t *file)
+{
+ LARGE_INTEGER dummy, position;
+
+ dummy.QuadPart = 0;
+
+ if (SetFilePointerEx(file->handle, dummy, &position, BH_IO_CURRENT))
+ return position.QuadPart;
+
+ return -1;
+}
+
+bh_off_t bh_file_available_base(bh_file_t *file)
+{
+ return 0;
+}
+
+void bh_file_clear_base(bh_file_t *file)
+{
+ file->base.flags &= ~BH_IO_ERROR;
+}
diff --git a/src/hashmap.c b/src/hashmap.c
index c9fb315..256644a 100644
--- a/src/hashmap.c
+++ b/src/hashmap.c
@@ -2,6 +2,22 @@
#include <stdlib.h>
#include <string.h>
+/**
+ * \defgroup hashmap Hashmap
+ *
+ * Data stucture for storing pointers in the hashmap.
+ * \{
+ */
+
+/**
+ * Creates the new hashmap object.
+ *
+ * \param equal Comparision function
+ * \param hash Key hash function
+ *
+ * \return On success, returns the pointer to the new hashmap object.
+ * \return On failure, returns a null pointer.
+ */
bh_hashmap_t *bh_hashmap_new(bh_equal_cb_t equal,
bh_hash_cb_t hash)
{
@@ -14,12 +30,26 @@ bh_hashmap_t *bh_hashmap_new(bh_equal_cb_t equal,
return result;
}
+/**
+ * Frees the \a hashmap object.
+ *
+ * \param hashmap Pointer to the hashmap object to be freed
+ */
void bh_hashmap_free(bh_hashmap_t *hashmap)
{
bh_hashmap_destroy(hashmap);
free(hashmap);
}
+/**
+ * Initializes the \a hashmap object.
+ *
+ * \warning This is an internal function.
+ *
+ * \param hashmap Pointer to the hashmap object to be initialized
+ * \param equal Comparision function
+ * \param hash Key hash function
+ */
void bh_hashmap_init(bh_hashmap_t *hashmap,
bh_equal_cb_t equal,
bh_hash_cb_t hash)
@@ -30,6 +60,13 @@ void bh_hashmap_init(bh_hashmap_t *hashmap,
hashmap->hash = hash;
}
+/**
+ * Destroys the \a hashmap object.
+ *
+ * \warning This is an internal function.
+ *
+ * \param hashmap Pointer to the hashmap object to be destroied
+ */
void bh_hashmap_destroy(bh_hashmap_t *hashmap)
{
if (hashmap->capacity)
@@ -39,6 +76,11 @@ void bh_hashmap_destroy(bh_hashmap_t *hashmap)
}
}
+/**
+ * Clears the \a hashmap object.
+ *
+ * \param hashmap Pointer to the hashmap object to be cleared
+ */
void bh_hashmap_clear(bh_hashmap_t *hashmap)
{
if (hashmap->capacity)
@@ -46,8 +88,24 @@ void bh_hashmap_clear(bh_hashmap_t *hashmap)
hashmap->size = 0;
}
+/**
+ * Reserves the space for \a size elements in the \a hashmap.
+ *
+ * This function can both expand and shrink the available space in \a hashmap.
+ * This function takes into account current hashmap load factor.
+ *
+ * \param hashmap Pointer to the hashmap object to be resized in terms of
+ * capacity
+ * \param size New capacity of the hashmap
+ *
+ * \note Calling this function will invalidate iterators.
+ * \note Actual hashmap capacity can be bigger then requested.
+ *
+ * \return On success, returns zero value.
+ * \return On failure, returns error code.
+ */
int bh_hashmap_reserve(bh_hashmap_t *hashmap,
- size_t size)
+ size_t size)
{
bh_hashmap_t other;
size_t capacity, max_capacity, threshold;
@@ -78,7 +136,7 @@ int bh_hashmap_reserve(bh_hashmap_t *hashmap,
/* Capacity can't be bigger than max capacity and overflow */
if (capacity > max_capacity || capacity < 16)
- return -1;
+ return BH_OOM;
}
}
else
@@ -93,7 +151,7 @@ int bh_hashmap_reserve(bh_hashmap_t *hashmap,
/* Prevent same size reallocation */
if (capacity == hashmap->capacity)
- return 0;
+ return BH_OK;
/* Initialize new hashmap */
bh_hashmap_init(&other, hashmap->equal, hashmap->hash);
@@ -105,7 +163,6 @@ int bh_hashmap_reserve(bh_hashmap_t *hashmap,
/* Allocate new memory for the hashmap */
other.data = malloc(sizeof(*other.data) * capacity);
-
other.psls = malloc(sizeof(size_t) * capacity);
other.threshold = threshold;
other.capacity = capacity;
@@ -117,7 +174,7 @@ int bh_hashmap_reserve(bh_hashmap_t *hashmap,
free(other.data);
if (other.psls)
free(other.psls);
- return -1;
+ return BH_OOM;
}
/* Reset probe sequence lengths */
@@ -140,9 +197,22 @@ int bh_hashmap_reserve(bh_hashmap_t *hashmap,
/* Swap hashmaps */
bh_hashmap_destroy(hashmap);
*hashmap = other;
- return 0;
+ return BH_OK;
}
+/**
+ * Inserts the pair of \a key and \a value into the \a hashmap. This function
+ * allows duplicates to be inserted.
+ *
+ * \param hashmap Pointer to the hashmap object
+ * \param key Key to be inserted
+ * \param value Value to be inserted
+ *
+ * \note Calling this function will invalidate iterators.
+ *
+ * \return On success, returns zero value.
+ * \return On failure, returns error code.
+ */
int bh_hashmap_insert(bh_hashmap_t *hashmap,
void *key,
void *value)
@@ -154,7 +224,7 @@ int bh_hashmap_insert(bh_hashmap_t *hashmap,
if (hashmap->size + 1 > hashmap->threshold)
if (bh_hashmap_reserve(hashmap, hashmap->size + 1))
if (hashmap->size >= hashmap->capacity)
- return -1;
+ return BH_OOM;
/* Prepare inserted data and set PSL to 1 */
item.key = key;
@@ -187,9 +257,19 @@ int bh_hashmap_insert(bh_hashmap_t *hashmap,
hashmap->psls[bucket] = psl;
hashmap->size++;
- return 0;
+ return BH_OK;
}
+/**
+ * Removes value from the \a hashmap by \a key.
+ *
+ * \param hashmap Pointer to the hashmap object
+ * \param key Key value
+ *
+ * \note Calling this function will invalidate iterators.
+ * \note If hashmap contains several key-value pairs with the same key, only
+ * one pair will be removed.
+ */
void bh_hashmap_remove(bh_hashmap_t *hashmap,
void *key)
{
@@ -200,6 +280,19 @@ void bh_hashmap_remove(bh_hashmap_t *hashmap,
bh_hashmap_iter_remove(hashmap, iter);
}
+/**
+ * Returns the value from the \a hashmap by the specified \a key.
+ *
+ * If the \a exists is not null pointer, the value will be stored to indicated,
+ * whether the \a key exists in the hashmap or not.
+ *
+ * \param hashmap Pointer to the hashmap object
+ * \param key Key value
+ * \param exists Pointer to the exists flag variable (optional)
+ *
+ * \return On success, returns value.
+ * \return On failure, return null pointer.
+ */
void *bh_hashmap_at(bh_hashmap_t *hashmap,
void *key,
int *exists)
@@ -221,36 +314,87 @@ void *bh_hashmap_at(bh_hashmap_t *hashmap,
return NULL;
}
+/**
+ * Checks if the \a hashmap is empty.
+ *
+ * \param hashmap Pointer to the hashmap object
+ *
+ * \return If hashmap is empty, returns non-zero value
+ * \return If hashmap is not empty, returns zero value
+ */
int bh_hashmap_empty(bh_hashmap_t *hashmap)
{
return !hashmap->size;
}
+/**
+ * Returns the size of the \a hashmap.
+ *
+ * \param hashmap Pointer to the hashmap object
+ *
+ * \return Returns the size of the hashmap.
+ */
size_t bh_hashmap_size(bh_hashmap_t *hashmap)
{
return hashmap->size;
}
+/**
+ * Returns the capacity of the \a hashmap.
+ *
+ * \param hashmap Pointer to the hashmap object
+ *
+ * \return Returns the capacity of the hashmap.
+ */
size_t bh_hashmap_capacity(bh_hashmap_t *hashmap)
{
return hashmap->capacity;
}
+/**
+ * Returns the load factor of the \a hashmap.
+ *
+ * \param hashmap Pointer to the hashmap object
+ *
+ * \return Returns the load factor of the hashmap.
+ */
float bh_hashmap_factor(bh_hashmap_t *hashmap)
{
return hashmap->factor;
}
+/**
+ * Sets the load \a factor of the \a hashmap.
+ *
+ * \param hashmap Pointer to the hashmap object
+ * \param factor Load factor value
+ *
+ * \note New load factor will be applied on the next reserve/insert operation.
+ */
void bh_hashmap_set_factor(bh_hashmap_t *hashmap,
float factor)
{
+ /* Limit the factor value to [0.15, 1.0] */
factor = (factor > 1.0f) ? (1.0f) : (factor);
factor = (factor < 0.15f) ? (0.15f) : (factor);
+ /* Calculate new threshold value */
hashmap->factor = factor;
hashmap->threshold = hashmap->capacity * factor;
}
+/**
+ * Returns the iterator to the element in the \a hashmap with specified \a key.
+ *
+ * \param hashmap Pointer to the hashmap object
+ * \param iter Opaque iterator value
+ *
+ * \return On success, returns iterator value.
+ * \return On failure, returns null pointer.
+ *
+ * \note If hashmap contains several key-value pairs with the same key, only
+ * iterator to the one of the pairs will returned.
+ */
void *bh_hashmap_iter_at(bh_hashmap_t *hashmap,
void *key)
{
@@ -278,6 +422,19 @@ void *bh_hashmap_iter_at(bh_hashmap_t *hashmap,
return NULL;
}
+/**
+ * Returns the iterator to the next element in the \a hashmap.
+ *
+ * \param hashmap Pointer to the hashmap object
+ * \param iter Opaque iterator value
+ *
+ * \return If the \a iter doesn't point to the last element of the hashmap,
+ * returns next iterator value.
+ * \return If the \a iter point to the last element of the hashmap, returns
+ * null pointer.
+ * \return If the \a iter is the null pointer, returns iterator to the
+ * first element of the hashmap.
+ */
void *bh_hashmap_iter_next(bh_hashmap_t *hashmap,
void *iter)
{
@@ -302,6 +459,14 @@ void *bh_hashmap_iter_next(bh_hashmap_t *hashmap,
}
}
+/**
+ * Removes value from the \a hashmap by iterator \a iter.
+ *
+ * \param hashmap Pointer to the hashmap object
+ * \param iter Iterator value
+ *
+ * \note Calling this function will invalidate iterators.
+ */
void bh_hashmap_iter_remove(bh_hashmap_t *hashmap,
void *iter)
{
@@ -332,12 +497,30 @@ void bh_hashmap_iter_remove(bh_hashmap_t *hashmap,
hashmap->psls[bucket] = 0;
}
+/**
+ * Returns the key, pointed by the hashmap iterator \a iter.
+ *
+ * \param iter Opaque iterator value
+ *
+ * \return Returns key, pointed by iterator.
+ */
void *bh_hashmap_iter_key(void *iter)
{
return ((bh_hashmap_node_t *)iter)->key;
}
+/**
+ * Returns the value, pointed by the hashmap iterator \a iter.
+ *
+ * \param iter Opaque iterator value
+ *
+ * \return Returns value, pointed by iterator.
+ */
void *bh_hashmap_iter_value(void *iter)
{
return ((bh_hashmap_node_t *)iter)->value;
}
+
+/**
+ * \}
+ */
diff --git a/src/io.c b/src/io.c
index e4d7833..5e94f35 100644
--- a/src/io.c
+++ b/src/io.c
@@ -1,6 +1,25 @@
#include <bh/internal/io.h>
#include <stdlib.h>
+/**
+ * \defgroup io Input/Output
+ *
+ * Input/output device abstraction layer.
+ * \{
+ */
+
+/**
+ * Creates the new io object with specified \a table and \a size.
+ *
+ * \param table Pointer to the io table
+ * \param size Size of the io object
+ *
+ * \return On success, returns new semi-initialized io object.
+ * \return On failure, returns null pointer.
+ *
+ * \warning This function should be used in context of implementing child
+ * io objects (files, sockets, streaming compression, etc).
+ */
bh_io_t *bh_io_new(bh_io_table_t *table,
size_t size)
{
@@ -13,24 +32,91 @@ bh_io_t *bh_io_new(bh_io_table_t *table,
return result;
}
+/**
+ * Frees the \a io object.
+ *
+ * \param io Pointer to the io object to be freed
+ */
void bh_io_free(bh_io_t *io)
{
bh_io_destroy(io);
free(io);
}
+/**
+ * Initializes the \a io object with specified \a table.
+ *
+ * \param io Pointer to the io object to be initialized
+ * \param table Pointer to the io table
+ */
void bh_io_init(bh_io_t *io,
- bh_io_table_t *table)
+ const bh_io_table_t *table)
{
io->table = table;
io->flags = 0;
}
+/**
+ * Destroys the \a io object.
+ *
+ * \param io Pointer to the io object to be destroyed
+ */
void bh_io_destroy(bh_io_t *io)
{
io->table->destroy(io);
}
+/**
+ * Opens the \a io object for specified \a mode of operation.
+ *
+ * \param io Pointer to the io object
+ * \param mode Bitmask determining access mode
+ *
+ * \return On success, returns zero.
+ * \return On failure, returns error code.
+ */
+int bh_io_open(bh_io_t *io,
+ int mode)
+{
+ return io->table->open(io, mode);
+}
+
+/**
+ * Closes the \a io object.
+ *
+ * \param io Pointer to the io object
+ */
+void bh_io_close(bh_io_t *io)
+{
+ io->table->close(io);
+}
+
+/**
+ * Checks if the \a io is open.
+ *
+ * \param io Pointer to the io object
+ *
+ * \return If io object is open - returns non-zero.
+ * \return If io object is closed - returns zero.
+ */
+int bh_io_is_open(bh_io_t *io)
+{
+ return io->table->is_open(io);
+}
+
+/**
+ * Reads up to \a size amount of bytes from the \a io object into memory buffer
+ * pointed by \a data pointer.
+ *
+ * \param io Pointer to the io object
+ * \param data Pointer to the memory buffer
+ * \param size Maximum number of bytes to be read
+ *
+ * \return On success, returns number of bytes successfuly read.
+ * \return On failure, returns zero.
+ *
+ * \note To check for end-of-file or error see bh_io_eof and bh_io_error.
+ */
size_t bh_io_read(bh_io_t *io,
char *data,
size_t size)
@@ -38,6 +124,19 @@ size_t bh_io_read(bh_io_t *io,
return io->table->read(io, data, size);
}
+/**
+ * Writes up to \a size amount of bytes to the \a io object from memory buffer
+ * pointed by \a data pointer.
+ *
+ * \param io Pointer to the io object
+ * \param data Pointer to the memory buffer
+ * \param size Maximum number of bytes to be read
+ *
+ * \return On success, returns number of bytes successfuly written.
+ * \return On failure, returns zero.
+ *
+ * \note To check for error see bh_io_error.
+ */
size_t bh_io_write(bh_io_t *io,
const char* data,
size_t size)
@@ -45,39 +144,112 @@ size_t bh_io_write(bh_io_t *io,
return io->table->write(io, data, size);
}
+/**
+ * Synchronizes the \a io object (if possible).
+ *
+ * In most cases, this function causes any unwritten/buffered data to be
+ * written.
+ *
+ * \param io Pointer to the io object
+ */
void bh_io_flush(bh_io_t *io)
{
io->table->flush(io);
}
-void bh_io_seek(bh_io_t *io,
- bh_off_t offset,
- int dir)
+/**
+ * Seeks the \a io object in the specified direction \a dir and \a offset (if
+ * possible).
+ *
+ * \param io Pointer to the io object
+ * \param offset Number of bytes to seek in specified direciton
+ * \param dir Seeking direction
+ *
+ * \return On success, returns zero.
+ * \return On failure, returns error code.
+ */
+int bh_io_seek(bh_io_t *io,
+ bh_off_t offset,
+ int dir)
{
- io->table->seek(io, offset, dir);
+ return io->table->seek(io, offset, dir);
}
+/**
+ * Returns total size of the \a io object (if possible)
+ *
+ * \param io Pointer to the io object
+ *
+ * \return On success, returns total size of the io object.
+ * \return On failure, returns -1.
+ */
+bh_off_t bh_io_size(bh_io_t *io)
+{
+ return io->table->size(io);
+}
+
+/**
+ * Returns current position in the \a io object (if possible).
+ *
+ * \param io Pointer to the io object
+ *
+ * \return On success, returns current position in the io object.
+ * \return On failure, returns -1.
+ */
bh_off_t bh_io_tell(bh_io_t *io)
{
return io->table->tell(io);
}
+/**
+ * Returns available number of bytes to be read from the \a io object.
+ *
+ * \param io Pointer to the io object
+ *
+ * \return On success, returns number of available bytes to be read.
+ * \return On failure, returns zero.
+ */
bh_off_t bh_io_available(bh_io_t *io)
{
return io->table->available(io);
}
+/**
+ * Checks error flag of the \a io object.
+ *
+ * \param io Pointer to the io object
+ *
+ * \return If error flag is set, returns non-zero.
+ * \return If error flag is not set, returns zero.
+ */
int bh_io_error(bh_io_t *io)
{
- return (io->flags & BH_IO_ERROR) > 0;
+ return (io->flags & BH_IO_ERROR) != 0;
}
+/**
+ * Checks end-of-file flag of the \a io object.
+ *
+ * \param io Pointer to the io object
+ *
+ * \return If end-of-file flag is set, returns non-zero.
+ * \return If end-of-file flag is not set, returns zero.
+ */
int bh_io_eof(bh_io_t *io)
{
- return (io->flags & BH_IO_EOF) > 0;
+ return (io->flags & BH_IO_EOF) != 0;
}
+/**
+ * Clears error of the \a io object.
+ *
+ * \param io Pointer to the io object
+ */
void bh_io_clear(bh_io_t *io)
{
io->table->clear(io);
}
+
+/**
+ * \}
+ */
diff --git a/src/queue.c b/src/queue.c
index 8d48401..e991769 100644
--- a/src/queue.c
+++ b/src/queue.c
@@ -2,6 +2,19 @@
#include <stdlib.h>
#include <string.h>
+/**
+ * \defgroup queue Queue
+ *
+ * Data stucture for storing pointers in queue.
+ * \{
+ */
+
+/**
+ * Creates the new queue object.
+ *
+ * \return On success, returns the pointer to the new queue object.
+ * \return On failure, returns a null pointer.
+ */
bh_queue_t *bh_queue_new(void)
{
bh_queue_t *result;
@@ -13,23 +26,47 @@ bh_queue_t *bh_queue_new(void)
return result;
}
+/**
+ * Frees the \a queue object.
+ *
+ * \param queue Pointer to the queue object to be freed
+ */
void bh_queue_free(bh_queue_t *queue)
{
bh_queue_destroy(queue);
free(queue);
}
+/**
+ * Initializes the \a queue object.
+ *
+ * \warning This is an internal function.
+ *
+ * \param queue Pointer to the queue object to be initialized
+ */
void bh_queue_init(bh_queue_t *queue)
{
memset(queue, 0, sizeof(*queue));
}
+/**
+ * Destroys the \a queue object.
+ *
+ * \warning This is an internal function.
+ *
+ * \param queue Pointer to the queue object to be destroyed
+ */
void bh_queue_destroy(bh_queue_t *queue)
{
if (queue->capacity)
free(queue->data);
}
+/**
+ * Clears the \a queue object.
+ *
+ * \param queue Pointer to the queue object to be cleared
+ */
void bh_queue_clear(bh_queue_t *queue)
{
queue->head = 0;
@@ -37,6 +74,20 @@ void bh_queue_clear(bh_queue_t *queue)
queue->size = 0;
}
+/**
+ * Reserves the space for \a size elements in the \a queue.
+ *
+ * This function can both expand and shrink the available space in \a queue.
+ *
+ * \param queue Pointer to the queue object to be resized in terms of capacity
+ * \param size New capacity of the queue
+ *
+ * \note Calling this function will invalidate iterators.
+ * \note Actual hashmap capacity can be bigger then requested.
+ *
+ * \return On success, returns zero value.
+ * \return On failure, returns error code.
+ */
int bh_queue_reserve(bh_queue_t *queue,
size_t size)
{
@@ -48,11 +99,11 @@ int bh_queue_reserve(bh_queue_t *queue,
/* New capacity should not exceed maximum capacity */
if (size > ((size_t)-1) / sizeof(void *))
- return -1;
+ return BH_OOM;
/* Prevent same size memory reallocation */
if (size == queue->capacity)
- return 0;
+ return BH_OK;
/* Prepare new empty queue */
bh_queue_init(&other);
@@ -64,7 +115,7 @@ int bh_queue_reserve(bh_queue_t *queue,
other.data = malloc(size * sizeof(void *));
other.capacity = size;
if (!other.data)
- return -1;
+ return BH_OOM;
/* Iterate over old queue and insert data into new queue */
iter = bh_queue_iter_next(queue, NULL);
@@ -81,9 +132,20 @@ int bh_queue_reserve(bh_queue_t *queue,
/* Copy queue information */
*queue = other;
- return 0;
+ return BH_OK;
}
+/**
+ * Inserts the \a value into the \a queue.
+ *
+ * \param queue Pointer to the queue object
+ * \param value Value to be inserted
+ *
+ * \note Calling this function will invalidate iterators.
+ *
+ * \return On success, returns zero value.
+ * \return On failure, returns error code.
+ */
int bh_queue_insert(bh_queue_t *queue,
void *value)
{
@@ -95,7 +157,7 @@ int bh_queue_insert(bh_queue_t *queue,
/* Check potential size overflow and reserve capacity */
capacity = (queue->capacity) ? (queue->capacity * 2) : (16);
if (capacity < queue->capacity || bh_queue_reserve(queue, capacity))
- return -1;
+ return BH_OOM;
}
/* Increase queue size and advance tail index */
@@ -104,9 +166,16 @@ int bh_queue_insert(bh_queue_t *queue,
if (++queue->tail >= queue->capacity)
queue->tail = 0;
- return 0;
+ return BH_OK;
}
+/**
+ * Removes front value from the \a queue.
+ *
+ * \param queue Pointer to the queue object
+ *
+ * \note Calling this function will invalidate iterators.
+ */
void bh_queue_remove(bh_queue_t *queue)
{
/* Do nothing if queue is empty */
@@ -119,6 +188,14 @@ void bh_queue_remove(bh_queue_t *queue)
queue->head = 0;
}
+/**
+ * Returns front value from the \a queue.
+ *
+ * \param queue Pointer to the queue object
+ *
+ * \return On success, returns front value from the queue.
+ * \return On failure, returns null pointer.
+ */
void *bh_queue_front(bh_queue_t *queue)
{
/* Do nothing if queue is empty */
@@ -129,21 +206,56 @@ void *bh_queue_front(bh_queue_t *queue)
return queue->data[queue->head];
}
+/**
+ * Checks if the \a queue is empty.
+ *
+ * \param queue Pointer to the queue object
+ *
+ * \return If queue is empty, returns non-zero value
+ * \return If queue is not empty, returns zero value
+ */
int bh_queue_empty(bh_queue_t *queue)
{
return !queue->size;
}
+/**
+ * Returns the size of the \a queue.
+ *
+ * \param queue Pointer to the queue object
+ *
+ * \return Returns the size of the queue.
+ */
size_t bh_queue_size(bh_queue_t *queue)
{
return queue->size;
}
+/**
+ * Returns the capacity of the \a queue.
+ *
+ * \param queue Pointer to the queue object
+ *
+ * \return Returns the capacity of the queue.
+ */
size_t bh_queue_capacity(bh_queue_t *queue)
{
return queue->capacity;
}
+/**
+ * Returns the iterator to the next element in the \a queue.
+ *
+ * \param queue Pointer to the queue object
+ * \param iter Opaque iterator value
+ *
+ * \return If the \a iter doesn't point to the last element of the queue,
+ * returns next iterator value.
+ * \return If the \a iter point to the last element of the queue, returns
+ * null pointer.
+ * \return If the \a iter is the null pointer, returns iterator to the
+ * first element of the queue.
+ */
void *bh_queue_iter_next(bh_queue_t *queue,
void *iter)
{
@@ -170,7 +282,18 @@ void *bh_queue_iter_next(bh_queue_t *queue,
return element;
}
+/**
+ * Returns the value, pointed by the queue iterator \a iter.
+ *
+ * \param iter Opaque iterator value
+ *
+ * \return Returns value, pointed by iterator.
+ */
void *bh_queue_iter_value(void *iter)
{
return *(void **)iter;
}
+
+/**
+ * \}
+ */
diff --git a/src/thread.c b/src/thread.c
index 84bc2c0..2744387 100644
--- a/src/thread.c
+++ b/src/thread.c
@@ -1,6 +1,27 @@
#include <bh/internal/thread.h>
#include <stdlib.h>
+/**
+ * \defgroup thread Multithreading
+ *
+ * Multithreading API
+ * \{
+ */
+
+/**
+ * Creates new task object with specified function \a func, \a data and \a
+ * flags.
+ *
+ * If the \a flags contain BH_THREAD_CLEANUP, upon successful completion task
+ * will be automaticly destroyed.
+ *
+ * \param func Task's function
+ * \param data Task's data
+ * \param flags Task's flags
+ *
+ * \return On success, returns new task object.
+ * \return On failure, returns null pointer.
+ */
bh_task_t *bh_task_new(void (*func)(void *),
void *data,
int flags)
@@ -14,12 +35,31 @@ bh_task_t *bh_task_new(void (*func)(void *),
return result;
}
+/**
+ * Frees the \a task object.
+ *
+ * \param task Pointer to the task object.
+ */
void bh_task_free(bh_task_t *task)
{
bh_task_destroy(task);
free(task);
}
+/**
+ * Initializes the \a task object with specified function \a func, \a data and
+ * \a flags.
+ *
+ * If the \a flags contain BH_THREAD_CLEANUP, upon successful completion task
+ * will be automaticly destroyed.
+ *
+ * \warning This is an internal function.
+ *
+ * \param task Pointer to the task object
+ * \param func Task's function
+ * \param data Task's data
+ * \param flags Task's flags
+ */
void bh_task_init(bh_task_t *task,
void (*func)(void *),
void *data,
@@ -30,24 +70,53 @@ void bh_task_init(bh_task_t *task,
task->flags = flags;
}
+/**
+ * Destroyes the \a task object.
+ *
+ * \warning This is an internal function.
+ *
+ * \param task Pointer to the task object
+ */
void bh_task_destroy(bh_task_t *task)
{
(void)task;
}
+/**
+ * Reuses the \a task object for different purpose.
+ *
+ * \param task Pointer to the task object
+ * \param func New task's function
+ * \param data New task's data
+ */
void bh_task_reuse(bh_task_t *task,
void (*func)(void *),
void *data)
{
+ /* If the task is done - reinitilize it with new data */
if (task->flags & BH_THREAD_DONE)
bh_task_init(task, func, data, task->flags & ~(BH_THREAD_DONE));
}
+/**
+ * Checks if the \a task is done.
+ *
+ * \param task Pointer to the task object
+ *
+ * \return If the task is done, returns non-zero.
+ * \return If the task is not done, returns zero.
+ */
int bh_task_done(bh_task_t *task)
{
return (task->flags & BH_THREAD_DONE) != 0;
}
+/**
+ * Creates the mutex object.
+ *
+ * \return On success, returns new mutex object.
+ * \return On failure, returns null pointer.
+ */
bh_mutex_t *bh_mutex_new(void)
{
bh_mutex_t *result;
@@ -62,32 +131,58 @@ bh_mutex_t *bh_mutex_new(void)
return result;
}
+/**
+ * Frees the \a mutex object.
+ *
+ * \param mutex Pointer to the mutex object
+ */
void bh_mutex_free(bh_mutex_t *mutex)
{
bh_mutex_destroy(mutex);
free(mutex);
}
+/**
+ * Creates the semaphore object with initial \a count value.
+ *
+ * \param count Initial semaphore value
+ *
+ * \return On success, returns new semaphore object.
+ * \return On failure, returns null pointer.
+ *
+ * \note Guranteed maximum \a count value is 32767.
+ */
bh_semaphore_t *bh_semaphore_new(int count)
{
bh_semaphore_t *result;
-
+
result = malloc(sizeof(*result));
if (result && bh_semaphore_init(result, count))
{
free(result);
result = NULL;
}
-
+
return result;
}
+/**
+ * Frees the \a semaphore object.
+ *
+ * \param semaphore Pointer to the semaphore object
+ */
void bh_semaphore_free(bh_semaphore_t *semaphore)
{
bh_semaphore_destroy(semaphore);
free(semaphore);
}
+/**
+ * Creates the condition variable object.
+ *
+ * \return On success, returns new condition variable object.
+ * \return On failure, returns null pointer.
+ */
bh_cond_t *bh_cond_new(void)
{
bh_cond_t *result;
@@ -102,12 +197,26 @@ bh_cond_t *bh_cond_new(void)
return result;
}
+/**
+ * Frees the condition variable object \a cond.
+ *
+ * \param cond Pointer to the condition variable object
+ */
void bh_cond_free(bh_cond_t *cond)
{
bh_cond_destroy(cond);
free(cond);
}
+/**
+ * Adds \a task to the thread \a pool.
+ *
+ * \param pool Pointer to the thread pool object
+ * \param task Pointer to the task object
+ *
+ * \return On success, returns zero.
+ * \return On failure, returns error code.
+ */
int bh_thread_pool_add(bh_thread_pool_t *pool,
bh_task_t *task)
{
@@ -119,16 +228,24 @@ int bh_thread_pool_add(bh_thread_pool_t *pool,
if (bh_queue_insert(&pool->tasks, task))
{
bh_mutex_unlock(&pool->lock);
- return -1;
+ return BH_ERROR;
}
/* Signal new job */
bh_mutex_unlock(&pool->lock);
bh_cond_signal(&pool->new_task);
- return 0;
+ return BH_OK;
}
+/**
+ * Waits unitl all task in thread \a pool are complete.
+ *
+ * \param pool Pointer to the thread pool.
+ *
+ * \return On success, returns zero.
+ * \return On failure, returns error code.
+ */
int bh_thread_pool_wait(bh_thread_pool_t *pool)
{
/* Lock and check if there is jobs in the queue */
@@ -160,9 +277,16 @@ int bh_thread_pool_wait(bh_thread_pool_t *pool)
/* Unlock */
bh_mutex_unlock(&pool->lock);
- return 0;
+ return BH_OK;
}
+/**
+ * Destroyes the thread \a pool object.
+ *
+ * \warning This is an internal function.
+ *
+ * \param pool Pointer to the thread pool object
+ */
void bh_thread_pool_destroy(bh_thread_pool_t *pool)
{
size_t i;
@@ -195,12 +319,21 @@ void bh_thread_pool_destroy(bh_thread_pool_t *pool)
bh_mutex_destroy(&pool->lock);
}
+/**
+ * Frees the thread \a pool object.
+ *
+ * \param pool Pointer to the thread pool object
+ */
void bh_thread_pool_free(bh_thread_pool_t *pool)
{
bh_thread_pool_destroy(pool);
free(pool);
}
+/**
+ * \warning This is an internal function.
+ * \warning Do not use this function directly!
+ */
void bh_thread_pool_worker(void *arg)
{
bh_thread_pool_t *pool;
@@ -210,26 +343,31 @@ void bh_thread_pool_worker(void *arg)
{
bh_task_t *task;
+ /* Wait unitl there is a task or shutdown signal */
bh_mutex_lock(&pool->lock);
while (!pool->shutdown && bh_queue_empty(&pool->tasks))
bh_cond_wait(&pool->new_task, &pool->lock);
+ /* If its shutdown signal - exit the loop */
if (pool->shutdown)
{
bh_mutex_unlock(&pool->lock);
break;
}
+ /* Fetch task from the front of the queue, increase active counter */
task = bh_queue_front(&pool->tasks);
bh_queue_remove(&pool->tasks);
pool->active++;
bh_mutex_unlock(&pool->lock);
+ /* Do the task, mark as done, and if required - free it */
task->func(task->data);
task->flags |= BH_THREAD_DONE;
if (task->flags & BH_THREAD_CLEANUP)
bh_task_free(task);
+ /* Decrease active counter and broadcast that we are done */
bh_mutex_lock(&pool->lock);
pool->active--;
@@ -237,3 +375,7 @@ void bh_thread_pool_worker(void *arg)
bh_cond_broadcast(&pool->done_task);
}
}
+
+/**
+ * \}
+ */ \ No newline at end of file
diff --git a/src/thread_null.c b/src/thread_null.c
index af73fd6..25d8a88 100644
--- a/src/thread_null.c
+++ b/src/thread_null.c
@@ -1,116 +1,300 @@
#include <bh/internal/thread.h>
-int bh_thread_init(bh_thread_t *thread,
- bh_task_t *task)
+/**
+ * \ingroup thread
+ * \{
+ */
+
+/**
+ * Creates the thread object with the given \a task.
+ *
+ * \warning This function can be implemented either as a macro or as a function.
+ *
+ * \param task Pointer to the task object
+ *
+ * \return On success, returns the pointer to the new queue object.
+ * \return On failure, returns a null pointer.
+ */
+bh_thread_t *bh_thread_new(bh_task_t *task)
{
- (void)thread;
(void)task;
- return -1;
+ return NULL;
}
-bh_thread_t *bh_thread_new(bh_task_t *task)
+/**
+ * Initializes the \a thread object with the given \a task.
+ *
+ * \warning This is an internal function.
+ * \warning This function can be implemented either as a macro or as a function.
+ *
+ * \param thread Pointer to the thread object
+ * \param task Pointer to the task object
+ *
+ * \return On success, returns zero.
+ * \return On failure, returns error code.
+ */
+int bh_thread_init(bh_thread_t *thread,
+ bh_task_t *task)
{
+ (void)thread;
(void)task;
- return NULL;
+ return BH_NO_IMPL;
}
+/**
+ * Joins the \a thread.
+ *
+ * If thread was created with bh_thread_new, this function also frees the \a
+ * thread object.
+ *
+ * \param thread Pointer to the thread object
+ *
+ * \return On success, returns zero.
+ * \return On failure, returns error code.
+ */
int bh_thread_join(bh_thread_t *thread)
{
(void)thread;
- return -1;
+ return BH_NO_IMPL;
}
+/**
+ * Detaches the \a thread.
+ *
+ * If thread was created with bh_thread_new, this function also frees the \a
+ * thread object.
+ *
+ * \param thread Pointer to the thread object
+ *
+ * \return On success, returns zero.
+ * \return On failure, returns error code.
+ */
int bh_thread_detach(bh_thread_t *thread)
{
(void)thread;
- return -1;
+ return BH_NO_IMPL;
}
+/**
+ * Initializes the \a mutex object.
+ *
+ * \warning This is an internal function.
+ *
+ * \param mutex Pointer to the mutex object
+ *
+ * \return On success, returns zero.
+ * \return On failure, returns error code.
+ */
int bh_mutex_init(bh_mutex_t *mutex)
{
(void)mutex;
- return -1;
+ return BH_NO_IMPL;
}
+/**
+ * Destroyes the \a mutex object.
+ *
+ * \param mutex Pointer to the mutex object
+ *
+ * \warning This is an internal function.
+ */
void bh_mutex_destroy(bh_mutex_t *mutex)
{
(void)mutex;
}
+/**
+ * Locks the \a mutex object.
+ *
+ * \param mutex Pointer to the mutex object
+ *
+ * \return On success, returns zero.
+ * \return On failure, returns error code.
+ */
int bh_mutex_lock(bh_mutex_t *mutex)
{
(void)mutex;
- return -1;
+ return BH_NO_IMPL;
}
+/**
+ * Tries to lock the \a mutex object.
+ *
+ * \param mutex Pointer to the mutex object
+ *
+ * \return On success, returns zero.
+ * \return On failure, returns error code.
+ */
int bh_mutex_try_lock(bh_mutex_t *mutex)
{
(void)mutex;
- return -1;
+ return BH_NO_IMPL;
}
+/**
+ * Unlocks the \a mutex object.
+ *
+ * \param mutex Pointer to the mutex object
+ *
+ * \return On success, returns zero.
+ * \return On failure, returns error code.
+ */
int bh_mutex_unlock(bh_mutex_t *mutex)
{
(void)mutex;
- return -1;
+ return BH_NO_IMPL;
}
+/**
+ * Initilizes the \a semaphore object with specified \a count value.
+ *
+ * \warning This is an internal function.
+ *
+ * \param semaphore Pointer to the semaphore object
+ * \param count Initial semaphore value
+ *
+ * \return On success, returns zero.
+ * \return On failure, returns error code.
+ *
+ * \note Guranteed maximum \a count value is 32767.
+ */
int bh_semaphore_init(bh_semaphore_t *semaphore, int count)
{
(void)semaphore;
(void)count;
- return -1;
+ return BH_NO_IMPL;
}
+/**
+ * Destroyes the \a semaphore object.
+ *
+ * \warning This is an internal function.
+ *
+ * \param semaphore Pointer to the semaphore object
+ */
void bh_semaphore_destroy(bh_semaphore_t *semaphore)
{
(void)semaphore;
}
+/**
+ * Posts (increases value of) the \a semaphore.
+ *
+ * \param semaphore Pointer to the semaphore object
+ *
+ * \return On success, returns zero.
+ * \return On failure, returns error code.
+ */
int bh_semaphore_post(bh_semaphore_t *semaphore)
{
(void)semaphore;
- return -1;
+ return BH_NO_IMPL;
}
+/**
+ * Waits (decreases value of) the \a semaphore.
+ *
+ * \param semaphore Pointer to the semaphore object
+ *
+ * \return On success, returns zero.
+ * \return On failure, returns error code.
+ */
int bh_semaphore_wait(bh_semaphore_t *semaphore)
{
(void)semaphore;
- return -1;
+ return BH_NO_IMPL;
}
+/**
+ * Waits (decreases value of) the \a semaphore for the specified \a timeout
+ * amount of milliseconds.
+ *
+ * \param semaphore Pointer to the semaphore object
+ * \param timeout Number of milliseconds to wait for
+ *
+ * \return On success, returns zero.
+ * \return On failure or timeout, returns error code.
+ */
int bh_semaphore_wait_for(bh_semaphore_t *semaphore,
unsigned long timeout)
{
(void)semaphore;
- return -1;
+ return BH_NO_IMPL;
}
+/**
+ * Tries to waits (decrease value of) the \a semaphore.
+ *
+ * \param semaphore Pointer to the semaphore object
+ *
+ * \return On success, returns zero.
+ * \return On failure, returns error code.
+ */
int bh_semaphore_try_wait(bh_semaphore_t *semaphore)
{
(void)semaphore;
- return -1;
+ return BH_NO_IMPL;
}
+/**
+ * Initializes the condition variable object \a cond.
+ *
+ * \warning This is an internal function.
+ *
+ * \param cond Pointer to the condition variable
+ *
+ * \return On success, returns zero.
+ * \return On failure, returns error code.
+ */
int bh_cond_init(bh_cond_t *cond)
{
(void)cond;
- return -1;
+ return BH_NO_IMPL;
}
+/**
+ * Destroyes the condition variable object \a cond.
+ *
+ * \param cond Pointer to the condition variable object
+ *
+ * \return On success, returns zero.
+ * \return On failure, returns error code.
+ *
+ * \warning This is an internal function.
+ */
void bh_cond_destroy(bh_cond_t *cond)
{
(void)cond;
}
+/**
+ * Waits on the condition variable \a cond with specified \a mutex.
+ *
+ * \param cond Pointer to the condition variable object
+ * \param mutex Pointer to the mutex object
+ *
+ * \return On success, returns zero.
+ * \return ON failure, returns error code.
+ */
int bh_cond_wait(bh_cond_t *cond,
bh_mutex_t *mutex)
{
(void)cond;
(void)mutex;
- return -1;
+ return BH_NO_IMPL;
}
+/**
+ * Waits on the condition variable \a cond with specified \a mutex for the
+ * specified \a timeout amount of milliseconds.
+ *
+ * \param cond Pointer to the condition variable object
+ * \param mutex Pointer to the mutex object
+ * \param timeout Number of milliseconds to wait for
+ *
+ * \return On success, returns zero.
+ * \return On failure or timeout, returns error code.
+ */
int bh_cond_wait_for(bh_cond_t *cond,
bh_mutex_t *mutex,
unsigned long timeout)
@@ -118,33 +302,71 @@ int bh_cond_wait_for(bh_cond_t *cond,
(void)cond;
(void)mutex;
(void)timeout;
- return -1;
+ return BH_NO_IMPL;
}
+/**
+ * Signals (notifies) one waiting thread on the condition variable \a cond.
+ *
+ * \param cond Pointer to the condition variable object
+ *
+ * \return On success, returns zero.
+ * \return On failure, returns error code.
+ */
int bh_cond_signal(bh_cond_t *cond)
{
(void)cond;
- return -1;
+ return BH_NO_IMPL;
}
+/**
+ * Signals (notifies) all waiting threads on the condition variable \a cond.
+ *
+ * \param cond Pointer to the condition variable object
+ *
+ * \return On success, returns zero.
+ * \return On failure, returns error code.
+ */
int bh_cond_broadcast(bh_cond_t *cond)
{
(void)cond;
- return -1;
+ return BH_NO_IMPL;
}
-int bh_thread_pool_init(bh_thread_pool_t *pool,
- size_t size)
+/**
+ * Creates the thread pool object with specified \a size amount of threads.
+ *
+ * \param size Number of threads in the thread pool
+ *
+ * \return On success, returns pointer to the new thread pool object.
+ * \return On failure, returns null pointer.
+ */
+bh_thread_pool_t *bh_thread_pool_new(size_t size)
{
- (void)pool;
(void)size;
- return -1;
+ return NULL;
}
-bh_thread_pool_t *bh_thread_pool_new(size_t size)
+/**
+ * Initilizes the thread \a pool object with specified \a size amount of
+ * threads.
+ *
+ * \warning This is an internal function.
+ *
+ * \param pool Pointer to the thread pool object
+ * \param size Number of threads in the thread pool
+ *
+ * \return On success, returns zero.
+ * \return On failure, returns error code.
+ */
+int bh_thread_pool_init(bh_thread_pool_t *pool,
+ size_t size)
{
+ (void)pool;
(void)size;
- return NULL;
+ return BH_NO_IMPL;
}
-
+/**
+ * \}
+ */ \ No newline at end of file
diff --git a/src/thread_posix.c b/src/thread_posix.c
index 21ca2be..2898644 100644
--- a/src/thread_posix.c
+++ b/src/thread_posix.c
@@ -5,9 +5,9 @@
static void *bh_thread_run(void *arg)
{
bh_task_t *task;
-
task = (bh_task_t *)arg;
+ /* Do the task, mark as done, and if required free it */
task->func(task->data);
task->flags |= BH_THREAD_DONE;
@@ -21,21 +21,25 @@ int bh_thread_init(bh_thread_t *thread,
bh_task_t *task)
{
thread->allocated = 0;
- return pthread_create(&thread->handle, NULL, bh_thread_run, task);
+ if (pthread_create(&thread->handle, NULL, bh_thread_run, task))
+ return BH_ERROR;
+
+ return BH_OK;
}
bh_thread_t *bh_thread_new(bh_task_t *task)
{
bh_thread_t *result;
+ /* Allocate thread object */
result = malloc(sizeof(*result));
-
if (result && bh_thread_init(result, task))
{
free(result);
result = NULL;
}
+ /* Mark thread as allocated for deallocation in join/detach */
if (result)
result->allocated = 1;
@@ -44,27 +48,35 @@ bh_thread_t *bh_thread_new(bh_task_t *task)
int bh_thread_join(bh_thread_t *thread)
{
- int result = pthread_join(thread->handle, NULL);
+ /* Join the thread */
+ if (pthread_join(thread->handle, NULL))
+ return BH_ERROR;
+ /* If thread is allocated, deallocate it */
if (thread->allocated)
free(thread);
- return result;
+ return BH_OK;
}
int bh_thread_detach(bh_thread_t *thread)
{
- int result = pthread_detach(thread->handle);
+ /* Detach the thread */
+ if (pthread_detach(thread->handle))
+ return BH_ERROR;
+ /* If thread is allocated, deallocate it */
if (thread->allocated)
free(thread);
- return result;
+ return BH_OK;
}
int bh_mutex_init(bh_mutex_t *mutex)
{
- return pthread_mutex_init(&mutex->handle, NULL);
+ if (pthread_mutex_init(&mutex->handle, NULL))
+ return BH_ERROR;
+ return BH_OK;
}
void bh_mutex_destroy(bh_mutex_t *mutex)
@@ -74,22 +86,30 @@ void bh_mutex_destroy(bh_mutex_t *mutex)
int bh_mutex_lock(bh_mutex_t *mutex)
{
- return pthread_mutex_lock(&mutex->handle);
+ if (pthread_mutex_lock(&mutex->handle))
+ return BH_ERROR;
+ return BH_OK;
}
int bh_mutex_try_lock(bh_mutex_t *mutex)
{
- return pthread_mutex_trylock(&mutex->handle);
+ if (pthread_mutex_trylock(&mutex->handle))
+ return BH_ERROR;
+ return BH_OK;
}
int bh_mutex_unlock(bh_mutex_t *mutex)
{
- return pthread_mutex_unlock(&mutex->handle);
+ if (pthread_mutex_unlock(&mutex->handle))
+ return BH_ERROR;
+ return BH_OK;
}
int bh_semaphore_init(bh_semaphore_t *semaphore, int count)
{
- return sem_init(&semaphore->handle, 0, count);
+ if (sem_init(&semaphore->handle, 0, count))
+ return BH_ERROR;
+ return BH_OK;
}
void bh_semaphore_destroy(bh_semaphore_t *semaphore)
@@ -99,12 +119,16 @@ void bh_semaphore_destroy(bh_semaphore_t *semaphore)
int bh_semaphore_post(bh_semaphore_t *semaphore)
{
- return sem_post(&semaphore->handle);
+ if (sem_post(&semaphore->handle))
+ return BH_ERROR;
+ return BH_OK;
}
int bh_semaphore_wait(bh_semaphore_t *semaphore)
{
- return sem_wait(&semaphore->handle);
+ if (sem_wait(&semaphore->handle))
+ return BH_ERROR;
+ return BH_OK;
}
int bh_semaphore_wait_for(bh_semaphore_t *semaphore,
@@ -115,17 +139,23 @@ int bh_semaphore_wait_for(bh_semaphore_t *semaphore,
ts.tv_sec = timeout / 1000;
ts.tv_nsec = (timeout - ts.tv_sec * 1000) * 1000000;
- return sem_timedwait(&semaphore->handle, &ts);
+ if (sem_timedwait(&semaphore->handle, &ts))
+ return BH_TIMEOUT;
+ return BH_OK;
}
int bh_semaphore_try_wait(bh_semaphore_t *semaphore)
{
- return sem_trywait(&semaphore->handle);
+ if (sem_trywait(&semaphore->handle))
+ return BH_ERROR;
+ return BH_OK;
}
int bh_cond_init(bh_cond_t *cond)
{
- return pthread_cond_init(&cond->handle, NULL);
+ if (pthread_cond_init(&cond->handle, NULL))
+ return BH_ERROR;
+ return BH_OK;
}
void bh_cond_destroy(bh_cond_t *cond)
@@ -136,7 +166,9 @@ void bh_cond_destroy(bh_cond_t *cond)
int bh_cond_wait(bh_cond_t *cond,
bh_mutex_t *mutex)
{
- return pthread_cond_wait(&cond->handle, &mutex->handle);
+ if (pthread_cond_wait(&cond->handle, &mutex->handle))
+ return BH_ERROR;
+ return BH_OK;
}
int bh_cond_wait_for(bh_cond_t *cond,
@@ -148,17 +180,23 @@ int bh_cond_wait_for(bh_cond_t *cond,
ts.tv_sec = timeout / 1000;
ts.tv_nsec = (timeout - ts.tv_sec * 1000) * 1000000;
- return pthread_cond_timedwait(&cond->handle, &mutex->handle, &ts);
+ if (pthread_cond_timedwait(&cond->handle, &mutex->handle, &ts))
+ return BH_TIMEOUT;
+ return BH_OK;
}
int bh_cond_signal(bh_cond_t *cond)
{
- return pthread_cond_signal(&cond->handle);
+ if (pthread_cond_signal(&cond->handle))
+ return BH_ERROR;
+ return BH_OK;
}
int bh_cond_broadcast(bh_cond_t *cond)
{
- return pthread_cond_broadcast(&cond->handle);
+ if (pthread_cond_broadcast(&cond->handle))
+ return BH_ERROR;
+ return BH_OK;
}
int bh_thread_pool_init(bh_thread_pool_t *pool,
@@ -215,7 +253,7 @@ int bh_thread_pool_init(bh_thread_pool_t *pool,
}
}
- return 0;
+ return BH_OK;
queue_fail:
free(pool->threads);
@@ -230,7 +268,7 @@ task_fail:
bh_mutex_destroy(&pool->lock);
lock_fail:
- return -1;
+ return BH_ERROR;
}
bh_thread_pool_t *bh_thread_pool_new(size_t size)
diff --git a/src/thread_win.c b/src/thread_win.c
index 098e7dd..bb14774 100644
--- a/src/thread_win.c
+++ b/src/thread_win.c
@@ -3,16 +3,19 @@
static unsigned __stdcall bh_thread_run(void *arg)
{
bh_thread_data_t data;
-
+
+ /* Fetch thread data, store it on stack and free from heap */
data = *(bh_thread_data_t *)arg;
free(arg);
-
+
+ /* Do the task, mark as done, and if required free it */
data.task->func(data.task->data);
data.task->flags |= BH_THREAD_DONE;
if (data.task->flags & BH_THREAD_CLEANUP)
bh_task_free(data.task);
-
+
+ /* Call thread specific end function (deallocate TLS) */
data.end(0);
return 0;
}
@@ -23,24 +26,28 @@ int bh_thread_init_base(bh_thread_t *thread,
bh_thread_end_cb_t end)
{
bh_thread_data_t *data;
-
+
+ /* Allocate thread specific data */
data = malloc(sizeof(*data));
if (!data)
- return -1;
-
+ return BH_OOM;
+
+ /* Setup thread specific data */
data->task = task;
data->end = end;
-
+
+ /* Create and setup thread (relative to the callers libc) */
thread->allocated = 0;
thread->handle = (HANDLE)_beginthreadex(NULL, 0, bh_thread_run, data, 0, NULL);
-
+
+ /* Check for errors */
if (!thread->handle)
{
free(data);
- return -1;
+ return BH_ERROR;
}
-
- return 0;
+
+ return BH_OK;
}
bh_thread_t *bh_thread_new_base(bh_task_t *task,
@@ -48,46 +55,53 @@ bh_thread_t *bh_thread_new_base(bh_task_t *task,
bh_thread_end_cb_t end)
{
bh_thread_t *result;
-
+
+ /* Allocate thread object */
result = malloc(sizeof(*result));
if (result && !bh_thread_init_base(result, task, begin, end))
{
free(result);
result = NULL;
}
-
+
+ /* Mark thread as allocated for deallocation in join/detach */
if (result)
result->allocated = 1;
-
+
return result;
}
int bh_thread_join(bh_thread_t *thread)
{
+ /* Join the thread */
WaitForSingleObject(thread->handle, INFINITE);
CloseHandle(thread->handle);
-
+
+ /* If thread is allocated, deallocate it */
if (thread->allocated)
free(thread);
-
- return 0;
+
+ return BH_OK;
}
int bh_thread_detach(bh_thread_t *thread)
{
+ /* Detach from thread */
CloseHandle(thread->handle);
-
+
+ /* If thread is allocated, deallocate it */
if (thread->allocated)
free(thread);
-
- return 0;
+
+ return BH_OK;
}
int bh_mutex_init(bh_mutex_t *mutex)
{
+ /* TODO: Is this spincount needed or sane? */
if (!InitializeCriticalSectionAndSpinCount(&mutex->handle, 0x400))
- return -1;
- return 0;
+ return BH_ERROR;
+ return BH_OK;
}
void bh_mutex_destroy(bh_mutex_t *mutex)
@@ -98,29 +112,30 @@ void bh_mutex_destroy(bh_mutex_t *mutex)
int bh_mutex_lock(bh_mutex_t *mutex)
{
EnterCriticalSection(&mutex->handle);
- return 0;
+ return BH_OK;
}
int bh_mutex_try_lock(bh_mutex_t *mutex)
{
if (!TryEnterCriticalSection(&mutex->handle))
- return -1;
- return 0;
+ return BH_ERROR;
+ return BH_OK;
}
int bh_mutex_unlock(bh_mutex_t *mutex)
{
LeaveCriticalSection(&mutex->handle);
- return 0;
+ return BH_OK;
}
int bh_semaphore_init(bh_semaphore_t *semaphore, int count)
{
+ /* Create semaphore with max value of 32767 (to match POSIX) */
semaphore->handle = CreateSemaphore(NULL, count, 0x7FFF, NULL);
if (!semaphore->handle)
- return -1;
-
- return 0;
+ return BH_ERROR;
+
+ return BH_OK;
}
void bh_semaphore_destroy(bh_semaphore_t *semaphore)
@@ -131,37 +146,38 @@ void bh_semaphore_destroy(bh_semaphore_t *semaphore)
int bh_semaphore_post(bh_semaphore_t *semaphore)
{
if (!ReleaseSemaphore(semaphore->handle, 1, NULL))
- return -1;
- return 0;
+ return BH_ERROR;
+ return BH_OK;
}
int bh_semaphore_wait(bh_semaphore_t *semaphore)
{
if (WaitForSingleObject(semaphore->handle, INFINITE))
- return -1;
- return 0;
+ return BH_ERROR;
+ return BH_OK;
}
int bh_semaphore_wait_for(bh_semaphore_t *semaphore,
unsigned long timeout)
{
+ /* FIXME: Check if we timed out or errored out */
if (WaitForSingleObject(semaphore->handle, timeout))
- return -1;
- return 0;
+ return BH_TIMEOUT;
+ return BH_ERROR;
}
int bh_semaphore_try_wait(bh_semaphore_t *semaphore)
{
if (WaitForSingleObject(semaphore->handle, 0))
- return -1;
- return 0;
+ return BH_ERROR;
+ return BH_OK;
}
#if WINVER >= _WIN32_WINNT_VISTA
int bh_cond_init(bh_cond_t *cond)
{
InitializeConditionVariable(&cond->handle);
- return 0;
+ return BH_OK;
}
void bh_cond_destroy(bh_cond_t *cond)
@@ -179,43 +195,46 @@ int bh_cond_wait_for(bh_cond_t *cond,
bh_mutex_t *mutex,
unsigned long timeout)
{
+ /* FIXME: Check if we timed out or errored out */
if (!SleepConditionVariableCS(&cond->handle, &mutex->handle, timeout))
- return -1;
- return 0;
+ return BH_TIMEOUT;
+ return BH_OK;
}
int bh_cond_signal(bh_cond_t *cond)
{
WakeConditionVariable(&cond->handle);
- return 0;
+ return BH_OK;
}
int bh_cond_broadcast(bh_cond_t *cond)
{
WakeAllConditionVariable(&cond->handle);
- return 0;
+ return BH_OK;
}
#else
/* Condition variable implementation based on BeOS article
* http://www-classic.be.com/aboutbe/benewsletter/volume_III/Issue40.html
- */
+ *
+ * Slow, but correct implementation of CVs.
+ */
int bh_cond_init(bh_cond_t *cond)
{
if (bh_mutex_init(&cond->lock))
- return -1;
-
+ return BH_ERROR;
+
if (bh_semaphore_init(&cond->wait, 0))
{
bh_mutex_destroy(&cond->lock);
- return -1;
+ return BH_ERROR;
}
-
+
if (bh_semaphore_init(&cond->done, 0))
{
bh_semaphore_destroy(&cond->wait);
bh_mutex_destroy(&cond->lock);
- return -1;
+ return BH_ERROR;
}
cond->waiting = 0;
cond->signals = 0;
@@ -234,27 +253,27 @@ int bh_cond_wait(bh_cond_t *cond,
bh_mutex_t *mutex)
{
int retval;
-
+
bh_mutex_lock(&cond->lock);
cond->waiting++;
bh_mutex_unlock(&cond->lock);
bh_mutex_unlock(mutex);
-
+
retval = bh_semaphore_wait(&cond->wait);
-
+
bh_mutex_lock(&cond->lock);
if (cond->signals > 0)
{
if (retval)
bh_semaphore_wait(&cond->wait);
-
+
bh_semaphore_post(&cond->done);
cond->signals--;
}
cond->waiting--;
- bh_mutex_unlock(&cond->lock);
+ bh_mutex_unlock(&cond->lock);
bh_mutex_lock(mutex);
-
+
return retval;
}
@@ -263,70 +282,70 @@ int bh_cond_wait_for(bh_cond_t *cond,
unsigned long timeout)
{
int retval;
-
+
bh_mutex_lock(&cond->lock);
cond->waiting++;
bh_mutex_unlock(&cond->lock);
bh_mutex_unlock(mutex);
-
+
retval = bh_semaphore_wait_for(&cond->wait, timeout);
-
+
bh_mutex_lock(&cond->lock);
if (cond->signals > 0)
{
if (retval)
bh_semaphore_wait(&cond->wait);
-
+
bh_semaphore_post(&cond->done);
cond->signals--;
}
cond->waiting--;
- bh_mutex_unlock(&cond->lock);
+ bh_mutex_unlock(&cond->lock);
bh_mutex_lock(mutex);
-
+
return retval;
}
int bh_cond_signal(bh_cond_t *cond)
{
bh_mutex_lock(&cond->lock);
-
+
if (cond->waiting > cond->signals)
{
cond->signals++;
-
+
bh_semaphore_post(&cond->wait);
bh_mutex_unlock(&cond->lock);
bh_semaphore_wait(&cond->done);
}
else
bh_mutex_unlock(&cond->lock);
-
- return 0;
+
+ return BH_OK;
}
int bh_cond_broadcast(bh_cond_t *cond)
{
int i, waiting;
-
+
bh_mutex_lock(&cond->lock);
if (cond->waiting > cond->signals)
{
waiting = cond->waiting - cond->signals;
cond->signals = cond->waiting;
-
+
for (i = 0; i < waiting; i++)
bh_semaphore_post(&cond->wait);
-
+
bh_mutex_unlock(&cond->lock);
-
+
for (i = 0; i < waiting; i++)
bh_semaphore_wait(&cond->done);
}
else
bh_mutex_unlock(&cond->lock);
-
- return 0;
+
+ return BH_OK;
}
#endif
@@ -386,7 +405,7 @@ int bh_thread_pool_init_base(bh_thread_pool_t *pool,
}
}
- return 0;
+ return BH_OK;
queue_fail:
free(pool->threads);
@@ -401,7 +420,7 @@ task_fail:
bh_mutex_destroy(&pool->lock);
lock_fail:
- return -1;
+ return BH_ERROR;
}
bh_thread_pool_t *bh_thread_pool_new_base(size_t size,
@@ -409,6 +428,7 @@ bh_thread_pool_t *bh_thread_pool_new_base(size_t size,
bh_thread_end_cb_t end)
{
bh_thread_pool_t *result;
+
result = malloc(sizeof(*result));
if (result && bh_thread_pool_init_base(result, size, begin, end))
{