Compare commits
6 Commits
b9d703f48a
...
trees
| Author | SHA1 | Date | |
|---|---|---|---|
|
21b2a3955d
|
|||
|
485b241595
|
|||
|
def4175d0a
|
|||
|
a8108178ee
|
|||
|
36f161a9cc
|
|||
|
dda645e188
|
@@ -1,11 +1,20 @@
|
|||||||
cmake_minimum_required(VERSION 3.10)
|
cmake_minimum_required(VERSION 3.10)
|
||||||
project(akstdlib LANGUAGES C)
|
project(akstdlib LANGUAGES C)
|
||||||
|
|
||||||
|
if(TARGET akerror::akerror)
|
||||||
|
message(STATUS "FOUND akerror::akerror")
|
||||||
|
else()
|
||||||
|
message(STATUS "MISSING akerror::akerror")
|
||||||
|
endif()
|
||||||
|
|
||||||
include(CTest)
|
include(CTest)
|
||||||
include(GNUInstallDirs)
|
include(GNUInstallDirs)
|
||||||
include(CMakePackageConfigHelpers)
|
include(CMakePackageConfigHelpers)
|
||||||
|
|
||||||
find_package(PkgConfig REQUIRED)
|
if(NOT TARGET akerror::akerror)
|
||||||
|
find_package(PkgConfig REQUIRED)
|
||||||
|
find_package(akerror REQUIRED)
|
||||||
|
endif()
|
||||||
|
|
||||||
set(akstdlib_install_cmakedir "${CMAKE_INSTALL_LIBDIR}/cmake/akstdlib")
|
set(akstdlib_install_cmakedir "${CMAKE_INSTALL_LIBDIR}/cmake/akstdlib")
|
||||||
set(prefix ${CMAKE_INSTALL_PREFIX})
|
set(prefix ${CMAKE_INSTALL_PREFIX})
|
||||||
@@ -41,7 +50,7 @@ install(FILES "include/akstdlib.h" DESTINATION "include/")
|
|||||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/akstdlib.pc DESTINATION "lib/pkgconfig/")
|
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/akstdlib.pc DESTINATION "lib/pkgconfig/")
|
||||||
|
|
||||||
|
|
||||||
install(EXPORT akstdlib
|
install(EXPORT akstdlibTargets
|
||||||
FILE akstdlibTargets.cmake
|
FILE akstdlibTargets.cmake
|
||||||
NAMESPACE akstdlib::
|
NAMESPACE akstdlib::
|
||||||
DESTINATION ${akstdlib_install_cmakedir}
|
DESTINATION ${akstdlib_install_cmakedir}
|
||||||
|
|||||||
@@ -5,6 +5,33 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
typedef struct aksl_ListNode {
|
||||||
|
void *data;
|
||||||
|
struct aksl_ListNode *next;
|
||||||
|
struct aksl_ListNode *prev;
|
||||||
|
} aksl_ListNode;
|
||||||
|
|
||||||
|
typedef struct aksl_TreeNode {
|
||||||
|
struct aksl_TreeNode *parent;
|
||||||
|
struct aksl_TreeNode *left;
|
||||||
|
struct aksl_TreeNode *right;
|
||||||
|
void *leaf;
|
||||||
|
} aksl_TreeNode;
|
||||||
|
|
||||||
|
#define AKSL_TREE_SEARCH_BFS 0 /** Breadth-first search mode for tree nodes. Currently unsupported. */
|
||||||
|
#define AKSL_TREE_SEARCH_BFS_RIGHT 1 /** Right-hand breadth-first search mode for tree nodes. Currentl unsupported. */
|
||||||
|
#define AKSL_TREE_SEARCH_DFS 2 /** Alias for AKSL_TREE_SEARCH_DFS_PREORDER */
|
||||||
|
#define AKSL_TREE_SEARCH_DFS_PREORDER 2 /** Depth first pre-order (root, left, right) search mode for tree nodes */
|
||||||
|
#define AKSL_TREE_SEARCH_DFS_INORDER 3 /** Depth first in-order (left, root, right) search mode for tree nodes. Currently unsupported. */
|
||||||
|
#define AKSL_TREE_SEARCH_DFS_POSTORDER 4 /** Depth first post-order (left, right, root) search mode for tree nodes. Currently unsupported. */
|
||||||
|
#define AKSL_TREE_SEARCH_VISIT 5 /** Used when iterating through a tree structure as a control flag: don't traverse the children, just visit the node */
|
||||||
|
|
||||||
|
typedef akerr_ErrorContext AKERR_NOIGNORE *(*aksl_ListNodeIterator)(aksl_ListNode *node, void *data);
|
||||||
|
typedef akerr_ErrorContext AKERR_NOIGNORE *(*aksl_TreeNodeIterator)(aksl_TreeNode *node, void *data);
|
||||||
|
typedef akerr_ErrorContext AKERR_NOIGNORE *(*aksl_AllocFunc)(size_t size, void **dest);
|
||||||
|
typedef akerr_ErrorContext AKERR_NOIGNORE *(*aksl_FreeFunc)(void *ptr);
|
||||||
|
|
||||||
akerr_ErrorContext AKERR_NOIGNORE *aksl_fopen(char *pathname, char *mode, FILE **fp);
|
akerr_ErrorContext AKERR_NOIGNORE *aksl_fopen(char *pathname, char *mode, FILE **fp);
|
||||||
akerr_ErrorContext AKERR_NOIGNORE *aksl_fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
|
akerr_ErrorContext AKERR_NOIGNORE *aksl_fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
|
||||||
@@ -21,5 +48,19 @@ akerr_ErrorContext AKERR_NOIGNORE *aksl_printf(int *count, const char *restrict
|
|||||||
akerr_ErrorContext AKERR_NOIGNORE *aksl_fprintf(int *count, FILE *restrict stream, const char *restrict format, ...);
|
akerr_ErrorContext AKERR_NOIGNORE *aksl_fprintf(int *count, FILE *restrict stream, const char *restrict format, ...);
|
||||||
akerr_ErrorContext AKERR_NOIGNORE *aksl_sprintf(int *count, char *restrict str, const char *restrict format, ...);
|
akerr_ErrorContext AKERR_NOIGNORE *aksl_sprintf(int *count, char *restrict str, const char *restrict format, ...);
|
||||||
|
|
||||||
|
akerr_ErrorContext AKERR_NOIGNORE *aksl_atoi(const char *nptr, int *dest);
|
||||||
|
akerr_ErrorContext AKERR_NOIGNORE *aksl_atol(const char *nptr, long *dest);
|
||||||
|
akerr_ErrorContext AKERR_NOIGNORE *aksl_atoll(const char *nptr, long long *dest);
|
||||||
|
akerr_ErrorContext AKERR_NOIGNORE *aksl_atof(const char *nptr, double *dest);
|
||||||
|
|
||||||
|
akerr_ErrorContext AKERR_NOIGNORE *aksl_realpath(const char *restrict path, char *restrict resolved_path);
|
||||||
|
|
||||||
|
// Linked list functions
|
||||||
|
akerr_ErrorContext AKERR_NOIGNORE *aksl_list_push(aksl_ListNode *list, aksl_ListNode *obj);
|
||||||
|
akerr_ErrorContext AKERR_NOIGNORE *aksl_list_pop(aksl_ListNode *node);
|
||||||
|
akerr_ErrorContext AKERR_NOIGNORE *aksl_list_iterate(aksl_ListNode *list, aksl_ListNodeIterator iter, void *data);
|
||||||
|
|
||||||
|
// Tree functions
|
||||||
|
akerr_ErrorContext AKERR_NOIGNORE *aksl_tree_iterate(aksl_TreeNode *root, aksl_TreeNodeIterator iter, aksl_AllocFunc lalloc, aksl_FreeFunc lfree, uint8_t searchmode, void *data, aksl_ListNode *queue);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
193
src/stdlib.c
193
src/stdlib.c
@@ -3,6 +3,7 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
akerr_ErrorContext AKERR_NOIGNORE *aksl_malloc(size_t size, void **dst)
|
akerr_ErrorContext AKERR_NOIGNORE *aksl_malloc(size_t size, void **dst)
|
||||||
{
|
{
|
||||||
@@ -97,6 +98,7 @@ akerr_ErrorContext AKERR_NOIGNORE *aksl_printf(int *count, const char *restrict
|
|||||||
PREPARE_ERROR(e);
|
PREPARE_ERROR(e);
|
||||||
FAIL_ZERO_RETURN(e, count, AKERR_NULLPOINTER, "count=%p, format=%p", (void *)count, (void *)format);
|
FAIL_ZERO_RETURN(e, count, AKERR_NULLPOINTER, "count=%p, format=%p", (void *)count, (void *)format);
|
||||||
FAIL_ZERO_RETURN(e, format, AKERR_NULLPOINTER, "count=%p, format=%p", (void *)count, (void *)format);
|
FAIL_ZERO_RETURN(e, format, AKERR_NULLPOINTER, "count=%p, format=%p", (void *)count, (void *)format);
|
||||||
|
va_start(args, format);
|
||||||
*count = vprintf(format, args);
|
*count = vprintf(format, args);
|
||||||
FAIL_NONZERO_RETURN(e, (*count == -1), errno, "Short write");
|
FAIL_NONZERO_RETURN(e, (*count == -1), errno, "Short write");
|
||||||
SUCCEED_RETURN(e);
|
SUCCEED_RETURN(e);
|
||||||
@@ -110,6 +112,7 @@ akerr_ErrorContext AKERR_NOIGNORE *aksl_fprintf(int *count, FILE *restrict strea
|
|||||||
FAIL_ZERO_RETURN(e, count, AKERR_NULLPOINTER, "count=%p, stream=%p, format=%p", (void *)count, (void *)stream, (void *)format);
|
FAIL_ZERO_RETURN(e, count, AKERR_NULLPOINTER, "count=%p, stream=%p, format=%p", (void *)count, (void *)stream, (void *)format);
|
||||||
FAIL_ZERO_RETURN(e, stream, AKERR_NULLPOINTER, "count=%p, stream=%p, format=%p", (void *)count, (void *)stream, (void *)format);
|
FAIL_ZERO_RETURN(e, stream, AKERR_NULLPOINTER, "count=%p, stream=%p, format=%p", (void *)count, (void *)stream, (void *)format);
|
||||||
FAIL_ZERO_RETURN(e, format, AKERR_NULLPOINTER, "count=%p, stream=%p, format=%p", (void *)count, (void *)stream, (void *)format);
|
FAIL_ZERO_RETURN(e, format, AKERR_NULLPOINTER, "count=%p, stream=%p, format=%p", (void *)count, (void *)stream, (void *)format);
|
||||||
|
va_start(args, format);
|
||||||
*count = vfprintf(stream, format, args);
|
*count = vfprintf(stream, format, args);
|
||||||
FAIL_NONZERO_RETURN(e, (*count == -1), errno, "Short write");
|
FAIL_NONZERO_RETURN(e, (*count == -1), errno, "Short write");
|
||||||
SUCCEED_RETURN(e);
|
SUCCEED_RETURN(e);
|
||||||
@@ -122,7 +125,197 @@ akerr_ErrorContext AKERR_NOIGNORE *aksl_sprintf(int *count, char *restrict str,
|
|||||||
FAIL_ZERO_RETURN(e, count, AKERR_NULLPOINTER, "count=%p, str=%p, format=%p", (void *)count, (void *)str, (void *)format);
|
FAIL_ZERO_RETURN(e, count, AKERR_NULLPOINTER, "count=%p, str=%p, format=%p", (void *)count, (void *)str, (void *)format);
|
||||||
FAIL_ZERO_RETURN(e, str, AKERR_NULLPOINTER, "count=%p, str=%p, format=%p", (void *)count, (void *)str, (void *)format);
|
FAIL_ZERO_RETURN(e, str, AKERR_NULLPOINTER, "count=%p, str=%p, format=%p", (void *)count, (void *)str, (void *)format);
|
||||||
FAIL_ZERO_RETURN(e, format, AKERR_NULLPOINTER, "count=%p, str=%p, format=%p", (void *)count, (void *)str, (void *)format);
|
FAIL_ZERO_RETURN(e, format, AKERR_NULLPOINTER, "count=%p, str=%p, format=%p", (void *)count, (void *)str, (void *)format);
|
||||||
|
va_start(args, format);
|
||||||
*count = vsprintf(str, format, args);
|
*count = vsprintf(str, format, args);
|
||||||
FAIL_NONZERO_RETURN(e, (*count == -1), errno, "Short write");
|
FAIL_NONZERO_RETURN(e, (*count == -1), errno, "Short write");
|
||||||
SUCCEED_RETURN(e);
|
SUCCEED_RETURN(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
akerr_ErrorContext AKERR_NOIGNORE *aksl_atoi(const char *nptr, int *dest)
|
||||||
|
{
|
||||||
|
PREPARE_ERROR(e);
|
||||||
|
FAIL_ZERO_RETURN(e, nptr, AKERR_NULLPOINTER, "nptr=%p, dest=%p", (void *)nptr, (void *)dest);
|
||||||
|
FAIL_ZERO_RETURN(e, dest, AKERR_NULLPOINTER, "nptr=%p, dest=%p", (void *)nptr, (void *)dest);
|
||||||
|
*dest = atoi(nptr);
|
||||||
|
SUCCEED_RETURN(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
akerr_ErrorContext AKERR_NOIGNORE *aksl_atol(const char *nptr, long *dest)
|
||||||
|
{
|
||||||
|
PREPARE_ERROR(e);
|
||||||
|
FAIL_ZERO_RETURN(e, nptr, AKERR_NULLPOINTER, "nptr=%p, dest=%p", (void *)nptr, (void *)dest);
|
||||||
|
FAIL_ZERO_RETURN(e, dest, AKERR_NULLPOINTER, "nptr=%p, dest=%p", (void *)nptr, (void *)dest);
|
||||||
|
*dest = atol(nptr);
|
||||||
|
SUCCEED_RETURN(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
akerr_ErrorContext AKERR_NOIGNORE *aksl_atoll(const char *nptr, long long *dest)
|
||||||
|
{
|
||||||
|
PREPARE_ERROR(e);
|
||||||
|
FAIL_ZERO_RETURN(e, nptr, AKERR_NULLPOINTER, "nptr=%p, dest=%p", (void *)nptr, (void *)dest);
|
||||||
|
FAIL_ZERO_RETURN(e, dest, AKERR_NULLPOINTER, "nptr=%p, dest=%p", (void *)nptr, (void *)dest);
|
||||||
|
*dest = atoll(nptr);
|
||||||
|
SUCCEED_RETURN(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
akerr_ErrorContext AKERR_NOIGNORE *aksl_atof(const char *nptr, double *dest)
|
||||||
|
{
|
||||||
|
PREPARE_ERROR(e);
|
||||||
|
FAIL_ZERO_RETURN(e, nptr, AKERR_NULLPOINTER, "nptr=%p, dest=%p", (void *)nptr, (void *)dest);
|
||||||
|
FAIL_ZERO_RETURN(e, dest, AKERR_NULLPOINTER, "nptr=%p, dest=%p", (void *)nptr, (void *)dest);
|
||||||
|
*dest = atof(nptr);
|
||||||
|
SUCCEED_RETURN(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
akerr_ErrorContext AKERR_NOIGNORE *aksl_realpath(const char *restrict path, char *restrict resolved_path)
|
||||||
|
{
|
||||||
|
char *result = NULL;
|
||||||
|
PREPARE_ERROR(e);
|
||||||
|
FAIL_ZERO_RETURN(e, path, AKERR_NULLPOINTER, "path=%p, dest=%p", (void *)path, (void *)resolved_path);
|
||||||
|
result = realpath(path, resolved_path);
|
||||||
|
FAIL_ZERO_RETURN(e, result, errno, "path=%s, dest=%s", path, resolved_path);
|
||||||
|
SUCCEED_RETURN(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
akerr_ErrorContext AKERR_NOIGNORE *aksl_list_push(aksl_ListNode *list, aksl_ListNode *obj)
|
||||||
|
{
|
||||||
|
PREPARE_ERROR(e);
|
||||||
|
FAIL_ZERO_RETURN(e, list, AKERR_NULLPOINTER, "list");
|
||||||
|
FAIL_ZERO_RETURN(e, obj, AKERR_NULLPOINTER, "obj");
|
||||||
|
aksl_ListNode *slow = list;
|
||||||
|
aksl_ListNode *fast = list;
|
||||||
|
aksl_ListNode *tail = list;
|
||||||
|
do {
|
||||||
|
if ( fast != NULL && fast->next != NULL ) {
|
||||||
|
fast = fast->next->next;
|
||||||
|
}
|
||||||
|
tail = slow;
|
||||||
|
slow = slow->next;
|
||||||
|
if ( fast != NULL && fast == slow) {
|
||||||
|
FAIL(e, AKERR_CIRCULAR_REFERENCE, "%p", list);
|
||||||
|
}
|
||||||
|
} while ( slow != NULL || (fast != NULL && fast->next != NULL) );
|
||||||
|
tail->next = obj;
|
||||||
|
obj->next = NULL;
|
||||||
|
obj->prev = slow;
|
||||||
|
SUCCEED_RETURN(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
akerr_ErrorContext AKERR_NOIGNORE *aksl_list_pop(aksl_ListNode *node)
|
||||||
|
{
|
||||||
|
PREPARE_ERROR(e);
|
||||||
|
FAIL_ZERO_RETURN(e, node, AKERR_NULLPOINTER, "node");
|
||||||
|
if ( node->prev != NULL ) {
|
||||||
|
node->prev->next = node->next;
|
||||||
|
}
|
||||||
|
if ( node->next != NULL ) {
|
||||||
|
node->next->prev = node->prev;
|
||||||
|
}
|
||||||
|
node->next = NULL;
|
||||||
|
node->prev = NULL;
|
||||||
|
SUCCEED_RETURN(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Iterates over a tree structure in breadth or depth first order, executing a callback function on each node.
|
||||||
|
*
|
||||||
|
* This function recursively calls itself to traverse a binary tree datastructure.
|
||||||
|
*
|
||||||
|
* @param[in] root The tree structure to search
|
||||||
|
* @param[in] iter An aksl_TreeNodeIterator function which will be called for each node found
|
||||||
|
* @param[in] lalloc An aksl_AllocFunc function which will be used to allocate aksl_ListNode elements for the search, or NULL to use the default allocator (aksl_malloc)
|
||||||
|
* @param[in] lfree An aksl_FreeFunc function which will be used to free the elements procured with lalloc, or NULL to use the default free function (aksl_free)
|
||||||
|
* @param[in] searchmode One of the AKSL_TREE_SEARCH_BFS* defines
|
||||||
|
* @param[in] data Any user data that should be provided when the iterator is called
|
||||||
|
* @param[in] queue The linked list node to use as the head of the queue. The caller should pass NULL here.
|
||||||
|
*
|
||||||
|
* @throws AKERR_NULLPOINTER On null pointer inputs
|
||||||
|
* @return akerr_ErrorContext
|
||||||
|
*/
|
||||||
|
|
||||||
|
akerr_ErrorContext AKERR_NOIGNORE *aksl_tree_iterate(
|
||||||
|
aksl_TreeNode *root,
|
||||||
|
aksl_TreeNodeIterator iter,
|
||||||
|
aksl_AllocFunc lalloc,
|
||||||
|
aksl_FreeFunc lfree,
|
||||||
|
uint8_t searchmode,
|
||||||
|
void *data,
|
||||||
|
aksl_ListNode *queue)
|
||||||
|
{
|
||||||
|
PREPARE_ERROR(e);
|
||||||
|
FAIL_ZERO_RETURN(e, root, AKERR_NULLPOINTER, "root");
|
||||||
|
FAIL_ZERO_RETURN(e, iter, AKERR_NULLPOINTER, "iter");
|
||||||
|
if ( lalloc == NULL ) {
|
||||||
|
lalloc = &aksl_malloc;
|
||||||
|
}
|
||||||
|
if ( lfree == NULL ) {
|
||||||
|
lfree = &aksl_free;
|
||||||
|
}
|
||||||
|
switch ( searchmode ) {
|
||||||
|
case AKSL_TREE_SEARCH_DFS_PREORDER:
|
||||||
|
PASS(e, iter(root, data));
|
||||||
|
if ( root->left != NULL ) {
|
||||||
|
PASS(e, aksl_tree_iterate(root->left, iter, lalloc, lfree, searchmode, data, NULL));
|
||||||
|
}
|
||||||
|
if ( root-> right != NULL ) {
|
||||||
|
PASS(e, aksl_tree_iterate(root->right, iter, lalloc, lfree, searchmode, data, NULL));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case AKSL_TREE_SEARCH_DFS_POSTORDER:
|
||||||
|
if ( root->left != NULL ) {
|
||||||
|
PASS(e, aksl_tree_iterate(root->left, iter, lalloc, lfree, searchmode, data, NULL));
|
||||||
|
}
|
||||||
|
if ( root-> right != NULL ) {
|
||||||
|
PASS(e, aksl_tree_iterate(root->right, iter, lalloc, lfree, searchmode, data, NULL));
|
||||||
|
}
|
||||||
|
PASS(e, iter(root, data));
|
||||||
|
break;
|
||||||
|
case AKSL_TREE_SEARCH_DFS_INORDER:
|
||||||
|
if ( root->left != NULL ) {
|
||||||
|
PASS(e, aksl_tree_iterate(root->left, iter, lalloc, lfree, searchmode, data, NULL));
|
||||||
|
}
|
||||||
|
PASS(e, iter(root, data));
|
||||||
|
if ( root-> right != NULL ) {
|
||||||
|
PASS(e, aksl_tree_iterate(root->right, iter, lalloc, lfree, searchmode, data, NULL));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case AKSL_TREE_SEARCH_BFS:
|
||||||
|
case AKSL_TREE_SEARCH_BFS_RIGHT:
|
||||||
|
FAIL_RETURN(e, AKERR_NOT_IMPLEMENTED, "Searchmode %d", searchmode);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
SUCCEED_RETURN(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Iterates over a linked list and execute a callback function on each node
|
||||||
|
*
|
||||||
|
* @param[in] list The linked list to iterate
|
||||||
|
* @param[in] iter An aksl_ListNodeIterator function which will be called for each node found
|
||||||
|
* @param[in] data Any user data that should be provided when the iterator is called
|
||||||
|
*
|
||||||
|
* @throws AKERR_NULLPOINTER on null pointer inputs
|
||||||
|
* @throws AKERR_CIRCULAR_REFERENCE when the linked list contains a circular reference
|
||||||
|
*
|
||||||
|
* @return akerr_ErrorContext
|
||||||
|
*/
|
||||||
|
akerr_ErrorContext AKERR_NOIGNORE *aksl_list_iterate(aksl_ListNode *list, aksl_ListNodeIterator iter, void *data)
|
||||||
|
{
|
||||||
|
PREPARE_ERROR(e);
|
||||||
|
FAIL_ZERO_RETURN(e, list, AKERR_NULLPOINTER, "list");
|
||||||
|
FAIL_ZERO_RETURN(e, iter, AKERR_NULLPOINTER, "iter");
|
||||||
|
aksl_ListNode *slow = list;
|
||||||
|
aksl_ListNode *fast = list;
|
||||||
|
aksl_ListNode *tail = list;
|
||||||
|
do {
|
||||||
|
if ( fast != NULL && fast->next != NULL ) {
|
||||||
|
fast = fast->next->next;
|
||||||
|
}
|
||||||
|
PASS(e, iter(slow, data));
|
||||||
|
slow = slow->next;
|
||||||
|
if ( fast != NULL && fast == slow) {
|
||||||
|
FAIL(e, AKERR_CIRCULAR_REFERENCE, "%p", list);
|
||||||
|
}
|
||||||
|
} while ( slow != NULL || (fast != NULL && fast->next != NULL) );
|
||||||
|
SUCCEED_RETURN(e);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user