diff --git a/CHANGES b/CHANGES index be6bf8855c8d6c5b2fd967e85d5244c893a4eb66..bb0375adfc61852119803d07ee4782c35016cf05 100644 --- a/CHANGES +++ b/CHANGES @@ -3,6 +3,12 @@ Changes since Pike 7.8 (scratch area for future release notes) o decode_value now throws the error object Error.DecodeError. Useful to catch format errors in the decode string. +o Added ADT.CritBit module + Mapping-like key-sorted data structures for string, int and float-keys + (ADT.CritBit.Tree, ADT.CritBit.IntTree, ADT.CritBit.FloatTree). Implemented + in C. + + Changes since Pike 7.8.352 (third 7.8 release): diff --git a/lib/modules/ADT.pmod/CritBit.pmod b/lib/modules/ADT.pmod/CritBit.pmod new file mode 100644 index 0000000000000000000000000000000000000000..d965b30b2d0c389d9c28bab63cc5670bcd96970d --- /dev/null +++ b/lib/modules/ADT.pmod/CritBit.pmod @@ -0,0 +1,323 @@ +#pike __REAL_VERSION__ + +//! @ignore +#if !constant (ADT._CritBit) +constant this_program_does_not_exist = 1; +#else + +inherit ADT._CritBit; +//! @endignore + +//! Creates a CritBit tree for keys of type @expr{type@}. If no argument is given, +//! an instance of @[ADT.CritBit.StringTree] is returned. Supported types are @expr{"string"@}, +//! @expr{"int"@}, @expr{"float"@}, @expr{"ipv4"@} and @expr{Calendar.TimeRange@}. +object Tree(void|string|program|mapping type) { + switch (type) { + case Calendar.TimeRange: + return DateTree(); + case "int": + return IntTree(); + case "ipv4": + return IPv4Tree(); + case "string": + case 0: + return StringTree(); + case "float": + return FloatTree(); + default: + if (mappingp(type)) + return StringTree(type); + + error("No tree available for type %O\n", type); + } +} + +string iptosortable(string ip) { + int a, b, c, d, e, i; + string s; + + switch (sscanf(ip, "%d.%d.%d.%d/%d", a, b, c, d, e)) { + case 4: + e = 32; + case 5: + e = max(0, min(32, e)); + s = sprintf("%c%c%c%c", a, b, c, d); + sscanf(s, "%4c", i); + i &= ~(0xffffffff >> e); + s = sprintf("%4c%c%c%c%c%c", i, a, b, c, d, e); + + break; + default: + error("NON-ip %O got passed to iptoint.\n", ip); + } + + + return s; +} + +//! Sorts an ARRAY OF IPv4-Adresses (and optional netmasks) given in dotted +//! decimal representation with the /23 netmask notation. +//! +//! @example +//! @code +//!> array(string) a = ({ "127.0.0.121", +//!> "127.0.0.0/16", +//!> "127.0.0.1/8", +//!> "127.0.0.0/8", +//!> "128.0.0.0/1", +//!> "192.168.21.3", +//!> "8.8.8.8" }); +//!> write("%O\n", CritBit.sort_ipv4(a)); +//!({ /* 7 elements */ +//! "8.8.8.8", +//! "127.0.0.0/8", +//! "127.0.0.0/16", +//! "127.0.0.1/8", +//! "127.0.0.121", +//! "128.0.0.0/1", +//! "192.168.21.3" +//!}) +//! @endcode +//! @ +array(string) sort_ipv4(array(string) a, array ... data) { + array b = map(a, iptosortable); + + sort(b, a, @data); + + return a; +} + +string get_ipv4(int ip, void|int prefix) { + string ret; + + if (undefinedp(prefix)) prefix = 32; + prefix = min(32, max(prefix, 0)); + ip &= ~(0xffffffff >> prefix); + ret = sprintf("%d.%d.%d.%d", + (ip & 0xff000000) >> 24, + (ip & 0xff0000) >> 16, + (ip & 0xff00) >> 8, + (ip & 0xff)); + if (prefix != 32) { + ret += sprintf("/%d", prefix); + } + + return ret; +} + +//! This module offers CritBit tree implementations for different +//! key types. +//! @note +//! These CritBit trees support prefixes as proper keys. Hence +//! they should really be called Tries. + +//! +class DateTree { + inherit IntTree; + mapping(int:Calendar.TimeRange) backwards = ([]); + + //! Encodes a Calendar.TimeRange object into unix timestanp. + int encode_key(object|int o) { + if (objectp(o) && Program.inherits(object_program(o), + Calendar.TimeRange)) { + int t = o->unix_time(); + backwards[t] = o; + return t; + } + return o; + } + + //! Decodes an integer back to a @[Calendar.TimeRange] object. Keeps + //! a mapping of all keys stored in the tree to transform back. + int|object decode_key(int i) { + return has_index(backwards, i) + ? backwards[i] + : Calendar.Second("unix", i); + } + + //! Copy callback to also clone backwards + this_program copy() { + this_program t = ::copy(); + t->backwards = copy_value(backwards); + return t; + } + + mixed _m_delete(mixed o) { + mixed v = ::_m_delete(o); + m_delete(backwards, decode_key(o)); + return v; + } +} + +//! Data structure representing a set of disjunct @[ADT.Interval] objects. Can be thought +//! of as an interval with gaps. +class RangeSet { + object tree; + + //! Create a RangeSet from a given tree program. + void create(function|object|program tree) { + this_program::tree = objectp(tree) ? tree : tree(); + } + + mixed `[](mixed key) { + if (objectp(key) && Program.inherits(object_program(key), ADT.Interval)) + return contains(key); + mixed next = tree[key]; + if (!undefinedp(next)) return next->contains(key); + next = tree->next(key); + + if (next) { + object interval = tree[next]; + return interval->contains(key); + } + + return UNDEFINED; + } + + int(0..1) overlaps(mixed o) { + if (objectp(o)) { + if (Program.inherits(object_program(o), ADT.Interval)) { + ADT.Interval r = tree[o->b->x]; + if (!r) { + mixed next = tree->next(o->b->x); + if (next) r = tree[next]; + } + if (!r) return 0; + return r->overlaps(o); + } else if (Program.inherits(object_program(o), this_program)) { + // TODO + } + } + return 0; + } + + int(0..1) contains(mixed o) { + if (objectp(o)) { + if (Program.inherits(object_program(o), ADT.Interval)) { + ADT.Interval r = tree[o->b->x]; + if (!r) { + mixed next = tree->next(o->b->x); + if (next) r = tree[next]; + } + if (!r) return 0; + return r->contains(o); + } else if (Program.inherits(object_program(o), this_program)) { + // TODO + } + } + return 0; + } + + mixed `[]=(mixed i, mixed v) { + if (objectp(i) && Program.inherits(object_program(i), ADT.Interval)) { + merge(i); + return v; + } else error("Bad key type: %t. Expects an instance of type ADT.Interval.\n", i); + } + + array(mixed) _indices() { + return values(tree); + } + + void merge(mixed i) { + werror("merge(%O)\n", i); + array a = values(tree[i->a->x..i->b->x]); + mixed next = tree->next(i->b->x); + + if (!undefinedp(next)) a += ({ tree[next] }); + + werror("all: %O\n", a); + a = filter(a, i->touches); + werror("filtered: %O\n", a); + + if (sizeof(a)) { + i |= a[0]; + if (sizeof(a) > 1) + i |= a[sizeof(a)-1]; + foreach (a;; mixed b) m_delete(tree, b->b->x); + } + + tree[i->b->x] = i; + } + + this_program `|(object o) { + this_program new = this_program(tree->copy()); + if (Program.inherits(object_program(o), this_program)) { + foreach (indices(o); ; mixed i) new->merge(i); + } else { + new->merge(o); + } + return new; + } + + string _sprintf(int type) { + if (type == 'O') { + array a = indices(this); + return sprintf("{%s}", map(a, Function.curry(sprintf)("%O"))*", "); + } + + return 0; + } +} + +class MultiRangeSet { + // mapping from left/right boundary to interval + object tree; + int|float max_len; + + void create(function|program|object tree) { + this_program::tree = objectp(tree) ? tree : tree(); + } + + mixed `[](mixed key) { + + if (!objectp(key) || !Program.inherits(object_program(key), + ADT.Interval)) { + key = ADT.Interval(key, key); + } + + return contains(key); + } + + mixed contains(mixed key) { + array ret = ({}); + + foreach (tree->Iterator(tree, -1, key->b->x); + ; object i) { + if (key->a - i[0]->a > max_len) break; + ret += filter(i, i->contains(key)); + } + + return ret; + } + + mixed overlaps(mixed key) { + array ret = ({}); + + foreach (tree->Iterator(tree, -1, key->b->x); + ; array(object) i) { + if (key->a - i[0]->a > max_len) { + werror("stopping early.\n"); + break; + } + + ret += filter(i, i->overlaps(key)); + } + + return ret; + } + + mixed `[]=(mixed i, mixed v) { + max_len = max(sizeof(i), max_len); + if (v) i->val = v; + if (tree[i->a->x]) + tree[i->a->x] += ({ i }); + else tree[i->a->x] = ({ i }); + } + + string _sprintf(int type) { + return sprintf("%O", values(tree)); + } +} + +#endif // constant (@module@) diff --git a/lib/modules/ADT.pmod/Interval.pike b/lib/modules/ADT.pmod/Interval.pike new file mode 100644 index 0000000000000000000000000000000000000000..55f18192c848497b3b012869654d3e30b413520f --- /dev/null +++ b/lib/modules/ADT.pmod/Interval.pike @@ -0,0 +1,252 @@ +class Boundary(mixed x) { + int (0..1) `<(object b) { + if (!objectp(b) || !Program.inherits(object_program(b), Boundary)) { + return x < b; + } + return x < b->x; + } + + int (0..1) `>(object b) { + if (!objectp(b) || !Program.inherits(object_program(b), Boundary)) { + return x > b; + } + return x > b->x; + } + + string _sprintf(int type) { + return sprintf("%O", x); + } + + int unix_time() { + return x->unix_time(); + } + + int `ux() { + return unix_time(); + } + + mixed `-(object b) { + if (intp(x) || floatp(x)) { + return x - b->x; + } else if (stringp(x)) { + error("lets see how we do this ;) \n"); + } + } +} + +class Open { + inherit Boundary; + + int (0..1) `==(object b) { + return objectp(b) && Program.inherits(object_program(b), Open) + && b->x == x; + } + + int (0..1) `<(mixed b) { + if (!objectp(b) || !Program.inherits(object_program(b), Boundary)) { + return x <= b; + } + return ::`<(b); + } + + int (0..1) `>(mixed b) { + if (!objectp(b) || !Program.inherits(object_program(b), Boundary)) { + return x >= b; + } + return ::`>(b); + } + + int(0..1) overlaps(object b) { + if (this > b) return 1; + return 0; + } + + int(0..1) touches(object b) { + werror("%O touches %-O == %d\n", this, b, overlaps(b) || (this->x == b->x && Program.inherits(object_program(b), Closed))); + return overlaps(b) || (this->x == b->x && Program.inherits(object_program(b), Closed)); + } + + string _sprintf(int type, mapping info) { + if (info->flag_left) { + return sprintf("(%O", x); + } + return sprintf("%O)", x); + } + + Boundary `~() { + return Closed(x); + } +} + +class Closed { + inherit Boundary; + + int (0..1) `==(object b) { + return objectp(b) && Program.inherits(object_program(b), Closed) + && b->x == x; + } + + int(0..1) overlaps(object b) { + if (this > b || (Program.inherits(object_program(b), Closed) && b == this)) return 1; + return 0; + } + + int(0..1) touches(object b) { + werror("%O touches %-O == %d\n", this, b, this > b); + if (this < b) return 0; + return 1; + } + string _sprintf(int type, mapping info) { + if (info->flag_left) { + return sprintf("[%O", x); + } + return sprintf("%O]", x); + } + + Boundary `~() { + return Open(x); + } +} + +Boundary min(Boundary a, Boundary b) { + if (a < b) return a; + if (b < a) return b; + + if (Program.inherits(object_program(a), Closed)) return a; + if (Program.inherits(object_program(b), Closed)) return b; + return a; +} + +Boundary max(Boundary a, Boundary b) { + if (a == min(a,b)) return b; + else return a; +} + +Boundary a, b; + +mixed `->start() { return a->x; } +mixed `->stop() { return b->x; } + +mixed `->start=(mixed v) { + a = Closed(v); + return v; +} +mixed `->stop=(mixed v) { + b = Closed(v); + return v; +} + +string _sprintf(int type) { + return sprintf("%-O..%O", a, b); +} + +void create(mixed a, mixed b) { + if (!objectp(a) || !Program.inherits(object_program(a), Boundary)) { + a = Closed(a); + } + if (!objectp(b) || !Program.inherits(object_program(b), Boundary)) { + b = Closed(b); + } + if (!b->overlaps(a)) error("Trying to create empty interval.\n"); + this_program::a = a; + this_program::b = b; +} + +int(0..1) `==(mixed i) { + return objectp(i) && Program.inherits(object_program(i), this_program) && a == i->a && b == i->b; +} + +// 0 (..)..[..] +// 1 (..[..)..] +// 2 [..(..)..] +// 3 [..(..]..) +// 4 [..]..(..) + +this_program `&(this_program i) { + Boundary l, r; + + l = max(a, i->a); + r = min(b, i->b); + + if (r->overlaps(l)) + return clone(l, r); + return 0; +} + +int(0..1) overlaps(this_program i) { + Boundary l, r; + + l = max(a, i->a); + r = min(b, i->b); + + return r->overlaps(l); +} + +int(0..1) touches(this_program i) { + Boundary l, r; + + l = max(a, i->a); + r = min(b, i->b); + + return r->touches(l); +} + +this_program clone(mixed ... args) { + return this_program(@args); +} + + +this_program `|(this_program i) { + if ((this & i) + || (b->x <= i->a->x && b->touches(i->a)) + || (i->b->x <= a->x && i->b->touches(a))) { + return clone(min(a, i->a), max(b, i->b)); + } + + error("%O and %O need to touch.\n", this, i); +} + +this_program `+(this_program i) { + return this | i; +} + +this_program `-(this_program interval) { + this_program i = interval & this; + if (i) { + if (i == this) return 0; + + if (a == i->a) { + return clone(~i->b, b); + } else if (b == i->b) { + return clone(a, ~i->a); + } + + error("%O and %O may not be contained.\n", this, i); + } + return this; +} + +int|float _sizeof() { + return b-a; +} + +int(0..1) contains(mixed x) { + if (!objectp(x) || !Program.inherits(object_program(x), ADT.Interval)) { + x = this_program(Closed(x), Closed(x)); + } + return !!(this&x); +} + +/* TODO: + * implement or remap the api offered by the timerange thing. + */ + +mixed beginning() { return start; } +mixed end() { return stop; } + +mixed cast(string type) { + switch (type) { + case "array": + return ({ start, stop }); + } +} diff --git a/src/post_modules/CritBit/.gitignore b/src/post_modules/CritBit/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..3c4d1ab9e1acf44dedc5048cf4ae9163cc50b51d --- /dev/null +++ b/src/post_modules/CritBit/.gitignore @@ -0,0 +1,14 @@ +/Makefile +/config.log +/config.status +/configure +/critbit_config.h +/critbit_config.h.in +/dependencies +/floattree.c +/inttree.c +/make_variables +/propagated_variables +/stamp-h +/stamp-h.in +/tree.c diff --git a/src/post_modules/CritBit/Makefile.in b/src/post_modules/CritBit/Makefile.in new file mode 100644 index 0000000000000000000000000000000000000000..4fa1ec08958ed373753361028c8210ef9d6fac3e --- /dev/null +++ b/src/post_modules/CritBit/Makefile.in @@ -0,0 +1,24 @@ +# $Id$ + +@make_variables@ + +VPATH=@srcdir@ +MODNAME=CritBit +MODDIR=ADT.pmod/ +MODULE_PMOD_IN= +MODULE_WRAPPER_PREFIX=_ +AUTODOC_SRC_IN=@srcdir@/stringtree.c @srcdir@/floattree.c @srcdir@/inttree.c +OBJS=glue.o inttree.o stringtree.o floattree.o +CONFIG_HEADERS=@CONFIG_HEADERS@ + +@dynamic_module_makefile@ + +strintree.o: $(SRCDIR)/stringtree.c + +inttree.o: $(SRCDIR)/inttree.c + +floattree.o: $(SRCDIR)/floattree.c + +glue.o: $(SRCDIR)/glue.c + +@dependencies@ diff --git a/src/post_modules/CritBit/acconfig.h b/src/post_modules/CritBit/acconfig.h new file mode 100644 index 0000000000000000000000000000000000000000..9442d19df263740408ed629377bbde6efc028bf3 --- /dev/null +++ b/src/post_modules/CritBit/acconfig.h @@ -0,0 +1,14 @@ +/* +|| This file is part of Pike. For copyright information see COPYRIGHT. +|| Pike is distributed under GPL, LGPL and MPL. See the file COPYING +|| for more information. +|| $Id$ +*/ + +#ifndef CRITBIT_CONFIG_H +#define CRITBIT_CONFIG_H + +@TOP@ +@BOTTOM@ + +#endif diff --git a/src/post_modules/CritBit/bitvector.h b/src/post_modules/CritBit/bitvector.h new file mode 100644 index 0000000000000000000000000000000000000000..d98046d9c4799d6e75d2b5838e1d3aa97d170a8d --- /dev/null +++ b/src/post_modules/CritBit/bitvector.h @@ -0,0 +1,323 @@ +#ifndef BITVECTOR_H +#define BITVECTOR_H + +#include "critbit_config.h" +#define MOD_PRIME + +#include <stdint.h> + +#if defined(HAS__BITSCANFORWARD) || defined(HAS__BITSCANFORWARD64) || defined(HAS__BITSCANREVERSE) || defined(HAS__BITSCANREVERSE64) +# include <intrin.h> +#endif + +#if defined(HAS__BYTESWAP_ULONG) || defined(HAS__BYTESWAP_UINT64) +#include <stdlib.h> +#endif + +#define MASK(type, bits) (~((~((type)0)) >> (bits))) +#define BITMASK(type, n) ((type)1 << (type)(sizeof(type)*8 - 1 - (n))) +#define BITN(type, p, n) (!!((p) & BITMASK(type, n))) + +#define TBITMASK(type, n) ((type)1 << (type)((n))) +#define TBITN(type, p, n) (!!((p) & TBITMASK(type, n))) + +#if 1 +# define LT(n) n, n, n, n, n, n, n, n, n, n, n, n, n, n, n, n +static const char logTable[256] = { + 0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, + LT(5), LT(6), LT(6), LT(7), LT(7), LT(7), LT(7), + LT(8), LT(8), LT(8), LT(8), LT(8), LT(8), LT(8), LT(8) +}; +# undef LT +#endif + +static const unsigned char LMultiplyDeBruijnBitPosition32[33] = +{ + 32, + 31, 22, 30, 21, 18, 10, 29, 2, + 20, 17, 15, 13, 9, 6, 28, 1, + 23, 19, 11, 3, 16, 14, 7, 24, + 12, 4, 8, 25, 5, 26, 27, 0, +/* + 0, 1, 28, 2, 29, 14, 24, 3, + 30, 22, 20, 15, 25, 17, 4, 8, + 31, 27, 13, 23, 21, 19, 16, 7, + 26, 12, 18, 6, 11, 5, 10, 9 +*/ +}; +static const unsigned char TMultiplyDeBruijnBitPosition32[33] = +{ + 32, + 31, 30, 3, 29, 2, 17, 7, 28, + 1, 9, 11, 16, 6, 14, 27, 23, + 0, 4, 18, 8, 10, 12, 15, 24, + 5, 19, 13, 25, 20, 26, 21, 22 +}; +static const unsigned char LMultiplyDeBruijnBitPosition64[65] = { + 64, +}; +static const unsigned char TMultiplyDeBruijnBitPosition64[65] = { + 64, + 0, 63, 5, 62, 4, 16, 10, 61, + 3, 24, 15, 36, 9, 30, 21, 60, + 2, 12, 26, 23, 14, 45, 35, 43, + 8, 33, 29, 52, 20, 49, 41, 59, + 1, 6, 17, 11, 25, 37, 31, 22, + 13, 27, 46, 44, 34, 53, 50, 42, + 7, 18, 38, 32, 28, 47, 54, 51, + 19, 39, 48, 55, 40, 56, 57, 58 +}; +/* maps 2^n - 1 -> n + * returns 32 is none is set. + */ +static inline uint32_t leading_bit_pos32(uint32_t v) { + return LMultiplyDeBruijnBitPosition32[!!v + (((uint32_t)((v) * 0x07C4ACDDU)) >> 27)]; +} +static inline uint32_t trailing_bit_pos32(uint32_t v) { + return TMultiplyDeBruijnBitPosition32[!!v + (((uint32_t)((v) * 0x077CB531U)) >> 27)]; +} +static inline uint64_t leading_bit_pos64(uint64_t v) { + /* + * v * M == (2*v - 1) * N + N = v * (2*N) + * N = M/2; + */ + const uint64_t N = 0x3F6EAF2CD271461UL; + + return LMultiplyDeBruijnBitPosition64[!!v + (((uint64_t)((v) * N + N)) >> 58)]; +} +static inline uint64_t trailing_bit_pos64(uint64_t v) { + return TMultiplyDeBruijnBitPosition64[!!v + (((uint64_t)((v) * 0x07EDD5E59A4E28C2UL)) >> 58)]; +} + +static inline uint32_t round_up32_(uint32_t v) { + v |= v >> 1; /* first round down to one less than a power of 2 */ + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + return v; +} + +static inline uint32_t round_up32(uint32_t v) { + v = round_up32_(v); + v++; + return v; +} + +static inline uint32_t round_down32(uint32_t v) { + v--; + return round_up32(v); +} + +static inline uint64_t round_up64_(uint64_t v) { + v |= v >> 1; /* first round down to one less than a power of 2 */ + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v |= v >> 32; + return v; +} + +static inline uint64_t round_up64(uint64_t v) { + v = round_up64_(v); + v++; + return v; +} + +static inline uint64_t round_down64(uint64_t v) { + v--; + return round_up64(v); +} + +#ifdef HAS___BUILTIN_CLZ +# define clz32(i) ((uint32_t)(!(i) ? 32 : __builtin_clz((uint32_t)i))) +#elif defined(HAS__BIT_SCAN_REVERSE) +# define clz32(i) ((uint32_t)(!(i) ? 32 : _bit_scan_reverse((int)i))) +#elif defined(HAS___CNTLZ4) +# define clz32(i) ((uint32_t)__cntlz4(i)) +#elif defined(HAS__BITSCANREVERSE) +static inline uint32_t clz32(const uint32_t i) { + static unsigned long index; + if (_BitScanReverse(&index, (unsigned long)i)) + return (uint32_t)index; + return 32; +} +#else +static inline uint32_t clz32(const uint32_t y) { +#ifdef MOD_PRIME + register uint32_t t; + + if ((t = y >> 24)) { + return 8 - logTable[t]; + } else if ((t = y >> 16)) { + return 16 - logTable[t]; + } else if ((t = y >> 8)) { + return 24 - logTable[t]; + } else { + return 32 - logTable[y]; + } +#else + return leading_bit_pos32(round_up32_(y)); +#endif +} +#endif + +#define clz16(i) ((uint32_t)(!(i) ? 16 : clz32((uint32_t)i)-16)) +#define clz8(i) ((char)(!(i) ? 8 : clz32((uint32_t)i)-24)) + +#ifdef HAS___BUILTIN_CTZ +# define ctz32(i) ((uint32_t)(!(i) ? 32 : __builtin_ctz((uint32_t)i))) +#elif defined(HAS__BIT_SCAN_FORWARD) +# define ctz32(i) ((uint32_t)(!(i) ? 32 : _bit_scan_forward((int)i))) +#elif defined(HAS___CNTTZ4) +# define ctz32(i) ((uint32_t)__cnttz4(i)) +#elif defined(HAS__BITSCANFORWARD) +static inline uint32_t ctz32(const uint32_t i) { + static unsigned long index; + if (_BitScanForward(&index, (unsigned long)i)) + return (uint32_t)index; + return 32; +} +#else +static inline uint32_t ctz32(const uint32_t i) { +#ifdef MOD_PRIME + /* this table maps (2^n % 37) to n */ + static const char ctz32Table[37] = { + 32, 0, 1, 26, 2, 23, 27, -1, 3, 16, 24, 30, 28, 11, -1, 13, 4, 7, 17, + -1, 25, 22, 31, 15, 29, 10, 12, 6, -1, 21, 14, 9, 5, 20, 8, 19, 18 + }; + return (uint32_t)ctz32Table[((uint32_t)((int)i & -(int)i))%37]; +#else + return trailing_bit_pos32((uint32_t)((int)i & -(int)i)); +#endif +} +#endif + +#define ctz16(i) ((uint32_t)(!(i) ? 16 : ctz32((uint32_t)i)-16)) +#define ctz8(i) ((char)(!(i) ? 8 : ctz32((uint32_t)i)-24)) + +#if SIZEOF_LONG == 8 || SIZEOF_LONG_LONG == 8 + +# if SIZEOF_LONG == 8 && defined(HAS___BUILTIN_CLZL) +# define clz64(x) ((uint32_t)(!(x) ? 64\ + : __builtin_clzl((unsigned long)(x)))) +# elif SIZEOF_LONG_LONG == 8 && defined(HAS___BUILTIN_CLZLL) +# define clz64(x) ((uint32_t)(!(x) ? 64\ + : __builtin_clzll((unsigned long long)(x)))) +# elif defined(HAS___CNTLZ8) +# define clz64(x) ((uint32_t)__cntlz8(x)) +# elif defined(HAS__BITSCANREVERSE64) +static inline uint32_t clz64(const uint64_t i) { + static unsigned long index; + if (_BitScanReverse64(&index, (__int64)i)) + return (uint32_t)index; + return 64; +} +# else +static inline uint32_t clz64(const uint64_t y) { +#ifdef MOD_PRIME + register uint64_t t; + + if ((t = y >> 56)) { + return 8 - logTable[t]; + } else if ((t = y >> 48)) { + return 16 - logTable[t]; + } else if ((t = y >> 40)) { + return 24 - logTable[t]; + } else if ((t = y >> 32)) { + return 32 - logTable[t]; + } else if ((t = y >> 24)) { + return 40 - logTable[t]; + } else if ((t = y >> 16)) { + return 48 - logTable[t]; + } else if ((t = y >> 8)) { + return 56 - logTable[t]; + } else { + return 64 - logTable[y]; + } +#else + /* This code comes from http://graphics.stanford.edu/~seander/bithacks.html */ + return leading_bit_pos64(round_up64(y)); +# endif +} +# endif + +# if SIZEOF_LONG == 8 && defined(HAS___BUILTIN_CTZL) +# define ctz64(x) ((uint32_t)(!(x) ? 64\ + : __builtin_ctzl((unsigned long)(x)))) +/* # warning __builtin_ctzl used */ +# elif SIZEOF_LONG_LONG == 8 && defined(HAS___BUILTIN_CTZLL) +# define ctz64(x) ((uint32_t)(!(x) ? 64\ + : __builtin_ctzll((unsigned long long)(x)))) +/* # warning __builtin_ctzll used */ +# elif defined(HAS___CNTTZ8) +# define ctz64(x) ((uint32_t)__cnttz8(x)) +/* # warning __cnttz8 used */ +# elif defined(HAS__BITSCANFORWARD64) +static inline uint32_t ctz64(const uint64_t i) { + static unsigned long index; + if (_BitScanForward64(&index, (__int64)i)) + return (uint32_t)index; + return 64; +} +# else +static inline uint32_t ctz64(const uint64_t i) { +#ifdef MOD_PRIME + /* this table maps (2^n % 67) to n */ + static const char ctz64Table[67] = { + 64, 0, 1, 39, 2, 15, 40, 23, 3, 12, 16, 59, 41, 19, 24, 54, 4, 63, 13, + 10, 17, 62, 60, 28, 42, 30, 20, 51, 25, 44, 55, 47, 5, 32, 63, 38, 14, + 22, 11, 58, 18, 53, 63, 9, 61, 27, 29, 50, 43, 46, 31, 37, 21, 57, 52, + 8, 26, 49, 45, 36, 56, 7, 48, 35, 6, 34, 33 + }; + return (uint32_t)ctz64Table[((uint64_t)((int64_t)i & -(int64_t)i))%67]; +#else + register uint64_t v = (uint64_t)((int64_t)i & -(int64_t)i); + return trailing_bit_pos64(v); +#endif +} +# endif + +#endif /* SIZEOF_LONG == 8 || SIZEOF_LONG_LONG == 8 */ + +#define hton8(x) (x) + +#if !(defined(PIKE_BYTEORDER)) +# error "Byte order could not be decided." +#else +# if PIKE_BYTEORDER == 1234 +/* # warning "little endian" */ +# define LBITMASK(type, n) ((type)1 << (type)(n)) +# define LBITN(type, p, n) (!!((p) & LBITMASK(type, n))) +# ifdef HAS___BUILTIN_BSWAP32 +# define hton32(x) __builtin_bswap32(x) +# elif defined(HAS__BSWAP) +# define hton32(x) _bswap(x) +# elif defined(HAS__BYTESWAP_ULONG) +# define hton32(x) _byteswap_ulong((unsigned long)x) +# else +# define hton32(x) ((((x) & 0xff000000) >> 24) | (((x) & 0x000000ff) << 24) \ + | (((x) & 0x00ff0000) >> 8) | (((x) & 0x0000ff00) << 8)) +# endif +# ifdef HAS___BUILTIN_BSWAP64 +# define hton64(x) __builtin_bswap64(x) +# elif defined(HAS__BSWAP64) +# define hton64(x) _bswap64(x) +# elif defined(HAS__BYTESWAP_UINT64) +# define hton64(x) _byteswap_uint64((unsigned __int64)x) +# else +# define hton64(x) ((int64_t)hton32((int)((x) >> 32))\ + | (((int64_t)hton32((int)((x) & 0x00000000ffffffff))) << 32)) +# endif +# else /* PIKE_BYTEORDER = 1234 */ +/* # warning "big endian" */ +# define hton64(x) (x) +# define hton32(x) (x) +# define LBITMASK(type, n) ((type)1 << (type)((sizeof(type) \ + - ((type)(n)<<8) - 1)*8 + (type)(n)&7)) +# define LBITN(type, p, n) (!!((p) & LBITMASK(type, n))) +# endif /* PIKE_BYTEORDER == 1234 */ +#endif + +#endif /* BITVECTOR_H */ diff --git a/src/post_modules/CritBit/configure.in b/src/post_modules/CritBit/configure.in new file mode 100644 index 0000000000000000000000000000000000000000..ee27852f26903245c611c799fcdad7c40448a9f9 --- /dev/null +++ b/src/post_modules/CritBit/configure.in @@ -0,0 +1,61 @@ +# $Id$ +AC_INIT(glue.c) +AC_MODULE_INIT() +AC_CONFIG_HEADER(critbit_config.h) +define(TEST_BUILTIN, [ + AC_MSG_CHECKING(for $1) + AC_TRY_RUN([ +$3 +unsigned long lint; +int main(int argc, char **argv) { + static volatile int foo = 0; + foo = (int)$1($2); + return 0; +} + ], + AC_MSG_RESULT(yes) + AC_DEFINE(translit([HAS_$1], [a-z], [A-Z]), 1, [Whether $1 is available]) + , + AC_MSG_RESULT(no) + ) +]) +# GCC builtins +TEST_BUILTIN(__builtin_clz, 23) +TEST_BUILTIN(__builtin_clzl, 23) +TEST_BUILTIN(__builtin_clzll, 23) +TEST_BUILTIN(__builtin_ctz, 23) +TEST_BUILTIN(__builtin_ctzl, 23) +TEST_BUILTIN(__builtin_ctzll, 23) +TEST_BUILTIN(__builtin_bswap32, 23) +TEST_BUILTIN(__builtin_bswap64, 23) +TEST_BUILTIN(__builtin_expect, [argc,0]) +# ICC builtins +TEST_BUILTIN(_bswap, 23) +TEST_BUILTIN(_bswap64, 23) +TEST_BUILTIN(_bit_scan_reverse, 23) +TEST_BUILTIN(_bit_scan_forward, 23) +# Visual Studio builtins +TEST_BUILTIN(_BitScanForward, [&lint, 23], [ + #include <intrin.h> +]) +TEST_BUILTIN(_BitScanForward64, [&lint, 23], [ + #include <intrin.h> +]) +TEST_BUILTIN(_BitScanReverse, [&lint, 23], [ + #include <intrin.h> +]) +TEST_BUILTIN(_BitScanReverse64, [&lint, 23], [ + #include <intrin.h> +]) +TEST_BUILTIN(_byteswap_ulong, 23, [ + #include <stdlib.h> +]) +TEST_BUILTIN(_byteswap_uint64, 23, [ + #include <stdlib.h> +]) +# IBM C builtins +TEST_BUILTIN(__cntlz4, 23) +TEST_BUILTIN(__cntlz8, 23) +TEST_BUILTIN(__cnttz4, 23) +TEST_BUILTIN(__cnttz8, 23) +AC_OUTPUT(Makefile,echo FOO >stamp-h) diff --git a/src/post_modules/CritBit/critbit/critbit.h b/src/post_modules/CritBit/critbit/critbit.h new file mode 100644 index 0000000000000000000000000000000000000000..28a38e454b6329f85fd3ba9c2e67ec12f99b958b --- /dev/null +++ b/src/post_modules/CritBit/critbit/critbit.h @@ -0,0 +1,23 @@ +#ifndef CB_CRITBIT_H +#define CB_CRITBIT_H +#define CONCAT2_(a, b) a ## ## b +#define CONCAT2(a, b) CONCAT2_(a, b) +#define CONCAT3(a, b, c) CONCAT2(a, CONCAT2(b, c)) +#define CONCAT4(a, b, c, d) CONCAT2(a, CONCAT3(b, c, d)) +#define CONCAT5(a, b, c, d, e) CONCAT2(a, CONCAT4(b, c, d, e)) + +#ifndef CB_NAMESPACE +# warn NAMESPACE NOT USED +#define CB_NAME(name) CONCAT2(cb_, name) +#define CB_TYPE(name) CONCAT3(cb_, name, _t) +#else +#define CB_NAME(name) CONCAT4(cb_, CB_NAMESPACE, _, name) +#define CB_TYPE(name) CONCAT5(cb_, CB_NAMESPACE, _, name, _t) +#endif + +typedef struct cb_size { + size_t bits; + size_t chars; +} cb_size; + +#endif diff --git a/src/post_modules/CritBit/critbit/float2svalue.h b/src/post_modules/CritBit/critbit/float2svalue.h new file mode 100644 index 0000000000000000000000000000000000000000..27c30812cf3b48f4e935389133bbfc183b39b394 --- /dev/null +++ b/src/post_modules/CritBit/critbit/float2svalue.h @@ -0,0 +1,26 @@ +#ifdef CB_NAMESPACE +# undef CB_NAMESPACE +#endif +#define CB_NAMESPACE float2svalue + +#ifndef CB_INLINE +# define CB_INLINE +#else +# undef CB_INLINE +# define CB_INLINE inline +#endif + +#ifndef CB_STATIC +# define CB_STATIC +#else +# undef CB_STATIC +# define CB_STATIC static +#endif + +#ifndef CB_FLOAT2SVALUE_H +# define CB_FLOAT2SVALUE_H +# include "critbit.h" +# include "key_float.h" +# include "value_svalue.h" +# include "tree_low.h" +#endif diff --git a/src/post_modules/CritBit/critbit/int2svalue.h b/src/post_modules/CritBit/critbit/int2svalue.h new file mode 100644 index 0000000000000000000000000000000000000000..be46504cfb242e04f9108af1789d6ca4f21874ef --- /dev/null +++ b/src/post_modules/CritBit/critbit/int2svalue.h @@ -0,0 +1,26 @@ +#ifdef CB_NAMESPACE +# undef CB_NAMESPACE +#endif +#define CB_NAMESPACE int2svalue + +#ifndef CB_INLINE +# define CB_INLINE +#else +# undef CB_INLINE +# define CB_INLINE inline +#endif + +#ifndef CB_STATIC +# define CB_STATIC +#else +# undef CB_STATIC +# define CB_STATIC static +#endif + +#ifndef CB_INT2SVALUE_H +# define CB_INT2SVALUE_H +# include "critbit.h" +# include "key_int.h" +# include "value_svalue.h" +# include "tree_low.h" +#endif diff --git a/src/post_modules/CritBit/critbit/key_float.h b/src/post_modules/CritBit/critbit/key_float.h new file mode 100644 index 0000000000000000000000000000000000000000..26514d196bf665ec3537f860c63552e9f7c32f8a --- /dev/null +++ b/src/post_modules/CritBit/critbit/key_float.h @@ -0,0 +1,108 @@ +#ifndef CB_FLOAT_H +#define CB_FLOAT_H +#include <stdint.h> +#include <math.h> +#include "bitvector.h" + +#if SIZEOF_FLOAT_TYPE == 8 +typedef uint64_t CB_NAME(string); +typedef uint64_t CB_NAME(char); +#else +typedef uint32_t CB_NAME(string); +typedef uint32_t CB_NAME(char); +#endif + + +#ifdef cb_char +# undef cb_char +#endif +#define cb_char CB_NAME(char) + +#ifdef cb_string +# undef cb_string +#endif +#define cb_string CB_NAME(string) + +#ifdef CB_SOURCE +#if SIZEOF_FLOAT_TYPE == 4 +# define gclz(x) clz32(x) +#else +# define gclz(x) clz64(x) +#endif +#define bitsof(x) (sizeof(x)*8) +#define int2float(x) (*(FLOAT_TYPE*)&(x)) +#define float2int(x) (*(cb_char*)&(x)) + +typedef union { + FLOAT_TYPE f; + cb_char i; +} cb_float; + + +static inline cb_string cb_encode_float(const cb_float f) { + cb_char str = f.i; + + if (str & MASK(cb_char, 1)) { + str = ~str; + } else { + str |= MASK(cb_char, 1); + } + + return str; +} + +static inline FLOAT_TYPE cb_decode_float(cb_char s) { + cb_float u; + if (s & MASK(cb_char, 1)) { + s = (s ^ MASK(cb_char, 1)); + } else { + s = (~s); + } + u.i = s; + + return u.f; +} + +#define CB_ADD_KEY_REF(x) do { } while(0) +#define CB_FREE_KEY(x) do { } while(0) +#define CB_SET_KEY(node, x) do { (node)->key = (x); } while (0) + +#define CB_KEY_EQ(k1, k2) \ + ( (k1).str == (k2).str || \ + (CB_S_EQ((k1).len, (k2).len) && \ + (k1).len.bits && \ + !(((k1).str ^ (k2).str) & MASK(cb_char, (k1).len.bits)))) +#define CB_KEY_LT(k1, k2) \ + ( (k1).str < (k2).str || \ + (((k1).str == (k2).str) && CB_LT((k1).len, (k2).len)) ) + +#define CB_GET_CHAR(str, n) (str) +#define CB_WIDTH(s) (bitsof(cb_string)) /* width in bits */ +#define CB_LENGTH(str) 1 +#define CB_SIZE(key) ((key)->len) +#define CB_GET_BIT(str, size) \ + (BITN(cb_char, CB_GET_CHAR((str), (size).chars), (size).bits)) +#define CB_COUNT_PREFIX(s1, s2, n) \ + ((size_t)gclz(CB_GET_CHAR((s1), (n)) ^ CB_GET_CHAR((s2), (n)))) + +static inline cb_size cb_prefix_count_float(const cb_string s1, + const cb_string s2, + const cb_size len, + const cb_size start) { + cb_size ret; + + if (s1 == s2) return len; + + ret.chars = 0; + ret.bits = (size_t)gclz(s1 ^ s2); + + if (!len.chars) ret.bits = MINIMUM(len.bits, ret.bits); + + return ret; +} + +#define cb_prefix_count cb_prefix_count_float + +#endif + +#endif diff --git a/src/post_modules/CritBit/critbit/key_int.h b/src/post_modules/CritBit/critbit/key_int.h new file mode 100644 index 0000000000000000000000000000000000000000..db8dc39f1a275a0e83942a9645a86b0bb4f96446 --- /dev/null +++ b/src/post_modules/CritBit/critbit/key_int.h @@ -0,0 +1,76 @@ +#ifndef CB_KEY_PIKEINT_H +#define CB_KEY_PIKEINT_H +#include <stdint.h> +#include "bitvector.h" + +#if SIZEOF_INT_TYPE == 4 +typedef uint32_t CB_NAME(string); +typedef uint32_t CB_NAME(char); +#else +typedef uint64_t CB_NAME(string); +typedef uint64_t CB_NAME(char); +#endif + +#ifdef cb_char +# undef cb_char +#endif +#define cb_char CB_NAME(char) + +#ifdef cb_string +# undef cb_string +#endif +#define cb_string CB_NAME(string) + +#ifdef CB_SOURCE +#if SIZEOF_INT_TYPE == 4 +# define gclz(x) clz32(x) +#else +# define gclz(x) clz64(x) +#endif + +#define CB_INT2UINT(x) (((cb_char)x) ^ ((cb_char)1 << (sizeof(cb_char)*8 - 1))) +#define CB_UINT2INT(x) \ + ((INT_TYPE)((x) ^ ((cb_char)1 << (sizeof(cb_char)*8 - 1)))) + +#define CB_ADD_KEY_REF(x) do { } while(0) +#define CB_FREE_KEY(x) do { } while(0) +#define CB_SET_KEY(node, x) do { (node)->key = (x); } while (0) + +#define CB_KEY_EQ(k1, k2) ( (k1).str == (k2).str || \ + (CB_S_EQ((k1).len, (k2).len) && (k1).len.bits && \ + !(((k1).str ^ (k2).str) & MASK(cb_char, (k1).len.bits)))) +#define CB_KEY_MATCH(k1, k2) ( (k1).str == (k2).str && \ + (CB_S_EQ((k1).len, (k2).len) && (k1).len.bits)) +#define CB_KEY_LT(k1, k2) ( (k1).str < (k2).str || \ + (((k1).str == (k2).str) && CB_LT((k1).len, (k2).len)) ) + +#define CB_GET_CHAR(str, n) (str) +#define CB_WIDTH(s) (sizeof(cb_char)*8) /* width in byte */ +#define CB_LENGTH(str) 1 +#define CB_SIZE(key) ((key)->len) +#define CB_GET_BIT(str, size) \ + (BITN(cb_char, CB_GET_CHAR((str), (size).chars), (size).bits)) +#define CB_COUNT_PREFIX(s1, s2, n) \ + ((size_t)gclz(CB_GET_CHAR((s1), (n)) ^ CB_GET_CHAR((s2), (n)))) + +static inline cb_size cb_prefix_count_integer(const cb_string s1, + const cb_string s2, + const cb_size len, + const cb_size start) { + cb_size ret; + + if (s1 == s2) return len; + + ret.chars = 0; + ret.bits = (size_t)gclz(s1 ^ s2); + + if (!len.chars) ret.bits = MINIMUM(len.bits, ret.bits); + + return ret; +} + +#define cb_prefix_count cb_prefix_count_integer + +#endif /* CB_SOURCE */ + +#endif diff --git a/src/post_modules/CritBit/critbit/key_pikestring.h b/src/post_modules/CritBit/critbit/key_pikestring.h new file mode 100644 index 0000000000000000000000000000000000000000..724e2347a9425528a2ac9efad7154b8e3122ee7a --- /dev/null +++ b/src/post_modules/CritBit/critbit/key_pikestring.h @@ -0,0 +1,156 @@ +#ifndef CB_KEY_PIKESTRING_H +#define CB_KEY_PIKESTRING_H +#include <stdint.h> +#include "bitvector.h" + +typedef struct pike_string * CB_NAME(string); +typedef p_wchar2 CB_NAME(char); + +#ifdef cb_string +# undef cb_string +#endif +#define cb_string CB_NAME(string) + +#ifdef cb_char +# undef cb_char +#endif +#define cb_char CB_NAME(char) + +#ifdef CB_SOURCE +#define CB_ADD_KEY_REF(x) do { if ((x).str) add_ref((x).str); } while(0) +#define CB_FREE_KEY(x) do { if ((x).str) free_string((x).str); } while(0) +#define CB_SET_KEY(node, x) \ + do { CB_ADD_KEY_REF(x); \ + CB_FREE_KEY((node)->key); (node)->key = (x); } while(0) + +#define CB_KEY_EQ(k1, k2) (CB_S_EQ((k1).len, (k2).len) && (k1).str == (k2).str) +#define CB_KEY_LT(k1, k2) \ + (CB_LT((k1).len, (k2).len) && my_quick_strcmp((k1).str, (k2).str) < 0) + +#define CB_GET_CHAR(s, n) ((cb_char)INDEX_CHARP((s)->str, (n), (s)->size_shift)) +#define CB_WIDTH(s) (1 << (3 + (s)->size_shift)) /* width in bits */ +#define CB_LENGTH(str) ((str)->len) +#define CB_SIZE(key) ((key).len) +#define CB_GET_BIT(str, pos) \ + (BITN(cb_char, CB_GET_CHAR((str), (pos).chars), (pos).bits)) +static inline uint32_t CB_COUNT_PREFIX(const cb_string s1, const cb_string s2, const size_t n) { + cb_char c; + c = CB_GET_CHAR(s1, n); + c ^= CB_GET_CHAR(s2, n); + + return clz32(c); +} + + +static inline cb_size cb_prefix_count_fallback(const cb_string, + const cb_string, + const cb_size, + cb_size); + +static inline cb_size cb_prefix_count_wide0(const cb_string s1, + const cb_string s2, + const cb_size len, + cb_size start) { + size_t j = start.chars; + const unsigned char * p1, * p2; + + p1 = STR0(s1); + p2 = STR0(s2); + +#if 0 + asm volatile( + "pushf\t\n" + "orl $(1<<18), (%esp)\t\n" + "popf\t\n" + ); +#endif + +#define PREFIX(n) do { \ + size_t k = j/sizeof(uint ##n ##_t); \ + for (;k < len.chars/sizeof(uint ##n ##_t);k++) { \ + uint ##n ##_t x = ((uint ##n ##_t *)p1)[k] \ + ^ ((uint ##n ##_t *)p2)[k]; \ + if (x) { \ + uint32_t a; \ + a = clz ##n(hton ##n(x)); \ + start.chars = k*sizeof(uint ##n ##_t) + a/8; \ + start.bits = 24 + a % 8; \ + return start; \ + } \ + } \ + j = k*sizeof(uint ##n ##_t); \ + } while(0) +#ifdef lzc64 + PREFIX(64); +#endif + PREFIX(32); + PREFIX(8); + +#if 0 + asm volatile( + "pushf\t\n" + "andl $(~(1<<18)), (%esp)\t\n" + "popf\t\n" + ); +#endif + + start.chars = j; + + if (len.bits) { + uint32_t a = clz8(((p_wchar0*)p1)[j]^((p_wchar0*)p2)[j]); + start.bits = MINIMUM(len.bits, 24+a); + return start; + } + + start.bits = 0; + return start; +} + +static inline cb_size cb_prefix_count_widestring(const cb_string s1, + const cb_string s2, + const cb_size len, + const cb_size start) { +#if 0 + size_t i; + cb_size t, r; + unsigned char* chrptr = s1->str;; + static volatile cb_string n1, n2; + static volatile void* zero = NULL; + static volatile cb_size *rp, *tp; + + n1 = s1; + n2 = s2; + +#endif + if (CB_WIDTH(s1) == 8 && 8 == CB_WIDTH(s2)) + return cb_prefix_count_wide0(s1, s2, len, start); +#if 0 + if (!CB_S_EQ(r, t)) { + rp = &r; + tp = &t; + fprintf(stderr, "%p(%p) and %p(%p) have prefix %lu,%lu. should " + + "have %lu,%lu (len: %lu, %lu start: %lu, %lu)\n", + s1->str, s1, s2->str, s2, t.chars, t.bits, r.chars, + r.bits, len.chars, len.bits, start.chars, start.bits); + fprintf(stderr, "here you die: %p, %lu\n", chrptr, + r.bits/(uint64_t)zero); + fprintf(stderr, "%p %p %p", zero, n1, n2); + n1 = n2 = (void*)(zero = (uint64_t)chrptr ^ (uint64_t)n1 + ^ (uint64_t)n2); + fprintf(stderr, "%p %p %p %p %p %p", zero, n1, n2, rp, (void*)rp, + tp, (void*)tp); + } +#endif + + + return cb_prefix_count_fallback(s1, s2, len, start); +} + +#define cb_prefix_count cb_prefix_count_widestring + +#undef CB_WIDTH +#define CB_WIDTH(s) (32) + +#endif /* CB_SOURCE */ + +#endif diff --git a/src/post_modules/CritBit/critbit/key_ptr.h b/src/post_modules/CritBit/critbit/key_ptr.h new file mode 100644 index 0000000000000000000000000000000000000000..1f80ca4b87971faef670ca34bbbf74d6d10eed55 --- /dev/null +++ b/src/post_modules/CritBit/critbit/key_ptr.h @@ -0,0 +1,74 @@ +#ifndef CB_KEY_PTR_H +#define CB_KEY_PTR_H +#include <stdint.h> +#include <sys/types.h> +#include "global.h" + +#include "bitvector.h" + +#ifndef SIZEOF_CHAR_P +# error SIZEOF_CHAR_P is not defined. +#endif + +#if SIZEOF_CHAR_P == 8 +# define _VOIDP_TYPE uint64_t +# define gclz(x) clz64(x) +#elif SIZEOF_CHAR_P == 4 +# define _VOIDP_TYPE uint32_t +# define gclz(x) clz32(x) +#else +# error UNKNOWN POINTER SIZE +#endif + +typedef _VOIDP_TYPE CB_NAME(char); +typedef _VOIDP_TYPE CB_NAME(string); + +#ifdef cb_char +# undef cb_char +#endif +#define cb_char CB_NAME(char) + +#ifdef cb_string +# undef cb_string +#endif +#define cb_string CB_NAME(string) + +#define CB_ADD_KEY_REF(x) do { } while(0) +#define CB_FREE_KEY(x) do { } while(0) +#define CB_SET_KEY(node, x) do { (node)->key = (x); } while (0) + +#define CB_KEY_EQ(k1, k2) ( (k1).str == (k2).str ) +#define CB_KEY_LT(k1, k2) ( (k1).str < (k2).str \ + || (((k1).str == (k2).str) && CB_LT((k1).len, (k2).len)) ) + +#define CB_GET_CHAR(str, n) (str) +#define CB_WIDTH(s) (sizeof(cb_char)*8) /* width in byte */ +#define CB_LENGTH(str) 1 +#define CB_SIZE(key) ((key)->len) +#define CB_GET_BIT(str, size) \ + (BITN(cb_char, CB_GET_CHAR((str), (size).chars), (size).bits)) +#define CB_COUNT_PREFIX(s1, s2, n) \ + ((size_t)gclz(CB_GET_CHAR((s1), (n)) ^ CB_GET_CHAR((s2), (n)))) + +#define CB_PRINT_KEY(buf, key) do \ + { string_builder_sprintf((buf), "%p", (void*)((key).str)); } while(0) + +static inline cb_size cb_prefix_count_pointer(const cb_string s1, + const cb_string s2, + const cb_size len, + const cb_size start) { + cb_size ret; + + if (s1 == s2) return len; + + ret.chars = 0; + ret.bits = (size_t)gclz(s1 ^ s2); + + if (!len.chars) ret.bits = MINIMUM(len.bits, ret.bits); + + return ret; +} + +#define cb_prefix_count cb_prefix_count_pointer + +#endif /* _CB_KEY_PTR_H */ diff --git a/src/post_modules/CritBit/critbit/string2svalue.c b/src/post_modules/CritBit/critbit/string2svalue.c new file mode 100644 index 0000000000000000000000000000000000000000..468c3fb63352fcfc29b5c1935decd7fbd8dfc013 --- /dev/null +++ b/src/post_modules/CritBit/critbit/string2svalue.c @@ -0,0 +1,4 @@ +#define CB_SOURCE + +#include "string2svalue.h" +#include "tree_low.c" diff --git a/src/post_modules/CritBit/critbit/string2svalue.h b/src/post_modules/CritBit/critbit/string2svalue.h new file mode 100644 index 0000000000000000000000000000000000000000..b3a8191a3f4c17787bf63881bfd7fa98832eb3df --- /dev/null +++ b/src/post_modules/CritBit/critbit/string2svalue.h @@ -0,0 +1,26 @@ +#ifdef CB_NAMESPACE +# undef CB_NAMESPACE +#endif +#define CB_NAMESPACE string2svalue + +#ifndef CB_INLINE +# define CB_INLINE +#else +# undef CB_INLINE +# define CB_INLINE inline +#endif + +#ifndef CB_STATIC +# define CB_STATIC +#else +# undef CB_STATIC +# define CB_STATIC static +#endif + +#ifndef CB_STRING2SVALUE_H +# define CB_STRING2SVALUE_H +# include "critbit.h" +# include "key_pikestring.h" +# include "value_svalue.h" +# include "tree_low.h" +#endif diff --git a/src/post_modules/CritBit/critbit/value_ptr.h b/src/post_modules/CritBit/critbit/value_ptr.h new file mode 100644 index 0000000000000000000000000000000000000000..12a2e81fe06e29cd8e2c4f6ac5e1a24643ab07e4 --- /dev/null +++ b/src/post_modules/CritBit/critbit/value_ptr.h @@ -0,0 +1,11 @@ +#ifndef _PTR_VALUE_H +#define _PTR_VALUE_H + +#define CB_SET_VALUE(node, v) \ + do { (node)->value = (cb_value)(*(v)); } while (0) +#define CB_GET_VALUE(node, v) do { (*(v)) = (node)->value; } while(0) +#define CB_FREE_VALUE(v) do {} while(0) +#define CB_HAS_VALUE(node) (!!((node)->value)) +#define CB_INIT_VALUE(node) do { (node)->value = NULL; } while(0) +#define CB_RM_VALUE(node) do { CB_INIT_VALUE(node); } while(0) +#endif /* _PTR_VALUE_H */ diff --git a/src/post_modules/CritBit/critbit/value_svalue.h b/src/post_modules/CritBit/critbit/value_svalue.h new file mode 100644 index 0000000000000000000000000000000000000000..4cb573db3e7a0096de53a92b633ead1a4e5c0bb5 --- /dev/null +++ b/src/post_modules/CritBit/critbit/value_svalue.h @@ -0,0 +1,21 @@ +#ifndef CB_VALUE_SVALUE_H +#define CB_VALUE_SVALUE_H +#include "svalue.h" + +typedef struct svalue CB_NAME(value); + +#ifdef cb_value +# undef cb_value +#endif +#define cb_value CB_NAME(value) + +#define CB_SET_VALUE(node, v) assign_svalue(&((node)->value), (v)) +#define CB_GET_VALUE(node, v) assign_svalue_no_free((v), &((node)->value)) +#define CB_FREE_VALUE(v) free_svalue(v) +#define CB_HAS_VALUE(node) ((node)->value.type != T_VOID) +#define CB_INIT_VALUE(node) do { (node)->value.type = T_VOID; } while(0) +#define CB_RM_VALUE(node) do { if (CB_HAS_VALUE(node)) \ + free_svalue(&((node)->value)); CB_INIT_VALUE(node); } while(0) +#define CB_PUSH_VALUE(v) push_svalue(&(v)) + +#endif diff --git a/src/post_modules/CritBit/floattree.cmod b/src/post_modules/CritBit/floattree.cmod new file mode 100644 index 0000000000000000000000000000000000000000..fcab48ba508daa4e5cd1f331fb6b087c0259d8b0 --- /dev/null +++ b/src/post_modules/CritBit/floattree.cmod @@ -0,0 +1,101 @@ +/* vim:syntax=c + */ +#ifdef cmod___CMOD__ +#ifdef __SSE4_2__ +# include <smmintrin.h> +#endif +#include "cyclic.h" +#include "global.h" +#include "interpret.h" +#include "mapping.h" +#include "module.h" +#include "pike_error.h" +#include "pike_float.h" +#include "pike_types.h" +#include "stralloc.h" +#include "svalue.h" +#include "array.h" +#include "operators.h" +#include "builtin_functions.h" + +#ifdef CB_USE_BLOCK_ALLOC +# include "tree_block_alloc.h" + +static blk_alloc_t allocator; + +# define CB_NODE_ALLOC() alloc_item(allocator) +# define CB_NODE_FREE(x) free_item(allocator, (void*)(x)) +#endif + +#define CB_SOURCE +#define CB_STATIC +#define CB_INLINE +#define CB_NAMESPACE + +#include "critbit/float2svalue.h" +#define CB_PRINT_KEY(buf, key) \ + do { string_builder_sprintf((buf), "%f",\ + cb_decode_float((key).str)); } while(0) +#include "tree_high.c" + +#define DEFAULT_CMOD_STORAGE static + +DECLARATIONS + +/*! @module ADT */ +/*! @module CritBit */ + +PIKECLASS FloatTree { +#cmod_define iterator_class Iterator +#cmod_define tree_class FloatTree +#cmod_define cmod_OBJ2_TREE OBJ2_FLOATTREE +#cmod_define T_KEY (PIKE_T_FLOAT|PIKE_T_INT) +#cmod_define key_ptype float|int +#cmod_define sample_key 12.0 +#cmod_define sample_keys ({ 80.4, 99.9, 14.2 }) +#cmod_define ordered_keys 14.2, 80.4 and 99.9 +#cmod_define reverse_ordered_keys 99.9, 80.4 and 14.2 +#cmod_define sample_value ({ 4, 5, 6 }) +#cmod_define extra_doc + +#define CB_LOW_ASSIGN_SVALUE_KEY(key, s) do { \ + struct svalue * __ = (s); \ + SET_SVAL(*__, PIKE_T_FLOAT, 0, float_number, cb_decode_float((key).str)); \ +} while(0) +#define CB_PUSH_KEY(key) push_float(cb_decode_float((key).str)); +#define CB_STRING_FROM_SVALUE(v) cb_encode_float((cb_float) \ + (((v)->type == PIKE_T_INT ) \ + ? ((FLOAT_TYPE)(v)->u.integer) \ + : (v)->u.float_number)) +#define CB_LOW_KEY_FROM_SVALUE(x) CB_KEY_FROM_STRING(CB_STRING_FROM_SVALUE(x)) + +#cmod_include "redefine.H" + +#cmod_include "tree_header.H" +#cmod_include "iterator_source.H" +#undef THIS +#define THIS THIS_FLOATTREE +#cmod_include "tree_source.H" +} + +/*! @endmodule */ +/*! @endmodule */ + +void pike_init_floattree_module(void) { +#ifdef CB_USE_BLOCK_ALLOC + allocator = init_allocator(128, sizeof(cb_node)); +#endif + INIT +} + +void pike_exit_floattree_module(void) { +#ifdef CB_USE_BLOCK_ALLOC + free(allocator); + allocator = NULL; +#endif + EXIT +} +#else +void pike_exit_floattree_module(void) {} +void pike_init_floattree_module(void) {} +#endif diff --git a/src/post_modules/CritBit/glue.c b/src/post_modules/CritBit/glue.c new file mode 100644 index 0000000000000000000000000000000000000000..c7aa00fd150fdc7b027640c1c7372d908b73f9e7 --- /dev/null +++ b/src/post_modules/CritBit/glue.c @@ -0,0 +1,38 @@ +#include "global.h" +#include "module.h" + +#include "interpret.h" +#include "svalue.h" +#include "stralloc.h" +#include "array.h" +#include "pike_macros.h" +#include "program.h" +#include "stralloc.h" +#include "object.h" +#include "pike_types.h" +#include "pike_error.h" +#include "builtin_functions.h" +#include "module_support.h" +#include "bignum.h" +#include "operators.h" +#include "gc.h" +#include <stdio.h> + +extern void pike_init_inttree_module(); +extern void pike_init_tree_module(); +extern void pike_init_floattree_module(); +extern void pike_exit_inttree_module(); +extern void pike_exit_tree_module(); +extern void pike_exit_floattree_module(); + +PIKE_MODULE_INIT { + pike_init_inttree_module(); + pike_init_tree_module(); + pike_init_floattree_module(); +} + +PIKE_MODULE_EXIT { + pike_exit_inttree_module(); + pike_exit_tree_module(); + pike_exit_floattree_module(); +} diff --git a/src/post_modules/CritBit/inttree.cmod b/src/post_modules/CritBit/inttree.cmod new file mode 100644 index 0000000000000000000000000000000000000000..43bde488b07083c446e0119d6e8afdbe4821d7d8 --- /dev/null +++ b/src/post_modules/CritBit/inttree.cmod @@ -0,0 +1,208 @@ +/* vim:syntax=c + */ +#ifdef cmod___CMOD__ +#ifdef __SSE4_2__ +# include <smmintrin.h> +#endif +#include "cyclic.h" +#include "global.h" +#include "interpret.h" +#include "mapping.h" +#include "module.h" +#include "array.h" +#include "pike_error.h" +#include "pike_float.h" +#include "pike_types.h" +#include "stralloc.h" +#include "svalue.h" +#include "array.h" +#include "operators.h" +#include "builtin_functions.h" + +#ifdef CB_USE_BLOCK_ALLOC +# include "tree_block_alloc.h" + +static blk_alloc_t allocator; + +# define CB_NODE_ALLOC() alloc_item(allocator) +# define CB_NODE_FREE(x) free_item(allocator, (void*)(x)) +#endif + +#define CB_STATIC +#define CB_INLINE +#define CB_SOURCE +#define CB_NAMESPACE + +#include "critbit/int2svalue.h" +#define CB_PRINT_KEY(buf, key) do \ + { string_builder_sprintf((buf), "%d", CB_UINT2INT((key).str)); } while(0) +#define CB_LOW_ASSIGN_SVALUE_KEY(key, s) do { \ + struct svalue * __ = (s); \ + SET_SVAL(*__, PIKE_T_INT, 0, integer, CB_UINT2INT((key).str)); \ +} while(0) +#define CB_PUSH_KEY(key) push_int(CB_UINT2INT((key).str)); +#define CB_STRING_FROM_SVALUE(v) CB_INT2UINT((v)->u.integer) +#define CB_LOW_KEY_FROM_SVALUE(x) CB_KEY_FROM_STRING(CB_STRING_FROM_SVALUE(x)) +#include "tree_high.c" + +#define DEFAULT_CMOD_STORAGE static + +DECLARATIONS + +/*! @module ADT */ +/*! @module CritBit */ + +PIKECLASS IntTree { +#cmod_define iterator_class Iterator +#cmod_define tree_class IntTree +#cmod_define cmod_OBJ2_TREE OBJ2_INTTREE +#cmod_define T_KEY PIKE_T_INT +#cmod_define key_ptype int +#cmod_define sval_type integer +#cmod_define sample_key 12 +#cmod_define sample_keys ({ 1025, 15000, 3 }) +#cmod_define ordered_keys 3, 1025 and 15000 +#cmod_define reverse_ordered_keys 15000, 1025 and 3 +#cmod_define sample_value ({ 1, 2 ,3 }) +#cmod_define extra_doc +#cmod_include "redefine.H" + +#cmod_include "tree_header.H" +#cmod_include "iterator_source.H" +#undef THIS +#define THIS THIS_INTTREE +#cmod_include "tree_source.H" +} + +#define WASTED_BITS (8*(sizeof(INT_TYPE)-4)) + +cb_key cb_key_from_ptype_ipv4(struct pike_string * s) { + cb_key key; + uint32_t a, b, c, d, e; + + key.len.chars = 1; + key.len.bits = 0; + + if (s->size_shift || s->len > 18) Pike_error("Malformed ip.\n"); + + switch (sscanf((char *)STR0(s), "%3u.%3u.%3u.%3u/%2u", &a, &b, &c, &d, &e)) + { + case 5: + if (e > 32) Pike_error("Mask is too big!"); + key.len.chars = 0; + key.len.bits = WASTED_BITS + e; + case 4: + if (a > 255 || b > 255 || c > 255 || d > 255) + Pike_error("Bad ip.\n"); + key.str = (INT_TYPE)(a << 24 | b << 16 | c << 8 | d); + break; + default: + Pike_error("Malformed ip.\n"); + } + + return key; +} + +struct pike_string * cb_ptype_from_key_ipv4(cb_key key) { + char buf[19]; + size_t len; + + uint32_t ip = (uint32_t)key.str; + + if (key.len.bits || !key.len.chars) { + len = snprintf(buf, 19, "%u.%u.%u.%u/%u", ip>>24, (ip>>16)&0xff, + (ip>>8)&0xff, ip&0xff, (uint32_t)(key.len.bits + - WASTED_BITS)); + } else { + len = snprintf(buf, 19, "%u.%u.%u.%u", ip>>24, (ip>>16)&0xff, + (ip>>8)&0xff, ip&0xff); + } + + return make_shared_binary_string(buf, MINIMUM(19, len)); +} + +PIKECLASS IPv4Tree { +#cmod_define iterator_class Iterator +#cmod_define tree_class IPv4Tree +#cmod_define cmod_OBJ2_TREE OBJ2_IPV4TREE +#cmod_define T_KEY PIKE_T_STRING +#cmod_define key_ptype string +#cmod_define sval_type string +#cmod_define sample_key "127.0.0.0/8" +#cmod_define sample_keys ({ "10.243.7.1", "127.0.0.1/8", "172.16.5.2" }) +#cmod_define ordered_keys "10.243.7.1", "127.0.0.1/8" and "172.16.5.2" +#cmod_define reverse_ordered_keys "172.16.5.2", "127.0.0.1/8" and "10.243.7.1" +#cmod_define sample_value "reject" +#cmod_define extra_doc + +#undef CB_INT2UINT +#undef CB_UINT2INT +#define CB_INT2UNIT(x) (x) +#define CB_UINT2INT(x) (x) +#undef CB_PUSH_KEY +#define CB_PUSH_KEY(key) push_string(cb_ptype_from_key_ipv4(key)) +#undef CB_KEY_FROM_PTYPE +#define CB_KEY_FROM_PTYPE(x) cb_key_from_ptype_ipv4(x) +#undef CB_STRING_FROM_SVALUE +#undef CB_LOW_KEY_FROM_SVALUE +#undef CB_KEY_FROM_SVALUE +#define CB_LOW_KEY_FROM_SVALUE(x) CB_KEY_FROM_PTYPE((x)->u.string) +#undef CB_LOW_ASSIGN_SVALUE_KEY +#define CB_LOW_ASSIGN_SVALUE_KEY(key, s) do { \ + struct svalue * __ = s; \ + SET_SVAL(*__, PIKE_T_STRING, 0, string, cb_ptype_from_key_ipv4(key)); \ +} while (0) + +#cmod_include "redefine.H" + +#cmod_include "tree_header.H" +#cmod_include "iterator_source.H" +#undef THIS +#define THIS THIS_IPV4TREE +#cmod_include "tree_source.H" + + PIKEFUN int mask(int n) { + RETURN MASK(INT_TYPE, n); + } + + PIKEFUN int umask(int n) { + RETURN MASK(uint64_t, n); + } + + PIKEFUN int cmp_key(mixed a, mixed b) { + cb_key k1 = CB_KEY_FROM_SVALUE(a); + cb_key k2 = CB_KEY_FROM_SVALUE(b); + INT_TYPE ret; + if (CB_KEY_LT(k1, k2)) { + ret = -1; + } else if (CB_KEY_MATCH(k1, k2)) { + ret = 0; + } else { + ret = 1; + } + pop_n_elems(args); + push_int(ret); + } +} + +/*! @endmodule */ +/*! @endmodule */ + +void pike_init_inttree_module(void) { +#ifdef CB_USE_BLOCK_ALLOC + allocator = init_allocator(128, sizeof(cb_node)); +#endif + INIT +} + +void pike_exit_inttree_module(void) { +#ifdef CB_USE_BLOCK_ALLOC + free(allocator); + allocator = NULL; +#endif + EXIT +} +#else +void pike_init_inttree_module(void) {} +void pike_exit_inttree_module(void) {} +#endif diff --git a/src/post_modules/CritBit/iterator_source.H b/src/post_modules/CritBit/iterator_source.H new file mode 100644 index 0000000000000000000000000000000000000000..7cb5237d654b24e6ca15afa8c8148a79b6fef36f --- /dev/null +++ b/src/post_modules/CritBit/iterator_source.H @@ -0,0 +1,247 @@ + DOCSTART() @class Iterator + *! Iterator class for tree_class trees. Supports iterating over ranges + *! with arbitrary stepping and direction. + *! + *! This is used by default when calling @expr{foreach@} on an object of + *! tree_class. In @expr{foreach@} the iterator runs over all elements + *! from the first to the last. + *! + *! @seealso + *! @[predef::Iterator] for a description of the interface. + DOCEND() + PIKECLASS iterator_class { + CVAR cb_node_t lastnode, tree; + CVAR cb_key lastkey; + CVAR cb_value lastval; + CVAR struct object * ptree; + CVAR size_t * revv, lastrev; + CVAR INT_TYPE step; + CVAR cb_key stop; + + INIT { + THIS->lastval.type = T_VOID; + THIS->ptree = NULL; + THIS->lastnode = NULL; + THIS->step = 1; + THIS->stop.len.chars = 0; + THIS->stop.len.bits = 0; + THIS->stop.str = (cb_string)0; + } + + EXIT { + if (THIS->stop.len.chars || THIS->stop.len.bits || THIS->stop.str) { + CB_FREE_KEY(THIS->stop); + } + + if (THIS->lastnode) { + CB_FREE_KEY(THIS->lastkey); + CB_FREE_VALUE(&THIS->lastval); + } + if (THIS->ptree) { + free_object(THIS->ptree); + THIS->ptree = NULL; + } + } + + static inline int cmod_CONCAT_EVAL(tree_class,_,iterator_class,_step)() { + cb_node_t t; + INT_TYPE c = THIS->step; + + if (THIS->lastrev == *THIS->revv) { + t = THIS->lastnode; + } else { + THIS->lastrev = * THIS->revv; + + if (THIS->tree) { + t = cb_index(THIS->tree, THIS->lastkey); + if (t) { + THIS->lastnode = t; + } else { /* the last node was removed and freed. */ + if (c > 0) { + t = cb_find_next(THIS->tree, THIS->lastkey); + c--; + } else { + t = cb_find_previous(THIS->tree, THIS->lastkey); + c++; + } + } + } else { + t = NULL; + } + } + + if (THIS->lastnode) { + CB_FREE_KEY(THIS->lastkey); + CB_FREE_VALUE(&THIS->lastval); + } + + if (t) { + if (c > 0) { + WALK_FORWARD(t, { + if (CB_HAS_VALUE(_)) c--; + if (c == 0) break; + }); + } else if (c < 0) { + WALK_BACKWARD(t, { + if (CB_HAS_VALUE(_)) c++; + if (c == 0) break; + }); + if (t == THIS->tree && !CB_HAS_VALUE(t)) t = NULL; + } + + if (t) { + /* check for stop + * TODO: if we know that the lastnode is a parent + * of t, and stop was bigger than both children, then + * we could skip this check. */ + if (THIS->stop.len.chars || THIS->stop.len.bits) { + if (THIS->step < 0) { + if (CB_KEY_LT(t->key, THIS->stop)) t = NULL; + } else { + if (CB_KEY_LT(THIS->stop, t->key)) t = NULL; + } + } + if (t) { + CB_GET_VALUE(t, &THIS->lastval); + THIS->lastkey = t->key; + CB_ADD_KEY_REF(THIS->lastkey); + } + } + } + THIS->lastnode = t; + return !!t; + } + + DOCSTART() @decl void create(tree_class tree, void|int step, @ + *!void|mixed start, void|mixed stop) + *! Returns an iterator object that runs from @expr{start@} to + *! @expr{stop@} using a stepsize of @expr{step@}. The arguments + *! default to @expr{1@}, @expr{tree->first()@} and + *! @expr{tree->last()@}, respectively. + DOCEND() + PIKEFUN void create(object tree, void|int step, void|mixed start, + void|mixed stop) { + cb_node_t t = NULL; + INT32 encode_fun; + if (-1 == low_get_storage(tree->prog, TREE_CLASSIFY(_program))) { + SIMPLE_BAD_ARG_ERROR("create", 1, "CritBit." cmod_STRFY_EVAL(tree_class)); + } + add_ref(THIS->ptree = tree); + THIS->revv = &(cmod_OBJ2_TREE(tree)->rev); + THIS->tree = cmod_OBJ2_TREE(tree)->tree; + + THIS->stop.len.chars = 0; + THIS->stop.len.bits = 0; + + encode_fun = cmod_OBJ2_TREE(tree)->encode_fun; + + if (THIS->tree) { + if (args < 2 || IS_UNDEFINED(step)) { + THIS->step = 1; + } else { + THIS->step = step->u.integer; + if (THIS->step == 0) + SIMPLE_BAD_ARG_ERROR("create", 2, "int(..-1)|int(1..)"); + } + + switch (args) { + case 4: + if (!IS_UNDEFINED(stop)) { + if (encode_fun >= 0) { + push_svalue(stop); + apply_low(tree, encode_fun, 1); + assign_svalue(stop, Pike_sp-1); + pop_stack(); + if (!(stop->type & T_KEY)) + SIMPLE_BAD_ARG_ERROR("create", 4, cmod_STRFY_EVAL(key_ptype)); + } + THIS->stop = CB_LOW_KEY_FROM_SVALUE(stop); + CB_ADD_KEY_REF(THIS->stop); + } + case 3: + if (!IS_UNDEFINED(start)) { + cb_key s; + if (encode_fun >= 0) { + push_svalue(start); + apply_low(tree, encode_fun, 1); + assign_svalue(start, Pike_sp-1); + pop_stack(); + if (!(start->type & T_KEY)) + SIMPLE_BAD_ARG_ERROR("create", 3, cmod_STRFY_EVAL(key_ptype)); + } + s = CB_LOW_KEY_FROM_SVALUE(start); + + t = cb_index(THIS->tree, s); + + if (!t) { + t = (THIS->step < 0) + ? cb_find_previous(THIS->tree, s) + : cb_find_next(THIS->tree, s); + if (t && (THIS->stop.len.chars || THIS->stop.len.bits)) { + /* check if we ran over stop */ + if (THIS->step < 0) { + if (CB_KEY_LT(t->key, THIS->stop)) + t = NULL; + } else { + if (CB_KEY_LT(THIS->stop, t->key)) + t = NULL; + } + } + } + } + case 2: + case 1: + if (!t) t = (THIS->step < 0) + ? cb_find_last(THIS->tree) + : cb_find_first(THIS->tree); + break; + default: + Pike_error("Too many arguments.\n"); + } + + if (t) { + CB_GET_VALUE(t, &THIS->lastval); + THIS->lastkey = t->key; + CB_ADD_KEY_REF(THIS->lastkey); + } + THIS->lastnode = t; + } + + pop_n_elems(args); + } + + PIKEFUN object _get_iterator() { + ref_push_object(Pike_fp->current_object); + } + + PIKEFUN int next() { + if(cmod_CONCAT_EVAL(tree_class,_,iterator_class,_step)()) { + push_int(1); + return; + } + push_undefined(); + return; + } + + PIKEFUN key_ptype index() { + if (THIS->lastnode) { + INT32 fun = cmod_OBJ2_TREE(THIS->ptree)->decode_fun; + CB_PUSH_KEY(THIS->lastkey); + if (fun >= 0) + apply_low(THIS->ptree, fun, 1); + } else push_undefined(); + } + + PIKEFUN mixed value() { + if (THIS->lastnode) { + CB_PUSH_VALUE(THIS->lastval); + return; + } + push_undefined(); + } + + PIKEFUN int `!() { + push_int(!THIS->lastnode); + } + } + /*! @endclass */ diff --git a/src/post_modules/CritBit/redefine.H b/src/post_modules/CritBit/redefine.H new file mode 100644 index 0000000000000000000000000000000000000000..4635b5919db3789e7de4e5693e4477d8f4281447 --- /dev/null +++ b/src/post_modules/CritBit/redefine.H @@ -0,0 +1,23 @@ +cmod_REDEFINE(iterator_class) +cmod_REDEFINE(tree_class) +cmod_REDEFINE(cmod_OBJ2_TREE) +cmod_REDEFINE(T_KEY) +cmod_REDEFINE(key_ptype) +cmod_REDEFINE(sval_type) +cmod_REDEFINE(sample_key) +cmod_REDEFINE(sample_keys) +cmod_REDEFINE(ordered_keys) +cmod_REDEFINE(reverse_ordered_keys) +cmod_REDEFINE(sample_value) +cmod_REDEFINE(extra_doc) +#ifndef STRFY2 +# define STRFY2(x) #x +# ifdef STRFY +# undef STRFY +# endif +# define STRFY(x...) STRFY2(x) +#endif +#cmod_ifdef TREE_CLASSIFY +# cmod_undef TREE_CLASSIFY +#cmod_endif +#cmod_define TREE_CLASSIFY(x...) cmod_CONCAT_EVAL(tree_class, ##x) diff --git a/src/post_modules/CritBit/stringtree.cmod b/src/post_modules/CritBit/stringtree.cmod new file mode 100644 index 0000000000000000000000000000000000000000..fb19786032e957fc2ba726d9cb2232dd50210164 --- /dev/null +++ b/src/post_modules/CritBit/stringtree.cmod @@ -0,0 +1,104 @@ +/* vim:syntax=c + */ +#include "cyclic.h" +#include "global.h" +#include "interpret.h" +#include "mapping.h" +#include "module.h" +#include "pike_error.h" +#include "pike_float.h" +#include "pike_types.h" +#include "stralloc.h" +#include "svalue.h" +#include "array.h" +#include "operators.h" +#include "builtin_functions.h" + +#ifdef cmod___CMOD__ + +#define CB_SOURCE +#define CB_STATIC +#define CB_INLINE +#define CB_NAMESPACE + +#include "critbit/string2svalue.h" + +/* this is only used in pike code! */ +#define CB_PRINT_CHAR(buf, str, n) \ + do { string_builder_sprintf((buf), "%c", CB_GET_CHAR(str, n)); } while(0) + +#define CB_PRINT_KEY(buf, key) \ + do { string_builder_shared_strcat((buf), (key).str); } while(0) +#define CB_LOW_ASSIGN_SVALUE_KEY(key, s) do { \ + struct pike_string * _key = (key).str; \ + struct svalue * _svalue = (s); \ + add_ref(_key); \ + SET_SVAL(*_svalue, PIKE_T_STRING, 0, string, _key); \ +} while(0) +#define CB_PUSH_KEY(key) ref_push_string((key).str) +#define CB_PUSH_STRING(str) ref_push_string(str) +#define CB_STRING_FROM_SVALUE(v) ((v)->u.string) +#define CB_LOW_KEY_FROM_SVALUE(v) CB_KEY_FROM_STRING(CB_STRING_FROM_SVALUE(v)) + +/* #define CB_USE_BLOCK_ALLOC */ +#ifdef CB_USE_BLOCK_ALLOC +# undef BLOCK_ALLOC_NEXT +# define BLOCK_ALLOC_NEXT parent +# include "block_alloc.h" + +BLOCK_ALLOC_FILL_PAGES(cb_node, 2) +# define CB_NODE_ALLOC() CONCAT2(alloc_,CB_NAME(node))() +# define CB_NODE_FREE(x) CONCAT2(really_free_,CB_NAME(node))(x) +#endif + +#include "tree_high.c" + +#define DEFAULT_CMOD_STORAGE static + +DECLARATIONS + +/*! @module ADT */ +/*! @module CritBit */ + +PIKECLASS StringTree { +#cmod_define iterator_class Iterator +#cmod_define tree_class StringTree +#cmod_define cmod_OBJ2_TREE OBJ2_STRINGTREE +#cmod_define T_KEY PIKE_T_STRING +#cmod_define key_ptype string +#cmod_define sval_type string +#cmod_define sample_key "foo" +#cmod_define sample_keys ({ "fooo", "bar", "ahead" }) +#cmod_define ordered_keys "ahead", "bar" and "foo" +#cmod_define reverse_ordered_keys "foo", "bar" and "ahead" +#cmod_define sample_value ({ 7, 8, 9 }) +#cmod_define extra_doc +#cmod_include "redefine.H" + +#cmod_include "tree_header.H" +#cmod_include "iterator_source.H" +#undef THIS +#define THIS THIS_STRINGTREE +#cmod_include "tree_source.H" +} + +/*! @endmodule */ +/*! @endmodule */ + +void pike_init_tree_module(void) { +#ifdef CB_USE_BLOCK_ALLOC + CONCAT3(init_, cb_node, _blocks)(); +#endif + INIT +} + +void pike_exit_tree_module(void) { + EXIT +} + +#else +void pike_init_tree_module(void) { + add_integer_constant("this_program_does_not_exist", 1, 0); +} +void pike_exit_tree_module(void) {} +#endif diff --git a/src/post_modules/CritBit/testsuite.in b/src/post_modules/CritBit/testsuite.in new file mode 100644 index 0000000000000000000000000000000000000000..2bd1d8ea46b31c38e3f01dea8d801af46f8e77e7 --- /dev/null +++ b/src/post_modules/CritBit/testsuite.in @@ -0,0 +1,192 @@ +START_MARKER + +test_true(programp(ADT.CritBit.StringTree)) +test_true(programp(ADT.CritBit.IntTree)) +test_true(programp(ADT.CritBit.FloatTree)) +test_true(programp(ADT.CritBit.IPv4Tree)) + +define(test_const_loop,[[ + test_any([[ + mapping m = GET_SAMPLE(); + object tree = GET_TREE(); + function sortfun = GET_SORTFUN(); + array a = GET_SORTED_A(); + + $1 + return 1; + ]], 1) +]]) + +define(test_tree_equal,[[ + test_equal([[$1]], [[$2]]) + test_equal([[object_program($1)]], [[object_program($2)]]) +]]) + +define(low_test_tree,[[ + test_do(add_constant("GET_SAMPLE", lambda() { return all_constants()->THE_SAMPLE; }) ) + test_do(add_constant("SET_SAMPLE", lambda(mixed m) { add_constant("THE_SAMPLE", m); }) ) + test_do(add_constant("GET_SORTFUN", lambda() { return all_constants()->THE_SORTFUN; }) ) + test_do(add_constant("SET_SORTFUN", lambda(mixed m) { add_constant("THE_SORTFUN", m); }) ) + test_do(add_constant("GET_TREE", lambda() { return all_constants()->THE_TREE; }) ) + test_do(add_constant("SET_TREE", lambda(mixed m) { add_constant("THE_TREE", m); }) ) + test_do(add_constant("GET_SORTED_A", lambda() { return all_constants()->THE_SORTED_A; }) ) + test_do(add_constant("SET_SORTED_A", lambda(mixed m) { add_constant("THE_SORTED_A", m); }) ) + test_do([[ + random_seed(4044); + SET_SAMPLE( $2 ); + SET_SORTFUN( $3 ); + SET_TREE($1(GET_SAMPLE())); + SET_SORTED_A($3(indices(GET_SAMPLE()))); + ]]) + $4 + test_do(add_constant("GET_M") ) + test_do(add_constant("GET_SAMPLE") ) + test_do(add_constant("GET_SORTFUN") ) + test_do(add_constant("GET_TREE") ) + test_do(add_constant("GET_SORTED_A") ) + test_do(add_constant("SET_M") ) + test_do(add_constant("SET_SAMPLE") ) + test_do(add_constant("SET_SORTFUN") ) + test_do(add_constant("SET_TREE") ) + test_do(add_constant("SET_SORTED_A") ) + + test_do(add_constant("THE_M") ) + test_do(add_constant("THE_SAMPLE") ) + test_do(add_constant("THE_SORTFUN") ) + test_do(add_constant("THE_TREE") ) + test_do(add_constant("THE_SORTED_A") ) + +]]) + +define(test_loop,[[ + low_test_tree([[$1]], [[$2]], [[$3]], [[ + test_equal((mapping)GET_TREE(), GET_SAMPLE()) + test_tree_equal(GET_TREE(), GET_TREE()->get_subtree()) + test_tree_equal(GET_TREE(), GET_TREE()->copy())) + test_tree_equal($1()+GET_TREE(), GET_TREE()) + test_tree_equal(GET_TREE()+GET_TREE(), GET_TREE()) + test_tree_equal(GET_TREE()+$1(), GET_TREE())) + test_tree_equal(GET_TREE()-GET_TREE(), $1()) + test_tree_equal(GET_TREE()-$1(), GET_TREE())) + test_tree_qual(GET_TREE()[GET_TREE()->first()..GET_TREE()->last()], GET_TREE())) + test_const_loop([[ + foreach (GET_SAMPLE(); mixed a; mixed b) { + if (b != GET_TREE()[a]) + error("Value mismatch: (%O : %O) vs (%O : %O)\n", a, b, a, GET_TREE()[a]); + } + ]]) + test_const_loop([[ + if (sizeof(GET_SAMPLE()) != sizeof(GET_TREE())) + error("Size mismatch: %O vs %O\n", sizeof(GET_SAMPLE()), sizeof(GET_TREE())); + ]]) + test_const_loop([[ + int i; + foreach (tree; mixed key; mixed val) { + mixed nth = GET_TREE()->nth(i++)[0]; + if (nth != key) + error("Key mismatch in nth() vs tree order: (%O : %O) vs (%O : %O)\n", i, key, i, nth); + } + ]]) + test_const_loop([[ + mixed t = GET_TREE()->first(); + foreach (GET_SORTED_A(); int i; mixed key) { + if (t != key) + error("Key mismatch in range iterate(): (%O : %O) vs (%O : %O)\n", i, t, i, key); + t = GET_TREE()->next(t); + if (GET_SAMPLE()[key] != GET_TREE()[key]) + error("Value mismatch in order: (%O : %O) vs (%O : %O)\n", key, GET_SAMPLE()[key], key, GET_TREE()[key]); + mixed nth = GET_TREE()->nth(i)[0]; + if (nth != key) + error("Value mismatch in nth(): (%O : %O) vs (%O : %O)\n", i, key, i, nth); + } + + ]]) + test_const_loop([[ + void test_range_operator(int step) { + int i = (step < 0) ? sizeof(tree)-1 : 0; + foreach (tree->Iterator(tree, step); mixed key; mixed value) { + if (GET_SORTED_A()[i] != key) + error("Key mismatch in range iterate(): (%O : %O) vs (%O : %O)\n", i, GET_SORTED_A()[i], i, key); + if (GET_SAMPLE()[GET_SORTED_A()[i] ] != value) + error("Value mismatch in range iterate(): (%O : %O) vs (%O : %O)\n", i, GET_SAMPLE()[GET_SORTED_A()[i] ], i, value); + i+=step; + } + + if (step > 0) test_range_operator(-step); + }; + + foreach (enumerate(40, 13, 1); ; int n) { + if (n) test_range_operator(n); + } + ]]) + test_const_loop([[ + void test_range_operator(int start, int stop, int step) { + int i = start; + + if (step == 0) return; + + foreach (tree->Iterator(tree, step, a[start], a[stop]); mixed key; mixed value) { + if (GET_SORTED_A()[i] != key) + error("Key mismatch in range iterate(): (%O : %O) vs (%O : %O)\n", i, GET_SORTED_A()[i], i, key); + if (GET_SAMPLE()[GET_SORTED_A()[i] ] != value) + error("Value mismatch in range iterate(): (%O : %O) vs (%O : %O)\n", i, GET_SAMPLE()[GET_SORTED_A()[i] ], i, value); + i+=step; + } + +#if constant(ADT.CritBit.Range) + object r = ADT.CritBit.Range(a[start], a[stop]); + object rs = ADT.CritBit.RangeSet(object_program(tree)); + rs[r] = 1; + foreach (a;; mixed key) { + if (equal(GET_SORTFUN()(({ key, r.b, r.a}))[1], key)) { + if (undefinedp(rs[key])) { + error("RangeSet error. %O not within [%O,%O]\n", key, r.a, r.b); + } + } else { + if (!undefinedp(rs[key])) { + error("RangeSet error. %O should not be within [%O,%O]\next: %O\n", key, r.a, r.b, rs->tree->next(key)); + } + } + } +#endif + }; + + for (int i = 0; i < 100; i++) { + int start = random(sizeof(a)); + int stop = start + random(sizeof(a)-start); + test_range_operator(start, stop, random(stop-start)); + } + ]]) + ]]) + low_test_tree([[$1]], [[$2]], [[$3]], [[ + test_const_loop([[ + foreach (tree; mixed key; mixed val) { + if (val != m[key]) + error("Key mismatch in random delete. %O : %O vs %O\n", key, val, m[key]); + array t = random(tree); + m_delete(tree, t[0]); + m_delete(m, t[0]); + if (!zero_type(tree[t[0] ])) error("m_delete failed in random delete (key: %O)\n", t[0]); + } + if (sizeof(m) != sizeof(tree)) { + error("Size mismatch after random delete. %O vs %O\n", sizeof(m), sizeof(tree)); + } + ]]) + ]]) +]]) + +define(test_tree, [[ + test_loop([[$1]], [[$2]], [[$3]]) + test_loop([[$1]], ([]), [[$3]]) +]]) + +test_tree(ADT.CritBit.Tree, [[all_constants()+mkmapping(map(allocate(1000, 1000), random_string), enumerate(1000))]], sort) +test_tree(ADT.CritBit.IntTree, [[mkmapping(enumerate(100)+enumerate(1000, -19, 666)+enumerate(1000, Int.NATIVE_MAX/600, Int.NATIVE_MIN), enumerate(2100))]], sort) +test_tree(ADT.CritBit.IPv4Tree, [[mkmapping(map(enumerate(100)+enumerate(1000, -19, 666)+enumerate(1000, Int.NATIVE_MAX/600, Int.NATIVE_MIN), lambda (int i) { + return ADT.CritBit.get_ipv4(i, (abs(i) % 51) & 31); +}), enumerate(2100))]], ADT.CritBit.sort_ipv4) +test_tree(ADT.CritBit.FloatTree, [[mkmapping((array(float))(enumerate(100)+enumerate(1000, -19, 666)+enumerate(1000, Int.NATIVE_MAX/600, Int.NATIVE_MIN)), enumerate(2100))]], sort) +test_tree(ADT.CritBit.DateTree, [[mkmapping(map(enumerate(2000, time()/3000, 1), Function.curry(Calendar.Second)("unix")), enumerate(2000))]], sort) + + +END_MARKER diff --git a/src/post_modules/CritBit/tree_header.H b/src/post_modules/CritBit/tree_header.H new file mode 100644 index 0000000000000000000000000000000000000000..db622bba6e6b9db4b62ebcd670269c5aa2562f5c --- /dev/null +++ b/src/post_modules/CritBit/tree_header.H @@ -0,0 +1,133 @@ +DOCSTART() @class tree_class + *! This class implements a CritBit-tree/trie that can be used as a + *! mapping-like data structure. Values of @expr{key_ptype@} can be + *! used as indices, while any possible type (also @expr{mixed@}) can + *! be stored. + *! + *! CritBit trees are prefixed based search trees that allow for fast random + *! access as well as prefix and range based lookups. Keys are stored in + *! alphabetical order and can be iterated over using @expr{foreach@}. + *! Other than that, it can be used like @expr{mapping(key_ptype:mixed)@}. + *! + *! @example + *! @code + *!ADT.CritBit.tree_class tree = ADT.CritBit.tree_class(); + *!key_ptype key1 = sample_key; + *!tree[key1] = sample_value; + *!tree[key1]; /cmod___SLASH__ now is sample_value + *!m_delete(tree, key1); // tree is empty again + *!@endcode + *! @example + *! @code + *!ADT.CritBit.tree_class tree = ADT.CritBit.tree_class(); + *!array(key_ptype) a = sample_keys; + *!foreach(a; int idx; key_ptype val) { + *! tree[val] = idx; + *!} + *!foreach(tree; key_ptype key; mixed val) { + *! /cmod___SLASH__ in here the keys will be reached in order ordered_keys. + *!} + *!@endcode + *! @example + *! @code + *!ADT.CritBit.tree_class tree = ADT.CritBit.tree_class(); + *!array(key_ptype) a = sample_keys; + *!foreach (a; int idx; key_ptype val) { + *! tree[val] = idx; + *!} + *!foreach(ADT.CritBit.TREE_CLASSIFY(.Iterator)(tree, -1); @ + *!key_ptype key; mixed val) { + *! /cmod___SLASH__ in here the keys will be reached in order reverse_ordered_keys. + *!} + *!@endcode + *! @seealso + *! @[ADT.CritBit.TREE_CLASSIFY(.Iterator)] + DOCEND() + +DOCSTART() @decl key_ptype encode_key(mixed o) + *! @decl mixed decode_key(key_ptype o) + *! These callbacks can be implemented when inheriting tree_class in order + *! to allow for arbitrary key types. @[encode_key] is similar to the + *! @[lfun::_hash()] callback. This only works as expected when it is possible + *! to implement a unique representation for keys. These callbacks are called + *! everytime a key is stored or indexed in the tree. +DOCEND() + + CVAR cb_node_t tree; + CVAR size_t rev; + CVAR INT32 encode_fun; + CVAR INT32 decode_fun; + CVAR INT32 copy_fun; + CVAR INT32 insert_fun; + + +#undef CB_TRANSFORM_KEY +#define CB_TRANSFORM_KEY(svalue) { \ + if (THIS->encode_fun >= 0) { \ + push_svalue(svalue); \ + apply_low(Pike_fp->current_object, THIS->encode_fun, 1);\ + assign_svalue((svalue), Pike_sp-1); \ + pop_stack(); \ + } \ +} while (0) +#undef CB_CHECK_KEY +#define CB_CHECK_KEY(svalue, fun, num) do { \ + CB_TRANSFORM_KEY(svalue); \ + if (!((svalue)->type & T_KEY)) \ + SIMPLE_BAD_ARG_ERROR(fun, (num), STRFY(key_ptype)); \ +} while(0) +#undef CB_PUSH_TRANSFORM_KEY +#define CB_PUSH_TRANSFORM_KEY(key) do { \ + CB_PUSH_KEY(key); \ + if (THIS->decode_fun >= 0) { \ + apply_low(Pike_fp->current_object, THIS->decode_fun, 1);\ + } \ +} while(0) + + +static inline cb_key +TREE_CLASSIFY(_transform_svalue_to_key)(const struct svalue * s) { + cb_key key; + if (THIS->encode_fun >= 0) { + push_svalue(s); + apply_low(Pike_fp->current_object, THIS->encode_fun, 1); + if (!(Pike_sp[-1].type & T_KEY)) + Pike_error("encode_key() is expected to return type " cmod_STRFY_EVAL(key_ptype) ".\n"); + key = CB_LOW_KEY_FROM_SVALUE(Pike_sp-1); + pop_stack(); + } else { + if (!(s->type & T_KEY)) Pike_error("Expected type " cmod_STRFY_EVAL(key_ptype) ".\n"); + key = CB_LOW_KEY_FROM_SVALUE(s); + } + return key; +} +cmod_DEFINE_EVAL(CB_KEY_FROM_SVALUE, TREE_CLASSIFY(_transform_svalue_to_key)) + +static inline void TREE_CLASSIFY(_assign_svalue_key)(const cb_key key, + struct svalue * s) { + if (THIS->decode_fun >= 0) { + CB_PUSH_KEY(key); + apply_low(Pike_fp->current_object, THIS->decode_fun, 1); + assign_svalue((s), Pike_sp-1); + pop_stack(); + } else { + CB_LOW_ASSIGN_SVALUE_KEY(key, s); + } +} +cmod_DEFINE_EVAL(CB_ASSIGN_SVALUE_KEY, TREE_CLASSIFY(_assign_svalue_key)) +/* + * GCC-BUG!!! we use the inline instead! +#undef CB_ASSIGN_SVALUE_KEY +#define CB_ASSIGN_SVALUE_KEY(key, s) CB_LOW_ASSIGN_SVALUE_KEY(key, s) +#define CB_ASSIGN_SVALUE_KEY(_key, s) do { \ + if (THIS->decode_fun >= 0) { \ + CB_PUSH_KEY(_key); \ + apply_low(Pike_fp->current_object, THIS->decode_fun, 1);\ + assign_svalue((s), Pike_sp-1); \ + pop_stack(); \ + } else { \ + CB_LOW_ASSIGN_SVALUE_KEY(_key, s); \ + } \ +} while(0) +*/ + diff --git a/src/post_modules/CritBit/tree_high.c b/src/post_modules/CritBit/tree_high.c new file mode 100644 index 0000000000000000000000000000000000000000..5ee5f90468ae5a5285645eb62f61f1a33043a86c --- /dev/null +++ b/src/post_modules/CritBit/tree_high.c @@ -0,0 +1,215 @@ +static inline void cb_print_key(struct string_builder *, const cb_key); +static inline size_t _low_cb_check_node(cb_node_t node, const char *, int); + +#ifdef DEBUG_CHECKS +# define cb_check_node(node) do { _low_cb_check_node(node, \ + __FILE__, __LINE__); } while (0) +#else +# define cb_check_node(node) do {} while(0) +#endif + +#define CB_FATAL(x) Pike_error x + +#include "tree_low.c" + +static inline void cb_debug_print_key(struct string_builder * buf, cb_key key) { + cb_size i; + + for (i.chars = 0; i.chars < key.len.chars; i.chars++) { +#ifdef CB_PRINT_CHAR + CB_PRINT_CHAR(buf, key.str, i.chars); +#else + string_builder_sprintf(buf, "(%d, %d) b: ", i.chars, CB_WIDTH(s)); + + for (i.bits = 0; i.bits < CB_WIDTH(s); i.bits++) { + string_builder_sprintf(buf, "%d", CB_GET_BIT(key.str, i)); + } + string_builder_putchar(buf, ' '); +#endif + } + + /* string_builder_putchars(buf, ' ', 11-key.len.chars); */ + + if (key.len.bits) { + i.chars = key.len.chars; + i.bits = 0; + + string_builder_sprintf(buf, "(%d, %d) b: ", key.len.chars, key.len.bits); + + for (; CB_LT(i, key.len); CB_INC(i, 0, 1)) { + string_builder_sprintf(buf, "%d", CB_GET_BIT(key.str, i)); + } + string_builder_sprintf(buf, " %d", CB_GET_BIT(key.str, key.len)); + } +} + +static inline void cb_print_key(struct string_builder * buf, cb_key key) { + cb_size i; + + for (i.chars = 0; i.chars < key.len.chars; i.chars++) { + for (i.bits = 0; i.bits < CB_WIDTH(s); i.bits++) { + string_builder_putchar(buf, CB_GET_BIT(key.str, i) ? '1' : '0'); + } + } + + /* string_builder_putchars(buf, ' ', 11-key.len.chars); */ + + if (key.len.bits) { + i.chars = key.len.chars; + i.bits = 0; + + for (; CB_LT(i, key.len); CB_INC(i, 0, 1)) { + string_builder_putchar(buf, CB_GET_BIT(key.str, i) ? '1' : '0'); + } + } +} + +static inline void cb_print_node(struct string_builder * buf, + cb_node_t node, int depth) { + string_builder_putchars(buf, ' ', depth); + string_builder_sprintf(buf, "%x #%lu (%d) --> ", node, + node->size, node->value.type); + string_builder_putchars(buf, ' ', MAXIMUM(0, 15-depth)); + cb_debug_print_key(buf, node->key); + if (CB_HAS_VALUE(node)) CB_PRINT_KEY(buf, node->key); + string_builder_putchar(buf, '\n'); +} + +static inline void cb_print_tree(struct string_builder *buf, + cb_node_t tree, int depth) { + cb_print_node(buf, tree, depth); +#if 0 + char *spaces = (char*)malloc(depth+1); + char fmt[4096] = "%s %"; + cb_size i; + int binary = 0; + memset(spaces, ' ', depth); + spaces[depth] = 0; + if (tree->key.len.bits) { + sprintf(fmt, "%%s-> %%.%ds %%0%dd \n", + tree->key.len.chars, tree->key.len.bits); + i.chars = tree->key.len.chars; + i.bits = 0; + + for (; CB_LT(i, tree->key.len); CB_INC(i, 0, 1)) { + binary *= 10; + binary += CB_GET_BIT(tree->key.str, i); + } + printf(fmt, spaces, tree->key->str, binary); + } else { + sprintf(fmt, "%%s-> %%.%ds \n", tree->key.len.chars); + printf(fmt, spaces, tree->key->str); + } + free(spaces); +#endif + if (CB_HAS_CHILD(tree, 0)) { + string_builder_putchar(buf, 'l'); + cb_print_tree(buf, CB_CHILD(tree, 0), depth + 1); + } + if (CB_HAS_CHILD(tree, 1)) { + string_builder_putchar(buf, 'r'); + cb_print_tree(buf, CB_CHILD(tree, 1), depth + 1); + } +} + +static inline int cb_print_path(struct string_builder *buf, cb_node_t node, + cb_key key, cb_size size, int depth, + cb_node_t end) { + cb_print_node(buf, node, depth); + + if (node == end) return 0; + + size = cb_prefix_count(node->key.str, key.str, + CB_MIN(node->key.len, key.len), size); + + if (CB_S_EQ(size, key.len)) { + if (CB_S_EQ(size, node->key.len)) { + return 1; + } else return 0; + } + + if (CB_S_EQ(size, node->key.len)) { + size_t bit = CB_GET_BIT(key.str, size); + if (CB_HAS_CHILD(node, bit)) + return cb_print_path(buf, CB_CHILD(node, bit), key, size, depth+1, + end); + } + + return 0; +} + +static inline void cb_check(cb_node_t node, cb_node_t last, char *issue) { +#ifdef DEBUG_CHECKS + if (CB_LT(node->key.len, last->key.len)) { + printf("ERROR AT %s\n", issue); + cb_print_tree(last, 0); + printf("[%d, %d] is shorter than [%d, %d]\n", node->key.len.chars, + node->key.len.bits, last->key.len.chars, last->key.len.bits); + return; + } +#endif /* DEBUG_CHECKS */ + + if (CB_HAS_CHILD(node, 0)) cb_check(CB_CHILD(node, 0), node, issue); + if (CB_HAS_CHILD(node, 1)) cb_check(CB_CHILD(node, 1), node, issue); +} + +static inline int cb_rec_check_parents(cb_node_t node) { + if (node == NULL) return 0; + if (CB_HAS_CHILD(node, 0)) { + if (CB_CHILD(node, 0)->parent != node) { + printf("Damaged 0.\n"); + return 1; + } + if (cb_rec_check_parents(CB_CHILD(node, 0))) return 1; + } + if (CB_HAS_CHILD(node, 1)) { + if (CB_CHILD(node, 1)->parent != node) { + printf("Damaged 1.\n"); + return 1; + } + if (cb_rec_check_parents(CB_CHILD(node, 1))) return 1; + } + return 0; +} + +static inline void cb_aggregate_values(cb_node_t node, + struct array * a, size_t start, + size_t len) { + if (CB_HAS_VALUE(node)) + CB_GET_VALUE(node, ITEM(a)+start++); + WALK_FORWARD(node, { + if (CB_HAS_VALUE(_)) CB_GET_VALUE(_, ITEM(a)+start++); + }); +} + +static inline size_t _low_cb_check_node(cb_node_t node, + const char *file, int line) { + size_t len = 0; + if (CB_HAS_CHILD(node, 0)) { + if (CB_GET_BIT(CB_CHILD(node, 0)->key.str, node->key.len) != 0) { + Pike_error("%s:%d EVIL DOER FOUND.\n", file, line); + } + if (CB_LE(CB_CHILD(node, 0)->key.len, node->key.len)) { + Pike_error("%s:%d Child is shorter than parent.\n", file, line); + } + len += _low_cb_check_node(CB_CHILD(node, 0), file, line); + } + if (CB_HAS_CHILD(node, 1)) { + if (CB_GET_BIT(CB_CHILD(node, 1)->key.str, node->key.len) != 1) { + Pike_error("%s:%d It was the gardener! \n", file, line); + } + if (CB_LE(CB_CHILD(node, 1)->key.len, node->key.len)) { + Pike_error("%s:%d Child is shorter than parent.\n", file, line); + } + len += _low_cb_check_node(CB_CHILD(node, 1), file, line); + } + + if (len + CB_HAS_VALUE(node) != node->size) { + /* Pike_error("Found node with wrong size. is: %p\n", node); */ + Pike_error("%s:%d Found node with wrong size. is: 0x%08X\n", + file, line, node); + } + + return node->size; +} + diff --git a/src/post_modules/CritBit/tree_low.c b/src/post_modules/CritBit/tree_low.c new file mode 100644 index 0000000000000000000000000000000000000000..09afd9497cd3b58adf1c101a519a3d622b4aab45 --- /dev/null +++ b/src/post_modules/CritBit/tree_low.c @@ -0,0 +1,581 @@ +#include <stdlib.h> +#include <stdint.h> +#include <sys/types.h> + +#ifndef CB_NODE_ALLOC +# define CB_NODE_ALLOC() ((cb_node_t)malloc(sizeof(cb_node))) +#endif +#ifndef CB_NODE_FREE +# define CB_NODE_FREE(p) free(p) +#endif + +#ifndef HAS___BUILTIN_EXPECT +# define __builtin_expect(x, expected_value) (x) +#endif + +#define likely(x) __builtin_expect((x), 1) +#define unlikely(x) __builtin_expect((x), 0) + +#ifndef cb_check_node +# define cb_check_node(node) do {} while(0) +#endif + +#ifndef MAX +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#endif + +#ifndef CB_FATAL +# define CB_FATAL(x) (printf x, exit(4)) +#endif + +#ifndef CB_ERROR +# define CB_ERROR CB_FATAL +#endif + +#ifndef cb_prefix_count +# define cb_prefix_count cb_prefix_count_fallback +#else +# undef cb_prefix_count +# define cb_prefix_count cb_prefix_count_fallback +#endif + +#ifndef CB_SOURCE +# define CB_SOURCE +#endif + +static inline cb_node_t cb_zap_node(cb_node_t); +static inline cb_node_t cb_node_from_string(const cb_key, const cb_value *); + + +static inline cb_key CB_KEY_FROM_STRING(const cb_string string) { + cb_key key; + /* printf("key from string: %p (%d, %d)\n", + string, key.len.chars, key.len.bits); */ + key.str = string; + key.len.chars = CB_LENGTH(string); + key.len.bits = 0; + return key; +} + +static inline cb_size cb_prefix_count_fallback(const cb_string s1, + const cb_string s2, + const cb_size len, + cb_size start) { + size_t i; + uint32_t width = MAX(CB_WIDTH(s1), CB_WIDTH(s2)); + + for (i = start.chars; i < len.chars; i++) { + uint32_t diffbit = CB_COUNT_PREFIX(s1, s2, i); + start.bits = 0; + + if (diffbit < width) { /* are different */ +#ifdef ANNOY_DEBUG + fprintf(stderr, "diff in bit %d (byte %d) %d\n", diffbit, i, __LINE__); +#endif + start.chars = i; + start.bits = diffbit; + return start; + } + } + + if (len.bits > start.bits) { + uint32_t diffbit = CB_COUNT_PREFIX(s1, s2, len.chars); + if (diffbit < len.bits) { /* are different */ +#ifdef ANNOY_DEBUG + fprintf(stderr, "diff in bit %d (byte %d) %d\n", diffbit, len.chars, __LINE__); +#endif + start.chars = len.chars; + start.bits = diffbit; + return start; + } else return len; + } + + return len; +} + + +static inline cb_node_t node_init() { + cb_node_t tree; + + tree = CB_NODE_ALLOC(); + memset(tree, 0, sizeof(cb_node)); + CB_INIT_VALUE(tree); + + return tree; +} + +CB_STATIC CB_INLINE cb_node_t cb_get_range(const cb_node_t tree, const cb_key a, + const cb_key b) { + cb_node_t node = cb_index(tree, a); + cb_node_t end = cb_index(tree, b); + /* printf("start: %p, stop: %p in line %d\n", node, end, __LINE__); */ + if (!node) node = cb_find_next(tree, a); + /* printf("start: %p, stop: %p in line %d\n", node, end, __LINE__); */ + if (node) { + cb_node_t ntree; + + if ((end && !CB_HAS_VALUE(end)) || (end = cb_find_next(tree, b))) { + /* printf("start: %p, stop: %p in line %d\n", node, end, __LINE__); */ + if (end == node) return NULL; + + WALK_BACKWARD(end, { + if (CB_HAS_VALUE(_)) { + if (_ == node) return cb_node_from_string(node->key, &node->value); + break; + } + if (_ == node) return NULL; + }); + /* printf("start: %p, stop: %p in line %d\n", node, end, __LINE__); */ + } + if (node && !CB_HAS_VALUE(node)) { + if (end == node) return NULL; + WALK_FORWARD(node, { + if (_ == end) return NULL; + if (CB_HAS_VALUE(_)) break; + }); + } + /* printf("start: %p, stop: %p in line %d\n", node, end, __LINE__); */ + ntree = cb_node_from_string(node->key, &node->value); + + if (node != end) WALK_FORWARD(node, { + if (CB_HAS_VALUE(_)) { + /* printf("adding %p\n", _); */ + cb_insert(ntree, _->key, &node->value); + if (_ == end) break; + } + }); + + /* printf("new range has %d members.\n", ntree->size); */ + return ntree; + } + return NULL; +} + +static inline cb_node_t cb_node_from_string(const cb_key s, + const cb_value * val) { + cb_node_t node = node_init(); + CB_SET_KEY(node, s); + node->size = 1; + CB_SET_VALUE(node, val); + +#ifdef DEBUG_CHECKS + if (!CB_HAS_VALUE(node)) + printf("culprit here. %d\n", __LINE__); +#endif + + return node; +} + +static inline cb_node_t cb_clone_node(const cb_node_t node) { + cb_node_t nnode = CB_NODE_ALLOC(); + + memcpy(nnode, node, sizeof(cb_node)); + CB_ADD_KEY_REF(node->key); + CB_INIT_VALUE(node); + CB_SET_CHILD(nnode, 0, CB_CHILD(nnode, 0)); + CB_SET_CHILD(nnode, 1, CB_CHILD(nnode, 1)); + CB_CHILD(node, 0) = NULL; + CB_CHILD(node, 1) = NULL; + + return nnode; +} + +CB_STATIC CB_INLINE cb_node_t cb_copy_tree(const cb_node_t from) { + cb_node_t new; + + if (!from) return NULL; + + new = CB_NODE_ALLOC(); + + memcpy(new, from, sizeof(cb_node)); + new->parent = NULL; + CB_ADD_KEY_REF(new->key); + CB_GET_VALUE(from, &new->value); + + if (CB_HAS_CHILD(new, 0)) CB_SET_CHILD(new, 0, + cb_copy_tree(CB_CHILD(new, 0))); + if (CB_HAS_CHILD(new, 1)) CB_SET_CHILD(new, 1, + cb_copy_tree(CB_CHILD(new, 1))); + return new; +} + +static inline cb_node_t cb_free_node(cb_node_t node) { + if (!node) { + CB_FATAL(("double free!\n")); + } + if (CB_HAS_CHILD(node, 0)) { + CB_CHILD(node, 0) = cb_free_node(CB_CHILD(node, 0)); + } + if (CB_HAS_CHILD(node, 1)) { + CB_CHILD(node, 1) = cb_free_node(CB_CHILD(node, 1)); + } + return cb_zap_node(node); +} + +static inline cb_node_t cb_zap_node(cb_node_t node) { + CB_FREE_KEY(node->key); + CB_RM_VALUE(node); + CB_NODE_FREE(node); + + return NULL; +} + +CB_STATIC CB_INLINE cb_node_t cb_find_first(cb_node_t tree) { + while (tree && !CB_HAS_VALUE(tree)) { tree = CB_CHILD(tree, 0); }; + + return tree; +} + +CB_STATIC CB_INLINE cb_node_t cb_find_last(cb_node_t tree) { + while (1) { + if (CB_HAS_CHILD(tree, 1)) tree = CB_CHILD(tree, 1); + else if (CB_HAS_CHILD(tree, 0)) tree = CB_CHILD(tree, 0); + else break; + } + return tree; +} + +CB_STATIC CB_INLINE size_t cb_get_depth(cb_node_t node) { + size_t a = 0, b = 0, len = 1; + + if (CB_HAS_CHILD(node, 0)) { + a = cb_get_depth(CB_CHILD(node, 0)); + } + + if (CB_HAS_CHILD(node, 1)) { + b = cb_get_depth(CB_CHILD(node, 1)); + } + + return len + MAX(b, a); +} + +CB_STATIC CB_INLINE cb_node_t cb_subtree_prefix(cb_node_t node, cb_key key) { + cb_size size; + uint32_t bit; + size = cb_prefix_count(node->key.str, key.str, + CB_MIN(node->key.len, key.len), size); + + if (CB_S_EQ(size, key.len)) { /* key is substring */ + return node; + } + + bit = CB_GET_BIT(key.str, size); + + if (CB_HAS_CHILD(node, bit)) { + return cb_subtree_prefix(CB_CHILD(node, bit), key); + } + + return NULL; +} + +CB_STATIC CB_INLINE cb_node_t cb_delete(cb_node_t tree, const cb_key key, + cb_value * val) { + cb_node_t node = cb_index(tree, key); + + if (node && CB_HAS_VALUE(node)) { + uint32_t bit; + cb_node_t t; + if (val) CB_GET_VALUE(node, val); + + CB_RM_VALUE(node); + node->size--; + + if (node == tree) goto PARENT; + + if (!CB_HAS_PARENT(node)) CB_ERROR(("broken tree\n")); + + t = node; + WALK_UP(t, bit, { + _->size--; + }); + + cb_check_node(tree); + + do { + switch (CB_HAS_CHILD(node, 0) + CB_HAS_CHILD(node, 1)) { + case 2: return tree; + case 1: + CB_SET_CHILD(CB_PARENT(node), CB_BIT(node), + CB_CHILD(node, CB_HAS_CHILD(node, 1))); + break; + case 0: + CB_SET_CHILD(CB_PARENT(node), CB_BIT(node), NULL); + break; + } + t = CB_PARENT(node); + cb_zap_node(node); + /* do some deletion */ + node = t; + } while (CB_HAS_PARENT(node) && !CB_HAS_VALUE(node)); + +PARENT: + cb_check_node(tree); + if (node == tree && !CB_HAS_VALUE(node)) { + switch (CB_HAS_CHILD(node, 0) + CB_HAS_CHILD(node, 1)) { + case 2: return tree; + case 1: + t = CB_CHILD(node, CB_HAS_CHILD(node, 1)); + cb_zap_node(tree); + cb_check_node(t); + t->parent = NULL; + return t; + case 0: + cb_zap_node(tree); + return NULL; + } + } + + } + + cb_check_node(tree); + return tree; +} + +CB_STATIC CB_INLINE cb_node_t cb_index(const cb_node_t tree, const cb_key key) { + cb_node_t node = tree; + if (tree) cb_check_node(tree); + + while (node) { + if (CB_LT(node->key.len, key.len)) { + uint32_t bit = CB_GET_BIT(key.str, node->key.len); + + if (CB_HAS_CHILD(node, bit)) { + node = CB_CHILD(node, bit); + continue; + } + } else if (CB_LT(key.len, node->key.len)) { + return NULL; + } else if (CB_KEY_EQ(node->key, key)) { + cb_check_node(tree); + return node; + } + + break; + } + + if (tree) cb_check_node(tree); + return NULL; +} + +CB_STATIC CB_INLINE cb_node_t cb_find_next(const cb_node_t tree, const cb_key key) { + cb_size size; + size_t bit; + cb_node_t node; + size.bits = size.chars = 0; + + /* index is cheap. also in many cases its quite likely that we */ + /* hit. */ + node = cb_index(tree, key); + + if (node) { + WALK_FORWARD(node, { + if (CB_HAS_VALUE(_)) break; + }); + return node; + } + + node = tree; + + while (1) { +#ifdef ANNOY_DEBUG + printf("prefix: (%d,%d)\n", key.len.chars, key.len.bits); + cb_size min = CB_MIN(node->key.len, key.len); + printf("(%p) start: (%d,%d) stop: (%d,%d) ", node, size.chars, + size.bits, min.chars, min.bits); +#endif + size = cb_prefix_count(node->key.str, key.str, + CB_MIN(node->key.len, key.len), size); +#ifdef ANNOY_DEBUG + printf("prefix: (%d,%d)\n", size.chars, size.bits); +#endif + + if (CB_S_EQ(size, key.len)) { /* key is substring */ + if (!CB_S_EQ(size, node->key.len)) { /* key is not equal */ + if (CB_HAS_VALUE(node)) + return node; /* key is smaller */ + } else WALK_FORWARD(node, { + if (CB_HAS_VALUE(_)) return _; + }); + } + + bit = CB_GET_BIT(key.str, size); + + /* printf("bit is %u\n", bit); */ + + if (CB_S_EQ(size, node->key.len)) { /* node is substring */ + if (CB_HAS_CHILD(node, bit)) { + node = CB_CHILD(node, bit); + continue; + } + if (!bit && CB_HAS_CHILD(node, 1)) { + WALK_FORWARD(node, { + if (CB_HAS_VALUE(_)) return _; + }); + } + + return NULL; + } + + if (!bit) break; + + WALK_UP(node, bit, { + if (!bit && CB_HAS_CHILD(_, 1)) { + _ = CB_CHILD(_, 1); + break; + } + }); + if (node == tree) return NULL; + break; + } + + if (node && !CB_HAS_VALUE(node)) + WALK_FORWARD(node, { + if (CB_HAS_VALUE(_)) break; + }); + return node; +} + +CB_STATIC CB_INLINE cb_node_t cb_find_ne(const cb_node_t tree, const cb_key key) { + cb_node_t ne = cb_index(tree, key); + if (!ne) ne = cb_find_next(tree, key); + return ne; +} + +CB_STATIC CB_INLINE cb_node_t cb_get_nth(const cb_node_t tree, size_t n) { + cb_node_t node = tree; + size_t ln; + + while (node) { + if (n >= node->size) return NULL; + + if (n == 0) return cb_find_first(node); + else if (n == node->size - 1) return cb_find_last(node); + + if (CB_HAS_VALUE(node)) n--; + + if (CB_HAS_CHILD(node, 0)) { + ln = CB_CHILD(node, 0)->size; + if (n < ln) { + node = CB_CHILD(node, 0); + continue; + } + n -= ln; + } + + node = CB_CHILD(node, 1); + } + + return NULL; +} + +CB_STATIC CB_INLINE cb_node_t cb_find_previous(const cb_node_t tree, + const cb_key key) { + cb_node_t node = cb_index(tree, key); + if (!node) node = cb_find_next(tree, key); + if (!node) return cb_find_last(tree); + if (node) WALK_BACKWARD(node, { + if (CB_HAS_VALUE(_)) break; + }); + return node; +} + +CB_STATIC CB_INLINE cb_node_t cb_find_le(const cb_node_t tree, const cb_key key) { + cb_node_t ne = cb_index(tree, key); + if (!ne) ne = cb_find_previous(tree, key); + return ne; +} + +static inline int cb_low_insert(cb_node_t node, const cb_key key, const cb_value *val) { + cb_size size; + size.bits = 0; + size.chars = 0; + + while (1) { + cb_node_t new; + size_t bit; + + size = cb_prefix_count(node->key.str, key.str, + CB_MIN(node->key.len, key.len), size); + + if (CB_S_EQ(size, key.len)) { + cb_node_t klon; + + if (CB_S_EQ(size, node->key.len)) { + uint32_t bit; + + klon = node; + if (CB_HAS_VALUE(klon)) + WALK_UP(klon, bit, { + _->size--; + }); + else node->size++; + /* remove ref */ + /* free_svalue(&node->value); */ + CB_SET_KEY(node, key); + CB_SET_VALUE(node, val); + + return 0; + } + /* overwrite not inplace by new key node */ + klon = cb_clone_node(node); + node->size++; + bit = CB_GET_BIT(node->key.str, size); + + /* add ref for value */ + CB_SET_KEY(node, key); + CB_SET_VALUE(node, val); + + node->key.len = size; + CB_SET_CHILD(node, bit, klon); + CB_SET_CHILD(node, !bit, NULL); + + return 1; + } + + if (likely(CB_S_EQ(size, node->key.len))) { + node->size++; + bit = CB_GET_BIT(key.str, size); + if (CB_HAS_CHILD(node, bit)) { + node = CB_CHILD(node, bit); + continue; + } + CB_SET_CHILD(node, bit, cb_node_from_string(key, val)); + return 1; + } + + new = cb_clone_node(node); + node->size++; +#ifdef DEBUG_CHECKS + if (CB_LT(CB_MIN(node->key.len, key.len), size)) { + CB_ERROR(("fooo\n")); + } + if (CB_LT(node->key.len, size)) { + CB_ERROR(("enlarging node [%d, %d] vs [%d, %d]\n", size.chars, + size.bits, node->key.len.chars, node->key.len.bits)); + } +#endif /* DEBUG_CHECKS */ + node->key.len = size; + bit = CB_GET_BIT(key.str, size); + CB_SET_CHILD(node, bit, cb_node_from_string(key, val)); + CB_SET_CHILD(node, !bit, new); + CB_RM_VALUE(node); /* do not free here, clone does take ref */ + + return 1; + } +} + +CB_STATIC CB_INLINE cb_node_t cb_insert(cb_node_t tree, const cb_key key, + const cb_value *val) { + + if (!tree) { + return tree = cb_node_from_string(key, val); + } + + cb_check_node(tree); + + cb_low_insert(tree, key, val); + + cb_check_node(tree); + + return tree; +} diff --git a/src/post_modules/CritBit/tree_low.h b/src/post_modules/CritBit/tree_low.h new file mode 100644 index 0000000000000000000000000000000000000000..6f8e8c6b7ee7e933a9b404d9aae1fbf6b12eaed9 --- /dev/null +++ b/src/post_modules/CritBit/tree_low.h @@ -0,0 +1,223 @@ +#ifndef _TREE_LOW_H_ +#define _TREE_LOW_H_ + +#include <sys/types.h> +#include "gc.h" +#include "dmalloc.h" + +#define CB_PRINTCHAR(buf, s, n) do { \ + cb_size j; \ + j.chars = (size_t)(n); j.bits = 0; \ + for (; j.bits < 32; j.bits++) { \ + (buf)[j.bits] = (CB_GET_BIT(s, j) ? '1' : '0'); \ + }\ +} while (0) + +#ifdef CB_SOURCE + +#ifndef CB_LE +# define CB_LE(a, b) ((a).chars < (b).chars || ((a).chars == (b).chars \ + && (a).bits <= (b).bits)) +# define CB_LT(a, b) ((a).chars < (b).chars || ((a).chars == (b).chars \ + && (a).bits < (b).bits)) +# define CB_S_EQ(a, b) ((a).chars == (b).chars && (a).bits == (b).bits) +#endif +#define CB_INC(a, c, b) ((a).chars += (c), (a).bits += (b)) +#define CB_MIN(a, b) (CB_LE((a), (b)) ? a : b) +#define CB_MAX(a, b) (CB_LE((b), (a)) ? a : b) + +#define CB_HAS_PARENT(node) (!!(node)->parent) +#define CB_PARENT(node) ((node)->parent) +#define CB_BIT(node) ((node) && \ + CB_HAS_PARENT(node) ? \ + (CB_CHILD(CB_PARENT(node), 1) == (node)) \ + : (CB_FATAL((\ + "CB_BIT does not make any sense without parent.\n" \ + )), -1)) +#define CB_HAS_CHILD(node, bit) (!!(node)->childs[!!(bit)]) +#define CB_SET_CHILD(__parent, __bit, __child) do { \ + cb_node_t _p = (__parent); \ + cb_node_t _child = (__child); \ + uint32_t _bit = (__bit); \ + if (_child) _child->parent = _p; \ + CB_CHILD(_p, _bit) = _child; \ +} while(0) +#define CB_CHILD(node, bit) ((node)->childs[!!(bit)]) +#define CB_SHORT(a, b) (CB_LE((a)->len, (b)->len) ? (a) : (b)) + +#define WALK_UP(node, bit, CODE) do { cb_node_t __ = (node); do { \ + cb_node_t _ = __; \ + /*printf("one step up: %d %d %d\n", CB_HAS_PARENT(_), \ + CB_HAS_CHILD(_, 0), CB_HAS_CHILD(_, 1));*/ \ + if (CB_HAS_PARENT(_)) { \ + bit = CB_BIT(_); \ + _ = CB_PARENT(_); \ + while (1) { if (1) CODE if (!CB_HAS_PARENT(_)) break; \ + bit = CB_BIT(_); _ = CB_PARENT(_); } \ + __ = _; \ + }\ +} while(0); (node) = __; } while(0) + +#define WALK_FORWARD(node, CODE) do { cb_node_t __ = (node); do { \ + cb_node_t _ = __; \ + while (1) { \ + /*printf("one step forward: %d %d %d\n", CB_HAS_PARENT(_), + CB_HAS_CHILD(_, 0), CB_HAS_CHILD(_, 1));*/ \ + if (CB_HAS_CHILD(_, 0)) { _ = CB_CHILD(_, 0); } \ + else if (CB_HAS_CHILD(_, 1)) { _ = CB_CHILD(_, 1); } \ + else { \ + uint32_t hit = 0, oldbit = 3; \ + WALK_UP(_, oldbit, { \ + /*printf("one step up: %d\n", oldbit); \ + printf("one step forward: %d %d %d\n", CB_HAS_PARENT(_), + CB_HAS_CHILD(_, 0), CB_HAS_CHILD(_, 1));*/ \ + if (!oldbit && CB_HAS_CHILD(_, 1)) { \ + hit = 1;_ = CB_CHILD(_, 1); break; \ + } \ + }); \ + if (oldbit == 3 || !hit) { _ = NULL; break; } \ + } \ + if (1) CODE \ + } \ + __ = _; \ +} while(0); (node) = __; } while(0) +#define WALK_BACKWARD(node, CODE) do { cb_node_t __ = (node); do { \ + cb_node_t _ = __; \ + while (1) { \ + /*printf("one step forward: %d %d %d\n", + * CB_HAS_PARENT(_), CB_HAS_CHILD(_, 0), CB_HAS_CHILD(_, 1));*/ \ + if (CB_HAS_PARENT(_)) { \ + uint32_t bit = CB_BIT(_); \ + _ = CB_PARENT(_); \ + if (bit && CB_HAS_CHILD(_, 0)) { \ + _ = CB_CHILD(_, 0); \ + while (1) { \ + if (CB_HAS_CHILD(_, 1)) { _ = CB_CHILD(_, 1); continue; } \ + if (CB_HAS_CHILD(_, 0)) { _ = CB_CHILD(_, 0); continue; } \ + break; \ + } \ + } \ + } else { _ = NULL; break; } \ + if (1) CODE \ + } \ + __ = _; \ +} while(0); (node) = __; } while(0) + +#endif /* CB_SOURCE */ + +#ifdef cb_node +# undef cb_node +#endif +#define cb_node CB_NAME(node) + +#ifdef cb_key +# undef cb_key +#endif +#define cb_key CB_NAME(key) + +#ifdef cb_node_t +# undef cb_node_t +#endif +#define cb_node_t CB_TYPE(node) + +#ifdef cb_find_ne +# undef cb_find_ne +#endif +#define cb_find_ne CB_NAME(find_ne) + +#ifdef cb_get_nth +# undef cb_get_nth +#endif +#define cb_get_nth CB_NAME(get_nth) + +#ifdef cb_find_previous +# undef cb_find_previous +#endif +#define cb_find_previous CB_NAME(find_previous) + +#ifdef cb_find_le +# undef cb_find_le +#endif +#define cb_find_le CB_NAME(find_le) + +#ifdef cb_subtree_prefix +# undef cb_subtree_prefix +#endif +#define cb_subtree_prefix CB_NAME(subtree_prefix) + +#ifdef cb_get_depth +# undef cb_get_depth +#endif +#define cb_get_depth CB_NAME(get_depth) + +#ifdef cb_find_last +# undef cb_find_last +#endif +#define cb_find_last CB_NAME(find_last) + +#ifdef cb_find_first +# undef cb_find_first +#endif +#define cb_find_first CB_NAME(find_first) + +#ifdef cb_copy_tree +# undef cb_copy_tree +#endif +#define cb_copy_tree CB_NAME(copy_tree) + +#ifdef cb_get_range +# undef cb_get_range +#endif +#define cb_get_range CB_NAME(get_range) + +#ifdef cb_index +# undef cb_index +#endif +#define cb_index CB_NAME(index) + +#ifdef cb_find_next +# undef cb_find_next +#endif +#define cb_find_next CB_NAME(find_next) + +#ifdef cb_insert +# undef cb_insert +#endif +#define cb_insert CB_NAME(insert) + +#endif /* TREE_LOW_H */ + +typedef struct cb_key { + cb_string str; + cb_size len; +} cb_key; + +typedef struct cb_node { + cb_key key; + cb_value value; + size_t size; /* size of the subtree */ + struct cb_node * parent; + struct cb_node * childs[2]; +} * cb_node_t; + +typedef struct cb_node cb_node; + +CB_STATIC CB_INLINE cb_node_t cb_insert(const cb_node_t, const cb_key, + const cb_value *); +CB_STATIC CB_INLINE cb_node_t cb_find_next(cb_node_t, const cb_key); +CB_STATIC CB_INLINE cb_node_t cb_index(const cb_node_t tree, const cb_key); +CB_STATIC CB_INLINE cb_node_t cb_get_range(const cb_node_t tree, + const cb_key a, const cb_key b); +CB_STATIC CB_INLINE cb_node_t cb_copy_tree(const cb_node_t from); +CB_STATIC CB_INLINE cb_node_t cb_find_first(cb_node_t tree); +CB_STATIC CB_INLINE cb_node_t cb_find_last(cb_node_t tree); +CB_STATIC CB_INLINE size_t cb_get_depth(cb_node_t node); +CB_STATIC CB_INLINE cb_node_t cb_subtree_prefix(cb_node_t node, cb_key key); +CB_STATIC CB_INLINE cb_node_t cb_find_le(const cb_node_t tree, + const cb_key key); +CB_STATIC CB_INLINE cb_node_t cb_find_previous(const cb_node_t tree, + const cb_key key); +CB_STATIC CB_INLINE cb_node_t cb_get_nth(const cb_node_t tree, size_t n); +CB_STATIC CB_INLINE cb_node_t cb_find_ne(const cb_node_t tree, + const cb_key key); + diff --git a/src/post_modules/CritBit/tree_source.H b/src/post_modules/CritBit/tree_source.H new file mode 100644 index 0000000000000000000000000000000000000000..88b5228191054b8ddb4b888c451ec22068a18cd3 --- /dev/null +++ b/src/post_modules/CritBit/tree_source.H @@ -0,0 +1,683 @@ + INIT { + THIS->tree = NULL; + THIS->encode_fun = find_identifier + ("encode_key", Pike_fp->current_object->prog); + THIS->decode_fun=find_identifier + ("decode_key", Pike_fp->current_object->prog); + THIS->copy_fun = find_identifier ("copy", Pike_fp->current_object->prog); + THIS->insert_fun = find_identifier ("`[]=", Pike_fp->current_object->prog); + if (find_identifier("copy", TREE_CLASSIFY(_program)) == THIS->copy_fun) { + THIS->copy_fun = -1; + } + + if (find_identifier("`[]=", TREE_CLASSIFY(_program)) == THIS->insert_fun) { + THIS->copy_fun = -1; + } + } +static inline void TREE_CLASSIFY(_copy_node)(struct object *o, cb_node_t node) { + if (THIS->copy_fun != -1 && THIS->insert_fun != -1) { + CB_PUSH_TRANSFORM_KEY(node->key); + CB_PUSH_VALUE(node->value); + apply_low(o, THIS->insert_fun, 2); + pop_stack(); + } else cmod_OBJ2_TREE(o)->tree = + cb_insert(cmod_OBJ2_TREE(o)->tree, node->key, &node->value); +} + +static inline void TREE_CLASSIFY(_copy_tree)(struct object *o, cb_node_t node) { + if (THIS->copy_fun != -1 && THIS->insert_fun != -1) { + WALK_FORWARD(node, { + if (CB_HAS_VALUE(_)) { + CB_PUSH_TRANSFORM_KEY(node->key); + push_svalue(&node->value); + apply_low(o, THIS->insert_fun, 2); + pop_stack(); + } + }); + } else cmod_OBJ2_TREE(o)->tree = cb_copy_tree(node); +} + +static inline struct object * +TREE_CLASSIFY(_clone_object)(const struct object *o) { + struct object * t = NULL; + if (THIS->copy_fun != -1) { + apply_low(o, THIS->copy_fun, 0); + if (!Pike_sp[-1].type != PIKE_T_OBJECT) + Pike_error("clone() is supposed to return an object.\n"); + t = Pike_sp[-1].u.object; + add_ref(t); + pop_stack(); + } else { + t = clone_object(Pike_fp->current_object->prog, 0); + if (cmod_OBJ2_TREE(o)->tree) + cmod_OBJ2_TREE(t)->tree = cb_copy_tree(cmod_OBJ2_TREE(o)->tree); + } + return t; +} + +cmod_DEFINE_EVAL(CLONE_OBJECT, TREE_CLASSIFY(_clone_object)) + +cmod_DEFINE_EVAL(COPY_TREE, TREE_CLASSIFY(_copy_tree)) + +cmod_DEFINE_EVAL(COPY_NODE, TREE_CLASSIFY(_copy_node)) + + /*! @decl mixed _m_delete(mixed key) + *! m_delete callback. + */ + PIKEFUN mixed _m_delete(mixed key) { + size_t size, oldsize; + cb_key k; + + k = CB_KEY_FROM_SVALUE(key); + + oldsize = (THIS->tree) ? THIS->tree->size : 0; + if (oldsize) { + THIS->tree = cb_delete(THIS->tree, k, Pike_sp++); + size = (THIS->tree) ? THIS->tree->size : 0; + if (size < oldsize) { + THIS->rev++; + stack_pop_keep_top(); + return; + } + } + pop_stack(); + push_undefined(); + } + + PIKEFUN int check_parenting_skills() { + push_int(cb_rec_check_parents(THIS->tree)); + } + + DOCSTART() @decl void create(array|mapping|void o) + *! Create a tree_class from o. + DOCEND() + PIKEFUN void create (array|mapping|void o) + optflags OPT_TRY_OPTIMIZE; + { + + if (args == 1) { + switch (o->type) { + case PIKE_T_MAPPING: { + struct keypair *k; + INT32 e; + struct mapping_data *md = o->u.mapping->data; + + NEW_MAPPING_LOOP(md) { + cb_key key = CB_KEY_FROM_SVALUE(&k->ind); + THIS->tree = cb_insert(THIS->tree, key, &k->val); + } + return; + } + case PIKE_T_ARRAY: { + INT32 i; + + if (o->u.array->size & 1) goto ARG_ERROR; + + for (i = 0; i < o->u.array->size; i+=2) { + cb_key key = CB_KEY_FROM_SVALUE(ITEM(o->u.array)+i); + THIS->tree = cb_insert(THIS->tree, key, ITEM(o->u.array)+i+1); + } + return; + } + } +ARG_ERROR: + SIMPLE_BAD_ARG_ERROR("create", 1, "mapping(" cmod_STRFY_EVAL(key_ptype) ":mixed)|array"); + } + } + +DOCSTART() @decl int _sizeof() + *! Gives the number of entries in the @[tree_class]. +DOCEND() + PIKEFUN int _sizeof() { + push_int(THIS->tree ? THIS->tree->size : 0); + } + +DOCSTART() @decl array _indices() + *! Returns a sorted array of indices of the @[tree_class]. +DOCEND() + PIKEFUN array _indices() { + size_t size = THIS->tree ? THIS->tree->size : 0; + if (size) { + size_t start = 0; + struct svalue * s; + cb_key k; + cb_node_t node = THIS->tree; + struct array * a = allocate_array_no_init(size, 0); + push_array(a); + if (CB_HAS_VALUE(node)) { + s = ITEM(a)+start; + s->type = T_VOID; + k = node->key; + CB_ASSIGN_SVALUE_KEY(k, s); + start ++; + } + WALK_FORWARD(node, { + if (CB_HAS_VALUE(_)) { + if (start == size) { + Pike_error("super bad!! tree has hidden entries.\n"); + } + s = ITEM(a)+start; + s->type = T_VOID; + CB_ASSIGN_SVALUE_KEY(_->key, s); + start ++; + } + }); + return; + } + ref_push_array(&empty_array); + } + +DOCSTART() @decl array _values() + *! Returns an array of values of the @[tree_class] object. The returned + *! array matches + *! @[_indices] so that @expr{mkmapping(indices(tree), values(tree))@} would + *! create a mapping with the same contents as this @[tree_class]. +DOCEND() + PIKEFUN array _values() { + size_t size = THIS->tree ? THIS->tree->size : 0; + if (size) { + struct array * a = allocate_array_no_init(size, 0); + push_array(a); + cb_aggregate_values(THIS->tree, a, 0, size); + return; + } + ref_push_array(&empty_array); + } + + /*! @decl mixed `[]=(mixed key, mixed val) */ + PIKEFUN mixed `[]=(mixed key, mixed val) { + cb_key k = CB_KEY_FROM_SVALUE(key); + THIS->tree = cb_insert(THIS->tree, k, val); +#ifdef DEBUG_CHECK + cb_check(THIS->tree, THIS->tree, k->str); +#endif /* DEBUG_CHECK */ + stack_pop_keep_top(); + } + + PIKEFUN mixed ninsert(mixed key, mixed val, int chars, int bits) { + cb_size len; + cb_key k; + + k = CB_KEY_FROM_SVALUE(key); + len.chars = chars; + len.bits = bits; + if (CB_LT(k.len, len)) { + Pike_error("chars, bits are too big for key.\n"); + } + THIS->tree = cb_insert(THIS->tree, k, val); + stack_pop_keep_top(); + } + + /*! @decl mixed cast(string type) + *! Cast callback. Supports only cast to mapping and + *! behaves as the inverse of create(). + */ + PIKEFUN mixed cast(string type) { + struct pike_string *mapping_t; + REF_MAKE_CONST_STRING(mapping_t, "mapping"); + + if (type == mapping_t) { + pop_stack(); + cmod_CONCAT_EVAL(f_, tree_class, _cq__indices)(0); + cmod_CONCAT_EVAL(f_, tree_class, _cq__values)(0); + f_mkmapping(2); + free_string(mapping_t); + return; + } + free_string(mapping_t); + Pike_error("Cannot cast to %S\n", type); + } + + /*! @decl mixed nth(int(0..) n) + *! Get the @expr{n@}th entry in order. + *! @returns + *! An array @expr{({ key, value })@}. + */ + PIKEFUN mixed nth(int n) { + cb_node_t node; + + if (THIS->tree) { + if (n >= 0 && (size_t)n < THIS->tree->size) { + node = cb_get_nth(THIS->tree, n); + if (node) { + pop_stack(); + CB_PUSH_TRANSFORM_KEY(node->key); + push_svalue(&node->value); + f_aggregate(2); + return; + } + } + } + + pop_stack(); + push_undefined(); + return; + } + + /*! @decl array _random() + *! Get a random entry. + *! @returns + *! An array @expr{({ key, value })@}. + */ + PIKEFUN array _random() { + cb_node_t node; + + if (THIS->tree) { + node = cb_get_nth(THIS->tree, my_rand() % THIS->tree->size); + if (node) { + CB_PUSH_TRANSFORM_KEY(node->key); + push_svalue(&node->value); + f_aggregate(2); + return; + } + } + + push_undefined(); + return; + } + + /*! @decl mixed `[](mixed key) + */ + PIKEFUN mixed `[](mixed key) { + CB_TRANSFORM_KEY(key); + if (key->type & T_KEY) { + cb_node_t n; + cb_key k = CB_LOW_KEY_FROM_SVALUE(key); + pop_stack(); + n = cb_index(THIS->tree, k); + + if (n && CB_HAS_VALUE(n)) { + push_svalue(&n->value); + return; + } + } else pop_stack(); + push_undefined(); + } + + /*! @decl int(0..1) _equal(mixed o) + */ + PIKEFUN int _equal(mixed o) { + if (o->type == PIKE_T_OBJECT + && o->u.object->prog == Pike_fp->current_object->prog) { + cb_node_t tree1, tree2; + tree1 = THIS->tree; + tree2 = cmod_OBJ2_TREE(o->u.object)->tree; + + if (tree1 == tree2) { + push_int(1); + return; + } + + if (tree1 && tree2 && tree1->size == tree2->size) { + WALK_FORWARD(tree1, { + if (CB_HAS_VALUE(_)) { + cb_node_t n = cb_index(tree2, _->key); + if (!n || !is_equal(&_->value, &n->value)) { + push_int(0); + return; + } + } + }); + push_int(1); + return; + } + } + push_int(0); + return; + } + + /*! @decl mixed `-(mixed o) + *! Sub[s]tract two trees from each other (key-wise). + */ + PIKEFUN mixed `-(mixed o) { + struct object * res; + cb_node_t node, tree = NULL; + + if (o->type != PIKE_T_OBJECT || -1 + == low_get_storage(o->u.object->prog, TREE_CLASSIFY(_program))) { + SIMPLE_BAD_ARG_ERROR("`-", 1, "CritBit." cmod_STRFY_EVAL(tree_class)); + } + + node = cmod_OBJ2_TREE(o->u.object)->tree; + + if (THIS->tree) { + if (node) { + cb_node_t t = THIS->tree; + res = clone_object(Pike_fp->current_object->prog, 0); + + if (node != THIS->tree) + WALK_FORWARD(t, { + if (CB_HAS_VALUE(_) && !cb_index(node, _->key)) + COPY_NODE(res, _); + }); + } else res = CLONE_OBJECT(Pike_fp->current_object); + } else res = clone_object(Pike_fp->current_object->prog, 0); + + push_object(res); + return; + } + + /* FIXME: we should allow for extra arguments and call += on the copy + * of the first one + */ + /*! @decl mixed `+(mixed o) + *! Add callback. Returns the union of two trees. + */ + PIKEFUN mixed `+(mixed o) { + cb_node_t tree1, tree2; + + if (o->type != PIKE_T_OBJECT || -1 + == low_get_storage(o->u.object->prog, TREE_CLASSIFY(_program))) { + SIMPLE_BAD_ARG_ERROR("`+", 1, "inherits(CritBit." cmod_STRFY_EVAL(tree_class) ")"); + } + tree1 = THIS->tree; + tree2 = cmod_OBJ2_TREE(o->u.object)->tree; + + if (tree1 && tree2) { + struct object * res = NULL; + if (tree1->size < tree2->size) { + tree1 = tree2; + tree2 = THIS->tree; + res = CLONE_OBJECT(o->u.object); + } else + res = CLONE_OBJECT(Pike_fp->current_object); + + if (tree1 != tree2) { + if (CB_HAS_VALUE(tree2)) COPY_NODE(res, tree2); + WALK_FORWARD(tree2, { + if (CB_HAS_VALUE(_)) COPY_NODE(res, _); + }); + } + push_object(res); + } else if (tree1) { + push_object(CLONE_OBJECT(Pike_fp->current_object)); + } else if (tree2) { + push_object(CLONE_OBJECT(o->u.object)); + } + + return; + } + + /*! @decl mixed `[..](mixed a, int atype, mixed b, int btype) + *! @seealso + *! @[predef::`[..]] + */ + PIKEFUN mixed `[..](mixed a, int atype, mixed b, int btype) { + struct object * o = NULL; + + if (THIS->tree) { + cb_node_t start = NULL, stop = NULL; + + switch (atype & (INDEX_FROM_BEG | INDEX_FROM_END | OPEN_BOUND)) { + case OPEN_BOUND: + start = cb_find_first(THIS->tree); + break; + case INDEX_FROM_BEG: + start = cb_find_ne(THIS->tree, CB_KEY_FROM_SVALUE(a)); + break; + case INDEX_FROM_END: + goto illegal_index; + } + + if (!start) goto range_do_clone; + + switch (btype & (INDEX_FROM_BEG | INDEX_FROM_END | OPEN_BOUND)) { + case OPEN_BOUND: + stop = cb_find_last(THIS->tree); + break; + case INDEX_FROM_BEG: + stop = cb_find_le(THIS->tree, CB_KEY_FROM_SVALUE(b)); + break; + case INDEX_FROM_END: + goto illegal_index; + } + + o = clone_object(Pike_fp->current_object->prog, 0); + + if (!stop) goto range_do_clone; + + if (start == stop) { + COPY_NODE(o, start); + goto range_do_clone; + } + + if (CB_KEY_LT(start->key, stop->key)) { + COPY_NODE(o, start); + WALK_FORWARD(start, { + if (CB_HAS_VALUE(_)) + COPY_NODE(o, _); + if (_ == stop) break; + }); + } + } else { + if (atype & INDEX_FROM_END || btype & INDEX_FROM_END) + goto illegal_index; + } +range_do_clone: + if (!o) + o = clone_object(Pike_fp->current_object->prog, 0); + pop_n_elems(args); /* REVIEW: we need this, right? */ + push_object(o); + return; + +illegal_index: + Pike_error("INDEX_FROM_END is not supported for CritBit `[..].\n"); + } + +#cmod_if 0 + PIKEFUN mixed `[](mixed a, mixed b) { + struct object * o = NULL; + + if (THIS->tree) { + cb_node_t start = NULL, stop = NULL; + + if (TYPEOF(*a) == PIKE_T_INT && !a->u.integer) { + start = cb_find_first(THIS->tree); + } else { + cb_key key = CB_KEY_FROM_SVALUE(a); + start = cb_find_ne(THIS->tree, key); + } + + if (!start) goto range_do_clone; + + if (TYPEOF(*b) == PIKE_T_INT && b->u.integer == MAX_INT_TYPE) { + stop = cb_find_last(THIS->tree); + } else { + cb_key key = CB_KEY_FROM_SVALUE(b); + stop = cb_find_le(THIS->tree, key); + } + + o = clone_object(Pike_fp->current_object->prog, 0); + + if (!stop) goto range_do_clone; + + if (start == stop) { + COPY_NODE(o, start); + goto range_do_clone; + } + + if (CB_KEY_LT(start->key, stop->key)) { + COPY_NODE(o, start); + WALK_FORWARD(start, { + if (CB_HAS_VALUE(_)) + COPY_NODE(o, _); + if (_ == stop) break; + }); + } + } +range_do_clone: + if (!o) + o = clone_object(Pike_fp->current_object->prog, 0); + pop_n_elems(args); /* REVIEW: we need this, right? */ + push_object(o); + return; + } +#cmod_endif + + PIKEFUN string ugly() { + if (THIS->tree) { + struct string_builder s; + + init_string_builder(&s, 0); + cb_print_tree(&s, THIS->tree, 0); + push_string(finish_string_builder(&s)); + return; + } + push_text(""); + } + + DOCSTART() @decl key_ptype first() + *! Get the lexicographically first index in the tree. + DOCEND() + PIKEFUN key_ptype first() { + if (THIS->tree) { + cb_node_t first = cb_find_first(THIS->tree); + if (first) { + CB_PUSH_TRANSFORM_KEY(first->key); + return; + } + } + push_undefined(); + } + + DOCSTART() @decl key_ptype last() + *! Get the lexicographically last index in the tree. + DOCEND() + PIKEFUN key_ptype last() { + if (THIS->tree) { + cb_node_t last = cb_find_last(THIS->tree); + if (last) { + CB_PUSH_TRANSFORM_KEY(last->key); + return; + } + } + push_undefined(); + } + + /*! @decl string bkey(mixed key) + *! Render the internally used binary representation of + *! the key into a string as a strings of '0's and '1's. + */ + PIKEFUN string bkey(mixed key) { + struct string_builder s; + cb_key k; + + init_string_builder(&s, 0); + k = CB_KEY_FROM_SVALUE(key); + pop_stack(); + cb_print_key(&s, k); + + push_string(finish_string_builder(&s)); + } + + DOCSTART() @decl key_ptype previous(mixed current) + *! Get the key before @expr{current@} in lexicographical order. + DOCEND() + PIKEFUN key_ptype previous(mixed current) { + CB_CHECK_KEY(current, "previous", 1); + + if (THIS->tree) { + cb_node_t previous; + + previous = cb_find_previous(THIS->tree, CB_LOW_KEY_FROM_SVALUE(current)); + pop_stack(); + if (previous) { + CB_PUSH_TRANSFORM_KEY(previous->key); + return; + } + } + push_undefined(); + } + + DOCSTART() @decl key_ptype next(mixed current) + *! Get the key after @expr{current@} in lexicographical order. + DOCEND() + PIKEFUN key_ptype next(mixed current) { + CB_CHECK_KEY(current, "next", 1); + + if (THIS->tree) { + cb_node_t next; + + next = cb_find_next(THIS->tree, CB_LOW_KEY_FROM_SVALUE(current)); + pop_stack(); + if (next) { + CB_PUSH_TRANSFORM_KEY(next->key); + return; + } + } + push_undefined(); + } + + DOCSTART() @decl object _get_iterator() + *! @returns + *! This function returns an @[Iterator]-instance as would be created + *! with @expr{ADT.CritBit.TREE_CLASSIFY(.Iterator)(tree)@} + DOCEND() + PIKEFUN object _get_iterator() { + ref_push_object(Pike_fp->current_object); + push_object(clone_object(cmod_CONCAT_EVAL(tree_class,_,iterator_class,_program), 1)); + } + + /*! @decl int(0..) depth() + *! Calculate the depth of the tree. + */ + PIKEFUN int depth() { + if (THIS->tree) { + push_int(cb_get_depth(THIS->tree)); + return; + } + + push_int(0); + return; + } + + DOCSTART() @decl tree_class copy() + *! Create a copy of the tree. + DOCEND() + PIKEFUN object copy() { + struct object * copy; + copy = clone_object(Pike_fp->current_object->prog, 0); + if (THIS->tree) { + cmod_OBJ2_TREE(copy)->tree = cb_copy_tree(THIS->tree); + } + push_object(copy); + } + + DOCSTART() @decl tree_class get_subtree(void|mixed key) + *! Get a copy of the subtree starting at prefix + *! @expr{key@}. + DOCEND() + PIKEFUN object get_subtree(void|mixed key) { + struct object * sub; + cb_node_t node = NULL; + + if (THIS->tree) { + if (args == 0 || IS_UNDEFINED(key)) + node = THIS->tree; + else + node = cb_subtree_prefix(THIS->tree, CB_LOW_KEY_FROM_SVALUE(key)); + } + sub = clone_object(Pike_fp->current_object->prog, 0); + + pop_n_elems(args); + + if (node) { + COPY_TREE(sub, node); + } + + push_object(sub); + } + + EXIT { +#if 0 /* HAS___SYNC_VAL_COMPARE_AND_SWAP */ + cb_node_t tree = THIS->tree; + if (tree && __sync_val_compare_and_swap(&(THIS->tree), tree, NULL)) { + cb_free_node(tree); + } +#else + if (THIS->tree) cb_free_node(THIS->tree); + THIS->tree = NULL; +#endif + } + +/*! @endclass */ diff --git a/src/post_modules/CritBit/widestring.h b/src/post_modules/CritBit/widestring.h new file mode 100644 index 0000000000000000000000000000000000000000..8676e837e03706c92e4c68f4f6ff90325e04366a --- /dev/null +++ b/src/post_modules/CritBit/widestring.h @@ -0,0 +1,169 @@ +#ifndef _WIDESTRING_H +#define _WIDESTRING_H +#include <stdint.h> +#include "bitvector.h" +#include "stralloc.h" + +typedef struct pike_string * CB_NAME(string); +typedef p_wchar2 CB_NAME(char); + +#include "svalue_value.h" +#include "tree_low.h" + +#define CB_DO_PRINTING + +/* the following is used by tree_low */ +#ifdef CB_SOURCE +#define CB_ADD_KEY_REF(x) do { if ((x).str) add_ref((x).str); } while(0) +#define CB_FREE_KEY(x) do { if ((x).str) free_string((x).str); } while(0) +#define CB_SET_KEY(node, x) \ + do { CB_ADD_KEY_REF(x); \ + CB_FREE_KEY((node)->key); (node)->key = (x); } while(0) + +#define CB_KEY_EQ(k1, k2) (CB_S_EQ((k1).len, (k2).len) && (k1).str == (k2).str) +#define CB_KEY_LT(k1, k2) \ + (CB_LT((k1).len, (k2).len) && my_quick_strcmp((k1).str, (k2).str) < 0) + +#define CB_GET_CHAR(s, n) ((cb_char)INDEX_CHARP((s)->str, (n), (s)->size_shift)) +#define CB_WIDTH(s) (1 << (3 + (s)->size_shift)) /* width in bits */ +#define CB_LENGTH(str) ((str)->len) +#define CB_SIZE(key) ((key).len) +#define CB_GET_BIT(str, pos) \ + (BITN(cb_char, CB_GET_CHAR((str), (pos).chars), (pos).bits)) +static inline uint32_t CB_COUNT_PREFIX(const cb_string s1, const cb_string s2, const size_t n) { + cb_char c; + c = CB_GET_CHAR(s1, n); + c ^= CB_GET_CHAR(s2, n); + + return clz32(c); +} +#if 0 +#define CB_COUNT_PREFIX(s1, s2, n) \ + (clz32(CB_GET_CHAR((s1), (n)) ^ CB_GET_CHAR((s2), (n)))) +#endif +#endif /* CB_SOURCE */ + +/* this is only used in pike code! */ +#define CB_PRINT_CHAR(buf, str, n) \ + do { string_builder_sprintf((buf), "%c", CB_GET_CHAR(str, n)); } while(0) + +#define CB_PRINT_KEY(buf, key) \ + do { string_builder_shared_strcat((buf), (key).str); } while(0) +#define CB_LOW_ASSIGN_SVALUE_KEY(key, s) do { \ + struct pike_string * _key = (key).str; \ + struct svalue * _svalue = (s); \ + add_ref(_key); \ + _svalue->subtype = 0; \ + _svalue->u.string = _key; \ + _svalue->type = PIKE_T_STRING; \ +} while(0) +#define CB_PUSH_KEY(key) ref_push_string((key).str) +#define CB_PUSH_STRING(str) ref_push_string(str) +#define CB_STRING_FROM_SVALUE(v) ((v)->u.string) +#define CB_LOW_KEY_FROM_SVALUE(v) CB_KEY_FROM_STRING(CB_STRING_FROM_SVALUE(v)) + +static inline cb_size cb_prefix_count_wide0(const cb_string s1, + const cb_string s2, + const cb_size len, + cb_size start) { + size_t j = start.chars; + const unsigned char * p1, * p2; + + p1 = STR0(s1); + p2 = STR0(s2); + +#if 0 + asm volatile( + "pushf\t\n" + "orl $(1<<18), (%esp)\t\n" + "popf\t\n" + ); +#endif + +#define PREFIX(n) do { \ + size_t k = j/sizeof(uint ##n ##_t); \ + for (;k < len.chars/sizeof(uint ##n ##_t);k++) { \ + uint ##n ##_t x = ((uint ##n ##_t *)p1)[k] \ + ^ ((uint ##n ##_t *)p2)[k]; \ + if (x) { \ + uint32_t a; \ + a = clz ##n(hton ##n(x)); \ + start.chars = k*sizeof(uint ##n ##_t) + a/8; \ + start.bits = 24 + a % 8; \ + return start; \ + } \ + } \ + j = k*sizeof(uint ##n ##_t); \ + } while(0) +#ifdef lzc64 + PREFIX(64); +#endif + PREFIX(32); + PREFIX(8); + +#if 0 + asm volatile( + "pushf\t\n" + "andl $(~(1<<18)), (%esp)\t\n" + "popf\t\n" + ); +#endif + + start.chars = j; + + if (len.bits) { + uint32_t a = clz8(((p_wchar0*)p1)[j]^((p_wchar0*)p2)[j]); + start.bits = MINIMUM(len.bits, 24+a); + return start; + } + + start.bits = 0; + return start; +} + +static inline cb_size cb_prefix_count_widestring(const cb_string s1, + const cb_string s2, + const cb_size len, + const cb_size start) { +#if 0 + size_t i; + cb_size t, r; + unsigned char* chrptr = s1->str;; + static volatile cb_string n1, n2; + static volatile void* zero = NULL; + static volatile cb_size *rp, *tp; + + n1 = s1; + n2 = s2; + +#endif + if (CB_WIDTH(s1) == 8 && 8 == CB_WIDTH(s2)) + return cb_prefix_count_wide0(s1, s2, len, start); +#if 0 + if (!CB_S_EQ(r, t)) { + rp = &r; + tp = &t; + fprintf(stderr, "%p(%p) and %p(%p) have prefix %lu,%lu. should " + + "have %lu,%lu (len: %lu, %lu start: %lu, %lu)\n", + s1->str, s1, s2->str, s2, t.chars, t.bits, r.chars, + r.bits, len.chars, len.bits, start.chars, start.bits); + fprintf(stderr, "here you die: %p, %lu\n", chrptr, + r.bits/(uint64_t)zero); + fprintf(stderr, "%p %p %p", zero, n1, n2); + n1 = n2 = (void*)(zero = (uint64_t)chrptr ^ (uint64_t)n1 + ^ (uint64_t)n2); + fprintf(stderr, "%p %p %p %p %p %p", zero, n1, n2, rp, (void*)rp, + tp, (void*)tp); + } +#endif + + + return cb_prefix_count_fallback(s1, s2, len, start); +} + +#define cb_prefix_count cb_prefix_count_widestring + +#undef CB_WIDTH +#define CB_WIDTH(s) (32) + +#endif