../
// Living on a BSD Tree
#import "/template-en.typ":doc-template

#doc-template(
title: "Living on a BSD Tree",
date: "August 17, 2022",
body: [

FreeBSD and OpenBSD systems both come with #link("https://github.com/openbsd/src/blob/master/sys/sys/tree.h")[a very useful implementation of red-black trees and splay trees].

This implementation is header-only, has no external dependencies, and is generic. Therefore, it is very easy to use on Linux as well.

= Getting the Header File

Run:

```
curl https://raw.githubusercontent.com/openbsd/src/master/sys/sys/tree.h -o tree.h
```

This header file includes `sys/_null.h`, which does not exist on Linux. However, `tree.h` only uses this file to define `NULL`, so you can just change it to `stdlib.h`.

```
// #include <sys/_null.h>
#include <stdlib.h>
```

= Declaration

The trees in the `tree.h` header file are actually entirely macros, so they need to be expanded before use. For example, if you want the value type in the tree to be `double`, create a new header file `double_tree.h` and declare it like this:

```
#ifndef DOUBLE_TREE_H_
#define DOUBLE_TREE_H_

#include "tree.h"

struct double_treenode {
    RB_ENTRY(double_treenode) entry;
    double val;
};

int double_cmp(struct double_treenode *e1, struct double_treenode *e2);

RB_HEAD(double_tree, double_treenode);
RB_PROTOTYPE(double_tree, double_treenode, entry, double_cmp)

#endif
```

= Definition

Then create the source file `double_tree.h` and add the necessary function definitions:

```
#include "double_tree.h"

int double_cmp(struct double_treenode *e1, struct double_treenode *e2); {
    if (e1->val < e2->val) {
        return -1;
    } else if (e1->val > e2->val) {
        return 1;
    }
    return 0;
}

RB_GENERATE(double_tree, double_treenode, entry, double_cmp)
```

= Usage

Next, we will introduce how to use this `double_tree`.

== Initialization

Create the tree and initialize it:

```
RB_HEAD(double_tree, double_treenode) head;
RB_INIT(&head);
```

Initialization can also be completed in one line:

```
RB_HEAD(double_tree, double_treenode) head = RB_INITIALIZER(&head);
```

== Insertion

```
struct double_treenode *n;
double data[5] = {1.0, 2.0, 3.0, 4.0, 5.0};
for (int i = 0; i < 5; i++) {
    n = malloc(sizeof(struct double_treenode));
    n->val = data[i];
    RB_INSERT(double_tree, &head, n);
}
```

== Search and Deletion

```
struct double_treenode find;
find.val = 3.0

struct double_treenode *iter;
iter = RB_FIND(double_tree, &head, &find);

if (iter != NULL) {
    printf("Found\n");
    RB_REMOVE(double_tree, &head, iter);
    free(iter);
}
```

== Traversal

```
RB_FOREACH(iter, double_tree, &head) {
    // Do something on iter->val
    ...
}
```

In fact, `RB_FOREACH(iter, double_tree, &head)` is essentially:

```
for (iter = RB_MIN(double_tree, &head);
        iter != NULL;
        iter = RB_NEXT(double_tree, &head, iter))
```

You can use `RB_MIN` to get the minimum node in the tree; use `RB_NEXT` to get the next element of `iter`; and `RB_MAX` is the maximum node.

If you want to traverse the tree in other orders, you can use `RB_LEFT` and `RB_RIGHT`. For example, printing the tree using pre-order traversal:

```
void
print_tree(struct double_treenode *n)
{
    struct double_treenode *left, *right;

    if (n == NULL) {
        printf("nil");
        return;
    }
    left = RB_LEFT(n, entry);
    right = RB_RIGHT(n, entry);
    if (left == NULL && right == NULL)
        printf("%d", n->val);
    else {
        printf("%d(", n->val);
        print_tree(left);
        printf(",");
        print_tree(right);
        printf(")");
    }
}
```

= Others

There is a very strange thing: Arch Linux has the man documentation for `tree.h`, which you can see by executing `man 3 tree`, but the header file itself cannot be found.

This article only covers red-black trees, but splay trees are actually quite similar, so I won't go into detail. You can directly check the #link("https://www.freebsd.org/cgi/man.cgi?query=tree&sektion=3&format=html")[documentation].

= Errata

Thanks to #link("https://c7.io/@w3cing")[\@w3cing] for the #link("https://mstdn.party/@w3cing@c7.io/111659300344465706")[reminder]. The `RB_TREE` in `tree.h` provided by FreeBSD is actually not a red-black tree; RB refers to a rank-balanced tree, and the specific implementation is a weak AVL tree. FreeBSD made an #link("https://github.com/freebsd/freebsd-src/commit/e605dcc939848312a201b4aa53bd7bb67d862b18")[update] to `tree.h` in August 2020.
]
)

Email: i (at) mistivia (dot) com