Commit 4f4e9665 authored by Beniamino Galvani's avatar Beniamino Galvani

Squashed 'shared/c-rbtree/' content from commit bf627e0c3

git-subtree-dir: shared/c-rbtree
git-subtree-split: bf627e0c32241915108f66ad9738444e4d045b45
parents
#!/bin/bash
set -e
rm -Rf "./ci-build"
mkdir "./ci-build"
cd "./ci-build"
${CHERRY_LIB_MESONSETUP} . "${CHERRY_LIB_SRCDIR}"
${CHERRY_LIB_NINJABUILD}
CRBTREE_TEST_PTRACE=1 ${CHERRY_LIB_MESONTEST}
(( ! CHERRY_LIB_VALGRIND )) || ${CHERRY_LIB_MESONTEST} "--wrapper=${CHERRY_LIB_VALGRINDWRAP}"
root = true
[*]
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
charset = utf-8
[*.{c,h}]
indent_style = space
indent_size = 8
os: linux
dist: trusty
language: c
services:
- docker
before_install:
- curl -O -L "https://raw.githubusercontent.com/cherry-pick/cherry-images/v1/scripts/vmrun"
- curl -O -L "https://raw.githubusercontent.com/cherry-pick/cherry-ci/v1/scripts/cherryci"
- chmod +x "./vmrun" "./cherryci"
jobs:
include:
- stage: test
script:
- ./vmrun -- ../src/cherryci -d ../src/.cherryci -s c-util -m
- script:
- ./vmrun -T armv7hl -- ../src/cherryci -d ../src/.cherryci -s c-util
- script:
- ./vmrun -T i686 -- ../src/cherryci -d ../src/.cherryci -s c-util
LICENSE:
This project is dual-licensed under both the Apache License, Version
2.0, and the GNU Lesser General Public License, Version 2.1+.
AUTHORS-ASL:
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
AUTHORS-LGPL:
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; If not, see <http://www.gnu.org/licenses/>.
COPYRIGHT: (ordered alphabetically)
Copyright (C) 2015-2018 Red Hat, Inc.
AUTHORS: (ordered alphabetically)
David Herrmann <dh.herrmann@gmail.com>
Tom Gundersen <teg@jklm.no>
This diff is collapsed.
This diff is collapsed.
AUTHORS-ASL
\ No newline at end of file
c-rbtree - Intrusive Red-Black Tree Collection
CHANGES WITH 3:
* Add more helpers. Add both a collection of iteratiors and helpers
for initializing a tree and checking if a tree is empty, without
explicitly accessing the data structure.
Contributions from: David Herrmann
- Berlin, 2017-08-13
CHANGES WITH 2:
* Relicense as ASL-2.0 to make c-rbtree useful for more projects. All
code is now fully available under the ASL-2.0. Nothing is covered by
the LGPL, anymore.
* Switch build-system from Autotools to Meson. This simplifies the code
base significantly. The Meson Build System is now used by many other
projects, including GStreamer, Weston, and several Gnome packages.
See http://mesonbuild.com/ for more information.
Contributions from: David Herrmann
- Berlin, 2016-12-14
CHANGES WITH 1:
* Initial release of c-rbtree.
* This projects provides an RB-Tree API, that is fully implemented in
ISO-C11 and has no external dependencies. Furthermore, tree
traversal, memory allocations, and key comparisons are completely
controlled by the API user. The implementation only provides the
RB-Tree specific rebalancing and coloring.
Contributions from: David Herrmann, Kay Sievers, Tom Gundersen
- Berlin, 2016-08-31
c-rbtree - Intrusive Red-Black Tree Collection
ABOUT:
The c-rbtree project implements an intrusive collection based on
red-black trees in ISO-C11. Its API guarantees the user full control
over its data-structures, and rather limits itself to just the
tree-specific rebalancing and coloring operations.
For API documentation, see the c-rbtree.h header file, as well as the
docbook comments for each function.
DETAILS:
https://c-util.github.io/c-rbtree
BUG REPORTS:
https://github.com/c-util/c-rbtree/issues
GIT:
git@github.com:c-util/c-rbtree.git
https://github.com/c-util/c-rbtree.git
GITWEB:
https://github.com/c-util/c-rbtree
LICENSE:
Apache Software License 2.0
Lesser General Public License 2.1+
See AUTHORS for details.
REQUIREMENTS:
The requirements for c-siphash are:
libc (e.g., glibc >= 2.16)
At build-time, the following software is required:
meson >= 0.41
pkg-config >= 0.29
INSTALL:
The meson build-system is used for this project. Contact upstream
documentation for detailed help. In most situations the following
commands are sufficient to build and install from source:
$ mkdir build
$ cd build
$ meson setup ..
$ ninja
$ meson test
# ninja install
No custom configuration options are available.
project(
'c-rbtree',
'c',
version: '3',
license: 'Apache',
default_options: [
'c_std=c11'
],
)
project_description = 'Intrusive Red-Black Tree Collection'
add_project_arguments('-D_GNU_SOURCE', language: 'c')
mod_pkgconfig = import('pkgconfig')
subdir('src')
#pragma once
/*
* Private definitions
* This file contains private definitions for the RB-Tree implementation, but
* which are used by our test-suite.
*/
#include <stddef.h>
#include "c-rbtree.h"
/*
* Macros
*/
#define _public_ __attribute__((__visibility__("default")))
/*
* Nodes
*/
static inline void *c_rbnode_raw(CRBNode *n) {
return (void *)(n->__parent_and_flags & ~C_RBNODE_FLAG_MASK);
}
static inline unsigned long c_rbnode_flags(CRBNode *n) {
return n->__parent_and_flags & C_RBNODE_FLAG_MASK;
}
static inline _Bool c_rbnode_is_red(CRBNode *n) {
return c_rbnode_flags(n) & C_RBNODE_RED;
}
static inline _Bool c_rbnode_is_black(CRBNode *n) {
return !(c_rbnode_flags(n) & C_RBNODE_RED);
}
static inline _Bool c_rbnode_is_root(CRBNode *n) {
return c_rbnode_flags(n) & C_RBNODE_ROOT;
}
This diff is collapsed.
This diff is collapsed.
LIBCRBTREE_3 {
global:
c_rbnode_leftmost;
c_rbnode_rightmost;
c_rbnode_leftdeepest;
c_rbnode_rightdeepest;
c_rbnode_next;
c_rbnode_prev;
c_rbnode_next_postorder;
c_rbnode_prev_postorder;
c_rbnode_link;
c_rbnode_unlink_stale;
c_rbtree_first;
c_rbtree_last;
c_rbtree_first_postorder;
c_rbtree_last_postorder;
c_rbtree_add;
c_rbtree_move;
local:
*;
};
#
# target: libcrbtree.so
#
libcrbtree_symfile = join_paths(meson.current_source_dir(), 'libcrbtree.sym')
libcrbtree_private = static_library(
'crbtree-private',
[
'c-rbtree.c',
],
c_args: [
'-fvisibility=hidden',
'-fno-common',
],
pic: true,
)
libcrbtree_shared = shared_library(
'crbtree',
objects: libcrbtree_private.extract_all_objects(),
install: not meson.is_subproject(),
soversion: 0,
link_depends: libcrbtree_symfile,
link_args: [
'-Wl,--no-undefined',
'-Wl,--version-script=@0@'.format(libcrbtree_symfile),
],
)
libcrbtree_dep = declare_dependency(
include_directories: include_directories('.'),
link_with: libcrbtree_private,
version: meson.project_version(),
)
if not meson.is_subproject()
install_headers('c-rbtree.h')
mod_pkgconfig.generate(
libraries: libcrbtree_shared,
version: meson.project_version(),
name: 'libcrbtree',
filebase: 'libcrbtree',
description: project_description,
)
endif
#
# target: test-*
#
test_api = executable('test-api', ['test-api.c'], link_with: libcrbtree_shared)
test('API Symbol Visibility', test_api)
test_basic = executable('test-basic', ['test-basic.c'], dependencies: libcrbtree_dep)
test('Basic API Behavior', test_basic)
test_map = executable('test-map', ['test-map.c'], dependencies: libcrbtree_dep)
test('Generic Map', test_map)
test_misc = executable('test-misc', ['test-misc.c'], dependencies: libcrbtree_dep)
test('Miscellaneous', test_misc)
test_parallel = executable('test-parallel', ['test-parallel.c'], dependencies: libcrbtree_dep)
test('Lockless Parallel Readers', test_parallel)
test_posix = executable('test-posix', ['test-posix.c'], dependencies: libcrbtree_dep)
test('Posix tsearch(3p) Comparison', test_posix)
/*
* Tests for Public API
* This test, unlikely the others, is linked against the real, distributed,
* shared library. Its sole purpose is to test for symbol availability.
*/
#undef NDEBUG
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "c-rbtree.h"
typedef struct TestNode {
CRBNode rb;
} TestNode;
static void test_api(void) {
CRBTree t = C_RBTREE_INIT, t2 = C_RBTREE_INIT;
CRBNode *i, *is, n = C_RBNODE_INIT(n), m = C_RBNODE_INIT(m);
TestNode *ie, *ies;
assert(c_rbtree_is_empty(&t));
assert(!c_rbnode_is_linked(&n));
assert(!c_rbnode_entry(NULL, TestNode, rb));
/* init, is_linked, add, link, {unlink{,_stale}} */
c_rbtree_add(&t, NULL, &t.root, &n);
assert(c_rbnode_is_linked(&n));
c_rbnode_link(&n, &n.left, &m);
assert(c_rbnode_is_linked(&m));
c_rbnode_unlink(&m);
assert(!c_rbnode_is_linked(&m));
c_rbtree_add(&t, NULL, &t.root, &n);
assert(c_rbnode_is_linked(&n));
c_rbnode_link(&n, &n.left, &m);
assert(c_rbnode_is_linked(&m));
c_rbnode_unlink_stale(&m);
assert(c_rbnode_is_linked(&m)); /* @m wasn't touched */
c_rbnode_init(&n);
assert(!c_rbnode_is_linked(&n));
c_rbnode_init(&m);
assert(!c_rbnode_is_linked(&m));
c_rbtree_init(&t);
assert(c_rbtree_is_empty(&t));
/* move */
c_rbtree_move(&t2, &t);
/* first, last, leftmost, rightmost, next, prev */
assert(!c_rbtree_first(&t));
assert(!c_rbtree_last(&t));
assert(&n == c_rbnode_leftmost(&n));
assert(&n == c_rbnode_rightmost(&n));
assert(!c_rbnode_next(&n));
assert(!c_rbnode_prev(&n));
/* postorder traversal */
assert(!c_rbtree_first_postorder(&t));
assert(!c_rbtree_last_postorder(&t));
assert(&n == c_rbnode_leftdeepest(&n));
assert(&n == c_rbnode_rightdeepest(&n));
assert(!c_rbnode_next_postorder(&n));
assert(!c_rbnode_prev_postorder(&n));
/* iterators */
c_rbtree_for_each(i, &t)
assert(!i);
c_rbtree_for_each_safe(i, is, &t)
assert(!i);
c_rbtree_for_each_entry(ie, &t, rb)
assert(!ie);
c_rbtree_for_each_entry_safe(ie, ies, &t, rb)
assert(!ie);
c_rbtree_for_each_postorder(i, &t)
assert(!i);
c_rbtree_for_each_safe_postorder(i, is, &t)
assert(!i);
c_rbtree_for_each_entry_postorder(ie, &t, rb)
assert(!ie);
c_rbtree_for_each_entry_safe_postorder(ie, ies, &t, rb)
assert(!ie);
c_rbtree_for_each_safe_postorder_unlink(i, is, &t)
assert(!i);
c_rbtree_for_each_entry_safe_postorder_unlink(ie, ies, &t, rb)
assert(!ie);
}
int main(int argc, char **argv) {
test_api();
return 0;
}
/*
* Tests for Basic Tree Operations
* This test does some basic tree operations and verifies their correctness. It
* validates the RB-Tree invariants after each operation, to guarantee the
* stability of the tree.
*
* For testing purposes, we use the memory address of a node as its key, and
* order nodes in ascending order.
*/
#undef NDEBUG
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "c-rbtree.h"
#include "c-rbtree-private.h"
static size_t validate(CRBTree *t) {
unsigned int i_black, n_black;
CRBNode *n, *p, *o;
size_t count = 0;
assert(t);
assert(!t->root || c_rbnode_is_black(t->root));
/* traverse to left-most child, count black nodes */
i_black = 0;
n = t->root;
while (n && n->left) {
if (c_rbnode_is_black(n))
++i_black;
n = n->left;
}
n_black = i_black;
/*
* Traverse tree and verify correctness:
* 1) A node is either red or black
* 2) The root is black
* 3) All leaves are black
* 4) Every red node must have two black child nodes
* 5) Every path to a leaf contains the same number of black nodes
*
* Note that NULL nodes are considered black, which is why we don't
* check for 3).
*/
o = NULL;
while (n) {
++count;
/* verify natural order */
assert(n > o);
o = n;
/* verify consistency */
assert(!n->right || c_rbnode_parent(n->right) == n);
assert(!n->left || c_rbnode_parent(n->left) == n);
/* verify 2) */
if (!c_rbnode_parent(n))
assert(c_rbnode_is_black(n));
if (c_rbnode_is_red(n)) {
/* verify 4) */
assert(!n->left || c_rbnode_is_black(n->left));
assert(!n->right || c_rbnode_is_black(n->right));
} else {
/* verify 1) */
assert(c_rbnode_is_black(n));
}
/* verify 5) */
if (!n->left && !n->right)
assert(i_black == n_black);
/* get next node */
if (n->right) {
n = n->right;
if (c_rbnode_is_black(n))
++i_black;
while (n->left) {
n = n->left;
if (c_rbnode_is_black(n))
++i_black;
}
} else {
while ((p = c_rbnode_parent(n)) && n == p->right) {
n = p;
if (c_rbnode_is_black(p->right))
--i_black;
}
n = p;
if (p && c_rbnode_is_black(p->left))
--i_black;
}
}
return count;
}
static void insert(CRBTree *t, CRBNode *n) {
CRBNode **i, *p;
assert(t);
assert(n);
assert(!c_rbnode_is_linked(n));
i = &t->root;
p = NULL;
while (*i) {
p = *i;
if (n < *i) {
i = &(*i)->left;
} else {
assert(n > *i);
i = &(*i)->right;
}
}
c_rbtree_add(t, p, i, n);
}
static void shuffle(CRBNode **nodes, size_t n_memb) {
unsigned int i, j;
CRBNode *t;
for (i = 0; i < n_memb; ++i) {
j = rand() % n_memb;
t = nodes[j];
nodes[j] = nodes[i];
nodes[i] = t;
}
}
static void test_shuffle(void) {
CRBNode *nodes[512];
CRBTree t = {};
unsigned int i, j;
size_t n;
/* allocate and initialize all nodes */
for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) {
nodes[i] = malloc(sizeof(*nodes[i]));
assert(nodes[i]);
c_rbnode_init(nodes[i]);
}
/* shuffle nodes and validate *empty* tree */
shuffle(nodes, sizeof(nodes) / sizeof(*nodes));
n = validate(&t);
assert(n == 0);
/* add all nodes and validate after each insertion */
for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) {
insert(&t, nodes[i]);
n = validate(&t);
assert(n == i + 1);
}
/* shuffle nodes again */
shuffle(nodes, sizeof(nodes) / sizeof(*nodes));
/* remove all nodes (in different order) and validate on each round */
for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) {
c_rbnode_unlink(nodes[i]);
n = validate(&t);
assert(n == sizeof(nodes) / sizeof(*nodes) - i - 1);
}
/* shuffle nodes and validate *empty* tree again */
shuffle(nodes, sizeof(nodes) / sizeof(*nodes));
n = validate(&t);
assert(n == 0);
/* add all nodes again */
for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) {
insert(&t, nodes[i]);
n = validate(&t);
assert(n == i + 1);
}
/* 4 times, remove half of the nodes and add them again */
for (j = 0; j < 4; ++j) {
/* shuffle nodes again */
shuffle(nodes, sizeof(nodes) / sizeof(*nodes));
/* remove half of the nodes */
for (i = 0; i < sizeof(nodes) / sizeof(*nodes) / 2; ++i) {
c_rbnode_unlink(nodes[i]);
n = validate(&t);
assert(n == sizeof(nodes) / sizeof(*nodes) - i - 1);
}
/* shuffle the removed half */
shuffle(nodes, sizeof(nodes) / sizeof(*nodes) / 2);
/* add the removed half again */
for (i = 0; i < sizeof(nodes) / sizeof(*nodes) / 2; ++i) {
insert(&t, nodes[i]);
n = validate(&t);
assert(n == sizeof(nodes) / sizeof(*nodes) / 2 + i + 1);
}
}
/* shuffle nodes again */
shuffle(nodes, sizeof(nodes) / sizeof(*nodes));
/* remove all */
for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) {
c_rbnode_unlink(nodes[i]);
n = validate(&t);
assert(n == sizeof(nodes) / sizeof(*nodes) - i - 1);
}
/* free nodes again */
for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i)
free(nodes[i]);
}
int main(int argc, char **argv) {
unsigned int i;
/* we want stable tests, so use fixed seed */
srand(0xdeadbeef);
/*
* The tests are pseudo random; run them multiple times, each run will
* have different orders and thus different results.
*/
for (i = 0; i < 4; ++i)
test_shuffle();
return 0;
}
/*
* RB-Tree based Map
* This implements a basic Map between integer keys and objects. It uses the
* lookup and insertion helpers, rather than open-coding it.
*/
#undef NDEBUG
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "c-rbtree.h"
#include "c-rbtree-private.h"
typedef struct {
unsigned long key;
unsigned int marker;
CRBNode rb;
} Node;
#define node_from_rb(_rb) ((Node *)((char *)(_rb) - offsetof(Node, rb)))
static int test_compare(CRBTree *t, void *k, CRBNode *n) {
unsigned long key = (unsigned long)k;
Node *node = node_from_rb(n);
return (key < node->key) ? -1 : (key > node->key) ? 1 : 0;
}
static void shuffle(Node **nodes, size_t n_memb) {
unsigned int i, j;
Node *t;
for (i = 0; i < n_memb; ++i) {
j = rand() % n_memb;
t = nodes[j];
nodes[j] = nodes[i];
nodes[i] = t;
}
}
static void test_map(void) {
CRBNode **slot, *p, *safe_p;
CRBTree t = {};
Node *n, *safe_n, *nodes[2048];
unsigned long i, v;
/* allocate and initialize all nodes */
for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) {
nodes[i] = malloc(sizeof(*nodes[i]));
assert(nodes[i]);
nodes[i]->key = i;
nodes[i]->marker = 0;
c_rbnode_init(&nodes[i]->rb);
}
/* shuffle nodes */
shuffle(nodes, sizeof(nodes) / sizeof(*nodes));
/* add all nodes, and verify that each node is linked */
for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) {
assert(!c_rbnode_is_linked(&nodes[i]->rb));
assert(!c_rbtree_find_entry(&t, test_compare, (void *)nodes[i]->key, Node, rb));
slot = c_rbtree_find_slot(&t, test_compare, (void *)nodes[i]->key, &p);