├── cskipdict ├── headers │ ├── fake-includes │ │ ├── time.h │ │ ├── stdlib.h │ │ └── string.h │ ├── random_uniform.h │ ├── skiplist-config.h │ ├── skiplist-header.h │ ├── random_uniform.cpp │ ├── skiplist.cdef │ └── skiplist-template.h └── __init__.py ├── MANIFEST.in ├── tox.ini ├── .travis.yml ├── Makefile ├── setup.py ├── cskipdict_build.py ├── .gitignore ├── test └── test_skipdict.py ├── README.md └── README.rst /cskipdict/headers/fake-includes/time.h: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /cskipdict/headers/fake-includes/stdlib.h: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /cskipdict/headers/fake-includes/string.h: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include cskipdict_build.py 2 | recursive-include cskipdict/headers * 3 | -------------------------------------------------------------------------------- /cskipdict/headers/random_uniform.h: -------------------------------------------------------------------------------- 1 | extern "C" uint32_t random_uniform(uint32_t max_plus_one); 2 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist=py27,py35,py36 3 | [testenv] 4 | usedevelop=true 5 | deps= 6 | nose 7 | cffi 8 | commands=nosetests 9 | -------------------------------------------------------------------------------- /cskipdict/headers/skiplist-config.h: -------------------------------------------------------------------------------- 1 | #define SKIPLIST_KEY int64_t 2 | #define SKIPLIST_VALUE void * 3 | #define SKIPLIST_NAMESPACE skiplist_ 4 | #define SKIPLIST_EXTERN 5 | -------------------------------------------------------------------------------- /cskipdict/headers/skiplist-header.h: -------------------------------------------------------------------------------- 1 | #include "skiplist-config.h" 2 | #include "skiplist-template.h" 3 | 4 | int skiplist_int64_cmp(int64_t a, int64_t b, void *userdata); 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | 3 | matrix: 4 | include: 5 | - python: "2.7" 6 | env: TOXENV=py27 7 | - python: "3.6" 8 | env: TOXENV=py36 9 | - python: "pypy" 10 | env: TOXENV=py27 11 | - python: "pypy3" 12 | env: TOXENV=py35 13 | 14 | install: pip install tox 15 | 16 | script: tox 17 | 18 | notifications: 19 | email: false 20 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: README.rst cskipdict/headers/skiplist.cdef 2 | 3 | README.rst: README.md 4 | pandoc -f markdown -t rst < $^ > $@ 5 | 6 | cskipdict/headers/skiplist.cdef: cskipdict/headers/skiplist-header.h cskipdict/headers/skiplist-config.h 7 | cc -C -E -Xpreprocessor -P -nostdinc -Icskipdict/headers/fake-includes cskipdict/headers/skiplist-header.h > $@ 8 | 9 | clean: 10 | rm cskipdict/headers/skiplist.cdef 11 | 12 | .PHONY: all clean 13 | -------------------------------------------------------------------------------- /cskipdict/headers/random_uniform.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "random_uniform.h" 4 | 5 | extern "C" uint32_t random_uniform(uint32_t max_plus_one) { 6 | std::random_device rd; //Will be used to obtain a seed for the random number engine 7 | std::mt19937 gen(rd()); //Standard mersenne_twister_engine seeded with rd() 8 | std::uniform_int_distribution dis(0, max_plus_one - 1); 9 | return dis(gen); 10 | } 11 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from codecs import open 2 | from os import path 3 | 4 | from setuptools import setup, find_packages 5 | 6 | 7 | here = path.abspath(path.dirname(__file__)) 8 | 9 | # Get the long description from the README file 10 | with open(path.join(here, 'README.rst'), encoding='utf-8') as f: 11 | readme = f.read() 12 | 13 | 14 | setup( 15 | name='cskipdict', 16 | version='0.0.4', 17 | 18 | description='A fast CFFI-based SkipDict implementation.', 19 | long_description=readme, 20 | url='https://github.com/zacharyvoase/python-cskipdict', 21 | 22 | author='Zachary Voase', 23 | author_email='zack@meat.io', 24 | license='UNLICENSE', 25 | 26 | packages=find_packages(exclude=['test']), 27 | 28 | setup_requires=["cffi>=1.4.0"], 29 | cffi_modules=["cskipdict_build.py:ffibuilder"], 30 | 31 | install_requires=["cffi>=1.4.0"], 32 | ) 33 | -------------------------------------------------------------------------------- /cskipdict_build.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from cffi import FFI 4 | 5 | 6 | src_root = os.path.join(os.path.dirname(__file__), 'cskipdict', 'headers') 7 | skiplist_header_file = os.path.join(src_root, 'skiplist.cdef') 8 | 9 | ffibuilder = FFI() 10 | 11 | with open(skiplist_header_file) as header_file: 12 | ffibuilder.cdef(header_file.read()) 13 | 14 | ffibuilder.set_source('cskipdict._skiplist', ''' 15 | extern "C" { 16 | #define SKIPLIST_IMPLEMENTATION 17 | #include "skiplist-config.h" 18 | #include "skiplist-template.h" 19 | int skiplist_int64_cmp(int64_t a, int64_t b, void *userdata); 20 | 21 | int skiplist_int64_cmp(int64_t a, int64_t b, void *userdata) { 22 | return (a > b) - (a < b); 23 | } 24 | } 25 | ''', 26 | include_dirs=[src_root], 27 | sources=[os.path.join(src_root, 'random_uniform.cpp')], 28 | extra_compile_args=['-std=c++11'], 29 | source_extension='.cpp') 30 | 31 | 32 | if __name__ == '__main__': 33 | ffibuilder.compile(verbose=True) 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | cskipdict/_skiplist.* 2 | cskipdict/headers/random_uniform.o 3 | 4 | # Finder files 5 | .DS_Store 6 | 7 | # Byte-compiled / optimized / DLL files 8 | __pycache__/ 9 | *.pyc 10 | *$py.class 11 | 12 | # C extensions 13 | *.so 14 | 15 | # Distribution / packaging 16 | .Python 17 | env/ 18 | build/ 19 | develop-eggs/ 20 | dist/ 21 | downloads/ 22 | eggs/ 23 | .eggs/ 24 | lib/ 25 | lib64/ 26 | parts/ 27 | sdist/ 28 | var/ 29 | *.egg-info/ 30 | .installed.cfg 31 | *.egg 32 | 33 | # PyInstaller 34 | # Usually these files are written by a python script from a template 35 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 36 | *.manifest 37 | *.spec 38 | 39 | # Installer logs 40 | pip-log.txt 41 | pip-delete-this-directory.txt 42 | 43 | # Unit test / coverage reports 44 | htmlcov/ 45 | .tox/ 46 | .coverage 47 | .coverage.* 48 | .cache 49 | nosetests.xml 50 | coverage.xml 51 | *,cover 52 | .hypothesis/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | 62 | # Flask stuff: 63 | instance/ 64 | .webassets-cache 65 | 66 | # Scrapy stuff: 67 | .scrapy 68 | 69 | # Sphinx documentation 70 | docs/_build/ 71 | 72 | # PyBuilder 73 | target/ 74 | 75 | # IPython Notebook 76 | .ipynb_checkpoints 77 | 78 | # pyenv 79 | .python-version 80 | 81 | # celery beat schedule file 82 | celerybeat-schedule 83 | 84 | # dotenv 85 | .env 86 | 87 | # virtualenv 88 | .venv/ 89 | venv/ 90 | ENV/ 91 | 92 | # Spyder project settings 93 | .spyderproject 94 | 95 | # Rope project settings 96 | .ropeproject 97 | -------------------------------------------------------------------------------- /test/test_skipdict.py: -------------------------------------------------------------------------------- 1 | from nose.tools import assert_raises 2 | 3 | from cskipdict import SkipDict 4 | 5 | 6 | def check_dict(skip_dict, expected): 7 | assert len(skip_dict) == len(expected) 8 | assert len(skip_dict._SkipDict__pointers) == len(set(expected.values())) 9 | for k in expected: 10 | assert k in skip_dict 11 | assert skip_dict[k] == expected[k] 12 | assert skip_dict.get(k) == expected[k] 13 | assert skip_dict.get(k + 1000) is None 14 | assert skip_dict.get(k + 1000, None) is None 15 | assert skip_dict.get(k + 1000, 'Hello') == 'Hello' 16 | # Ensure iteration order is always increasing 17 | last_k = None 18 | for k in skip_dict: 19 | assert k in expected 20 | if last_k is not None: 21 | assert k > last_k 22 | last_k = k 23 | 24 | if expected: 25 | min_key = min(expected.keys()) 26 | assert skip_dict.minimum() == (min_key, expected[min_key]) 27 | 28 | max_key = max(expected.keys()) 29 | assert skip_dict.maximum() == (max_key, expected[max_key]) 30 | else: 31 | assert skip_dict.minimum() is None 32 | assert skip_dict.maximum() is None 33 | 34 | 35 | def test_smoke(): 36 | sd = SkipDict() 37 | check_dict(sd, {}) 38 | sd[123] = 'foo' 39 | check_dict(sd, {123: 'foo'}) 40 | sd[456] = 'bar' 41 | check_dict(sd, {123: 'foo', 456: 'bar'}) 42 | sd[123] = 'baz' 43 | check_dict(sd, {123: 'baz', 456: 'bar'}) 44 | assert sd.pop(456) == 'bar' 45 | check_dict(sd, {123: 'baz'}) 46 | with assert_raises(KeyError): 47 | sd.pop(456) 48 | assert sd.pop(456, None) is None 49 | assert sd.pop(456, 'Hello') == 'Hello' 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cSkipDict 2 | 3 | A fast, CFFI-based SkipDict implementation for Python. 4 | 5 | `SkipDict` instances are [Skip List][]-based associative arrays ('dictionaries' 6 | in Python terms), mapping 64-bit signed integers to arbitrary Python objects. 7 | Keys are always stored and returned in order, while maintaining average-case 8 | *O(log n)* search, insert and delete times, and average-case space consumption 9 | of *O(n)*. 10 | 11 | This libary uses Peter Cannici's public-domain [skiplist.h][] C library, 12 | adapting it slightly for use with Python and CFFI. 13 | 14 | [Skip List]: https://en.wikipedia.org/wiki/Skip_list 15 | [skiplist.h]: https://github.com/alpha123/skiplist.h 16 | 17 | 18 | ## Installation 19 | 20 | Installation on most systems should be pretty easy: 21 | 22 | $ pip install cskipdict 23 | 24 | 25 | ## Usage 26 | 27 | You can use it just like a normal `dict`, except that all keys should be 28 | integers between -2^63^ and +2^63^ - 1 inclusive. 29 | 30 | >>> from cskipdict import SkipDict 31 | >>> d = SkipDict() 32 | >>> d 33 | SkipDict({}) 34 | >>> len(d) 35 | 0 36 | >>> d[123] = 'abc' 37 | >>> d 38 | SkipDict({123: 'abc'}) 39 | >>> d[420] = 'def' 40 | >>> d 41 | SkipDict({123: 'abc', 420: 'def'}) 42 | >>> d[69] = 'foo' 43 | >>> d 44 | SkipDict({69: 'foo', 123: 'abc', 420: 'def'}) 45 | 46 | Iteration will always happen in ascending order of the keys: 47 | 48 | >>> for k, v in d.iteritems(): 49 | ... print((k, v)) 50 | (69, 'foo') 51 | (123, 'abc') 52 | (420, 'def') 53 | 54 | 55 | ## Unlicense 56 | 57 | This is free and unencumbered software released into the public domain. 58 | 59 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 60 | software, either in source code form or as a compiled binary, for any purpose, 61 | commercial or non-commercial, and by any means. 62 | 63 | In jurisdictions that recognize copyright laws, the author or authors of this 64 | software dedicate any and all copyright interest in the software to the public 65 | domain. We make this dedication for the benefit of the public at large and to 66 | the detriment of our heirs and successors. We intend this dedication to be an 67 | overt act of relinquishment in perpetuity of all present and future rights to 68 | this software under copyright law. 69 | 70 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 71 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 72 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 73 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 74 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 75 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 76 | 77 | For more information, please refer to 78 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | cSkipDict 2 | ========= 3 | 4 | A fast, CFFI-based SkipDict implementation for Python. 5 | 6 | ``SkipDict`` instances are `Skip 7 | List `__-based associative 8 | arrays ('dictionaries' in Python terms), mapping 64-bit signed integers 9 | to arbitrary Python objects. Keys are always stored and returned in 10 | order, while maintaining average-case *O(log n)* search, insert and 11 | delete times, and average-case space consumption of *O(n)*. 12 | 13 | This libary uses Peter Cannici's public-domain 14 | `skiplist.h `__ C library, 15 | adapting it slightly for use with Python and CFFI. 16 | 17 | Installation 18 | ------------ 19 | 20 | Installation on most systems should be pretty easy: 21 | 22 | :: 23 | 24 | $ pip install cskipdict 25 | 26 | Usage 27 | ----- 28 | 29 | You can use it just like a normal ``dict``, except that all keys should 30 | be integers between -2:sup:`63` and +2\ :sup:`63` - 1 inclusive. 31 | 32 | :: 33 | 34 | >>> from cskipdict import SkipDict 35 | >>> d = SkipDict() 36 | >>> d 37 | SkipDict({}) 38 | >>> len(d) 39 | 0 40 | >>> d[123] = 'abc' 41 | >>> d 42 | SkipDict({123: 'abc'}) 43 | >>> d[420] = 'def' 44 | >>> d 45 | SkipDict({123: 'abc', 420: 'def'}) 46 | >>> d[69] = 'foo' 47 | >>> d 48 | SkipDict({69: 'foo', 123: 'abc', 420: 'def'}) 49 | 50 | Iteration will always happen in ascending order of the keys: 51 | 52 | :: 53 | 54 | >>> for k, v in d.iteritems(): 55 | ... print((k, v)) 56 | (69, 'foo') 57 | (123, 'abc') 58 | (420, 'def') 59 | 60 | Unlicense 61 | --------- 62 | 63 | This is free and unencumbered software released into the public domain. 64 | 65 | Anyone is free to copy, modify, publish, use, compile, sell, or 66 | distribute this software, either in source code form or as a compiled 67 | binary, for any purpose, commercial or non-commercial, and by any means. 68 | 69 | In jurisdictions that recognize copyright laws, the author or authors of 70 | this software dedicate any and all copyright interest in the software to 71 | the public domain. We make this dedication for the benefit of the public 72 | at large and to the detriment of our heirs and successors. We intend 73 | this dedication to be an overt act of relinquishment in perpetuity of 74 | all present and future rights to this software under copyright law. 75 | 76 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 77 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 78 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 79 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 80 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 81 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 82 | DEALINGS IN THE SOFTWARE. 83 | 84 | For more information, please refer to http://unlicense.org/ 85 | -------------------------------------------------------------------------------- /cskipdict/__init__.py: -------------------------------------------------------------------------------- 1 | from ._skiplist import ffi, lib 2 | 3 | 4 | _undefined = object() 5 | 6 | 7 | class ObjectRef(object): 8 | 9 | def __init__(self): 10 | self.__alloc = ffi.new('uintptr_t **') 11 | self.ref = ffi.cast('void **', self.__alloc) 12 | 13 | @property 14 | def handle(self): 15 | return ffi.cast('void *', self.ref[0]) 16 | 17 | @property 18 | def deref(self): 19 | return ffi.from_handle(self.handle) 20 | 21 | 22 | class SkipDict(object): 23 | 24 | __slots__ = ('__list', '__pointers') 25 | 26 | def __init__(self): 27 | self.__list = ffi.new('skiplist_skiplist *') 28 | self.__pointers = set() 29 | init_err = lib.skiplist_init( 30 | self.__list, 31 | ffi.addressof(lib, 'skiplist_int64_cmp'), 32 | # Null `void *userdata` for cmp and malloc functions. 33 | ffi.NULL, 34 | ffi.NULL) 35 | if init_err: 36 | raise RuntimeError("Error initializing skiplist") 37 | 38 | def __del__(self): 39 | lib.skiplist_free(self.__list) 40 | 41 | def __repr__(self): 42 | return 'SkipDict({' + ', '.join('{!r}: {!r}'.format(k, v) for k, v in self.iteritems()) + '})' 43 | 44 | def __len__(self): 45 | return lib.skiplist_size(self.__list) 46 | 47 | def __contains__(self, k): 48 | return bool(lib.skiplist_find(self.__list, k, ffi.NULL)) 49 | 50 | def __nonzero__(self): 51 | return len(self) > 0 52 | 53 | def __getitem__(self, k): 54 | result = lib.skiplist_get(self.__list, k, ffi.NULL) 55 | if result == ffi.NULL: 56 | raise KeyError(k) 57 | return ffi.from_handle(result) 58 | 59 | def get(self, k, default=None): 60 | result = lib.skiplist_get(self.__list, k, ffi.NULL) 61 | if result == ffi.NULL: 62 | return default 63 | return ffi.from_handle(result) 64 | 65 | def __setitem__(self, k, value): 66 | handle = ffi.new_handle(value) 67 | self.__pointers.add(handle) 68 | prev = ObjectRef() 69 | did_overwrite = lib.skiplist_insert(self.__list, k, handle, prev.ref) 70 | if did_overwrite: 71 | self.__pointers.discard(prev.handle) 72 | 73 | def __delitem__(self, k): 74 | prev = ObjectRef() 75 | did_remove = lib.skiplist_remove(self.__list, k, prev.ref) 76 | if not did_remove: 77 | raise KeyError(k) 78 | self.__pointers.discard(prev.handle) 79 | 80 | def pop(self, k, default=_undefined): 81 | prev = ObjectRef() 82 | did_remove = lib.skiplist_remove(self.__list, k, prev.ref) 83 | if not did_remove: 84 | if default is _undefined: 85 | raise KeyError(k) 86 | return default 87 | popped = prev.deref 88 | self.__pointers.discard(prev.handle) 89 | return popped 90 | 91 | def __iter__(self): 92 | for k, v in self.iteritems(): 93 | yield k 94 | 95 | def iteritems(self): 96 | node = self.__list.head 97 | while node.next[0]: 98 | yield (node.next[0].key, ffi.from_handle(node.next[0].val)) 99 | node = node.next[0] 100 | 101 | def minimum(self): 102 | key_pointer = ffi.new('int64_t *') 103 | val_ref = ObjectRef() 104 | has_result = lib.skiplist_min(self.__list, key_pointer, val_ref.ref) 105 | if not has_result: 106 | return None 107 | return (key_pointer[0], val_ref.deref) 108 | 109 | def maximum(self): 110 | key_pointer = ffi.new('int64_t *') 111 | val_ref = ObjectRef() 112 | has_result = lib.skiplist_max(self.__list, key_pointer, val_ref.ref) 113 | if not has_result: 114 | return None 115 | return (key_pointer[0], val_ref.deref) 116 | 117 | def pop_minimum(self): 118 | key_pointer = ffi.new('int64_t *') 119 | val_ref = ObjectRef() 120 | has_result = lib.skiplist_pop(self.__list, key_pointer, val_ref.ref) 121 | if not has_result: 122 | return None 123 | output = (key_pointer[0], val_ref.deref) 124 | self.__pointers.discard(val_ref.handle) 125 | return output 126 | 127 | def pop_maximum(self): 128 | key_pointer = ffi.new('int64_t *') 129 | val_ref = ObjectRef() 130 | has_result = lib.skiplist_shift(self.__list, key_pointer, val_ref.ref) 131 | if not has_result: 132 | return None 133 | output = (key_pointer[0], val_ref.deref) 134 | self.__pointers.discard(val_ref.handle) 135 | return output 136 | -------------------------------------------------------------------------------- /cskipdict/headers/skiplist.cdef: -------------------------------------------------------------------------------- 1 | /* vi: set expandttab softtabstop=4 tabstop=4 shiftwidth=4 */ 2 | 3 | /** 4 | * This is free and unencumbered software released into the public domain. 5 | * 6 | * Anyone is free to copy, modify, publish, use, compile, sell, or 7 | * distribute this software, either in source code form or as a compiled 8 | * binary, for any purpose, commercial or non-commercial, and by any 9 | * means. 10 | * 11 | * In jurisdictions that recognize copyright laws, the author or authors 12 | * of this software dedicate any and all copyright interest in the 13 | * software to the public domain. We make this dedication for the benefit 14 | * of the public at large and to the detriment of our heirs and 15 | * successors. We intend this dedication to be an overt act of 16 | * relinquishment in perpetuity of all present and future rights to this 17 | * software under copyright law. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 22 | * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 23 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 24 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | * OTHER DEALINGS IN THE SOFTWARE. 26 | * 27 | * For more information, please refer to 28 | */ 29 | 30 | /** 31 | * 1. Create a guarded header that defines SKIPLIST_KEY and SKIPLIST_VALUE 32 | * and includes skiplist.h. Optionally define SKIPLIST_NAMESPACE. 33 | * 2. Define SKIPLIST_IMPLEMENTATION somewhere in your program above 34 | * your custom header include. 35 | * 3. Repeat for any different key/value pair types you need. Be sure to 36 | * define different SKIPLIST_NAMESPACE values and define SKIPLIST_IMPLEMENTATION 37 | * once for each key/value type pair. 38 | * 4. Other options: 39 | * - SKIPLIST_MAX_LEVELS - 33 by default. 40 | * - SKIPLIST_MALLOC & SKIPLIST_FREE - wrappers for stdlib malloc/free by default. 41 | * Both are passed a void \* data pointer (for memory pool, gc context, etc). 42 | * - SKIPLIST_HEIGHT - a macro function taking `(SKIPLIST_KEY)` which should 43 | * produce an unsigned int 0 <= height < SKIPLIST_MAX_LEVELS. By default 44 | * this is implemented using `std::uniform_int_distribution` in the C++ 45 | * standard library, but you might want to replace it with a key-dependent 46 | * function. 47 | * - SKIPLIST_STATIC - if defined, declare all public functions static 48 | * (make skiplist local to the file it's included from). 49 | * - SKIPLIST_EXTERN - 'extern' by default; define to change calling convention 50 | * or linkage etc. 51 | * 52 | * Example: 53 | * 54 | * // skiplist_str_int.h 55 | * // Include this file wherever you need a string -> int skiplist 56 | * #ifndef SKIPLIST_STR_INT_H 57 | * #define SKIPLIST_STR_INT_H 58 | * 59 | * #define SKIPLIST_KEY const char * 60 | * #define SKIPLIST_VALUE int 61 | * #define SKIPLIST_NAMESPACE sl_strint_ 62 | * #include "skiplist.h" 63 | * 64 | * #endif 65 | * 66 | * // program.c 67 | * // short test drive program 68 | * #include 69 | * #include 70 | * #include 71 | * #define SKIPLIST_IMPLEMENTATION 72 | * #include "skiplist_str_int.h" 73 | * 74 | * int cmp(const char *a, const char *b, void *userdata) { 75 | * return strcmp(a, b); 76 | * } 77 | * 78 | * int iter(const char *key, int val, void *userdata) { 79 | * printf("%s = %d\n", key, val); 80 | * return 0; 81 | * } 82 | * 83 | * int main(int argc, const char **argv) { 84 | * sl_strint_skiplist list; 85 | * int err = sl_strint_init(&list, cmp, NULL); 86 | * assert(err == 0); 87 | * 88 | * sl_strint_insert(&list, "a", 1, NULL); 89 | * sl_strint_insert(&list, "c", 3, NULL); 90 | * sl_strint_insert(&list, "b", 2, NULL); 91 | * 92 | * short has_b = sl_strint_find(&list, "b", NULL), // => 1 93 | * has_d = sl_strint_find(&list, "d", NULL); // => 0 94 | * 95 | * int a_val; 96 | * short exists = sl_strint_find(&list, "a", &a_val); 97 | * if (exists) 98 | * printf("a = %d\n", a_val); 99 | * else 100 | * puts("a does not exist"); 101 | * 102 | * int default_val = 10; 103 | * int d_val = sl_strint_get(&list, "d", default_val); // => 10 104 | * 105 | * sl_strint_iter(&list, iter, NULL); 106 | * // Prints a = 1, b = 2, c = 3 107 | * 108 | * int b_val; 109 | * short existed = sl_strint_remove(&list, "b", &b_val); 110 | * if (existed) 111 | * print("b used to be %d, but now it is no more\n", b_val); 112 | * else 113 | * puts("b was only an illusion, a fleeting glimpse of a dream"); 114 | * 115 | * sl_strint_free(&list); 116 | * return 0; 117 | * } 118 | */ 119 | typedef int (* skiplist_cmp_fn)(int64_t, int64_t, void *); 120 | typedef int (* skiplist_iter_fn)(int64_t, void *, void *); 121 | 122 | uint32_t random_uniform(uint32_t max_plus_one); 123 | 124 | typedef struct skiplist__node { 125 | unsigned int height; 126 | int64_t key; 127 | void * val; 128 | struct skiplist__node *prev; 129 | struct skiplist__node *next[33]; 130 | } skiplist_node; 131 | 132 | typedef struct { 133 | unsigned long size; 134 | unsigned int highest; 135 | skiplist_cmp_fn cmp; 136 | void *cmp_udata; 137 | void *mem_udata; 138 | skiplist_node *head; 139 | } skiplist_skiplist; 140 | 141 | /* Must be called prior to using any other functions on a skiplist. 142 | * @list a pointer to the skiplist to initialize 143 | * @cmp the comparator function to use to order nodes 144 | * @cmp_udata Opaque pointer to pass to cmp 145 | * @mem_udata Opaque pointer to pass to the SKIPLIST_MALLOC and 146 | * SKIPLIST_FREE macros. Unused by default, but custom 147 | * memory allocators may use it. 148 | * 149 | * @return 0 if successful and nonzero if something failed 150 | */ 151 | 152 | int skiplist_init(skiplist_skiplist *list, skiplist_cmp_fn cmp, void *cmp_udata, void *mem_udata); 153 | 154 | /* Free memory used by a skiplist. 155 | * @list a pointer to the skiplist to be freed 156 | */ 157 | 158 | void skiplist_free(skiplist_skiplist *list); 159 | 160 | /* Sets a value in the skiplist. 161 | * @list An initialized skiplist 162 | * @key Associate the value with this key 163 | * @val Value 164 | * @prior If non-NULL, put the prior value at this location. Not touched if 165 | * there was no prior value associated with this key. 166 | * 167 | * If a value already exists at that key, 168 | * overwrite it and stick the prior value in this function's out parameter. 169 | * 170 | * @return 0 if no value was at this key, 1 if a value did exist and was 171 | * overwritten. 172 | */ 173 | 174 | short skiplist_insert(skiplist_skiplist *list, int64_t key, void * val, void * *prior); 175 | 176 | /* Gets a value associated with a key. 177 | * @list An initialized skiplist 178 | * @key Get the value associated with this key 179 | * @out If a value exists, store it at this location. 180 | * If this parameter is NULL, nothing is stored. 181 | * 182 | * @return 0 if the key does not exist, 1 if it does 183 | */ 184 | 185 | short skiplist_find(skiplist_skiplist *list, int64_t key, void * *out); 186 | 187 | /* Gets a value associated with a key, or a default value. 188 | * @list An initialized skiplist 189 | * @key Get the value associated with this key 190 | * @default_val If the key does not exist in this list, 191 | * return this value instead. 192 | * 193 | * @return The value associated with the key or default_val if the 194 | * key is not set. 195 | */ 196 | 197 | void * skiplist_get(skiplist_skiplist *list, int64_t key, void * default_val); 198 | 199 | /* Removes a key/value pair from this list. 200 | * @list An initialized skiplist 201 | * @key Key indicating the key/value pair to remove 202 | * @out If non-NULL and the key existed, store the old value at this location 203 | * 204 | * @return 1 if the key used to be in the list (and was thus removed), 205 | * 0 if it was never there 206 | */ 207 | 208 | short skiplist_remove(skiplist_skiplist *list, int64_t key, void * *out); 209 | 210 | /* Iterates through all key/value pairs in order. 211 | * @list An initialized skiplist 212 | * @iter An iterator function to call for each key/value pair 213 | * @userdata An opaque pointer to pass to `iter`. 214 | * 215 | * If `iter` returns a non-zero value, stop the iteration and return 216 | * that value. 217 | * 218 | * @return The first non-zero result of `iter` or 0 if `iter` always 219 | * returned 0. 220 | */ 221 | 222 | int skiplist_iter(skiplist_skiplist *list, skiplist_iter_fn iter, void *userdata); 223 | 224 | /* Returns the number of items in the skiplist 225 | * @list An initialized skiplist 226 | * 227 | * @return The number of key/value pairs in the skiplist 228 | */ 229 | 230 | unsigned long skiplist_size(skiplist_skiplist *list); 231 | 232 | /* Returns the minimum key and value in this list. 233 | * @list An initalized skiplist 234 | * @key_out Set to the smallest key if non-NULL and the list is not empty 235 | * @val_out Set to the value associated with the smallest key if non-NULL 236 | * and the list is not empty. 237 | * 238 | * @return 0 if the list is empty and 1 if it is not 239 | */ 240 | 241 | short skiplist_min(skiplist_skiplist *list, int64_t *key_out, void * *val_out); 242 | 243 | /* Returns the maximum key and value in this list. 244 | * @list An initalized skiplist 245 | * @key_out Set to the largest key if non-NULL and the list is not empty 246 | * @val_out Set to the value associated with the largest key if non-NULL 247 | * and the list is not empty. 248 | * 249 | * @return 0 if the list is empty and 1 if it is not 250 | */ 251 | 252 | short skiplist_max(skiplist_skiplist *list, int64_t *key_out, void * *val_out); 253 | 254 | /* Removes and returns the minimum key/value pair from a list. 255 | * @list An initialized skiplist 256 | * @key_out Set to the smallest key if non-NULL and the list is not empty 257 | * @val_out Set to the value associated with the smallest key if non-NULL 258 | * and the list is not empty. 259 | * 260 | * @return 0 if the list was already empty and 1 if it was not 261 | */ 262 | 263 | short skiplist_pop(skiplist_skiplist *list, int64_t *key_out, void * *val_out); 264 | 265 | /* Removes and returns the maximum key/value pair from a list. 266 | * @list An initialized skiplist 267 | * @key_out Set to the largest key if non-NULL and the list is not empty 268 | * @val_out Set to the value associated with the largest key if non-NULL 269 | * and the list is not empty. 270 | * 271 | * @return 0 if the list was already empty and 1 if it was not 272 | */ 273 | 274 | short skiplist_shift(skiplist_skiplist *list, int64_t *key_out, void * *val_out); 275 | 276 | int skiplist_int64_cmp(int64_t a, int64_t b, void *userdata); 277 | -------------------------------------------------------------------------------- /cskipdict/headers/skiplist-template.h: -------------------------------------------------------------------------------- 1 | /* vi: set expandttab softtabstop=4 tabstop=4 shiftwidth=4 */ 2 | 3 | /** 4 | * This is free and unencumbered software released into the public domain. 5 | * 6 | * Anyone is free to copy, modify, publish, use, compile, sell, or 7 | * distribute this software, either in source code form or as a compiled 8 | * binary, for any purpose, commercial or non-commercial, and by any 9 | * means. 10 | * 11 | * In jurisdictions that recognize copyright laws, the author or authors 12 | * of this software dedicate any and all copyright interest in the 13 | * software to the public domain. We make this dedication for the benefit 14 | * of the public at large and to the detriment of our heirs and 15 | * successors. We intend this dedication to be an overt act of 16 | * relinquishment in perpetuity of all present and future rights to this 17 | * software under copyright law. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 22 | * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 23 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 24 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | * OTHER DEALINGS IN THE SOFTWARE. 26 | * 27 | * For more information, please refer to 28 | */ 29 | 30 | /** 31 | * 1. Create a guarded header that defines SKIPLIST_KEY and SKIPLIST_VALUE 32 | * and includes skiplist.h. Optionally define SKIPLIST_NAMESPACE. 33 | * 2. Define SKIPLIST_IMPLEMENTATION somewhere in your program above 34 | * your custom header include. 35 | * 3. Repeat for any different key/value pair types you need. Be sure to 36 | * define different SKIPLIST_NAMESPACE values and define SKIPLIST_IMPLEMENTATION 37 | * once for each key/value type pair. 38 | * 4. Other options: 39 | * - SKIPLIST_MAX_LEVELS - 33 by default. 40 | * - SKIPLIST_MALLOC & SKIPLIST_FREE - wrappers for stdlib malloc/free by default. 41 | * Both are passed a void \* data pointer (for memory pool, gc context, etc). 42 | * - SKIPLIST_HEIGHT - a macro function taking `(SKIPLIST_KEY)` which should 43 | * produce an unsigned int 0 <= height < SKIPLIST_MAX_LEVELS. By default 44 | * this is implemented using `std::uniform_int_distribution` in the C++ 45 | * standard library, but you might want to replace it with a key-dependent 46 | * function. 47 | * - SKIPLIST_STATIC - if defined, declare all public functions static 48 | * (make skiplist local to the file it's included from). 49 | * - SKIPLIST_EXTERN - 'extern' by default; define to change calling convention 50 | * or linkage etc. 51 | * 52 | * Example: 53 | * 54 | * // skiplist_str_int.h 55 | * // Include this file wherever you need a string -> int skiplist 56 | * #ifndef SKIPLIST_STR_INT_H 57 | * #define SKIPLIST_STR_INT_H 58 | * 59 | * #define SKIPLIST_KEY const char * 60 | * #define SKIPLIST_VALUE int 61 | * #define SKIPLIST_NAMESPACE sl_strint_ 62 | * #include "skiplist.h" 63 | * 64 | * #endif 65 | * 66 | * // program.c 67 | * // short test drive program 68 | * #include 69 | * #include 70 | * #include 71 | * #define SKIPLIST_IMPLEMENTATION 72 | * #include "skiplist_str_int.h" 73 | * 74 | * int cmp(const char *a, const char *b, void *userdata) { 75 | * return strcmp(a, b); 76 | * } 77 | * 78 | * int iter(const char *key, int val, void *userdata) { 79 | * printf("%s = %d\n", key, val); 80 | * return 0; 81 | * } 82 | * 83 | * int main(int argc, const char **argv) { 84 | * sl_strint_skiplist list; 85 | * int err = sl_strint_init(&list, cmp, NULL); 86 | * assert(err == 0); 87 | * 88 | * sl_strint_insert(&list, "a", 1, NULL); 89 | * sl_strint_insert(&list, "c", 3, NULL); 90 | * sl_strint_insert(&list, "b", 2, NULL); 91 | * 92 | * short has_b = sl_strint_find(&list, "b", NULL), // => 1 93 | * has_d = sl_strint_find(&list, "d", NULL); // => 0 94 | * 95 | * int a_val; 96 | * short exists = sl_strint_find(&list, "a", &a_val); 97 | * if (exists) 98 | * printf("a = %d\n", a_val); 99 | * else 100 | * puts("a does not exist"); 101 | * 102 | * int default_val = 10; 103 | * int d_val = sl_strint_get(&list, "d", default_val); // => 10 104 | * 105 | * sl_strint_iter(&list, iter, NULL); 106 | * // Prints a = 1, b = 2, c = 3 107 | * 108 | * int b_val; 109 | * short existed = sl_strint_remove(&list, "b", &b_val); 110 | * if (existed) 111 | * print("b used to be %d, but now it is no more\n", b_val); 112 | * else 113 | * puts("b was only an illusion, a fleeting glimpse of a dream"); 114 | * 115 | * sl_strint_free(&list); 116 | * return 0; 117 | * } 118 | */ 119 | 120 | #ifndef SKIPLIST_MALLOC 121 | #ifdef SKIPLIST_IMPLEMENTATION 122 | #include 123 | #endif 124 | #define SKIPLIST_MALLOC(udata, sz) malloc((sz)) 125 | #define SKIPLIST_FREE(udata, ptr) free((ptr)) 126 | #endif 127 | 128 | #ifndef SKIPLIST_HEIGHT 129 | #ifdef SKIPLIST_IMPLEMENTATION 130 | #include 131 | #endif 132 | #include "random_uniform.h" 133 | #define SKIPLIST_HEIGHT(key) random_uniform(SKIPLIST_MAX_LEVELS); 134 | #endif 135 | 136 | #if !defined(SKIPLIST_KEY) || !defined(SKIPLIST_VALUE) 137 | #error Please define SKIPLIST_KEY and SKIPLIST_VALUE before including \ 138 | this file. See the comments at the top for usage instructions. 139 | #endif 140 | 141 | #ifndef SKIPLIST_NAMESPACE 142 | #define SKIPLIST_NAMESPACE sl_ 143 | #endif 144 | 145 | #if !defined(SKIPLIST_EXTERN) && !defined(SKIPLIST_STATIC) 146 | #define SKIPLIST_EXTERN extern 147 | #elif defined(SKIPLIST_STATIC) 148 | #define SKIPLIST_EXTERN static 149 | #endif 150 | 151 | #ifndef SKIPLIST_MAX_LEVELS 152 | #define SKIPLIST_MAX_LEVELS 33 153 | #endif 154 | 155 | #define SL_PASTE_(x,y) x ## y 156 | #define SL_CAT_(x,y) SL_PASTE_(x,y) 157 | #define SKIPLIST_NAME(name) SL_CAT_(SKIPLIST_NAMESPACE,name) 158 | 159 | #define SL_NODE SKIPLIST_NAME(node) 160 | #define SL_LIST SKIPLIST_NAME(skiplist) 161 | #define SL_CMP_FN SKIPLIST_NAME(cmp_fn) 162 | #define SL_ITER_FN SKIPLIST_NAME(iter_fn) 163 | #define SL_KEY SKIPLIST_KEY 164 | #define SL_VAL SKIPLIST_VALUE 165 | 166 | 167 | typedef int (* SL_CMP_FN)(SL_KEY, SL_KEY, void *); 168 | typedef int (* SL_ITER_FN)(SL_KEY, SL_VAL, void *); 169 | 170 | typedef struct SKIPLIST_NAME(_node) { 171 | unsigned int height; 172 | SL_KEY key; 173 | SL_VAL val; 174 | struct SKIPLIST_NAME(_node) *prev; 175 | struct SKIPLIST_NAME(_node) *next[SKIPLIST_MAX_LEVELS]; 176 | } SL_NODE; 177 | 178 | typedef struct { 179 | unsigned long size; 180 | unsigned int highest; 181 | SL_CMP_FN cmp; 182 | void *cmp_udata; 183 | void *mem_udata; 184 | SKIPLIST_NAME(node) *head; 185 | } SL_LIST; 186 | 187 | /* Must be called prior to using any other functions on a skiplist. 188 | * @list a pointer to the skiplist to initialize 189 | * @cmp the comparator function to use to order nodes 190 | * @cmp_udata Opaque pointer to pass to cmp 191 | * @mem_udata Opaque pointer to pass to the SKIPLIST_MALLOC and 192 | * SKIPLIST_FREE macros. Unused by default, but custom 193 | * memory allocators may use it. 194 | * 195 | * @return 0 if successful and nonzero if something failed 196 | */ 197 | SKIPLIST_EXTERN 198 | int SKIPLIST_NAME(init)(SL_LIST *list, SL_CMP_FN cmp, void *cmp_udata, void *mem_udata); 199 | 200 | /* Free memory used by a skiplist. 201 | * @list a pointer to the skiplist to be freed 202 | */ 203 | SKIPLIST_EXTERN 204 | void SKIPLIST_NAME(free)(SL_LIST *list); 205 | 206 | /* Sets a value in the skiplist. 207 | * @list An initialized skiplist 208 | * @key Associate the value with this key 209 | * @val Value 210 | * @prior If non-NULL, put the prior value at this location. Not touched if 211 | * there was no prior value associated with this key. 212 | * 213 | * If a value already exists at that key, 214 | * overwrite it and stick the prior value in this function's out parameter. 215 | * 216 | * @return 0 if no value was at this key, 1 if a value did exist and was 217 | * overwritten. 218 | */ 219 | SKIPLIST_EXTERN 220 | short SKIPLIST_NAME(insert)(SL_LIST *list, SL_KEY key, SL_VAL val, SL_VAL *prior); 221 | 222 | /* Gets a value associated with a key. 223 | * @list An initialized skiplist 224 | * @key Get the value associated with this key 225 | * @out If a value exists, store it at this location. 226 | * If this parameter is NULL, nothing is stored. 227 | * 228 | * @return 0 if the key does not exist, 1 if it does 229 | */ 230 | SKIPLIST_EXTERN 231 | short SKIPLIST_NAME(find)(SL_LIST *list, SL_KEY key, SL_VAL *out); 232 | 233 | /* Gets a value associated with a key, or a default value. 234 | * @list An initialized skiplist 235 | * @key Get the value associated with this key 236 | * @default_val If the key does not exist in this list, 237 | * return this value instead. 238 | * 239 | * @return The value associated with the key or default_val if the 240 | * key is not set. 241 | */ 242 | SKIPLIST_EXTERN 243 | SL_VAL SKIPLIST_NAME(get)(SL_LIST *list, SL_KEY key, SL_VAL default_val); 244 | 245 | /* Removes a key/value pair from this list. 246 | * @list An initialized skiplist 247 | * @key Key indicating the key/value pair to remove 248 | * @out If non-NULL and the key existed, store the old value at this location 249 | * 250 | * @return 1 if the key used to be in the list (and was thus removed), 251 | * 0 if it was never there 252 | */ 253 | SKIPLIST_EXTERN 254 | short SKIPLIST_NAME(remove)(SL_LIST *list, SL_KEY key, SL_VAL *out); 255 | 256 | /* Iterates through all key/value pairs in order. 257 | * @list An initialized skiplist 258 | * @iter An iterator function to call for each key/value pair 259 | * @userdata An opaque pointer to pass to `iter`. 260 | * 261 | * If `iter` returns a non-zero value, stop the iteration and return 262 | * that value. 263 | * 264 | * @return The first non-zero result of `iter` or 0 if `iter` always 265 | * returned 0. 266 | */ 267 | SKIPLIST_EXTERN 268 | int SKIPLIST_NAME(iter)(SL_LIST *list, SL_ITER_FN iter, void *userdata); 269 | 270 | /* Returns the number of items in the skiplist 271 | * @list An initialized skiplist 272 | * 273 | * @return The number of key/value pairs in the skiplist 274 | */ 275 | SKIPLIST_EXTERN 276 | unsigned long SKIPLIST_NAME(size)(SL_LIST *list); 277 | 278 | /* Returns the minimum key and value in this list. 279 | * @list An initalized skiplist 280 | * @key_out Set to the smallest key if non-NULL and the list is not empty 281 | * @val_out Set to the value associated with the smallest key if non-NULL 282 | * and the list is not empty. 283 | * 284 | * @return 0 if the list is empty and 1 if it is not 285 | */ 286 | SKIPLIST_EXTERN 287 | short SKIPLIST_NAME(min)(SL_LIST *list, SL_KEY *key_out, SL_VAL *val_out); 288 | 289 | /* Returns the maximum key and value in this list. 290 | * @list An initalized skiplist 291 | * @key_out Set to the largest key if non-NULL and the list is not empty 292 | * @val_out Set to the value associated with the largest key if non-NULL 293 | * and the list is not empty. 294 | * 295 | * @return 0 if the list is empty and 1 if it is not 296 | */ 297 | SKIPLIST_EXTERN 298 | short SKIPLIST_NAME(max)(SL_LIST *list, SL_KEY *key_out, SL_VAL *val_out); 299 | 300 | /* Removes and returns the minimum key/value pair from a list. 301 | * @list An initialized skiplist 302 | * @key_out Set to the smallest key if non-NULL and the list is not empty 303 | * @val_out Set to the value associated with the smallest key if non-NULL 304 | * and the list is not empty. 305 | * 306 | * @return 0 if the list was already empty and 1 if it was not 307 | */ 308 | SKIPLIST_EXTERN 309 | short SKIPLIST_NAME(pop)(SL_LIST *list, SL_KEY *key_out, SL_VAL *val_out); 310 | 311 | /* Removes and returns the maximum key/value pair from a list. 312 | * @list An initialized skiplist 313 | * @key_out Set to the largest key if non-NULL and the list is not empty 314 | * @val_out Set to the value associated with the largest key if non-NULL 315 | * and the list is not empty. 316 | * 317 | * @return 0 if the list was already empty and 1 if it was not 318 | */ 319 | SKIPLIST_EXTERN 320 | short SKIPLIST_NAME(shift)(SL_LIST *list, SL_KEY *key_out, SL_VAL *val_out); 321 | 322 | #ifdef SKIPLIST_IMPLEMENTATION 323 | 324 | #define CMP_EQ(list, a, b) (list->cmp(a, b, list->cmp_udata) == 0) 325 | #define CMP_LT(list, a, b) (list->cmp(a, b, list->cmp_udata) < 0) 326 | 327 | SKIPLIST_EXTERN 328 | int SKIPLIST_NAME(init)(SL_LIST *list, SL_CMP_FN cmp, void *cmp_udata, void *mem_udata) { 329 | list->cmp = cmp; 330 | list->cmp_udata = cmp_udata; 331 | list->mem_udata = mem_udata; 332 | list->highest = 0; 333 | list->size = 0; 334 | list->head = (SL_NODE *)SKIPLIST_MALLOC(mem_udata, sizeof(SL_NODE)); 335 | list->head->height = SKIPLIST_MAX_LEVELS; 336 | memset(list->head->next, 0, SKIPLIST_MAX_LEVELS * sizeof(SL_NODE *)); 337 | return 0; 338 | } 339 | 340 | SKIPLIST_EXTERN 341 | void SKIPLIST_NAME(free)(SL_LIST *list) { 342 | SL_NODE *n, *next; 343 | n = list->head; 344 | while (n) { 345 | next = n->next[0]; 346 | SKIPLIST_FREE(list->mem_udata, n); 347 | n = next; 348 | } 349 | } 350 | 351 | SKIPLIST_EXTERN 352 | short SKIPLIST_NAME(insert)(SL_LIST *list, SL_KEY key, SL_VAL val, SL_VAL *prior) { 353 | SL_NODE *n, *nn, *update[SKIPLIST_MAX_LEVELS]; 354 | unsigned int i; 355 | short replaced; 356 | 357 | n = list->head; 358 | nn = (SL_NODE *)SKIPLIST_MALLOC(list->mem_udata, sizeof(SL_NODE)); 359 | nn->key = key; 360 | nn->val = val; 361 | memset(nn->next, 0, SKIPLIST_MAX_LEVELS * sizeof(SL_NODE *)); 362 | 363 | nn->height = SKIPLIST_HEIGHT(key); 364 | 365 | i = list->highest; 366 | while (i --> 0) { 367 | while (n->next[i] && CMP_LT(list, n->next[i]->key, key)) 368 | n = n->next[i]; 369 | update[i] = n; 370 | } 371 | n = n->next[0]; 372 | 373 | replaced = n != NULL && CMP_EQ(list, key, n->key); 374 | if (replaced) { 375 | if (prior) 376 | *prior = n->val; 377 | n->val = val; 378 | SKIPLIST_FREE(list->mem_udata, nn); 379 | } 380 | else { 381 | i = nn->height; 382 | while (nn->height >= list->highest) 383 | update[list->highest++] = list->head; 384 | 385 | for (i = 0; i <= nn->height; i++) { 386 | nn->next[i] = update[i]->next[i]; 387 | update[i]->next[i] = nn; 388 | } 389 | } 390 | 391 | list->size += !replaced; 392 | 393 | return replaced; 394 | } 395 | 396 | SKIPLIST_EXTERN 397 | short SKIPLIST_NAME(find)(SL_LIST *list, SL_KEY key, SL_VAL *out) { 398 | SL_NODE *n; 399 | unsigned int i; 400 | n = list->head; 401 | i = list->highest; 402 | 403 | while (i --> 0) { 404 | while (n->next[i] && CMP_LT(list, n->next[i]->key, key)) { 405 | n = n->next[i]; 406 | } 407 | } 408 | n = n->next[0]; 409 | 410 | if (n && CMP_EQ(list, key, n->key)) { 411 | if (out) 412 | *out = n->val; 413 | return 1; 414 | } 415 | return 0; 416 | } 417 | 418 | SKIPLIST_EXTERN 419 | SL_VAL SKIPLIST_NAME(get)(SL_LIST *list, SL_KEY key, SL_VAL default_val) { 420 | SL_VAL v; 421 | if (SKIPLIST_NAME(find)(list, key, &v)) 422 | return v; 423 | return default_val; 424 | } 425 | 426 | SKIPLIST_EXTERN 427 | short SKIPLIST_NAME(remove)(SL_LIST *list, SL_KEY key, SL_VAL *out) { 428 | SL_NODE *n; 429 | SL_NODE *update[SKIPLIST_MAX_LEVELS]; 430 | unsigned int i; 431 | 432 | if (list->size == 0) { 433 | return 0; 434 | } 435 | 436 | n = list->head; 437 | i = list->highest - 1; 438 | do { 439 | while (n->next[i] && CMP_LT(list, n->next[i]->key, key)) { 440 | n = n->next[i]; 441 | } 442 | update[i] = n; 443 | } while (i --> 0); 444 | 445 | n = n->next[0]; 446 | if (n && CMP_EQ(list, n->key, key)) { 447 | if (out) *out = n->val; 448 | i = 0; 449 | while (i < list->highest) { 450 | if (update[i]->next[i] != n) break; 451 | update[i]->next[i] = n->next[i]; 452 | i++; 453 | } 454 | SKIPLIST_FREE(list->mem_udata, n); 455 | while (list->highest > 0 && list->head->next[list->highest - 1] == NULL) { 456 | --list->highest; 457 | } 458 | --list->size; 459 | return 1; 460 | } 461 | return 0; 462 | } 463 | 464 | SKIPLIST_EXTERN 465 | int SKIPLIST_NAME(iter)(SL_LIST *list, SL_ITER_FN iter, void *userdata) { 466 | SL_NODE *n; 467 | int stop; 468 | n = list->head; 469 | while (n->next[0]) { 470 | if ((stop = iter(n->next[0]->key, n->next[0]->val, userdata))) 471 | return stop; 472 | n = n->next[0]; 473 | } 474 | return 0; 475 | } 476 | 477 | SKIPLIST_EXTERN 478 | unsigned long SKIPLIST_NAME(size)(SL_LIST *list) { 479 | return list->size; 480 | } 481 | 482 | SKIPLIST_EXTERN 483 | short SKIPLIST_NAME(min)(SL_LIST *list, SL_KEY *key_out, SL_VAL *val_out) { 484 | if (list->size == 0) 485 | return 0; 486 | if (key_out) 487 | *key_out = list->head->next[0]->key; 488 | if (val_out) 489 | *val_out = list->head->next[0]->val; 490 | return 1; 491 | } 492 | 493 | SKIPLIST_EXTERN 494 | short SKIPLIST_NAME(max)(SL_LIST *list, SL_KEY *key_out, SL_VAL *val_out) { 495 | int i; 496 | SL_NODE *n; 497 | if (list->size == 0) 498 | return 0; 499 | /* TODO store the biggest */ 500 | n = list->head; 501 | for (i = 0; i < list->size; ++i) 502 | n = n->next[0]; 503 | if (key_out) 504 | *key_out = n->key; 505 | if (val_out) 506 | *val_out = n->val; 507 | return 1; 508 | } 509 | 510 | SKIPLIST_EXTERN 511 | short SKIPLIST_NAME(pop)(SL_LIST *list, SL_KEY *key_out, SL_VAL *val_out) { 512 | int i; 513 | SL_NODE *first; 514 | 515 | if (list->size == 0) 516 | return 0; 517 | 518 | first = list->head->next[0]; 519 | i = first->height; 520 | do { 521 | if (first->next[i] == NULL) 522 | list->highest--; 523 | list->head->next[i] = first->next[i]; 524 | } while (i --> 0); 525 | 526 | if (key_out) 527 | *key_out = first->key; 528 | if (val_out) 529 | *val_out = first->val; 530 | SKIPLIST_FREE(list->mem_udata, first); 531 | --list->size; 532 | return 1; 533 | } 534 | 535 | SKIPLIST_EXTERN 536 | short SKIPLIST_NAME(shift)(SL_LIST *list, SL_KEY *key_out, SL_VAL *val_out) { 537 | int i; 538 | SL_NODE *penultimate, *last; 539 | if (list->size == 0) 540 | return 0; 541 | penultimate = list->head; 542 | for (i = 0; i < list->size - 1; ++i) 543 | penultimate = penultimate->next[0]; 544 | last = penultimate->next[0]; 545 | if (key_out) 546 | *key_out = last->key; 547 | if (val_out) 548 | *val_out = last->val; 549 | i = penultimate->height; 550 | while (i --> 0) 551 | penultimate->next[i] = NULL; 552 | SKIPLIST_FREE(list->mem_udata, last); 553 | --list->size; 554 | return 1; 555 | } 556 | 557 | #undef CMP_EQ 558 | #undef CMP_LT 559 | 560 | #endif 561 | 562 | #undef SL_PASTE_ 563 | #undef SL_CAT_ 564 | 565 | #undef SL_NODE 566 | #undef SL_LIST 567 | #undef SL_CMP_FN 568 | #undef SL_ITER_FN 569 | #undef SL_KEY 570 | #undef SL_VAL 571 | --------------------------------------------------------------------------------