Skip to content

Commit

Permalink
Add treap implementation
Browse files Browse the repository at this point in the history
Provide macros to generate a type-specific treap implementation and
various functions to operate on it. It uses obj_pool.h to store memory
nodes in a treap.  Previously committed nodes are never removed from
the pool; after any *_commit operation, it is assumed (correctly, in
the case of svn-fast-export) that someone else must care about them.

Treaps provide a memory-efficient binary search tree structure.
Insertion/deletion/search are about as about as fast in the average
case as red-black trees and the chances of worst-case behavior are
vanishingly small, thanks to (pseudo-)randomness.  The bad worst-case
behavior is a small price to pay, given that treaps are much simpler
to implement.

>From http://www.canonware.com/download/trp/trp_hash/trp.h

[db: Altered to reference nodes by offset from a common base pointer]
[db: Bob Jenkins' hashing implementation dropped for Knuth's]
[db: Methods unnecessary for search and insert dropped]
[rr: Squelched compiler warnings]
[db: Added support for immutable treap nodes]
[jn: Reintroduced treap_nsearch(); with tests]

Signed-off-by: David Barr <david.barr@cordelta.com>
Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
  • Loading branch information
Jason Evans authored and Junio C Hamano committed Aug 15, 2010
1 parent 4709455 commit 951f316
Show file tree
Hide file tree
Showing 7 changed files with 432 additions and 1 deletion.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@
/test-run-command
/test-sha1
/test-sigchain
/test-treap
/common-cmds.h
*.tar.gz
*.dsc
Expand Down
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,7 @@ TEST_PROGRAMS_NEED_X += test-path-utils
TEST_PROGRAMS_NEED_X += test-run-command
TEST_PROGRAMS_NEED_X += test-sha1
TEST_PROGRAMS_NEED_X += test-sigchain
TEST_PROGRAMS_NEED_X += test-treap
TEST_PROGRAMS_NEED_X += test-index-version

TEST_PROGRAMS = $(patsubst %,%$X,$(TEST_PROGRAMS_NEED_X))
Expand Down Expand Up @@ -1866,7 +1867,7 @@ xdiff-interface.o $(XDIFF_OBJS): \
xdiff/xutils.h xdiff/xprepare.h xdiff/xdiffi.h xdiff/xemit.h

$(VCSSVN_OBJS): \
vcs-svn/obj_pool.h
vcs-svn/obj_pool.h vcs-svn/trp.h
endif

exec_cmd.s exec_cmd.o: EXTRA_CPPFLAGS = \
Expand Down
22 changes: 22 additions & 0 deletions t/t0080-vcs-svn.sh
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,26 @@ test_expect_success 'obj pool: high-water mark' '
test_cmp expected actual
'

test_expect_success 'treap sort' '
cat <<-\EOF >unsorted &&
68
12
13
13
68
13
13
21
10
11
12
13
13
EOF
sort unsorted >expected &&
test-treap <unsorted >actual &&
test_cmp expected actual
'

test_done
65 changes: 65 additions & 0 deletions test-treap.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* test-treap.c: code to exercise the svn importer's treap structure
*/

#include "cache.h"
#include "vcs-svn/obj_pool.h"
#include "vcs-svn/trp.h"

struct int_node {
uintmax_t n;
struct trp_node children;
};

obj_pool_gen(node, struct int_node, 3)

static int node_cmp(struct int_node *a, struct int_node *b)
{
return (a->n > b->n) - (a->n < b->n);
}

trp_gen(static, treap_, struct int_node, children, node, node_cmp)

static void strtonode(struct int_node *item, const char *s)
{
char *end;
item->n = strtoumax(s, &end, 10);
if (*s == '\0' || (*end != '\n' && *end != '\0'))
die("invalid integer: %s", s);
}

int main(int argc, char *argv[])
{
struct strbuf sb = STRBUF_INIT;
struct trp_root root = { ~0 };
uint32_t item;

if (argc != 1)
usage("test-treap < ints");

while (strbuf_getline(&sb, stdin, '\n') != EOF) {
item = node_alloc(1);
strtonode(node_pointer(item), sb.buf);
treap_insert(&root, node_pointer(item));
}

item = node_offset(treap_first(&root));
while (~item) {
uint32_t next;
struct int_node *tmp = node_pointer(node_alloc(1));

tmp->n = node_pointer(item)->n;
next = node_offset(treap_next(&root, node_pointer(item)));

treap_remove(&root, node_pointer(item));
item = node_offset(treap_nsearch(&root, tmp));

if (item != next && (!~item || node_pointer(item)->n != tmp->n))
die("found %"PRIuMAX" in place of %"PRIuMAX"",
~item ? node_pointer(item)->n : ~(uintmax_t) 0,
~next ? node_pointer(next)->n : ~(uintmax_t) 0);
printf("%"PRIuMAX"\n", tmp->n);
}
node_reset();
return 0;
}
3 changes: 3 additions & 0 deletions vcs-svn/LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
Copyright (C) 2010 David Barr <david.barr@cordelta.com>.
All rights reserved.

