Author: Armin Rigo <[email protected]>
Branch: vmprof
Changeset: r76332:4c96d8444ca3
Date: 2015-03-11 19:58 +0100
http://bitbucket.org/pypy/pypy/changeset/4c96d8444ca3/
Log: Add a skiplist implementation
diff --git a/rpython/jit/backend/llsupport/src/skiplist.c
b/rpython/jit/backend/llsupport/src/skiplist.c
new file mode 100644
--- /dev/null
+++ b/rpython/jit/backend/llsupport/src/skiplist.c
@@ -0,0 +1,95 @@
+#include <stdlib.h>
+#include <stdint.h>
+
+
+#define SKIPLIST_HEIGHT 8
+
+typedef struct skipnode_s {
+ uintptr_t key;
+ char *data;
+ struct skipnode_s *next[SKIPLIST_HEIGHT]; /* may be smaller */
+} skipnode_t;
+
+static skipnode_t *skiplist_malloc(uintptr_t datasize)
+{
+ char *result;
+ uintptr_t basesize;
+ uintptr_t length = 1;
+ while (length < SKIPLIST_HEIGHT && (rand() & 3) == 0)
+ length++;
+ basesize = sizeof(skipnode_t) -
+ (SKIPLIST_HEIGHT - length) * sizeof(skipnode_t *);
+ result = malloc(basesize + datasize);
+ if (result != NULL) {
+ ((skipnode_t *)result)->data = result + basesize;
+ }
+ return (skipnode_t *)result;
+}
+
+static skipnode_t *skiplist_search(skipnode_t *head, uintptr_t searchkey)
+{
+ /* Returns the skipnode with key closest (but <=) searchkey.
+ Note that if there is no item with key <= searchkey in the list,
+ this will return the head node. */
+ uintptr_t level = SKIPLIST_HEIGHT - 1;
+ while (1) {
+ skipnode_t *next = head->next[level];
+ if (next != NULL && next->key <= searchkey) {
+ head = next;
+ }
+ else {
+ if (level == 0)
+ break;
+ level -= 1;
+ }
+ }
+ return head;
+}
+
+static void skiplist_insert(skipnode_t *head, skipnode_t *new)
+{
+ uintptr_t size0 = sizeof(skipnode_t) -
+ SKIPLIST_HEIGHT * sizeof(skipnode_t *);
+ uintptr_t height_of_new = (new->data - ((char *)new + size0)) /
+ sizeof(skipnode_t *);
+
+ uintptr_t level = SKIPLIST_HEIGHT - 1;
+ uintptr_t searchkey = new->key;
+ while (1) {
+ skipnode_t *next = head->next[level];
+ if (next != NULL && next->key <= searchkey) {
+ head = next;
+ }
+ else {
+ if (level < height_of_new) {
+ new->next[level] = next;
+ head->next[level] = new;
+ if (level == 0)
+ break;
+ }
+ level -= 1;
+ }
+ }
+}
+
+static void skiplist_remove(skipnode_t *head, uintptr_t exact_key)
+{
+ uintptr_t level = SKIPLIST_HEIGHT - 1;
+ while (1) {
+ skipnode_t *next = head->next[level];
+ if (next != NULL && next->key <= exact_key) {
+ if (next->key == exact_key) {
+ head->next[level] = next->next[level];
+ if (level == 0)
+ return; /* successfully removed */
+ level -= 1;
+ }
+ else
+ head = next;
+ }
+ else {
+ assert(level > 0); /* else, 'exact_key' not found! */
+ level -= 1;
+ }
+ }
+}
diff --git a/rpython/jit/backend/llsupport/test/test_skiplist.py
b/rpython/jit/backend/llsupport/test/test_skiplist.py
new file mode 100644
--- /dev/null
+++ b/rpython/jit/backend/llsupport/test/test_skiplist.py
@@ -0,0 +1,85 @@
+import random, os
+import cffi
+
+ffi = cffi.FFI()
+
+ffi.cdef("""
+typedef struct {
+ uintptr_t key;
+ char *data;
+ ...;
+} skipnode_t;
+
+skipnode_t *skiplist_malloc(uintptr_t datasize);
+skipnode_t *skiplist_search(skipnode_t *head, uintptr_t searchkey);
+void skiplist_insert(skipnode_t *head, skipnode_t *new);
+void skiplist_remove(skipnode_t *head, uintptr_t exact_key);
+""")
+
+filename = os.path.join(os.path.dirname(__file__), '..', 'src', 'skiplist.c')
+lib = ffi.verify(open(filename).read())
+
+
+def test_insert_search_remove():
+ my_head = ffi.new("skipnode_t *")
+ assert lib.skiplist_search(my_head, 0) == my_head
+ #
+ keys = random.sample(xrange(2, 10**9), 50000)
+ nodes = {}
+ for key in keys:
+ node = lib.skiplist_malloc(4)
+ node.key = key
+ ffi.cast("int *", node.data)[0] = key
+ lib.skiplist_insert(my_head, node)
+ nodes[key] = node
+ #
+ random.shuffle(keys)
+ for key in keys:
+ node = lib.skiplist_search(my_head, key)
+ assert nodes[key] == node
+ if key + 1 not in nodes:
+ assert node == lib.skiplist_search(my_head, key + 1)
+ #
+ keys.sort()
+ following = {}
+ preceeding = {}
+ for key, next_key in zip(keys[:-1], keys[1:]):
+ following[key] = next_key
+ preceeding[next_key] = key
+ following[0] = keys[0]
+ following[keys[-1]] = 10**9
+ preceeding[keys[0]] = 0
+ #
+ for i in range(100000):
+ random_key = random.randrange(2, 10**9)
+ node = lib.skiplist_search(my_head, random_key)
+ assert node.key <= random_key
+ if node == my_head:
+ assert random_key < following[0]
+ else:
+ assert node == nodes[node.key]
+ assert following[node.key] > random_key
+ #
+ random_keys = list(keys)
+ random.shuffle(random_keys)
+ for i in range(10000):
+ node = nodes.pop(random_keys.pop())
+ prev = preceeding[node.key]
+ next = following[node.key]
+ following[prev] = next
+ preceeding[next] = prev
+ lib.skiplist_remove(my_head, node.key)
+ if prev == 0:
+ assert lib.skiplist_search(my_head, node.key) == my_head
+ else:
+ assert lib.skiplist_search(my_head, node.key) == nodes[prev]
+ #
+ for i in range(100000):
+ random_key = random.randrange(2, 10**9)
+ node = lib.skiplist_search(my_head, random_key)
+ assert node.key <= random_key
+ if node == my_head:
+ assert random_key < following[0]
+ else:
+ assert node == nodes[node.key]
+ assert following[node.key] > random_key
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit