Skip to content

Commit 951f316

Browse files
jasonegitster
authored andcommitted
Add treap implementation
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>
1 parent 4709455 commit 951f316

File tree

7 files changed

+432
-1
lines changed

7 files changed

+432
-1
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@
173173
/test-run-command
174174
/test-sha1
175175
/test-sigchain
176+
/test-treap
176177
/common-cmds.h
177178
*.tar.gz
178179
*.dsc

Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,7 @@ TEST_PROGRAMS_NEED_X += test-path-utils
415415
TEST_PROGRAMS_NEED_X += test-run-command
416416
TEST_PROGRAMS_NEED_X += test-sha1
417417
TEST_PROGRAMS_NEED_X += test-sigchain
418+
TEST_PROGRAMS_NEED_X += test-treap
418419
TEST_PROGRAMS_NEED_X += test-index-version
419420

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

18681869
$(VCSSVN_OBJS): \
1869-
vcs-svn/obj_pool.h
1870+
vcs-svn/obj_pool.h vcs-svn/trp.h
18701871
endif
18711872

18721873
exec_cmd.s exec_cmd.o: EXTRA_CPPFLAGS = \

t/t0080-vcs-svn.sh

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,4 +76,26 @@ test_expect_success 'obj pool: high-water mark' '
7676
test_cmp expected actual
7777
'
7878

79+
test_expect_success 'treap sort' '
80+
cat <<-\EOF >unsorted &&
81+
68
82+
12
83+
13
84+
13
85+
68
86+
13
87+
13
88+
21
89+
10
90+
11
91+
12
92+
13
93+
13
94+
EOF
95+
sort unsorted >expected &&
96+
97+
test-treap <unsorted >actual &&
98+
test_cmp expected actual
99+
'
100+
79101
test_done

test-treap.c

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* test-treap.c: code to exercise the svn importer's treap structure
3+
*/
4+
5+
#include "cache.h"
6+
#include "vcs-svn/obj_pool.h"
7+
#include "vcs-svn/trp.h"
8+
9+
struct int_node {
10+
uintmax_t n;
11+
struct trp_node children;
12+
};
13+
14+
obj_pool_gen(node, struct int_node, 3)
15+
16+
static int node_cmp(struct int_node *a, struct int_node *b)
17+
{
18+
return (a->n > b->n) - (a->n < b->n);
19+
}
20+
21+
trp_gen(static, treap_, struct int_node, children, node, node_cmp)
22+
23+
static void strtonode(struct int_node *item, const char *s)
24+
{
25+
char *end;
26+
item->n = strtoumax(s, &end, 10);
27+
if (*s == '\0' || (*end != '\n' && *end != '\0'))
28+
die("invalid integer: %s", s);
29+
}
30+
31+
int main(int argc, char *argv[])
32+
{
33+
struct strbuf sb = STRBUF_INIT;
34+
struct trp_root root = { ~0 };
35+
uint32_t item;
36+
37+
if (argc != 1)
38+
usage("test-treap < ints");
39+
40+
while (strbuf_getline(&sb, stdin, '\n') != EOF) {
41+
item = node_alloc(1);
42+
strtonode(node_pointer(item), sb.buf);
43+
treap_insert(&root, node_pointer(item));
44+
}
45+
46+
item = node_offset(treap_first(&root));
47+
while (~item) {
48+
uint32_t next;
49+
struct int_node *tmp = node_pointer(node_alloc(1));
50+
51+
tmp->n = node_pointer(item)->n;
52+
next = node_offset(treap_next(&root, node_pointer(item)));
53+
54+
treap_remove(&root, node_pointer(item));
55+
item = node_offset(treap_nsearch(&root, tmp));
56+
57+
if (item != next && (!~item || node_pointer(item)->n != tmp->n))
58+
die("found %"PRIuMAX" in place of %"PRIuMAX"",
59+
~item ? node_pointer(item)->n : ~(uintmax_t) 0,
60+
~next ? node_pointer(next)->n : ~(uintmax_t) 0);
61+
printf("%"PRIuMAX"\n", tmp->n);
62+
}
63+
node_reset();
64+
return 0;
65+
}

vcs-svn/LICENSE

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
Copyright (C) 2010 David Barr <david.barr@cordelta.com>.
22
All rights reserved.
33

4+
Copyright (C) 2008 Jason Evans <jasone@canonware.com>.
5+
All rights reserved.
6+
47
Redistribution and use in source and binary forms, with or without
58
modification, are permitted provided that the following conditions
69
are met:

vcs-svn/trp.h

Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
/*
2+
* C macro implementation of treaps.
3+
*
4+
* Usage:
5+
* #include <stdint.h>
6+
* #include "trp.h"
7+
* trp_gen(...)
8+
*
9+
* Licensed under a two-clause BSD-style license.
10+
* See LICENSE for details.
11+
*/
12+
13+
#ifndef TRP_H_
14+
#define TRP_H_
15+
16+
#define MAYBE_UNUSED __attribute__((__unused__))
17+
18+
/* Node structure. */
19+
struct trp_node {
20+
uint32_t trpn_left;
21+
uint32_t trpn_right;
22+
};
23+
24+
/* Root structure. */
25+
struct trp_root {
26+
uint32_t trp_root;
27+
};
28+
29+
/* Pointer/Offset conversion. */
30+
#define trpn_pointer(a_base, a_offset) (a_base##_pointer(a_offset))
31+
#define trpn_offset(a_base, a_pointer) (a_base##_offset(a_pointer))
32+
#define trpn_modify(a_base, a_offset) \
33+
do { \
34+
if ((a_offset) < a_base##_pool.committed) { \
35+
uint32_t old_offset = (a_offset);\
36+
(a_offset) = a_base##_alloc(1); \
37+
*trpn_pointer(a_base, a_offset) = \
38+
*trpn_pointer(a_base, old_offset); \
39+
} \
40+
} while (0);
41+
42+
/* Left accessors. */
43+
#define trp_left_get(a_base, a_field, a_node) \
44+
(trpn_pointer(a_base, a_node)->a_field.trpn_left)
45+
#define trp_left_set(a_base, a_field, a_node, a_left) \
46+
do { \
47+
trpn_modify(a_base, a_node); \
48+
trp_left_get(a_base, a_field, a_node) = (a_left); \
49+
} while(0)
50+
51+
/* Right accessors. */
52+
#define trp_right_get(a_base, a_field, a_node) \
53+
(trpn_pointer(a_base, a_node)->a_field.trpn_right)
54+
#define trp_right_set(a_base, a_field, a_node, a_right) \
55+
do { \
56+
trpn_modify(a_base, a_node); \
57+
trp_right_get(a_base, a_field, a_node) = (a_right); \
58+
} while(0)
59+
60+
/*
61+
* Fibonacci hash function.
62+
* The multiplier is the nearest prime to (2^32 times (√5 - 1)/2).
63+
* See Knuth §6.4: volume 3, 3rd ed, p518.
64+
*/
65+
#define trpn_hash(a_node) (uint32_t) (2654435761u * (a_node))
66+
67+
/* Priority accessors. */
68+
#define trp_prio_get(a_node) trpn_hash(a_node)
69+
70+
/* Node initializer. */
71+
#define trp_node_new(a_base, a_field, a_node) \
72+
do { \
73+
trp_left_set(a_base, a_field, (a_node), ~0); \
74+
trp_right_set(a_base, a_field, (a_node), ~0); \
75+
} while(0)
76+
77+
/* Internal utility macros. */
78+
#define trpn_first(a_base, a_field, a_root, r_node) \
79+
do { \
80+
(r_node) = (a_root); \
81+
if ((r_node) == ~0) \
82+
return NULL; \
83+
while (~trp_left_get(a_base, a_field, (r_node))) \
84+
(r_node) = trp_left_get(a_base, a_field, (r_node)); \
85+
} while (0)
86+
87+
#define trpn_rotate_left(a_base, a_field, a_node, r_node) \
88+
do { \
89+
(r_node) = trp_right_get(a_base, a_field, (a_node)); \
90+
trp_right_set(a_base, a_field, (a_node), \
91+
trp_left_get(a_base, a_field, (r_node))); \
92+
trp_left_set(a_base, a_field, (r_node), (a_node)); \
93+
} while(0)
94+
95+
#define trpn_rotate_right(a_base, a_field, a_node, r_node) \
96+
do { \
97+
(r_node) = trp_left_get(a_base, a_field, (a_node)); \
98+
trp_left_set(a_base, a_field, (a_node), \
99+
trp_right_get(a_base, a_field, (r_node))); \
100+
trp_right_set(a_base, a_field, (r_node), (a_node)); \
101+
} while(0)
102+
103+
#define trp_gen(a_attr, a_pre, a_type, a_field, a_base, a_cmp) \
104+
a_attr a_type MAYBE_UNUSED *a_pre##first(struct trp_root *treap) \
105+
{ \
106+
uint32_t ret; \
107+
trpn_first(a_base, a_field, treap->trp_root, ret); \
108+
return trpn_pointer(a_base, ret); \
109+
} \
110+
a_attr a_type MAYBE_UNUSED *a_pre##next(struct trp_root *treap, a_type *node) \
111+
{ \
112+
uint32_t ret; \
113+
uint32_t offset = trpn_offset(a_base, node); \
114+
if (~trp_right_get(a_base, a_field, offset)) { \
115+
trpn_first(a_base, a_field, \
116+
trp_right_get(a_base, a_field, offset), ret); \
117+
} else { \
118+
uint32_t tnode = treap->trp_root; \
119+
ret = ~0; \
120+
while (1) { \
121+
int cmp = (a_cmp)(trpn_pointer(a_base, offset), \
122+
trpn_pointer(a_base, tnode)); \
123+
if (cmp < 0) { \
124+
ret = tnode; \
125+
tnode = trp_left_get(a_base, a_field, tnode); \
126+
} else if (cmp > 0) { \
127+
tnode = trp_right_get(a_base, a_field, tnode); \
128+
} else { \
129+
break; \
130+
} \
131+
} \
132+
} \
133+
return trpn_pointer(a_base, ret); \
134+
} \
135+
a_attr a_type MAYBE_UNUSED *a_pre##search(struct trp_root *treap, a_type *key) \
136+
{ \
137+
int cmp; \
138+
uint32_t ret = treap->trp_root; \
139+
while (~ret && (cmp = (a_cmp)(key, trpn_pointer(a_base,ret)))) { \
140+
if (cmp < 0) { \
141+
ret = trp_left_get(a_base, a_field, ret); \
142+
} else { \
143+
ret = trp_right_get(a_base, a_field, ret); \
144+
} \
145+
} \
146+
return trpn_pointer(a_base, ret); \
147+
} \
148+
a_attr a_type MAYBE_UNUSED *a_pre##nsearch(struct trp_root *treap, a_type *key) \
149+
{ \
150+
int cmp; \
151+
uint32_t ret = treap->trp_root; \
152+
while (~ret && (cmp = (a_cmp)(key, trpn_pointer(a_base,ret)))) { \
153+
if (cmp < 0) { \
154+
if (!~trp_left_get(a_base, a_field, ret)) \
155+
break; \
156+
ret = trp_left_get(a_base, a_field, ret); \
157+
} else { \
158+
ret = trp_right_get(a_base, a_field, ret); \
159+
} \
160+
} \
161+
return trpn_pointer(a_base, ret); \
162+
} \
163+
a_attr uint32_t MAYBE_UNUSED a_pre##insert_recurse(uint32_t cur_node, uint32_t ins_node) \
164+
{ \
165+
if (cur_node == ~0) { \
166+
return (ins_node); \
167+
} else { \
168+
uint32_t ret; \
169+
int cmp = (a_cmp)(trpn_pointer(a_base, ins_node), \
170+
trpn_pointer(a_base, cur_node)); \
171+
if (cmp < 0) { \
172+
uint32_t left = a_pre##insert_recurse( \
173+
trp_left_get(a_base, a_field, cur_node), ins_node); \
174+
trp_left_set(a_base, a_field, cur_node, left); \
175+
if (trp_prio_get(left) < trp_prio_get(cur_node)) \
176+
trpn_rotate_right(a_base, a_field, cur_node, ret); \
177+
else \
178+
ret = cur_node; \
179+
} else { \
180+
uint32_t right = a_pre##insert_recurse( \
181+
trp_right_get(a_base, a_field, cur_node), ins_node); \
182+
trp_right_set(a_base, a_field, cur_node, right); \
183+
if (trp_prio_get(right) < trp_prio_get(cur_node)) \
184+
trpn_rotate_left(a_base, a_field, cur_node, ret); \
185+
else \
186+
ret = cur_node; \
187+
} \
188+
return (ret); \
189+
} \
190+
} \
191+
a_attr void MAYBE_UNUSED a_pre##insert(struct trp_root *treap, a_type *node) \
192+
{ \
193+
uint32_t offset = trpn_offset(a_base, node); \
194+
trp_node_new(a_base, a_field, offset); \
195+
treap->trp_root = a_pre##insert_recurse(treap->trp_root, offset); \
196+
} \
197+
a_attr uint32_t MAYBE_UNUSED a_pre##remove_recurse(uint32_t cur_node, uint32_t rem_node) \
198+
{ \
199+
int cmp = a_cmp(trpn_pointer(a_base, rem_node), \
200+
trpn_pointer(a_base, cur_node)); \
201+
if (cmp == 0) { \
202+
uint32_t ret; \
203+
uint32_t left = trp_left_get(a_base, a_field, cur_node); \
204+
uint32_t right = trp_right_get(a_base, a_field, cur_node); \
205+
if (left == ~0) { \
206+
if (right == ~0) \
207+
return (~0); \
208+
} else if (right == ~0 || trp_prio_get(left) < trp_prio_get(right)) { \
209+
trpn_rotate_right(a_base, a_field, cur_node, ret); \
210+
right = a_pre##remove_recurse(cur_node, rem_node); \
211+
trp_right_set(a_base, a_field, ret, right); \
212+
return (ret); \
213+
} \
214+
trpn_rotate_left(a_base, a_field, cur_node, ret); \
215+
left = a_pre##remove_recurse(cur_node, rem_node); \
216+
trp_left_set(a_base, a_field, ret, left); \
217+
return (ret); \
218+
} else if (cmp < 0) { \
219+
uint32_t left = a_pre##remove_recurse( \
220+
trp_left_get(a_base, a_field, cur_node), rem_node); \
221+
trp_left_set(a_base, a_field, cur_node, left); \
222+
return (cur_node); \
223+
} else { \
224+
uint32_t right = a_pre##remove_recurse( \
225+
trp_right_get(a_base, a_field, cur_node), rem_node); \
226+
trp_right_set(a_base, a_field, cur_node, right); \
227+
return (cur_node); \
228+
} \
229+
} \
230+
a_attr void MAYBE_UNUSED a_pre##remove(struct trp_root *treap, a_type *node) \
231+
{ \
232+
treap->trp_root = a_pre##remove_recurse(treap->trp_root, \
233+
trpn_offset(a_base, node)); \
234+
} \
235+
236+
#endif

0 commit comments

Comments
 (0)