Copyright (C) 2008 Jason Evans <jasone@canonware.com>.
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
Expand Down
236 changes: 236 additions & 0 deletions vcs-svn/trp.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
/*
* C macro implementation of treaps.
*
* Usage:
* #include <stdint.h>
* #include "trp.h"
* trp_gen(...)
*
* Licensed under a two-clause BSD-style license.
* See LICENSE for details.
*/

#ifndef TRP_H_
#define TRP_H_

#define MAYBE_UNUSED __attribute__((__unused__))

/* Node structure. */
struct trp_node {
uint32_t trpn_left;
uint32_t trpn_right;
};

/* Root structure. */
struct trp_root {
uint32_t trp_root;
};

/* Pointer/Offset conversion. */
#define trpn_pointer(a_base, a_offset) (a_base##_pointer(a_offset))
#define trpn_offset(a_base, a_pointer) (a_base##_offset(a_pointer))
#define trpn_modify(a_base, a_offset) \
do { \
if ((a_offset) < a_base##_pool.committed) { \
uint32_t old_offset = (a_offset);\
(a_offset) = a_base##_alloc(1); \
*trpn_pointer(a_base, a_offset) = \
*trpn_pointer(a_base, old_offset); \
} \
} while (0);

/* Left accessors. */
#define trp_left_get(a_base, a_field, a_node) \
(trpn_pointer(a_base, a_node)->a_field.trpn_left)
#define trp_left_set(a_base, a_field, a_node, a_left) \
do { \
trpn_modify(a_base, a_node); \
trp_left_get(a_base, a_field, a_node) = (a_left); \
} while(0)

/* Right accessors. */
#define trp_right_get(a_base, a_field, a_node) \
(trpn_pointer(a_base, a_node)->a_field.trpn_right)
#define trp_right_set(a_base, a_field, a_node, a_right) \
do { \
trpn_modify(a_base, a_node); \
trp_right_get(a_base, a_field, a_node) = (a_right); \
} while(0)

/*
* Fibonacci hash function.
* The multiplier is the nearest prime to (2^32 times (√5 - 1)/2).
* See Knuth §6.4: volume 3, 3rd ed, p518.
*/
#define trpn_hash(a_node) (uint32_t) (2654435761u * (a_node))

/* Priority accessors. */
#define trp_prio_get(a_node) trpn_hash(a_node)

/* Node initializer. */
#define trp_node_new(a_base, a_field, a_node) \
do { \
trp_left_set(a_base, a_field, (a_node), ~0); \
trp_right_set(a_base, a_field, (a_node), ~0); \
} while(0)

/* Internal utility macros. */
#define trpn_first(a_base, a_field, a_root, r_node) \
do { \
(r_node) = (a_root); \
if ((r_node) == ~0) \
return NULL; \
while (~trp_left_get(a_base, a_field, (r_node))) \
(r_node) = trp_left_get(a_base, a_field, (r_node)); \
} while (0)

#define trpn_rotate_left(a_base, a_field, a_node, r_node) \
do { \
(r_node) = trp_right_get(a_base, a_field, (a_node)); \
trp_right_set(a_base, a_field, (a_node), \
trp_left_get(a_base, a_field, (r_node))); \
trp_left_set(a_base, a_field, (r_node), (a_node)); \
} while(0)

#define trpn_rotate_right(a_base, a_field, a_node, r_node) \
do { \
(r_node) = trp_left_get(a_base, a_field, (a_node)); \
trp_left_set(a_base, a_field, (a_node), \
trp_right_get(a_base, a_field, (r_node))); \
trp_right_set(a_base, a_field, (r_node), (a_node)); \
} while(0)

#define trp_gen(a_attr, a_pre, a_type, a_field, a_base, a_cmp) \
a_attr a_type MAYBE_UNUSED *a_pre##first(struct trp_root *treap) \
{ \
uint32_t ret; \
trpn_first(a_base, a_field, treap->trp_root, ret); \
return trpn_pointer(a_base, ret); \
} \
a_attr a_type MAYBE_UNUSED *a_pre##next(struct trp_root *treap, a_type *node) \
{ \
uint32_t ret; \
uint32_t offset = trpn_offset(a_base, node); \
if (~trp_right_get(a_base, a_field, offset)) { \
trpn_first(a_base, a_field, \
trp_right_get(a_base, a_field, offset), ret); \
} else { \
uint32_t tnode = treap->trp_root; \
ret = ~0; \
while (1) { \
int cmp = (a_cmp)(trpn_pointer(a_base, offset), \
trpn_pointer(a_base, tnode)); \
if (cmp < 0) { \
ret = tnode; \
tnode = trp_left_get(a_base, a_field, tnode); \
} else if (cmp > 0) { \
tnode = trp_right_get(a_base, a_field, tnode); \
} else { \
break; \
} \
} \
} \
return trpn_pointer(a_base, ret); \
} \
a_attr a_type MAYBE_UNUSED *a_pre##search(struct trp_root *treap, a_type *key) \
{ \
int cmp; \
uint32_t ret = treap->trp_root; \
while (~ret && (cmp = (a_cmp)(key, trpn_pointer(a_base,ret)))) { \
if (cmp < 0) { \
ret = trp_left_get(a_base, a_field, ret); \
} else { \
ret = trp_right_get(a_base, a_field, ret); \
} \
} \
return trpn_pointer(a_base, ret); \
} \
a_attr a_type MAYBE_UNUSED *a_pre##nsearch(struct trp_root *treap, a_type *key) \
{ \
int cmp; \
uint32_t ret = treap->trp_root; \
while (~ret && (cmp = (a_cmp)(key, trpn_pointer(a_base,ret)))) { \
if (cmp < 0) { \
if (!~trp_left_get(a_base, a_field, ret)) \
break; \
ret = trp_left_get(a_base, a_field, ret); \
} else { \
ret = trp_right_get(a_base, a_field, ret); \
} \
} \
return trpn_pointer(a_base, ret); \
} \
a_attr uint32_t MAYBE_UNUSED a_pre##insert_recurse(uint32_t cur_node, uint32_t ins_node) \
{ \
if (cur_node == ~0) { \
return (ins_node); \
} else { \
uint32_t ret; \
int cmp = (a_cmp)(trpn_pointer(a_base, ins_node), \
trpn_pointer(a_base, cur_node)); \
if (cmp < 0) { \
uint32_t left = a_pre##insert_recurse( \
trp_left_get(a_base, a_field, cur_node), ins_node); \
trp_left_set(a_base, a_field, cur_node, left); \
if (trp_prio_get(left) < trp_prio_get(cur_node)) \
trpn_rotate_right(a_base, a_field, cur_node, ret); \
else \
ret = cur_node; \
} else { \
uint32_t right = a_pre##insert_recurse( \
trp_right_get(a_base, a_field, cur_node), ins_node); \
trp_right_set(a_base, a_field, cur_node, right); \
if (trp_prio_get(right) < trp_prio_get(cur_node)) \
trpn_rotate_left(a_base, a_field, cur_node, ret); \
else \
ret = cur_node; \
} \
return (ret); \
} \
} \
a_attr void MAYBE_UNUSED a_pre##insert(struct trp_root *treap, a_type *node) \
{ \
uint32_t offset = trpn_offset(a_base, node); \
trp_node_new(a_base, a_field, offset); \
treap->trp_root = a_pre##insert_recurse(treap->trp_root, offset); \
} \
a_attr uint32_t MAYBE_UNUSED a_pre##remove_recurse(uint32_t cur_node, uint32_t rem_node) \
{ \
int cmp = a_cmp(trpn_pointer(a_base, rem_node), \
trpn_pointer(a_base, cur_node)); \
if (cmp == 0) { \
uint32_t ret; \
uint32_t left = trp_left_get(a_base, a_field, cur_node); \
uint32_t right = trp_right_get(a_base, a_field, cur_node); \
if (left == ~0) { \
if (right == ~0) \
return (~0); \
} else if (right == ~0 || trp_prio_get(left) < trp_prio_get(right)) { \
trpn_rotate_right(a_base, a_field, cur_node, ret); \
right = a_pre##remove_recurse(cur_node, rem_node); \
trp_right_set(a_base, a_field, ret, right); \
return (ret); \
} \
trpn_rotate_left(a_base, a_field, cur_node, ret); \
left = a_pre##remove_recurse(cur_node, rem_node); \
trp_left_set(a_base, a_field, ret, left); \
return (ret); \
} else if (cmp < 0) { \
uint32_t left = a_pre##remove_recurse( \
trp_left_get(a_base, a_field, cur_node), rem_node); \
trp_left_set(a_base, a_field, cur_node, left); \
return (cur_node); \
} else { \
uint32_t right = a_pre##remove_recurse( \
trp_right_get(a_base, a_field, cur_node), rem_node); \
trp_right_set(a_base, a_field, cur_node, right); \
return (cur_node); \
} \
} \
a_attr void MAYBE_UNUSED a_pre##remove(struct trp_root *treap, a_type *node) \
{ \
treap->trp_root = a_pre##remove_recurse(treap->trp_root, \
trpn_offset(a_base, node)); \
} \

#endif
Loading

0 comments on commit 951f316

Please sign in to comment.