diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 98020848997baf23b5339a0755f013ca9ce2084c..f0028d08b1bb00e15c09ce7cbec2b18955d4b725 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -67,7 +67,6 @@ set (common_sources crc32.c random.c scramble.c - tbuf.c opts.c cfg.cc cpu_feature.c diff --git a/src/box/CMakeLists.txt b/src/box/CMakeLists.txt index 8cb6295982548b00add6647eaaf34ff606589f1e..72ae654e3ceeeacfed51e9cbe7d8a37116e2a120 100644 --- a/src/box/CMakeLists.txt +++ b/src/box/CMakeLists.txt @@ -28,6 +28,7 @@ add_library(box index.cc hash_index.cc tree_index.cc + rtree_index.cc bitset_index.cc engine.cc engine_memtx.cc diff --git a/src/box/engine_memtx.cc b/src/box/engine_memtx.cc index 24f30946565638c2b25bda4935a5ef1a06314337..fa9c459ee482373e4e20f868d3888c1b945dabf2 100644 --- a/src/box/engine_memtx.cc +++ b/src/box/engine_memtx.cc @@ -32,6 +32,7 @@ #include "index.h" #include "hash_index.h" #include "tree_index.h" +#include "rtree_index.h" #include "bitset_index.h" #include "space.h" #include "exception.h" @@ -105,6 +106,8 @@ MemtxFactory::createIndex(struct key_def *key_def) return new HashIndex(key_def); case TREE: return new TreeIndex(key_def); + case RTREE: + return new RTreeIndex(key_def); case BITSET: return new BitsetIndex(key_def); default: @@ -138,6 +141,20 @@ MemtxFactory::keydefCheck(struct key_def *key_def) case TREE: /* TREE index has no limitations. */ break; + case RTREE: + if (key_def->part_count != 1) { + tnt_raise(ClientError, ER_MODIFY_INDEX, + (unsigned) key_def->iid, + (unsigned) key_def->space_id, + "RTREE index key can not be multipart"); + } + if (key_def->is_unique) { + tnt_raise(ClientError, ER_MODIFY_INDEX, + (unsigned) key_def->iid, + (unsigned) key_def->space_id, + "RTREE index can not be unique"); + } + break; case BITSET: if (key_def->part_count != 1) { tnt_raise(ClientError, ER_MODIFY_INDEX, @@ -158,4 +175,28 @@ MemtxFactory::keydefCheck(struct key_def *key_def) (unsigned) key_def->space_id); break; } + for (uint32_t i = 0; i < key_def->part_count; i++) { + switch (key_def->parts[i].type) { + case NUM: + case STRING: + if (key_def->type == RTREE) { + tnt_raise(ClientError, ER_MODIFY_INDEX, + (unsigned) key_def->iid, + (unsigned) key_def->space_id, + "RTREE index field type must be ARRAY"); + } + break; + case ARRAY: + if (key_def->type != RTREE) { + tnt_raise(ClientError, ER_MODIFY_INDEX, + (unsigned) key_def->iid, + (unsigned) key_def->space_id, + "ARRAY field type is not supported"); + } + break; + default: + assert(false); + break; + } + } } diff --git a/src/box/engine_sophia.cc b/src/box/engine_sophia.cc index 20a9555196e7656f493456181535adde9e14d226..e1058e6305fef5b257c9cc8cd85a147f32d893d1 100644 --- a/src/box/engine_sophia.cc +++ b/src/box/engine_sophia.cc @@ -202,6 +202,13 @@ SophiaFactory::keydefCheck(struct key_def *key_def) (unsigned) key_def->space_id, "Sophia TREE index key can not be multipart"); } + if (key_def->parts[0].type != NUM && + key_def->parts[0].type != STRING) { + tnt_raise(ClientError, ER_MODIFY_INDEX, + (unsigned) key_def->iid, + (unsigned) key_def->space_id, + "Sophia TREE index field type must be STR or NUM"); + } break; default: tnt_raise(ClientError, ER_INDEX_TYPE, diff --git a/src/box/index.cc b/src/box/index.cc index d03db083b9be1555b495ed3ffa1e706cc886d751..4d3074d50b07f13d07dc6d3096dc507c2e4a4fe1 100644 --- a/src/box/index.cc +++ b/src/box/index.cc @@ -73,16 +73,29 @@ key_validate(struct key_def *key_def, enum iterator_type type, const char *key, /* Fall through. */ } - if (part_count > key_def->part_count) - tnt_raise(ClientError, ER_KEY_PART_COUNT, - key_def->part_count, part_count); - - /* Partial keys are allowed only for TREE index type. */ - if (key_def->type != TREE && part_count < key_def->part_count) { - tnt_raise(ClientError, ER_EXACT_MATCH, - key_def->part_count, part_count); - } - key_validate_parts(key_def, key, part_count); + if (key_def->type == RTREE) { + if (part_count != 1 && part_count != 2 && part_count != 4) { + tnt_raise(ClientError, ER_KEY_PART_COUNT, 4, part_count); + } +#if 0 + for (uint32_t part = 0; part < part_count; part++) { + enum mp_type mp_type = mp_typeof(*key); + mp_next(&key); + key_mp_type_validate(NUM, mp_type, ER_KEY_PART_TYPE, part); + } +#endif + } else { + if (part_count > key_def->part_count) + tnt_raise(ClientError, ER_KEY_PART_COUNT, + key_def->part_count, part_count); + + /* Partial keys are allowed only for TREE index type. */ + if (key_def->type != TREE && part_count < key_def->part_count) { + tnt_raise(ClientError, ER_EXACT_MATCH, + key_def->part_count, part_count); + } + key_validate_parts(key_def, key, part_count); + } } void diff --git a/src/box/index.h b/src/box/index.h index 5ba9fd7ad8a92e33b908f33cddbae0c490504990..0b22c3fb9e4cdbb3511c806c1edf8b1a875c527b 100644 --- a/src/box/index.h +++ b/src/box/index.h @@ -70,6 +70,8 @@ struct tuple; _(ITER_BITS_ALL_SET, 7) /* all bits from x are set in key */ \ _(ITER_BITS_ANY_SET, 8) /* at least one x's bit is set */ \ _(ITER_BITS_ALL_NOT_SET, 9) /* all bits are not set */ \ + _(ITER_OVERLAPS, 10) /* key overlaps x */ \ + _(ITER_NEIGHBOR, 11) /* typles in distance ascending order from specified point */ \ ENUM(iterator_type, ITERATOR_TYPE); extern const char *iterator_type_strs[]; diff --git a/src/box/iproto.cc b/src/box/iproto.cc index c184871f4b49f884c2ff0f39f6d2bba4362f8441..bce1016e6c2c27ffe470ed24f173ca4dcb6edf5a 100644 --- a/src/box/iproto.cc +++ b/src/box/iproto.cc @@ -774,7 +774,6 @@ iproto_process_connect(struct iproto_request *request) return; } catch (Exception *e) { e->log(); - assert(con->session == NULL); iproto_connection_close(con); return; } diff --git a/src/box/key_def.cc b/src/box/key_def.cc index 42283d80aeb7268d526eb517a7e283ea9d881780..0f670e3396dcac2a7f3d7a663a257392c16166b3 100644 --- a/src/box/key_def.cc +++ b/src/box/key_def.cc @@ -33,13 +33,14 @@ #include <stdio.h> #include "exception.h" -const char *field_type_strs[] = {"UNKNOWN", "NUM", "STR", "\0"}; +const char *field_type_strs[] = {"UNKNOWN", "NUM", "STR", "ARRAY", "\0"}; STRS(index_type, ENUM_INDEX_TYPE); const uint32_t key_mp_type[] = { /* [UNKNOWN] = */ UINT32_MAX, - /* [NUM] = */ 1U << MP_UINT, - /* [_STR] = */ 1U << MP_STR + /* [NUM] = */ 1U << MP_UINT, + /* [STR] = */ 1U << MP_STR, + /* [ARRAY] = */ 1U << MP_ARRAY, }; enum schema_object_type diff --git a/src/box/key_def.h b/src/box/key_def.h index 91982d07ebcab8b110a859185a36b8a590752189..fb7a3a4b7059256a6f590adebbd42e721b85303e 100644 --- a/src/box/key_def.h +++ b/src/box/key_def.h @@ -75,21 +75,22 @@ schema_object_type(const char *name); * since there is a mismatch between enum name (STRING) and type * name literal ("STR"). STR is already used as Objective C type. */ -enum field_type { UNKNOWN = 0, NUM, STRING, field_type_MAX }; +enum field_type { UNKNOWN = 0, NUM, STRING, ARRAY, field_type_MAX }; extern const char *field_type_strs[]; static inline uint32_t field_type_maxlen(enum field_type type) { static const uint32_t maxlen[] = - { UINT32_MAX, 4, 8, UINT32_MAX, UINT32_MAX }; + { UINT32_MAX, 8, UINT32_MAX, UINT32_MAX, UINT32_MAX }; return maxlen[type]; } #define ENUM_INDEX_TYPE(_) \ _(HASH, 0) /* HASH Index */ \ _(TREE, 1) /* TREE Index */ \ - _(BITSET, 2) /* BITSET Index */ + _(BITSET, 2) /* BITSET Index */ \ + _(RTREE, 3) /* R-Tree Index */ \ ENUM(index_type, ENUM_INDEX_TYPE); extern const char *index_type_strs[]; @@ -265,7 +266,6 @@ key_mp_type_validate(enum field_type key_type, enum mp_type mp_type, { assert(key_type < field_type_MAX); assert((int) mp_type < (int) CHAR_BIT * sizeof(*key_mp_type)); - if (unlikely((key_mp_type[key_type] & (1U << mp_type)) == 0)) tnt_raise(ClientError, err, field_no, field_type_strs[key_type]); diff --git a/src/box/lua/call.cc b/src/box/lua/call.cc index 00a661a72c564257ff57303ceaccb12358b71bc3..9eb6643b7968676dc6ec670a7f9a5ddc3c63bab8 100644 --- a/src/box/lua/call.cc +++ b/src/box/lua/call.cc @@ -40,7 +40,7 @@ #include "lua/utils.h" #include "lua/msgpack.h" -#include "tbuf.h" +#include "iobuf.h" #include "fiber.h" #include "scoped_guard.h" #include "box/box.h" @@ -248,18 +248,20 @@ lbox_request_create(struct request *request, request_create(request, type); request->space_id = lua_tointeger(L, 1); if (key > 0) { - struct tbuf *key_buf = tbuf_new(&fiber()->gc); - luamp_encode(L, luaL_msgpack_default, key_buf, key); - request->key = key_buf->data; - request->key_end = key_buf->data + key_buf->size; + struct obuf key_buf; + obuf_create(&key_buf, &fiber()->gc, LUAMP_ALLOC_FACTOR); + luamp_encode(L, luaL_msgpack_default, &key_buf, key); + request->key = obuf_join(&key_buf); + request->key_end = request->key + obuf_size(&key_buf); if (mp_typeof(*request->key) != MP_ARRAY) tnt_raise(ClientError, ER_TUPLE_NOT_ARRAY); } if (tuple > 0) { - struct tbuf *tuple_buf = tbuf_new(&fiber()->gc); - luamp_encode(L, luaL_msgpack_default, tuple_buf, tuple); - request->tuple = tuple_buf->data; - request->tuple_end = tuple_buf->data + tuple_buf->size; + struct obuf tuple_buf; + obuf_create(&tuple_buf, &fiber()->gc, LUAMP_ALLOC_FACTOR); + luamp_encode(L, luaL_msgpack_default, &tuple_buf, tuple); + request->tuple = obuf_join(&tuple_buf); + request->tuple_end = request->tuple + obuf_size(&tuple_buf); if (mp_typeof(*request->tuple) != MP_ARRAY) tnt_raise(ClientError, ER_TUPLE_NOT_ARRAY); } diff --git a/src/box/lua/index.cc b/src/box/lua/index.cc index 9293cf8ae52fef4df1539bd28096a7d1b15cdb2b..b2a607c27f9e40aa52a9702dd008210c075cc919 100644 --- a/src/box/lua/index.cc +++ b/src/box/lua/index.cc @@ -34,7 +34,6 @@ #include "box/user_def.h" #include "box/lua/tuple.h" #include "fiber.h" -#include "tbuf.h" /** {{{ box.index Lua library: access to spaces and indexes */ diff --git a/src/box/lua/tuple.cc b/src/box/lua/tuple.cc index f0c204c4bbdec6d62ebdb804a331cb86e46464d3..57968d387f41b57593b08bd0df723a384442b684 100644 --- a/src/box/lua/tuple.cc +++ b/src/box/lua/tuple.cc @@ -31,7 +31,7 @@ #include "box/tuple.h" #include "box/tuple_update.h" #include "fiber.h" -#include "tbuf.h" +#include "iobuf.h" #include "lua/utils.h" #include "lua/msgpack.h" #include "third_party/lua-yaml/lyaml.h" @@ -169,11 +169,11 @@ lbox_tuple_slice(struct lua_State *L) /* A MsgPack extensions handler that supports tuples */ static int -luamp_encode_extension_box(struct lua_State *L, int idx, struct tbuf *b) +luamp_encode_extension_box(struct lua_State *L, int idx, struct obuf *b) { struct tuple *tuple = lua_istuple(L, idx); if (tuple != NULL) { - tuple_to_tbuf(tuple, b); + tuple_to_obuf(tuple, b); return 0; } @@ -185,7 +185,7 @@ luamp_encode_extension_box(struct lua_State *L, int idx, struct tbuf *b) * Will be removed after API change. */ int -luamp_encodestack(struct lua_State *L, struct tbuf *b, int first, int last) +luamp_encodestack(struct lua_State *L, struct obuf *b, int first, int last) { if (first == last && (lua_istable(L, first) || lua_istuple(L, first))) { /* New format */ @@ -193,7 +193,7 @@ luamp_encodestack(struct lua_State *L, struct tbuf *b, int first, int last) return 1; } else { /* Backward-compatible format */ - /* sic: if arg_count is 0, first > last */ + /* sic: first > last */ luamp_encode_array(luaL_msgpack_default, b, last + 1 - first); for (int k = first; k <= last; ++k) { luamp_encode(L, luaL_msgpack_default, b, k); @@ -269,27 +269,29 @@ lbox_tuple_transform(struct lua_State *L) /* * Prepare UPDATE expression */ - struct tbuf *b = tbuf_new(&fiber()->gc); - luamp_encode_array(luaL_msgpack_default,b, op_cnt); + struct obuf buf; + obuf_create(&buf, &fiber()->gc, LUAMP_ALLOC_FACTOR); + luamp_encode_array(luaL_msgpack_default, &buf, op_cnt); if (len > 0) { - luamp_encode_array(luaL_msgpack_default, b, 3); - luamp_encode_str(luaL_msgpack_default, b, "#", 1); - luamp_encode_uint(luaL_msgpack_default, b, offset); - luamp_encode_uint(luaL_msgpack_default, b, len); + luamp_encode_array(luaL_msgpack_default, &buf, 3); + luamp_encode_str(luaL_msgpack_default, &buf, "#", 1); + luamp_encode_uint(luaL_msgpack_default, &buf, offset); + luamp_encode_uint(luaL_msgpack_default, &buf, len); } for (int i = argc ; i > 3; i--) { - luamp_encode_array(luaL_msgpack_default, b, 3); - luamp_encode_str(luaL_msgpack_default, b, "!", 1); - luamp_encode_uint(luaL_msgpack_default, b, offset); - luamp_encode(L, luaL_msgpack_default, b, i); + luamp_encode_array(luaL_msgpack_default, &buf, 3); + luamp_encode_str(luaL_msgpack_default, &buf, "!", 1); + luamp_encode_uint(luaL_msgpack_default, &buf, offset); + luamp_encode(L, luaL_msgpack_default, &buf, i); } /* Execute tuple_update */ + const char *expr = obuf_join(&buf); struct tuple *new_tuple = tuple_update(tuple_format_ber, tuple_update_region_alloc, &fiber()->gc, - tuple, tbuf_str(b), tbuf_end(b), + tuple, expr, expr + obuf_size(&buf), 0); lbox_pushtuple(L, new_tuple); return 1; @@ -328,22 +330,25 @@ static const struct luaL_reg lbox_tuple_iterator_meta[] = { }; -struct tuple* +struct tuple * lua_totuple(struct lua_State *L, int first, int last) { RegionGuard region_guard(&fiber()->gc); - struct tbuf *b = tbuf_new(&fiber()->gc); + struct obuf buf; + obuf_create(&buf, &fiber()->gc, LUAMP_ALLOC_FACTOR); + try { - luamp_encodestack(L, b, first, last); + luamp_encodestack(L, &buf, first, last); } catch (Exception *e) { throw; } catch (...) { tnt_raise(ClientError, ER_PROC_LUA, lua_tostring(L, -1)); } - const char *data = b->data; + const char *data = obuf_join(&buf); if (unlikely(mp_typeof(*data) != MP_ARRAY)) tnt_raise(ClientError, ER_TUPLE_NOT_ARRAY); - struct tuple *tuple = tuple_new(tuple_format_ber, data, tbuf_end(b)); + struct tuple *tuple = tuple_new(tuple_format_ber, data, + data + obuf_size(&buf)); return tuple; } diff --git a/src/box/rtree_index.cc b/src/box/rtree_index.cc new file mode 100644 index 0000000000000000000000000000000000000000..f8ddddf7e216f5a60ce0be2fadd95921191002b8 --- /dev/null +++ b/src/box/rtree_index.cc @@ -0,0 +1,361 @@ +/* + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include "rtree_index.h" +#include "tuple.h" +#include "space.h" +#include "exception.h" +#include "errinj.h" +#include "fiber.h" +#include "small/small.h" + +/** For all memory used by all rtree indexes. */ +static struct mempool rtree_page_pool; +/** Number of allocated pages. */ +static int rtree_page_pool_initialized = 0; + +/* {{{ Utilities. *************************************************/ + +inline void extract_rectangle(struct rtree_rect *rect, + const struct tuple *tuple, struct key_def *kd) +{ + const char *elems = tuple_field(tuple, kd->parts[0].fieldno); + uint32_t size = mp_decode_array(&elems); + assert (kd->part_count == 1); + switch (size) { + case 1: // array + { + const char* elems = tuple_field(tuple, kd->parts[0].fieldno); + uint32_t size = mp_decode_array(&elems); + switch (size) { + case 2: // point + rect->lower_point.coords[0] = + rect->upper_point.coords[0] = + mp_decode_num(&elems, 0); + rect->lower_point.coords[1] = + rect->upper_point.coords[1] = + mp_decode_num(&elems, 1); + break; + case 4: + rect->lower_point.coords[0] = mp_decode_num(&elems, 0); + rect->lower_point.coords[1] = mp_decode_num(&elems, 1); + rect->upper_point.coords[0] = mp_decode_num(&elems, 2); + rect->upper_point.coords[1] = mp_decode_num(&elems, 3); + break; + default: + tnt_raise(ClientError, ER_UNSUPPORTED, + "R-Tree index", "Field should be array with " + "size 2 (point) or 4 (rectangle)"); + + } + break; + } + case 2: // point + rect->lower_point.coords[0] = + rect->upper_point.coords[0] = + mp_decode_num(&elems, 0); + rect->lower_point.coords[1] = + rect->upper_point.coords[1] = + mp_decode_num(&elems, 1); + break; + case 4: + rect->lower_point.coords[0] = mp_decode_num(&elems, 0); + rect->lower_point.coords[1] = mp_decode_num(&elems, 1); + rect->upper_point.coords[0] = mp_decode_num(&elems, 2); + rect->upper_point.coords[1] = mp_decode_num(&elems, 3); + break; + default: + tnt_raise(ClientError, ER_UNSUPPORTED, + "R-Tree index", "Key should contain 2 (point) or 4 (rectangle) coordinates"); + + } + rtree_rect_normalize(rect); +} +/* {{{ TreeIndex Iterators ****************************************/ + +struct index_rtree_iterator { + struct iterator base; + struct rtree_iterator impl; +}; + +static void +index_rtree_iterator_free(struct iterator *i) +{ + struct index_rtree_iterator *itr = (struct index_rtree_iterator *)i; + rtree_iterator_destroy(&itr->impl); + delete itr; +} + +static struct tuple * +index_rtree_iterator_next(struct iterator *i) +{ + struct index_rtree_iterator *itr = (struct index_rtree_iterator *)i; + return (struct tuple *)rtree_iterator_next(&itr->impl); +} + +/* }}} */ + +/* {{{ TreeIndex **********************************************************/ + +static void * +rtree_page_alloc() +{ + ERROR_INJECT(ERRINJ_INDEX_ALLOC, return 0); + return mempool_alloc(&rtree_page_pool); +} + +static void +rtree_page_free(void *page) +{ + return mempool_free(&rtree_page_pool, page); +} +RTreeIndex::~RTreeIndex() +{ + // Iterator has to be destroye prior to tree + if (m_position != NULL) { + index_rtree_iterator_free(m_position); + m_position = NULL; + } + rtree_destroy(&tree); +} + +RTreeIndex::RTreeIndex(struct key_def *key_def) + : Index(key_def) +{ + assert(key_def->part_count == 1); + assert(key_def->parts[0].type = ARRAY); + assert(key_def->is_unique == false); + + if (rtree_page_pool_initialized == 0) { + mempool_create(&rtree_page_pool, &cord()->slabc, + RTREE_PAGE_SIZE); + rtree_page_pool_initialized = 1; + } + rtree_init(&tree, rtree_page_alloc, rtree_page_free); +} + +size_t +RTreeIndex::size() const +{ + return rtree_number_of_records(&tree); +} + +size_t +RTreeIndex::memsize() const +{ + return rtree_used_size(&tree); +} + +struct tuple * +RTreeIndex::findByKey(const char *key, uint32_t part_count) const +{ + rtree_rect rect; + struct rtree_iterator iterator; + rtree_iterator_init(&iterator); + switch (part_count) { + case 1: + { + uint32_t size = mp_decode_array(&key); + switch (size) { + case 2: + rect.lower_point.coords[0] = + rect.upper_point.coords[0] = + mp_decode_num(&key, 0); + rect.lower_point.coords[1] = + rect.upper_point.coords[1] = + mp_decode_num(&key, 1); + break; + case 4: + rect.lower_point.coords[0] = mp_decode_num(&key, 0); + rect.lower_point.coords[1] = mp_decode_num(&key, 1); + rect.upper_point.coords[0] = mp_decode_num(&key, 2); + rect.upper_point.coords[1] = mp_decode_num(&key, 3); + break; + default: + tnt_raise(ClientError, ER_UNSUPPORTED, + "R-Tree key", "Key should be array of 2 (point) " + "or 4 (rectangle) numeric coordinates"); + } + break; + } + case 2: + rect.lower_point.coords[0] = + rect.upper_point.coords[0] = + mp_decode_num(&key, 0); + rect.lower_point.coords[1] = + rect.upper_point.coords[1] = + mp_decode_num(&key, 1); + break; + case 4: + rect.lower_point.coords[0] = mp_decode_num(&key, 0); + rect.lower_point.coords[1] = mp_decode_num(&key, 1); + rect.upper_point.coords[0] = mp_decode_num(&key, 2); + rect.upper_point.coords[1] = mp_decode_num(&key, 3); + break; + default: + tnt_raise(ClientError, ER_UNSUPPORTED, + "R-Tree key", "Key should contain 2 (point) " + "or 4 (rectangle) numeric coordinates"); + } + struct tuple *result = NULL; + if (rtree_search(&tree, &rect, SOP_OVERLAPS, &iterator)) + result = (struct tuple *)rtree_iterator_next(&iterator); + rtree_iterator_destroy(&iterator); + return result; +} + +struct tuple * +RTreeIndex::replace(struct tuple *old_tuple, struct tuple *new_tuple, + enum dup_replace_mode) +{ + struct rtree_rect rect; + if (new_tuple) { + extract_rectangle(&rect, new_tuple, key_def); + rtree_insert(&tree, &rect, new_tuple); + } + if (old_tuple) { + extract_rectangle(&rect, old_tuple, key_def); + if (!rtree_remove(&tree, &rect, old_tuple)) + old_tuple = NULL; + } + return old_tuple; +} + +struct iterator * +RTreeIndex::allocIterator() const +{ + index_rtree_iterator *it = new index_rtree_iterator; + memset(it, 0, sizeof(*it)); + rtree_iterator_init(&it->impl); + if (it == NULL) { + tnt_raise(ClientError, ER_MEMORY_ISSUE, + sizeof(struct index_rtree_iterator), + "RTreeIndex", "iterator"); + } + it->base.next = index_rtree_iterator_next; + it->base.free = index_rtree_iterator_free; + return &it->base; +} + +void +RTreeIndex::initIterator(struct iterator *iterator, enum iterator_type type, + const char *key, uint32_t part_count) const +{ + struct rtree_rect rect; + index_rtree_iterator *it = (index_rtree_iterator *)iterator; + switch (part_count) { + case 0: + if (type != ITER_ALL) { + tnt_raise(ClientError, ER_UNSUPPORTED, + "R-Tree index", "It is possible to omit key only for ITER_ALL"); + } + break; + case 1: + { + uint32_t size = mp_decode_array(&key); + switch (size) { + case 2: + rect.lower_point.coords[0] = + rect.upper_point.coords[0] = + mp_decode_num(&key, 0); + rect.lower_point.coords[1] = + rect.upper_point.coords[1] = + mp_decode_num(&key, 1); + break; + case 4: + rect.lower_point.coords[0] = mp_decode_num(&key, 0); + rect.lower_point.coords[1] = mp_decode_num(&key, 1); + rect.upper_point.coords[0] = mp_decode_num(&key, 2); + rect.upper_point.coords[1] = mp_decode_num(&key, 3); + break; + default: + tnt_raise(ClientError, ER_UNSUPPORTED, + "R-Tree index", "Key should be array of 2 (point) " + "or 4 (rectangle) numeric coordinates"); + } + break; + } + case 2: + rect.lower_point.coords[0] = + rect.upper_point.coords[0] = + mp_decode_num(&key, 0); + rect.lower_point.coords[1] = + rect.upper_point.coords[1] = + mp_decode_num(&key, 1); + break; + case 4: + rect.lower_point.coords[0] = mp_decode_num(&key, 0); + rect.lower_point.coords[1] = mp_decode_num(&key, 1); + rect.upper_point.coords[0] = mp_decode_num(&key, 2); + rect.upper_point.coords[1] = mp_decode_num(&key, 3); + break; + default: + tnt_raise(ClientError, ER_UNSUPPORTED, + "R-Tree index", "Key contain 2 (point) " + "or 4 (rectangle) numeric coordinates"); + } + spatial_search_op op; + switch (type) { + case ITER_ALL: + op = SOP_ALL; + break; + case ITER_EQ: + op = SOP_EQUALS; + break; + case ITER_GT: + op = SOP_STRICT_CONTAINS; + break; + case ITER_GE: + op = SOP_CONTAINS; + break; + case ITER_LT: + op = SOP_STRICT_BELONGS; + break; + case ITER_LE: + op = SOP_BELONGS; + break; + case ITER_OVERLAPS: + op = SOP_OVERLAPS; + break; + case ITER_NEIGHBOR: + op = SOP_NEIGHBOR; + break; + default: + tnt_raise(ClientError, ER_UNSUPPORTED, + "R-Tree index", "Unsupported search operation for R-Tree"); + } + rtree_search(&tree, &rect, op, &it->impl); +} + +void +RTreeIndex::beginBuild() +{ + rtree_purge(&tree); +} + + diff --git a/src/box/rtree_index.h b/src/box/rtree_index.h new file mode 100644 index 0000000000000000000000000000000000000000..451e0d77775857b8bcfdc1bd472ea1ff39b5bf3a --- /dev/null +++ b/src/box/rtree_index.h @@ -0,0 +1,59 @@ +#ifndef TARANTOOL_BOX_RTREE_INDEX_H_INCLUDED +#define TARANTOOL_BOX_RTREE_INDEX_H_INCLUDED +/* + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "index.h" + +#include <salad/rtree.h> + +class RTreeIndex: public Index +{ +public: + RTreeIndex(struct key_def *key_def); + ~RTreeIndex(); + + virtual void beginBuild(); + virtual size_t size() const; + virtual struct tuple *findByKey(const char *key, uint32_t part_count) const; + virtual struct tuple *replace(struct tuple *old_tuple, + struct tuple *new_tuple, + enum dup_replace_mode mode); + + virtual size_t memsize() const; + virtual struct iterator *allocIterator() const; + virtual void initIterator(struct iterator *iterator, + enum iterator_type type, + const char *key, uint32_t part_count) const; + +protected: + struct rtree tree; +}; + +#endif /* TARANTOOL_BOX_RTREE_INDEX_H_INCLUDED */ diff --git a/src/box/tuple.cc b/src/box/tuple.cc index b1b873df4d6df227fdccdc1976a1f9e5d96b2e18..de65e1af7b86c997256824d9bd0fc7e2844ac9ea 100644 --- a/src/box/tuple.cc +++ b/src/box/tuple.cc @@ -30,13 +30,21 @@ #include "small/small.h" #include "small/quota.h" -#include "tbuf.h" #include "key_def.h" #include "tuple_update.h" #include <exception.h> #include <stdio.h> +#ifndef DBL_MIN +#define DBL_MIN 4.94065645841246544e-324 +#define FLT_MIN ((float)1.40129846432481707e-45) +#endif +#ifndef DBL_MAX +#define DBL_MAX 1.79769313486231470e+308 +#define FLT_MAX ((float)3.40282346638528860e+38) +#endif + /** Global table of tuple formats */ struct tuple_format **tuple_formats; struct tuple_format *tuple_format_ber; @@ -536,7 +544,7 @@ tuple_init(float tuple_arena_max_size, uint32_t objsize_min, say_warn("disable shared arena since running under OpenVZ " "(https://bugzilla.openvz.org/show_bug.cgi?id=2805)"); flags = MAP_PRIVATE; - } else { + } else { say_info("mapping %zu bytes for a shared arena...", max_size); flags = MAP_SHARED; @@ -589,3 +597,26 @@ tuple_end_snapshot() { small_alloc_setopt(&memtx_alloc, SMALL_DELAYED_FREE_MODE, false); } + +double mp_decode_num(const char **data, uint32_t i) +{ + double val; + switch (mp_typeof(**data)) { + case MP_UINT: + val = mp_decode_uint(data); + break; + case MP_INT: + val = mp_decode_int(data); + break; + case MP_FLOAT: + val = mp_decode_float(data); + break; + case MP_DOUBLE: + val = mp_decode_double(data); + break; + default: + tnt_raise(ClientError, ER_FIELD_TYPE, i, field_type_strs[NUM]); + } + return val; +} + diff --git a/src/box/tuple.h b/src/box/tuple.h index 5c973d512e063059dc7854c293c4edb8e6a19a2e..4e2b2902ea65bcf7d3763229ea7830dd331ec3fe 100644 --- a/src/box/tuple.h +++ b/src/box/tuple.h @@ -34,8 +34,6 @@ enum { FORMAT_ID_MAX = UINT16_MAX - 1, FORMAT_ID_NIL = UINT16_MAX }; enum { FORMAT_REF_MAX = INT32_MAX, TUPLE_REF_MAX = UINT16_MAX }; -struct tbuf; - /** Common quota for tuples and indexes */ extern struct quota memtx_quota; /** Tuple allocator */ @@ -344,6 +342,25 @@ tuple_field_u32(struct tuple *tuple, uint32_t i) return (uint32_t) val; } +/** + * Decode numeric field and return its value as double + */ +double +mp_decode_num(const char **data, uint32_t i); + +/** + * A convenience shortcut for data dictionary - get a numeric tuple field as double + */ +inline double +tuple_field_num(const struct tuple* tuple, uint32_t field_no) +{ + const char* field = tuple_field(tuple, field_no); + if (field == NULL) { + tnt_raise(ClientError, ER_NO_SUCH_FIELD, field_no); + } + return mp_decode_num(&field, field_no); +} + /** * A convenience shortcut for data dictionary - get a tuple field * as a NUL-terminated string - returns a string of up to 256 bytes. @@ -509,10 +526,6 @@ tuple_compare_with_key(const struct tuple *tuple_a, const char *key, void tuple_to_obuf(struct tuple *tuple, struct obuf *buf); -/* Store tuple fields in the tbuf, BER-length-encoded. */ -void -tuple_to_tbuf(struct tuple *tuple, struct tbuf *buf); - /** * Store tuple fields in the memory buffer. Buffer must have at least * tuple->bsize bytes. diff --git a/src/box/tuple_convert.cc b/src/box/tuple_convert.cc index 5e1346dd85115dcff78798035294e1278e66159c..5e9b505dc35a38746c0d26f6fb4b864090411a9b 100644 --- a/src/box/tuple_convert.cc +++ b/src/box/tuple_convert.cc @@ -28,7 +28,6 @@ */ #include "tuple.h" #include "iobuf.h" -#include "tbuf.h" void tuple_to_obuf(struct tuple *tuple, struct obuf *buf) @@ -36,12 +35,6 @@ tuple_to_obuf(struct tuple *tuple, struct obuf *buf) obuf_dup(buf, tuple->data, tuple->bsize); } -void -tuple_to_tbuf(struct tuple *tuple, struct tbuf *buf) -{ - tbuf_append(buf, tuple->data, tuple->bsize); -} - void tuple_to_buf(struct tuple *tuple, char *buf) { diff --git a/src/errinj.cc b/src/errinj.cc index 1617a26f4f6984490cb31b7d9a8aa85660ecc51d..f4fad088f565bcccbb1454f2bf3019c083d2f02d 100644 --- a/src/errinj.cc +++ b/src/errinj.cc @@ -34,7 +34,6 @@ #include "trivia/config.h" #include "trivia/util.h" #include "say.h" -#include "tbuf.h" #include "errinj.h" #define ERRINJ_MEMBER(n, s) { /* .name = */ #n, /* .state = */ s }, diff --git a/src/iobuf.cc b/src/iobuf.cc index d4837d673a03e3124e69cc5a6df657ff2d354a94..4f565693b4b43f19dd80a4d35e54c55bcc19aa4f 100644 --- a/src/iobuf.cc +++ b/src/iobuf.cc @@ -112,7 +112,7 @@ obuf_init_pos(struct obuf *buf, size_t pos) static inline void obuf_alloc_pos(struct obuf *buf, size_t pos, size_t size) { - size_t capacity = pos > 0 ? buf->capacity[pos-1] * 2 : iobuf_readahead; + size_t capacity = pos > 0 ? buf->capacity[pos-1] * 2 : buf->alloc_factor; while (capacity < size) { capacity *=2; } @@ -126,11 +126,12 @@ obuf_alloc_pos(struct obuf *buf, size_t pos, size_t size) * yet -- it may never be needed. */ void -obuf_create(struct obuf *buf, struct region *pool) +obuf_create(struct obuf *buf, struct region *pool, size_t alloc_factor) { buf->pool = pool; buf->pos = 0; buf->size = 0; + buf->alloc_factor = alloc_factor; obuf_init_pos(buf, buf->pos); } @@ -155,7 +156,7 @@ obuf_dup(struct obuf *buf, const void *data, size_t size) /** * @pre buf->pos points at an array of allocated buffers. * The array ends with a zero-initialized buffer. - */ + */ while (iov->iov_len + size > capacity) { /* * The data doesn't fit into this buffer. @@ -206,36 +207,39 @@ obuf_dup(struct obuf *buf, const void *data, size_t size) assert(iov->iov_len <= buf->capacity[buf->pos]); } -/** Book a few bytes in the output buffer. */ -struct obuf_svp -obuf_book(struct obuf *buf, size_t size) +void +obuf_ensure_resize(struct obuf *buf, size_t size) { struct iovec *iov = &buf->iov[buf->pos]; size_t capacity = buf->capacity[buf->pos]; - if (iov->iov_len + size > capacity) { - if (iov->iov_len > 0) { - /* Move to the next buffer. */ - buf->pos++; - iov = &buf->iov[buf->pos]; - capacity = buf->capacity[buf->pos]; - } - /* Make sure the next buffer can store size. */ - if (capacity == 0) { - obuf_init_pos(buf, buf->pos + 1); - obuf_alloc_pos(buf, buf->pos, size); - } else if (size > capacity) { - /* Simply realloc. */ - obuf_alloc_pos(buf, buf->pos, size); - } + if (iov->iov_len > 0) { + /* Move to the next buffer. */ + buf->pos++; + iov = &buf->iov[buf->pos]; + capacity = buf->capacity[buf->pos]; + } + /* Make sure the next buffer can store size. */ + if (capacity == 0) { + obuf_init_pos(buf, buf->pos + 1); + obuf_alloc_pos(buf, buf->pos, size); + } else if (size > capacity) { + /* Simply realloc. */ + obuf_alloc_pos(buf, buf->pos, size); } +} + +/** Book a few bytes in the output buffer. */ +struct obuf_svp +obuf_book(struct obuf *buf, size_t size) +{ + obuf_ensure(buf, size); + struct obuf_svp svp; svp.pos = buf->pos; - svp.iov_len = iov->iov_len; + svp.iov_len = buf->iov[buf->pos].iov_len; svp.size = buf->size; - iov->iov_len += size; - buf->size += size; - assert(iov->iov_len <= buf->capacity[buf->pos]); + obuf_advance(buf, size); return svp; } @@ -285,7 +289,7 @@ iobuf_new(const char *name) region_create(&iobuf->pool, &cord()->slabc); /* Note: do not allocate memory upfront. */ ibuf_create(&iobuf->in, &iobuf->pool); - obuf_create(&iobuf->out, &iobuf->pool); + obuf_create(&iobuf->out, &iobuf->pool, iobuf_readahead); } else { iobuf = SLIST_FIRST(&iobuf_cache); SLIST_REMOVE_HEAD(&iobuf_cache, next); @@ -307,7 +311,7 @@ iobuf_delete(struct iobuf *iobuf) } else { region_free(pool); ibuf_create(&iobuf->in, pool); - obuf_create(&iobuf->out, pool); + obuf_create(&iobuf->out, pool, iobuf_readahead); } region_set_name(pool, "iobuf_cache"); SLIST_INSERT_HEAD(&iobuf_cache, iobuf, next); diff --git a/src/iobuf.h b/src/iobuf.h index 25d97babc89a8c2e689955175c4da0692d79d03e..2e0e80b710021ffca38a08c6ddebbc3abde51864 100644 --- a/src/iobuf.h +++ b/src/iobuf.h @@ -113,6 +113,8 @@ struct obuf size_t size; /** Position of the "current" iovec. */ size_t pos; + /** Allocation factor (allocations are a multiple of this number) */ + size_t alloc_factor; /** How many bytes are actually allocated for each iovec. */ size_t capacity[IOBUF_IOV_MAX]; /** @@ -124,6 +126,9 @@ struct obuf struct iovec iov[IOBUF_IOV_MAX]; }; +void +obuf_create(struct obuf *buf, struct region *pool, size_t alloc_factor); + /** How many bytes are in the output buffer. */ static inline size_t obuf_size(struct obuf *obuf) @@ -151,6 +156,41 @@ struct obuf_svp size_t size; }; +void +obuf_ensure_resize(struct obuf *buf, size_t size); + +/** + * \brief Ensure \a buf to have at least \a size bytes of contiguous memory + * for write and return a point to this chunk. + * After write please call obuf_advance(wsize) where wsize <= size to advance + * a write position. + * \param buf + * \param size + * \return a pointer to contiguous chunk of memory + */ +static inline char * +obuf_ensure(struct obuf *buf, size_t size) +{ + if (buf->iov[buf->pos].iov_len + size > buf->capacity[buf->pos]) + obuf_ensure_resize(buf, size); + struct iovec *iov = &buf->iov[buf->pos]; + return (char *) iov->iov_base + iov->iov_len; +} + +/** + * \brief Advance write position after using obuf_ensure() + * \param buf + * \param size + * \sa obuf_ensure + */ +static inline void +obuf_advance(struct obuf *buf, size_t size) +{ + buf->iov[buf->pos].iov_len += size; + buf->size += size; + assert(buf->iov[buf->pos].iov_len <= buf->capacity[buf->pos]); +} + /** * Reserve size bytes in the output buffer * and return a pointer to the reserved @@ -192,6 +232,28 @@ obuf_svp_to_ptr(struct obuf *buf, struct obuf_svp *svp) void obuf_rollback_to_svp(struct obuf *buf, struct obuf_svp *svp); +/** + * \brief Conventional function to join iovec into a solid memory chunk. + * For iovec of size 1 returns iov->iov_base without allocation extra memory. + * \param[out] size calculated length of \a iov + * \return solid memory chunk + */ +static inline char * +obuf_join(struct obuf *obuf) +{ + size_t iovcnt = obuf_iovcnt(obuf); + if (iovcnt == 1) + return (char *) obuf->iov[0].iov_base; + + char *data = (char *) region_alloc(obuf->pool, obuf_size(obuf)); + char *pos = data; + for (int i = 0; i < iovcnt; i++) { + memcpy(pos, obuf->iov[i].iov_base, obuf->iov[i].iov_len); + pos += obuf->iov[i].iov_len; + } + return data; +} + /* }}} */ /** {{{ Input/output pair. */ diff --git a/src/lib/salad/CMakeLists.txt b/src/lib/salad/CMakeLists.txt index fe1be4c4bc9729f4d198ad6f0ccac9655687890f..9db3d800e80b8288bb0ed709e7b0e65865a53c06 100644 --- a/src/lib/salad/CMakeLists.txt +++ b/src/lib/salad/CMakeLists.txt @@ -1,3 +1,3 @@ -set(lib_sources rope.c rlist.c) +set(lib_sources rope.c rlist.c rtree.c) set_source_files_compile_flags(${lib_sources}) add_library(salad ${lib_sources}) diff --git a/src/lib/salad/bps_tree.h b/src/lib/salad/bps_tree.h index 02708a15623e1f1c4c8cabbceeb844ff094b3979..887de62bd13928c37e8a3ccfebb49c262c8f04be 100644 --- a/src/lib/salad/bps_tree.h +++ b/src/lib/salad/bps_tree.h @@ -1,3 +1,35 @@ +/* + * *No header guard*: the header is allowed to be included twice + * with different sets of defines. + */ +/* + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ #include <string.h> /* memmove, memset */ #include <stdint.h> #include <assert.h> @@ -426,8 +458,7 @@ typedef uint32_t bps_tree_block_id_t; /** * struct bps_block forward declaration (Used in struct bps_tree) - */ -struct bps_block; + */struct bps_block; #ifdef BPS_TREE_DEBUG_BRANCH_VISIT #define BPS_TREE_BRANCH_TRACE(tree, type, branch_bit) \ diff --git a/src/lib/salad/mhash.h b/src/lib/salad/mhash.h index 1d8c080811ce0bd637498a578315e2f7b813f224..c04adfafa1cbf2faa3a83639e1482fd3289a44de 100644 --- a/src/lib/salad/mhash.h +++ b/src/lib/salad/mhash.h @@ -1,3 +1,7 @@ +/* + * *No header guard*: the header is allowed to be included twice + * with different sets of defines. + */ /* * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following diff --git a/src/lib/salad/rtree.c b/src/lib/salad/rtree.c new file mode 100644 index 0000000000000000000000000000000000000000..1ad73cd886e99468e18ff89aeec660f94fcc124a --- /dev/null +++ b/src/lib/salad/rtree.c @@ -0,0 +1,979 @@ +/* + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include "rtree.h" +#include <string.h> +#include <assert.h> + +/*------------------------------------------------------------------------- */ +/* R-tree internal structures definition */ +/*------------------------------------------------------------------------- */ + +struct rtree_page_branch { + struct rtree_rect rect; + union { + struct rtree_page *page; + record_t record; + } data; +}; + +enum { + /* maximal number of branches at page */ + RTREE_MAX_FILL = (RTREE_PAGE_SIZE - sizeof(int)) / + sizeof(struct rtree_page_branch), + /* minimal number of branches at non-root page */ + RTREE_MIN_FILL = RTREE_MAX_FILL / 2 +}; + +struct rtree_page { + /* number of branches at page */ + int n; + /* branches */ + struct rtree_page_branch b[RTREE_MAX_FILL]; +}; + +struct rtree_neighbor { + void *child; + struct rtree_neighbor *next; + int level; + sq_coord_t distance; +}; + +enum { + RTREE_NEIGHBORS_IN_PAGE = (RTREE_PAGE_SIZE - sizeof(void*)) / + sizeof(struct rtree_neighbor) +}; + +struct rtree_neighbor_page { + struct rtree_neighbor_page* next; + struct rtree_neighbor buf[RTREE_NEIGHBORS_IN_PAGE]; +}; + +struct rtree_reinsert_list { + struct rtree_page *chain; + int level; +}; + +/*------------------------------------------------------------------------- */ +/* R-tree rectangle methods */ +/*------------------------------------------------------------------------- */ + + +void +rtree_rect_normalize(struct rtree_rect *rect) +{ + for (int i = RTREE_DIMENSION; --i >= 0; ) { + if (rect->lower_point.coords[i] <= rect->upper_point.coords[i]) + continue; + coord_t tmp = rect->lower_point.coords[i]; + rect->lower_point.coords[i] = rect->upper_point.coords[i]; + rect->upper_point.coords[i] = tmp; + } +} + +void +rtree_set2d(struct rtree_rect *rect, + coord_t left, coord_t bottom, coord_t right, coord_t top) +{ + assert(RTREE_DIMENSION == 2); + rect->lower_point.coords[0] = left; + rect->lower_point.coords[1] = bottom; + rect->upper_point.coords[0] = right; + rect->upper_point.coords[1] = top; +} + +static sq_coord_t +rtree_rect_point_distance2(const struct rtree_rect *rect, + const struct rtree_point *point) +{ + sq_coord_t result = 0; + for (int i = RTREE_DIMENSION; --i >= 0; ) { + if (point->coords[i] < rect->lower_point.coords[i]) { + sq_coord_t diff = (sq_coord_t)(point->coords[i] - + rect->lower_point.coords[i]); + result += diff * diff; + } else if (point->coords[i] > rect->upper_point.coords[i]) { + sq_coord_t diff = (sq_coord_t)(point->coords[i] - + rect->upper_point.coords[i]); + result += diff * diff; + } + } + return result; +} + +static area_t +rtree_rect_area(const struct rtree_rect *rect) +{ + area_t area = 1; + for (int i = RTREE_DIMENSION; --i >= 0; ) { + area *= rect->upper_point.coords[i] - + rect->lower_point.coords[i]; + } + return area; +} + +static void +rtree_rect_add(struct rtree_rect *to, const struct rtree_rect *item) +{ + for (int i = RTREE_DIMENSION; --i >= 0; ) { + if (to->lower_point.coords[i] > item->lower_point.coords[i]) + to->lower_point.coords[i] = item->lower_point.coords[i]; + if (to->upper_point.coords[i] < item->upper_point.coords[i]) + to->upper_point.coords[i] = item->upper_point.coords[i]; + } +} + +static coord_t +rtree_min(coord_t a, coord_t b) +{ + return a < b ? a : b; +} + +static coord_t +rtree_max(coord_t a, coord_t b) +{ + return a > b ? a : b; +} + +static struct rtree_rect +rtree_rect_cover(const struct rtree_rect *item1, + const struct rtree_rect *item2) +{ + struct rtree_rect res; + for (int i = RTREE_DIMENSION; --i >= 0; ) { + res.lower_point.coords[i] = + rtree_min(item1->lower_point.coords[i], + item2->lower_point.coords[i]); + res.upper_point.coords[i] = + rtree_max(item1->upper_point.coords[i], + item2->upper_point.coords[i]); + } + return res; +} + +static bool +rtree_rect_intersects_rect(const struct rtree_rect *rt1, + const struct rtree_rect *rt2) +{ + for (int i = RTREE_DIMENSION; --i >= 0; ) + if (rt1->lower_point.coords[i] > rt2->upper_point.coords[i] || + rt1->upper_point.coords[i] < rt2->lower_point.coords[i]) + return false; + return true; +} + +static bool +rtree_rect_in_rect(const struct rtree_rect *rt1, + const struct rtree_rect *rt2) +{ + for (int i = RTREE_DIMENSION; --i >= 0; ) + if (rt1->lower_point.coords[i] < rt2->lower_point.coords[i] || + rt1->upper_point.coords[i] > rt2->upper_point.coords[i]) + return false; + return true; +} + +static bool +rtree_rect_strict_in_rect(const struct rtree_rect *rt1, + const struct rtree_rect *rt2) +{ + for (int i = RTREE_DIMENSION; --i >= 0; ) + if (rt1->lower_point.coords[i] <= rt2->lower_point.coords[i] || + rt1->upper_point.coords[i] >= rt2->upper_point.coords[i]) + return false; + return true; +} + +static bool +rtree_rect_holds_rect(const struct rtree_rect *rt1, + const struct rtree_rect *rt2) +{ + return rtree_rect_in_rect(rt2, rt1); +} + +static bool +rtree_rect_strict_holds_rect(const struct rtree_rect *rt1, + const struct rtree_rect *rt2) +{ + return rtree_rect_strict_in_rect(rt2, rt1); +} + +static bool +rtree_rect_equal_to_rect(const struct rtree_rect *rt1, + const struct rtree_rect *rt2) +{ + for (int i = RTREE_DIMENSION; --i >= 0; ) + if (rt1->lower_point.coords[i] != rt2->lower_point.coords[i] || + rt1->upper_point.coords[i] != rt2->upper_point.coords[i]) + return false; + return true; +} + +static bool +rtree_always_true(const struct rtree_rect *rt1, + const struct rtree_rect *rt2) +{ + (void)rt1; + (void)rt2; + return true; +} + +/*------------------------------------------------------------------------- */ +/* R-tree page methods */ +/*------------------------------------------------------------------------- */ + +static struct rtree_page * +rtree_alloc_page(struct rtree *tree) +{ + return (struct rtree_page *)tree->page_alloc(); +} + +static void +rtree_free_page(struct rtree *tree, struct rtree_page *page) +{ + tree->page_free(page); +} + +static void +set_next_reinsert_page(struct rtree_page *page, struct rtree_page *next_page) +{ + /* The page must be MIN_FILLed, so last branch is unused */ + page->b[RTREE_MAX_FILL - 1].data.page = next_page; +} + +struct rtree_page * +get_next_reinsert_page(const struct rtree_page *page) +{ + return page->b[RTREE_MAX_FILL - 1].data.page; +} + +/* Calculate cover of all rectangles at page */ +static struct rtree_rect +rtree_page_cover(const struct rtree_page *page) +{ + struct rtree_rect res = page->b[0].rect; + for (int i = 1; i < page->n; i++) + rtree_rect_add(&res, &page->b[i].rect); + return res; +} + +/* Create root page by first inserting record */ +static void +rtree_page_init_record(struct rtree_page *page, + struct rtree_rect *rect, record_t obj) +{ + page->n = 1; + page->b[0].rect = *rect; + page->b[0].data.record = obj; +} + +/* Create root page by branch */ +static void +rtree_page_init_branch(struct rtree_page *page, + const struct rtree_page_branch *br) +{ + page->n = 1; + page->b[0] = *br; +} + +/* Create new root page (root splitting) */ +static void +rtree_page_init_page(struct rtree_page *page, + struct rtree_page *page1, + struct rtree_page *page2) +{ + page->n = 2; + page->b[0].rect = rtree_page_cover(page1); + page->b[0].data.page = page1; + page->b[1].rect = rtree_page_cover(page2); + page->b[1].data.page = page2; +} + +static struct rtree_page * +rtree_split_page(struct rtree *tree, struct rtree_page *page, + const struct rtree_page_branch *br) +{ + area_t rect_area[RTREE_MAX_FILL + 1]; + rect_area[0] = rtree_rect_area(&br->rect); + for (int i = 0; i < RTREE_MAX_FILL; i++) + rect_area[i + 1] = rtree_rect_area(&page->b[i].rect); + + /* + * As the seeds for the two groups, find two rectangles + * which waste the most area if covered by a single + * rectangle. + */ + int seed[2] = {-1, -1}; + coord_t worst_waste = 0; + bool worst_waste_set = false; + + const struct rtree_page_branch *bp = br; + for (int i = 0; i < RTREE_MAX_FILL; i++) { + for (int j = i + 1; j <= RTREE_MAX_FILL; j++) { + struct rtree_rect cover = + rtree_rect_cover(&bp->rect, + &page->b[j - 1].rect); + coord_t waste = rtree_rect_area(&cover) - + rect_area[i] - rect_area[j]; + if (!worst_waste_set) { + worst_waste_set = true; + worst_waste = waste; + seed[0] = i; + seed[1] = j; + }else if (waste > worst_waste) { + worst_waste = waste; + seed[0] = i; + seed[1] = j; + } + } + bp = page->b + i; + } + assert(seed[0] >= 0); + + char taken[RTREE_MAX_FILL]; + memset(taken, 0, sizeof(taken)); + struct rtree_rect group_rect[2]; + struct rtree_page *p = rtree_alloc_page(tree); + tree->n_pages++; + + taken[seed[1] - 1] = 2; + group_rect[1] = page->b[seed[1] - 1].rect; + + if (seed[0] == 0) { + group_rect[0] = br->rect; + rtree_page_init_branch(p, br); + } else { + group_rect[0] = page->b[seed[0] - 1].rect; + rtree_page_init_branch(p, &page->b[seed[0] - 1]); + page->b[seed[0] - 1] = *br; + } + area_t group_area[2] = {rect_area[seed[0]], rect_area[seed[1]]}; + int group_card[2] = {1, 1}; + + /* + * Split remaining rectangles between two groups. + * The one chosen is the one with the greatest difference in area + * expansion depending on which group - the rect most strongly + * attracted to one group and repelled from the other. + */ + while (group_card[0] + group_card[1] < RTREE_MAX_FILL + 1 + && group_card[0] < RTREE_MAX_FILL + 1 - RTREE_MIN_FILL + && group_card[1] < RTREE_MAX_FILL + 1 - RTREE_MIN_FILL) + { + int better_group = -1, chosen = -1; + area_t biggest_diff = -1; + for (int i = 0; i < RTREE_MAX_FILL; i++) { + if (taken[i]) + continue; + struct rtree_rect cover0 = + rtree_rect_cover(&group_rect[0], + &page->b[i].rect); + struct rtree_rect cover1 = + rtree_rect_cover(&group_rect[1], + &page->b[i].rect); + area_t diff = rtree_rect_area(&cover0) - group_area[0] + - (rtree_rect_area(&cover1) - group_area[1]); + if (diff > biggest_diff || -diff > biggest_diff) { + chosen = i; + if (diff < 0) { + better_group = 0; + biggest_diff = -diff; + } else { + better_group = 1; + biggest_diff = diff; + } + } + } + assert(chosen >= 0); + group_card[better_group]++; + rtree_rect_add(&group_rect[better_group], + &page->b[chosen].rect); + group_area[better_group] = + rtree_rect_area(&group_rect[better_group]); + taken[chosen] = better_group + 1; + if (better_group == 0) + p->b[group_card[0] - 1] = page->b[chosen]; + } + /* + * If one group gets too full, then remaining rectangle + * are split between two groups in such way to balance + * CARDs of two groups. + */ + if (group_card[0] + group_card[1] < RTREE_MAX_FILL + 1) { + for (int i = 0; i < RTREE_MAX_FILL; i++) { + if (taken[i]) + continue; + if (group_card[0] >= group_card[1]) { + taken[i] = 2; + group_card[1] += 1; + } else { + taken[i] = 1; + p->b[group_card[0]++] = page->b[i]; + } + } + } + p->n = group_card[0]; + page->n = group_card[1]; + for (int i = 0, j = 0; i < page->n; j++) { + if (taken[j] == 2) + page->b[i++] = page->b[j]; + } + return p; +} + +static struct rtree_page* +rtree_page_add_branch(struct rtree *tree, struct rtree_page *page, + const struct rtree_page_branch *br) +{ + if (page->n < RTREE_MAX_FILL) { + page->b[page->n++] = *br; + return NULL; + } else { + return rtree_split_page(tree, page, br); + } +} + +static void +rtree_page_remove_branch(struct rtree_page *page, int i) +{ + page->n -= 1; + memmove(page->b + i, page->b + i + 1, + (page->n - i) * sizeof(struct rtree_page_branch)); +} + +static struct rtree_page * +rtree_page_insert(struct rtree *tree, struct rtree_page *page, + const struct rtree_rect *rect, record_t obj, int level) +{ + struct rtree_page_branch br; + if (--level != 0) { + /* not a leaf page */ + int mini = -1; + area_t min_incr, best_area; + for (int i = 0; i < page->n; i++) { + area_t r_area = rtree_rect_area(&page->b[i].rect); + struct rtree_rect cover = + rtree_rect_cover(&page->b[i].rect, rect); + area_t incr = rtree_rect_area(&cover) - r_area; + assert(incr >= 0); + if (i == 0) { + best_area = r_area; + min_incr = incr; + mini = i; + } else if (incr < min_incr) { + best_area = r_area; + min_incr = incr; + mini = i; + } else if (incr == min_incr && r_area < best_area) { + best_area = r_area; + mini = i; + } + } + assert(mini >= 0); + struct rtree_page *p = page->b[mini].data.page; + struct rtree_page *q = rtree_page_insert(tree, p, + rect, obj, level); + if (q == NULL) { + /* child was not split */ + rtree_rect_add(&page->b[mini].rect, rect); + return NULL; + } else { + /* child was split */ + page->b[mini].rect = rtree_page_cover(p); + br.data.page = q; + br.rect = rtree_page_cover(q); + return rtree_page_add_branch(tree, page, &br); + } + } else { + br.data.record = obj; + br.rect = *rect; + return rtree_page_add_branch(tree, page, &br); + } +} + +static bool +rtree_page_remove(struct rtree *tree, struct rtree_page *page, + const struct rtree_rect *rect, record_t obj, + int level, struct rtree_reinsert_list *rlist) +{ + if (--level != 0) { + for (int i = 0; i < page->n; i++) { + if (!rtree_rect_intersects_rect(&page->b[i].rect, rect)) + continue; + struct rtree_page *next_page = page->b[i].data.page; + if (!rtree_page_remove(tree, next_page, rect, + obj, level, rlist)) + continue; + if (next_page->n >= RTREE_MIN_FILL) { + page->b[i].rect = + rtree_page_cover(next_page); + } else { + /* not enough entries in child */ + set_next_reinsert_page(next_page, rlist->chain); + rlist->chain = next_page; + rlist->level = level - 1; + rtree_page_remove_branch(page, i); + } + return true; + } + } else { + for (int i = 0; i < page->n; i++) { + if (page->b[i].data.page == obj) { + rtree_page_remove_branch(page, i); + return true; + } + } + } + return false; +} + +static void +rtree_page_purge(struct rtree *tree, struct rtree_page *page, int level) +{ + if (--level != 0) { /* this is an internal node in the tree */ + for (int i = 0; i < page->n; i++) + rtree_page_purge(tree, page->b[i].data.page, level); + } + tree->page_free(page); +} + +/*------------------------------------------------------------------------- */ +/* R-tree iterator methods */ +/*------------------------------------------------------------------------- */ + +static bool +rtree_iterator_goto_first(struct rtree_iterator *itr, int sp, struct rtree_page* pg) +{ + if (sp + 1 == itr->tree->height) { + for (int i = 0, n = pg->n; i < n; i++) { + if (itr->leaf_cmp(&itr->rect, &pg->b[i].rect)) { + itr->stack[sp].page = pg; + itr->stack[sp].pos = i; + return true; + } + } + } else { + for (int i = 0, n = pg->n; i < n; i++) { + if (itr->intr_cmp(&itr->rect, &pg->b[i].rect) + && rtree_iterator_goto_first(itr, sp + 1, + pg->b[i].data.page)) + { + itr->stack[sp].page = pg; + itr->stack[sp].pos = i; + return true; + } + } + } + return false; +} + + +static bool +rtree_iterator_goto_next(struct rtree_iterator *itr, int sp) +{ + struct rtree_page *pg = itr->stack[sp].page; + if (sp + 1 == itr->tree->height) { + for (int i = itr->stack[sp].pos, n = pg->n; ++i < n;) { + if (itr->leaf_cmp(&itr->rect, &pg->b[i].rect)) { + itr->stack[sp].pos = i; + return true; + } + } + } else { + for (int i = itr->stack[sp].pos, n = pg->n; ++i < n;) { + if (itr->intr_cmp(&itr->rect, &pg->b[i].rect) + && rtree_iterator_goto_first(itr, sp + 1, + pg->b[i].data.page)) + { + itr->stack[sp].page = pg; + itr->stack[sp].pos = i; + return true; + } + } + } + return sp > 0 ? rtree_iterator_goto_next(itr, sp - 1) : false; +} + +void +rtree_iterator_destroy(struct rtree_iterator *itr) +{ + struct rtree_neighbor_page *curr, *next; + for (curr = itr->page_list; curr != NULL; curr = next) { + next = curr->next; + itr->tree->page_free(curr); + } + itr->page_list = NULL; + itr->page_pos = RTREE_NEIGHBORS_IN_PAGE; +} + +static void +rtree_iterator_reset(struct rtree_iterator *itr) +{ + if (itr->neigh_list != NULL) { + struct rtree_neighbor **npp = &itr->neigh_free_list; + while (*npp != NULL) { + npp = &(*npp)->next; + } + *npp = itr->neigh_list; + itr->neigh_list = NULL; + } +} + +static struct rtree_neighbor * +rtree_iterator_allocate_neighbour(struct rtree_iterator *itr) +{ + if (itr->page_pos >= RTREE_NEIGHBORS_IN_PAGE) { + struct rtree_neighbor_page *new_page = + (struct rtree_neighbor_page *)itr->tree->page_alloc(); + new_page->next = itr->page_list; + itr->page_list = new_page; + itr->page_pos = 0; + } + return itr->page_list->buf + itr->page_pos++; +} + +static struct rtree_neighbor * +rtree_iterator_new_neighbor(struct rtree_iterator *itr, + void *child, sq_coord_t distance, int level) +{ + struct rtree_neighbor *n = itr->neigh_free_list; + if (n == NULL) + n = rtree_iterator_allocate_neighbour(itr); + else + itr->neigh_free_list = n->next; + n->child = child; + n->distance = distance; + n->level = level; + n->next = NULL; + return n; +} + +static void +rtree_iterator_free_neighbor(struct rtree_iterator *itr, + struct rtree_neighbor *n) +{ + n->next = itr->neigh_free_list; + itr->neigh_free_list = n; +} + +void +rtree_iterator_init(struct rtree_iterator *itr) +{ + itr->neigh_list = NULL; + itr->neigh_free_list = NULL; + itr->page_list = NULL; + itr->page_pos = RTREE_NEIGHBORS_IN_PAGE; +} + +static void +rtree_iterator_insert_neighbor(struct rtree_iterator *itr, + struct rtree_neighbor *node) +{ + struct rtree_neighbor *prev = NULL, *next = itr->neigh_list; + sq_coord_t distance = node->distance; + while (next != NULL && next->distance < distance) { + prev = next; + next = prev->next; + } + node->next = next; + if (prev == NULL) + itr->neigh_list = node; + else + prev->next = node; +} + +record_t +rtree_iterator_next(struct rtree_iterator *itr) +{ + if (itr->version != itr->tree->version) { + /* Index was updated since cursor initialziation */ + return NULL; + } + if (itr->op == SOP_NEIGHBOR) { + /* To return element in order of increasing distance from + * specified point, we build sorted list of R-Tree items + * (ordered by distance from specified point) starting from + * root page. + * Algorithm is the following: + * + * insert root R-Tree page in the sorted list + * while sorted list is not empty: + * get top element from the sorted list + * if it is tree leaf (record) then return it as + * current element + * otherwise (R-Tree page) get siblings of this R-Tree + * page and insert them in sorted list + */ + while (true) { + struct rtree_neighbor *neighbor = itr->neigh_list; + if (neighbor == NULL) + return NULL; + void *child = neighbor->child; + int level = neighbor->level; + itr->neigh_list = neighbor->next; + rtree_iterator_free_neighbor(itr, neighbor); + if (level == 0) + return (record_t)child; + struct rtree_page *pg = (struct rtree_page *)child; + for (int i = 0, n = pg->n; i < n; i++) { + struct rtree_page *pg = + (struct rtree_page *)child; + coord_t distance = + rtree_rect_point_distance2(&pg->b[i].rect, + &itr->rect.lower_point); + struct rtree_neighbor *neigh = + rtree_iterator_new_neighbor(itr, pg->b[i].data.page, + distance, level - 1); + rtree_iterator_insert_neighbor(itr, neigh); + } + } + } + int sp = itr->tree->height - 1; + if (!itr->eof && rtree_iterator_goto_next(itr, sp)) + return itr->stack[sp].page->b[itr->stack[sp].pos].data.record; + itr->eof = true; + return NULL; +} + +/*------------------------------------------------------------------------- */ +/* R-tree methods */ +/*------------------------------------------------------------------------- */ + +void +rtree_init(struct rtree *tree, + rtree_page_alloc_t page_alloc, rtree_page_free_t page_free) +{ + tree->n_records = 0; + tree->height = 0; + tree->root = NULL; + tree->version = 0; + tree->n_pages = 0; + tree->page_alloc = page_alloc; + tree->page_free = page_free; +} + +void +rtree_destroy(struct rtree *tree) +{ + rtree_purge(tree); +} + +void +rtree_insert(struct rtree *tree, struct rtree_rect *rect, record_t obj) +{ + if (tree->root == NULL) { + tree->root = rtree_alloc_page(tree); + rtree_page_init_record(tree->root, rect, obj); + tree->height = 1; + tree->n_pages++; + } else { + struct rtree_page *p = + rtree_page_insert(tree, tree->root, rect, obj, tree->height); + if (p != NULL) { + /* root splitted */ + struct rtree_page *new_root = rtree_alloc_page(tree); + rtree_page_init_page(new_root, tree->root, p); + tree->root = new_root; + tree->height++; + tree->n_pages++; + } + } + tree->version++; + tree->n_records++; +} + + +bool +rtree_remove(struct rtree *tree, const struct rtree_rect *rect, record_t obj) +{ + struct rtree_reinsert_list rlist; + rlist.chain = NULL; + if (tree->height == 0) + return false; + if (!rtree_page_remove(tree, tree->root, rect, obj, tree->height, &rlist)) + return false; + struct rtree_page *pg = rlist.chain; + int level = rlist.level; + while (pg != NULL) { + for (int i = 0, n = pg->n; i < n; i++) { + struct rtree_page *p = + rtree_page_insert(tree, tree->root, + &pg->b[i].rect, + pg->b[i].data.record, + tree->height - level); + if (p != NULL) { + /* root splitted */ + struct rtree_page *new_root + = rtree_alloc_page(tree); + rtree_page_init_page(new_root, tree->root, p); + tree->root = new_root; + tree->height++; + tree->n_pages++; + } + } + level--; + struct rtree_page *next = get_next_reinsert_page(pg); + rtree_free_page(tree, pg); + tree->n_pages--; + pg = next; + } + if (tree->root->n == 1 && tree->height > 1) { + struct rtree_page *new_root = tree->root->b[0].data.page; + rtree_free_page(tree, tree->root); + tree->root = new_root; + tree->height--; + tree->n_pages--; + } + tree->n_records--; + tree->version++; + return true; +} + +bool +rtree_search(const struct rtree *tree, const struct rtree_rect *rect, + enum spatial_search_op op, struct rtree_iterator *itr) +{ + rtree_iterator_reset(itr); + itr->tree = tree; + itr->version = tree->version; + itr->rect = *rect; + itr->op = op; + assert(tree->height <= RTREE_MAX_HEIGHT); + switch (op) { + case SOP_ALL: + itr->intr_cmp = itr->leaf_cmp = rtree_always_true; + break; + case SOP_EQUALS: + itr->intr_cmp = rtree_rect_in_rect; + itr->leaf_cmp = rtree_rect_equal_to_rect; + break; + case SOP_CONTAINS: + itr->intr_cmp = itr->leaf_cmp = rtree_rect_in_rect; + break; + case SOP_STRICT_CONTAINS: + itr->intr_cmp = itr->leaf_cmp = rtree_rect_strict_in_rect; + break; + case SOP_OVERLAPS: + itr->intr_cmp = itr->leaf_cmp = rtree_rect_intersects_rect; + break; + case SOP_BELONGS: + itr->intr_cmp = rtree_rect_intersects_rect; + itr->leaf_cmp = rtree_rect_holds_rect; + break; + case SOP_STRICT_BELONGS: + itr->intr_cmp = rtree_rect_intersects_rect; + itr->leaf_cmp = rtree_rect_strict_holds_rect; + break; + case SOP_NEIGHBOR: + if (tree->root) { + struct rtree_rect cover = rtree_page_cover(tree->root); + sq_coord_t distance = + rtree_rect_point_distance2(&cover, + &rect->lower_point); + itr->neigh_list = + rtree_iterator_new_neighbor(itr, tree->root, + distance, + tree->height); + return true; + } else { + itr->neigh_list = NULL; + return false; + } + } + if (tree->root && rtree_iterator_goto_first(itr, 0, tree->root)) { + itr->stack[tree->height-1].pos -= 1; + /* will be incremented by goto_next */ + itr->eof = false; + return true; + } else { + itr->eof = true; + return false; + } +} + +void +rtree_purge(struct rtree *tree) +{ + if (tree->root != NULL) { + rtree_page_purge(tree, tree->root, tree->height); + tree->root = NULL; + tree->n_records = 0; + tree->n_pages = 0; + tree->height = 0; + } +} + +size_t +rtree_used_size(const struct rtree *tree) +{ + return tree->n_pages * RTREE_PAGE_SIZE; +} + +unsigned +rtree_number_of_records(const struct rtree *tree) { + return tree->n_records; +} + +#if 0 +void +rtree_debug_print_page(const struct rtree_page *page, unsigned level, unsigned path) +{ + printf("%d:", path); + if (--level) { + for (int i = 0; i < page->n; i++) { + printf(" ["); + for (int j = 0; j < RTREE_DIMENSION; j++) + printf("%lg ", (double)page->b[i].rect.lower_point.coords[j]); + for (int j = 0; j < RTREE_DIMENSION; j++) + printf("%lg ", (double)page->b[i].rect.upper_point.coords[j]); + printf("]"); + } + printf("\n"); + for (int i = 0; i < page->n; i++) + rtree_debug_print_page(page->b[i].data.page, level, path * 100 + i); + } else { + for (int i = 0; i < page->n; i++) { + printf(" ["); + for (int j = 0; j < RTREE_DIMENSION; j++) + printf("%lg ", (double)page->b[i].rect.lower_point.coords[j]); + for (int j = 0; j < RTREE_DIMENSION; j++) + printf("%lg ", (double)page->b[i].rect.upper_point.coords[j]); + printf(": %p]", (void *)page->b[i].data.record); + } + printf("\n"); + } +} + +void +rtree_debug_print(const struct rtree *tree) +{ + if (tree->root) + rtree_debug_print_page(tree->root, tree->height, 0); +} +#endif + diff --git a/src/lib/salad/rtree.h b/src/lib/salad/rtree.h new file mode 100644 index 0000000000000000000000000000000000000000..6eb8a3eb1fc8262afc5cd2d2baa1e35c9fb95649 --- /dev/null +++ b/src/lib/salad/rtree.h @@ -0,0 +1,205 @@ +#ifndef INCLUDES_TARANTOOL_SALAD_RTREE_H +#define INCLUDES_TARANTOOL_SALAD_RTREE_H +/* + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include <stddef.h> +#include <stdbool.h> + +/** + * In-memory Guttman's R-tree + */ + +/* Type of payload data */ +typedef void *record_t; +/* Type of coordinate */ +typedef double coord_t; +/* Type of square coordinate */ +typedef double sq_coord_t; +/* Type of area (volume) of rectangle (box) */ +typedef double area_t; + +#if defined(__cplusplus) +extern "C" { +#endif /* defined(__cplusplus) */ + +enum { + /** Number of dimensions of R-tree geometry */ + RTREE_DIMENSION = 2, + /** Maximal possible R-tree height */ + RTREE_MAX_HEIGHT = 16, + /** + * R-Tree uses linear search for elements on a page, + * so a larger page size can hurt performance. + */ + RTREE_PAGE_SIZE = 1024 +}; + +enum spatial_search_op +{ + SOP_ALL, + SOP_EQUALS, + SOP_CONTAINS, + SOP_STRICT_CONTAINS, + SOP_OVERLAPS, + SOP_BELONGS, + SOP_STRICT_BELONGS, + SOP_NEIGHBOR +}; + +/* pointers to page allocation and deallocations functions */ +typedef void *(*rtree_page_alloc_t)(); +typedef void (*rtree_page_free_t)(void *); + +/* A point in RTREE_DIMENSION space */ +struct rtree_point +{ + /* coordinates of the point */ + coord_t coords[RTREE_DIMENSION]; +}; + +/* A box in RTREE_DIMENSION space */ +struct rtree_rect +{ + /* vertex with minimal coordinates */ + struct rtree_point lower_point; + /* vertex with maximal coordinates (diagonal to lower_point) */ + struct rtree_point upper_point; +}; + +/* Type of function, comparing two rectangles */ +typedef bool (*rtree_comparator_t)(const struct rtree_rect *rt1, + const struct rtree_rect *rt2); + +/* Main rtree struct */ +struct rtree +{ + /* Root node (page) */ + struct rtree_page *root; + /* Number of records in entire tree */ + unsigned n_records; + /* Height of a tree */ + unsigned height; + /* Unique version that increments on every tree modification */ + unsigned version; + /* Number of allocated (used) pages */ + unsigned n_pages; + /* Function for allocation new pages */ + rtree_page_alloc_t page_alloc; + /* Function for deallocation new pages */ + rtree_page_free_t page_free; +}; + +/* Struct for iteration and retrieving rtree values */ +struct rtree_iterator +{ + /* Pointer to rtree */ + const struct rtree *tree; + /* Rectangle of current iteration operation */ + struct rtree_rect rect; + /* Type of current iteration operation */ + enum spatial_search_op op; + /* Flag that means that no more values left */ + bool eof; + /* A verion of a tree when the iterator was created */ + int version; + + /* Special single-linked list of closest neqighbors + * Used only for iteration with op = SOP_NEIGHBOR + * For allocating list entries, page allocator of tree is used. + * Allocated page is much bigger than list entry and thus + * provides several list entries. + */ + struct rtree_neighbor *neigh_list; + /* List of unused (deleted) list entries */ + struct rtree_neighbor *neigh_free_list; + /* List of tree pages, allocated for list entries */ + struct rtree_neighbor_page *page_list; + /* Position of ready-to-use list entry in allocated page */ + int page_pos; + + rtree_comparator_t intr_cmp; + rtree_comparator_t leaf_cmp; + + struct { + struct rtree_page *page; + int pos; + } stack[RTREE_MAX_HEIGHT]; +}; + +void +rtree_rect_normalize(struct rtree_rect *rect); + +void +rtree_set2d(struct rtree_rect *rect, + coord_t left, coord_t bottom, coord_t right, coord_t top); + +void +rtree_init(struct rtree *tree, rtree_page_alloc_t page_alloc, rtree_page_free_t page_free); + +void +rtree_destroy(struct rtree *tree); + +void +rtree_purge(struct rtree *tree); + +bool +rtree_search(const struct rtree *tree, const struct rtree_rect *rect, + enum spatial_search_op op, struct rtree_iterator *itr); + +void +rtree_insert(struct rtree *tree, struct rtree_rect *rect, record_t obj); + +bool +rtree_remove(struct rtree *tree, const struct rtree_rect *rect, record_t obj); + +size_t +rtree_used_size(const struct rtree *tree); + +unsigned +rtree_number_of_records(const struct rtree *tree); + +#if 0 +void +rtree_debug_print(const struct rtree *tree); +#endif + +void +rtree_iterator_init(struct rtree_iterator *itr); + +void +rtree_iterator_destroy(struct rtree_iterator *itr); + +record_t +rtree_iterator_next(struct rtree_iterator *itr); + +#if defined(__cplusplus) +} +#endif /* defined(__cplusplus) */ + +#endif /* #ifndef INCLUDES_TARANTOOL_SALAD_RTREE_H */ diff --git a/src/lua/init.cc b/src/lua/init.cc index 667cfbc314d010f6ec51eaabe6726bd3876c381a..7a39e454b74f91d309c49e3dc87283f63141078c 100644 --- a/src/lua/init.cc +++ b/src/lua/init.cc @@ -30,7 +30,6 @@ #include "lua/utils.h" #include "tarantool.h" #include "box/box.h" -#include "tbuf.h" #if defined(__FreeBSD__) || defined(__APPLE__) #include "libgen.h" #endif diff --git a/src/lua/msgpack.cc b/src/lua/msgpack.cc index b260e095ecb0e83d3b8dff619f86113dc14c9091..747258c242bade123e7e3a1a92b32fc3b53a1af8 100644 --- a/src/lua/msgpack.cc +++ b/src/lua/msgpack.cc @@ -39,14 +39,14 @@ extern "C" { } /* extern "C" */ #include <msgpuck/msgpuck.h> -#include <tbuf.h> +#include <iobuf.h> #include <fiber.h> #include "small/region.h" struct luaL_serializer *luaL_msgpack_default = NULL; static int -luamp_encode_extension_default(struct lua_State *L, int idx, struct tbuf *buf); +luamp_encode_extension_default(struct lua_State *L, int idx, struct obuf *buf); static void luamp_decode_extension_default(struct lua_State *L, const char **data); @@ -57,115 +57,98 @@ static luamp_decode_extension_f luamp_decode_extension = luamp_decode_extension_default; void -luamp_encode_array(struct luaL_serializer *cfg, struct tbuf *buf, uint32_t size) +luamp_encode_array(struct luaL_serializer *cfg, struct obuf *buf, uint32_t size) { (void) cfg; assert(mp_sizeof_array(size) <= 5); - tbuf_ensure(buf, 5 + size); - char *data = mp_encode_array(buf->data + buf->size, size); - assert(data <= buf->data + buf->capacity); - buf->size = data - buf->data; + char *data = obuf_ensure(buf, 5); + char *pos = mp_encode_array(data, size); + obuf_advance(buf, pos - data); } void -luamp_encode_map(struct luaL_serializer *cfg, struct tbuf *buf, uint32_t size) +luamp_encode_map(struct luaL_serializer *cfg, struct obuf *buf, uint32_t size) { (void) cfg; assert(mp_sizeof_map(size) <= 5); - tbuf_ensure(buf, 5 + size); - - char *data = mp_encode_map(buf->data + buf->size, size); - assert(data <= buf->data + buf->capacity); - buf->size = data - buf->data; + char *data = obuf_ensure(buf, 5); + char *pos = mp_encode_map(data, size); + obuf_advance(buf, pos - data); } void -luamp_encode_uint(struct luaL_serializer *cfg, struct tbuf *buf, uint64_t num) +luamp_encode_uint(struct luaL_serializer *cfg, struct obuf *buf, uint64_t num) { (void) cfg; assert(mp_sizeof_uint(num) <= 9); - tbuf_ensure(buf, 9); - - char *data = mp_encode_uint(buf->data + buf->size, num); - assert(data <= buf->data + buf->capacity); - buf->size = data - buf->data; + char *data = obuf_ensure(buf, 9); + char *pos = mp_encode_uint(data, num); + obuf_advance(buf, pos - data); } void -luamp_encode_int(struct luaL_serializer *cfg, struct tbuf *buf, int64_t num) +luamp_encode_int(struct luaL_serializer *cfg, struct obuf *buf, int64_t num) { (void) cfg; assert(mp_sizeof_int(num) <= 9); - tbuf_ensure(buf, 9); - - char *data = mp_encode_int(buf->data + buf->size, num); - assert(data <= buf->data + buf->capacity); - buf->size = data - buf->data; + char *data = obuf_ensure(buf, 9); + char *pos = mp_encode_int(data, num); + obuf_advance(buf, pos - data); } void -luamp_encode_float(struct luaL_serializer *cfg, struct tbuf *buf, float num) +luamp_encode_float(struct luaL_serializer *cfg, struct obuf *buf, float num) { (void) cfg; assert(mp_sizeof_float(num) <= 5); - tbuf_ensure(buf, 5); - - char *data = mp_encode_float(buf->data + buf->size, num); - assert(data <= buf->data + buf->capacity); - buf->size = data - buf->data; + char *data = obuf_ensure(buf, 5); + char *pos = mp_encode_float(data, num); + obuf_advance(buf, pos - data); } void -luamp_encode_double(struct luaL_serializer *cfg, struct tbuf *buf, double num) +luamp_encode_double(struct luaL_serializer *cfg, struct obuf *buf, double num) { (void) cfg; assert(mp_sizeof_double(num) <= 9); - tbuf_ensure(buf, 9); - - char *data = mp_encode_double(buf->data + buf->size, num); - assert(data <= buf->data + buf->capacity); - buf->size = data - buf->data; + char *data = obuf_ensure(buf, 9); + char *pos = mp_encode_double(data, num); + obuf_advance(buf, pos - data); } void -luamp_encode_str(struct luaL_serializer *cfg, struct tbuf *buf, +luamp_encode_str(struct luaL_serializer *cfg, struct obuf *buf, const char *str, uint32_t len) { (void) cfg; assert(mp_sizeof_str(len) <= 5 + len); - tbuf_ensure(buf, 5 + len); - - char *data = mp_encode_str(buf->data + buf->size, str, len); - assert(data <= buf->data + buf->capacity); - buf->size = data - buf->data; + char *data = obuf_ensure(buf, 5 + len); + char *pos = mp_encode_str(data, str, len); + obuf_advance(buf, pos - data); } void -luamp_encode_nil(struct luaL_serializer *cfg, struct tbuf *buf) +luamp_encode_nil(struct luaL_serializer *cfg, struct obuf *buf) { (void) cfg; assert(mp_sizeof_nil() <= 1); - tbuf_ensure(buf, 1); - - char *data = mp_encode_nil(buf->data + buf->size); - assert(data <= buf->data + buf->capacity); - buf->size = data - buf->data; + char *data = obuf_ensure(buf, 1); + char *pos = mp_encode_nil(data); + obuf_advance(buf, pos - data); } void -luamp_encode_bool(struct luaL_serializer *cfg, struct tbuf *buf, bool val) +luamp_encode_bool(struct luaL_serializer *cfg, struct obuf *buf, bool val) { (void) cfg; assert(mp_sizeof_bool(val) <= 1); - tbuf_ensure(buf, 1); - - char *data = mp_encode_bool(buf->data + buf->size, val); - assert(data <= buf->data + buf->capacity); - buf->size = data - buf->data; + char *data = obuf_ensure(buf, 1); + char *pos = mp_encode_bool(data, val); + obuf_advance(buf, pos - data); } static int -luamp_encode_extension_default(struct lua_State *L, int idx, struct tbuf *b) +luamp_encode_extension_default(struct lua_State *L, int idx, struct obuf *b) { (void) L; (void) idx; @@ -202,7 +185,7 @@ luamp_set_decode_extension(luamp_decode_extension_f handler) } static void -luamp_encode_r(struct lua_State *L, struct luaL_serializer *cfg, struct tbuf *b, +luamp_encode_r(struct lua_State *L, struct luaL_serializer *cfg, struct obuf *b, int level) { int index = lua_gettop(L); @@ -266,7 +249,7 @@ luamp_encode_r(struct lua_State *L, struct luaL_serializer *cfg, struct tbuf *b, } void -luamp_encode(struct lua_State *L, struct luaL_serializer *cfg, struct tbuf *b, +luamp_encode(struct lua_State *L, struct luaL_serializer *cfg, struct obuf *b, int index) { int top = lua_gettop(L); @@ -370,9 +353,13 @@ lua_msgpack_encode(lua_State *L) struct luaL_serializer *cfg = luaL_checkserializer(L); RegionGuard region_guard(&fiber()->gc); - struct tbuf *buf = tbuf_new(&fiber()->gc); - luamp_encode_r(L, cfg, buf, 0); - lua_pushlstring(L, buf->data, buf->size); + + struct obuf buf; + obuf_create(&buf, &fiber()->gc, LUAMP_ALLOC_FACTOR); + luamp_encode_r(L, cfg, &buf, 0); + + const char *res = obuf_join(&buf); + lua_pushlstring(L, res, obuf_size(&buf)); return 1; } diff --git a/src/lua/msgpack.h b/src/lua/msgpack.h index 9ed18abbd19914372b68e3d3cbbffe4595b8827c..c9b6cdd107f2965753a37a78068ba88820f1598f 100644 --- a/src/lua/msgpack.h +++ b/src/lua/msgpack.h @@ -48,38 +48,40 @@ extern "C" { */ extern luaL_serializer *luaL_msgpack_default; -struct tbuf; +struct obuf; + +enum { LUAMP_ALLOC_FACTOR = 256 }; void -luamp_encode_array(struct luaL_serializer *cfg, struct tbuf *buf, uint32_t size); +luamp_encode_array(struct luaL_serializer *cfg, struct obuf *buf, uint32_t size); void -luamp_encode_map(struct luaL_serializer *cfg, struct tbuf *buf, uint32_t size); +luamp_encode_map(struct luaL_serializer *cfg, struct obuf *buf, uint32_t size); void -luamp_encode_uint(struct luaL_serializer *cfg, struct tbuf *buf, uint64_t num); +luamp_encode_uint(struct luaL_serializer *cfg, struct obuf *buf, uint64_t num); void -luamp_encode_int(struct luaL_serializer *cfg, struct tbuf *buf, int64_t num); +luamp_encode_int(struct luaL_serializer *cfg, struct obuf *buf, int64_t num); void -luamp_encode_float(struct luaL_serializer *cfg, struct tbuf *buf, float num); +luamp_encode_float(struct luaL_serializer *cfg, struct obuf *buf, float num); void -luamp_encode_double(struct luaL_serializer *cfg, struct tbuf *buf, double num); +luamp_encode_double(struct luaL_serializer *cfg, struct obuf *buf, double num); void -luamp_encode_str(struct luaL_serializer *cfg, struct tbuf *buf, const char *str, +luamp_encode_str(struct luaL_serializer *cfg, struct obuf *buf, const char *str, uint32_t len); void luamp_encode_nil(struct luaL_serializer *cfg); void -luamp_encode_bool(struct luaL_serializer *cfg, struct tbuf *buf, bool val); +luamp_encode_bool(struct luaL_serializer *cfg, struct obuf *buf, bool val); void -luamp_encode(struct lua_State *L, struct luaL_serializer *cfg, struct tbuf *buf, +luamp_encode(struct lua_State *L, struct luaL_serializer *cfg, struct obuf *buf, int index); void @@ -87,7 +89,7 @@ luamp_decode(struct lua_State *L, struct luaL_serializer *cfg, const char **data); typedef int -(*luamp_encode_extension_f)(struct lua_State *, int, struct tbuf *); +(*luamp_encode_extension_f)(struct lua_State *, int, struct obuf *); /** * @brief Set a callback that executed by encoder on unsupported Lua type diff --git a/src/lua/pickle.cc b/src/lua/pickle.cc index 15c7dcc2ecc69b58c34285b1c4a72906e916d944..5b7d5e0af449359a22d9f2a9a5cec786aa96b26c 100644 --- a/src/lua/pickle.cc +++ b/src/lua/pickle.cc @@ -39,8 +39,8 @@ extern "C" { #include "lua/utils.h" #include "lua/msgpack.h" /* luaL_msgpack_default */ -#include <tbuf.h> #include <fiber.h> +#include "iobuf.h" #include "bit/bit.h" static int @@ -54,7 +54,8 @@ lbox_pack(struct lua_State *L) const char *str; RegionGuard region_guard(&fiber()->gc); - struct tbuf *b = tbuf_new(&fiber()->gc); + struct obuf buf; + obuf_create(&buf, &fiber()->gc, LUAMP_ALLOC_FACTOR); struct luaL_field field; double dbl; @@ -71,7 +72,7 @@ lbox_pack(struct lua_State *L) if (field.type != MP_UINT && field.type != MP_INT) luaL_error(L, "pickle.pack: expected 8-bit int"); - tbuf_append(b, (char *) &field.ival, sizeof(uint8_t)); + obuf_dup(&buf, &field.ival, sizeof(uint8_t)); break; case 'S': case 's': @@ -79,7 +80,7 @@ lbox_pack(struct lua_State *L) if (field.type != MP_UINT && field.type != MP_INT) luaL_error(L, "pickle.pack: expected 16-bit int"); - tbuf_append(b, (char *) &field.ival, sizeof(uint16_t)); + obuf_dup(&buf, &field.ival, sizeof(uint16_t)); break; case 'n': /* signed and unsigned 16-bit big endian integers */ @@ -87,7 +88,7 @@ lbox_pack(struct lua_State *L) luaL_error(L, "pickle.pack: expected 16-bit int"); field.ival = (uint16_t) htons((uint16_t) field.ival); - tbuf_append(b, (char *) &field.ival, sizeof(uint16_t)); + obuf_dup(&buf, &field.ival, sizeof(uint16_t)); break; case 'I': case 'i': @@ -95,7 +96,7 @@ lbox_pack(struct lua_State *L) if (field.type != MP_UINT && field.ival != MP_INT) luaL_error(L, "pickle.pack: expected 32-bit int"); - tbuf_append(b, (char *) &field.ival, sizeof(uint32_t)); + obuf_dup(&buf, &field.ival, sizeof(uint32_t)); break; case 'N': /* signed and unsigned 32-bit big endian integers */ @@ -103,7 +104,7 @@ lbox_pack(struct lua_State *L) luaL_error(L, "pickle.pack: expected 32-bit int"); field.ival = htonl(field.ival); - tbuf_append(b, (char *) &field.ival, sizeof(uint32_t)); + obuf_dup(&buf, &field.ival, sizeof(uint32_t)); break; case 'L': case 'l': @@ -111,7 +112,7 @@ lbox_pack(struct lua_State *L) if (field.type != MP_UINT && field.type != MP_INT) luaL_error(L, "pickle.pack: expected 64-bit int"); - tbuf_append(b, (char *) &field.ival, sizeof(uint64_t)); + obuf_dup(&buf, &field.ival, sizeof(uint64_t)); break; case 'Q': case 'q': @@ -120,21 +121,21 @@ lbox_pack(struct lua_State *L) luaL_error(L, "pickle.pack: expected 64-bit int"); field.ival = bswap_u64(field.ival); - tbuf_append(b, (char *) &field.ival, sizeof(uint64_t)); + obuf_dup(&buf, &field.ival, sizeof(uint64_t)); break; case 'd': dbl = (double) lua_tonumber(L, i); - tbuf_append(b, (char *) &dbl, sizeof(double)); + obuf_dup(&buf, &dbl, sizeof(double)); break; case 'f': flt = (float) lua_tonumber(L, i); - tbuf_append(b, (char *) &flt, sizeof(float)); + obuf_dup(&buf, &flt, sizeof(float)); break; case 'A': case 'a': /* A sequence of bytes */ str = luaL_checklstring(L, i, &size); - tbuf_append(b, str, size); + obuf_dup(&buf, str, size); break; default: luaL_error(L, "pickle.pack: unsupported pack " @@ -144,8 +145,8 @@ lbox_pack(struct lua_State *L) format++; } - lua_pushlstring(L, tbuf_str(b), b->size); - + const char *res = obuf_join(&buf); + lua_pushlstring(L, res, obuf_size(&buf)); return 1; } diff --git a/src/stat.cc b/src/stat.cc index f0b8d5259540e5a7929b360a244a5a8af873622c..56f9f3a431838707ac9be56c6ebe18a5e5dd87c4 100644 --- a/src/stat.cc +++ b/src/stat.cc @@ -30,7 +30,6 @@ #include "trivia/util.h" #include "fiber.h" -#include <tbuf.h> #include <say.h> #include <assoc.h> diff --git a/src/stat.h b/src/stat.h index 26b9fdb4e1e82b5422b9eac235754d07dabe0fbc..95f0bc0bc5e3731a7eb3499110b18c99145f171f 100644 --- a/src/stat.h +++ b/src/stat.h @@ -28,7 +28,9 @@ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include <tbuf.h> + +#include <stddef.h> +#include <stdint.h> void stat_init(void); void stat_free(void); diff --git a/src/tbuf.c b/src/tbuf.c deleted file mode 100644 index d3d792099d57505eb814cd89ea138fee3d0e1627..0000000000000000000000000000000000000000 --- a/src/tbuf.c +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Redistribution and use in source and binary forms, with or - * without modification, are permitted provided that the following - * conditions are met: - * - * 1. Redistributions of source code must retain the above - * copyright notice, this list of conditions and the - * following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF - * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ -#include "tbuf.h" - -#include <string.h> -#include <stddef.h> -#include <stdio.h> -#include <stdarg.h> -#include <stdbool.h> - -#include "lib/small/region.h" - -#ifdef POISON -# define TBUF_POISON -#endif - -#ifdef TBUF_POISON -# define poison(ptr, len) memset((ptr), 'A', (len)) -#else -# define poison(ptr, len) -#endif - -/** Try to make all region allocations a multiple of this number. */ -enum { TBUF_ALLOC_FACTOR = 128 }; - -static void -tbuf_assert(const struct tbuf *b) -{ - (void)b; /* arg used :-) */ - assert(b->size <= b->capacity); -} - -struct tbuf * -tbuf_new(struct region *pool) -{ - struct tbuf *e = (struct tbuf *) region_alloc_nothrow(pool, TBUF_ALLOC_FACTOR); - e->size = 0; - e->capacity = TBUF_ALLOC_FACTOR - sizeof(struct tbuf); - e->data = (char *)e + sizeof(struct tbuf); - e->pool = pool; - poison(e->data, e->capacity); - tbuf_assert(e); - return e; -} - -void -tbuf_ensure_resize(struct tbuf *e, size_t required) -{ - tbuf_assert(e); - - /* Make new capacity a multiple of alloc factor. */ - size_t new_capacity = MAX(e->capacity, (uint32_t)TBUF_ALLOC_FACTOR) * 2; - - while (new_capacity < e->size + required) - new_capacity *= 2; - - char *p = (char *) region_alloc_nothrow(e->pool, new_capacity); - - poison(p, new_capacity); - memcpy(p, e->data, e->size); - poison(e->data, e->size); - e->data = p; - e->capacity = new_capacity; - tbuf_assert(e); -} - -struct tbuf * -tbuf_clone(struct region *pool, const struct tbuf *orig) -{ - struct tbuf *clone = tbuf_new(pool); - tbuf_assert(orig); - tbuf_append(clone, orig->data, orig->size); - return clone; -} - -struct tbuf * -tbuf_split(struct tbuf *orig, size_t at) -{ - struct tbuf *head = (struct tbuf *) region_alloc_nothrow(orig->pool, sizeof(*orig)); - assert(at <= orig->size); - tbuf_assert(orig); - head->pool = orig->pool; - head->data = orig->data; - head->size = head->capacity = at; - orig->data += at; - orig->capacity -= at; - orig->size -= at; - return head; -} - -void * -tbuf_peek(struct tbuf *b, size_t count) -{ - void *p = b->data; - tbuf_assert(b); - if (count <= b->size) { - b->data += count; - b->size -= count; - b->capacity -= count; - return p; - } - return NULL; -} - -/** Remove first count bytes from the beginning. */ - -void -tbuf_ltrim(struct tbuf *b, size_t count) -{ - tbuf_assert(b); - assert(count <= b->size); - - memmove(b->data, b->data + count, b->size - count); - b->size -= count; -} - -void -tbuf_reset(struct tbuf *b) -{ - tbuf_assert(b); - poison(b->data, b->size); - b->size = 0; -} - -void -tbuf_vprintf(struct tbuf *b, const char *format, va_list ap) -{ - int printed_len; - size_t free_len = b->capacity - b->size; - va_list ap_copy; - - va_copy(ap_copy, ap); - - tbuf_assert(b); - printed_len = vsnprintf(b->data + b->size, free_len, format, ap); - - /* - * if buffer too short, resize buffer and - * print it again - */ - if (free_len <= printed_len) { - tbuf_ensure(b, printed_len + 1); - free_len = b->capacity - b->size - 1; - printed_len = vsnprintf(b->data + b->size, free_len, format, ap_copy); - } - - b->size += printed_len; - - va_end(ap_copy); -} - -void -tbuf_printf(struct tbuf *b, const char *format, ...) -{ - va_list args; - - va_start(args, format); - tbuf_vprintf(b, format, args); - va_end(args); -} - -/* for debug printing */ -char * -tbuf_to_hex(const struct tbuf *x) -{ - const char *data = x->data; - size_t size = x->size; - char *out = (char *) region_alloc_nothrow(x->pool, size * 3 + 1); - out[size * 3] = 0; - - for (int i = 0; i < size; i++) { - int c = *(data + i); - sprintf(out + i * 3, "%02x ", c); - } - - return out; -} diff --git a/src/tbuf.h b/src/tbuf.h deleted file mode 100644 index e9c972418cc20ceb3c4d417da50746feeb0e0184..0000000000000000000000000000000000000000 --- a/src/tbuf.h +++ /dev/null @@ -1,105 +0,0 @@ -#ifndef TARANTOOL_TBUF_H_INCLUDED -#define TARANTOOL_TBUF_H_INCLUDED -/* - * Redistribution and use in source and binary forms, with or - * without modification, are permitted provided that the following - * conditions are met: - * - * 1. Redistributions of source code must retain the above - * copyright notice, this list of conditions and the - * following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF - * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ -#include <stdarg.h> -#include <stddef.h> -#include <string.h> - -#include "trivia/util.h" - -#if defined(__cplusplus) -extern "C" { -#endif /* defined(__cplusplus) */ - -struct tbuf { - /* Used space in the buffer. */ - uint32_t size; - /* Total allocated buffer capacity. */ - uint32_t capacity; - /* Allocated buffer. */ - char *data; - struct region *pool; -}; - -struct tbuf * -tbuf_new(struct region *pool); - -void tbuf_ensure_resize(struct tbuf *e, size_t bytes_required); -static inline void tbuf_ensure(struct tbuf *e, size_t required) -{ - assert(e->size <= e->capacity); - if (unlikely(e->size + required > e->capacity)) - tbuf_ensure_resize(e, required); -} - -static inline void tbuf_append(struct tbuf *b, const void *data, size_t len) -{ - tbuf_ensure(b, len + 1); /* +1 for trailing '\0' */ - memcpy(b->data + b->size, data, len); - b->size += len; - *((b->data) + b->size) = '\0'; -} - -static inline char * -tbuf_str(struct tbuf *tbuf) { return tbuf->data; } - -static inline char * -tbuf_end(struct tbuf *tbuf) { return tbuf->data + tbuf->size; } - -static inline size_t -tbuf_unused(const struct tbuf *tbuf) { return tbuf->capacity - tbuf->size; } - -struct tbuf *tbuf_clone(struct region *pool, const struct tbuf *orig); -struct tbuf *tbuf_split(struct tbuf *e, size_t at); -void tbuf_reset(struct tbuf *b); -void *tbuf_peek(struct tbuf *b, size_t count); - -/** - * Remove count bytes from the beginning, and adjust all sizes - * accordingly. - * - * @param count the number of bytes to forget about. - * - * @pre 0 <= count <= tbuf->len - */ -void tbuf_ltrim(struct tbuf *b, size_t count); - -void tbuf_vprintf(struct tbuf *b, const char *format, va_list ap) - __attribute__ ((format(FORMAT_PRINTF, 2, 0))); -void tbuf_printf(struct tbuf *b, const char *format, ...) - __attribute__ ((format(FORMAT_PRINTF, 2, 3))); - -char *tbuf_to_hex(const struct tbuf *x); - -#if defined(__cplusplus) -} /* extern "C" */ -#endif /* defined(__cplusplus) */ - -#endif /* TARANTOOL_TBUF_H_INCLUDED */ diff --git a/test/box/rtree_array.result b/test/box/rtree_array.result new file mode 100644 index 0000000000000000000000000000000000000000..e58a1b45aeadd41caa6edc757bfb44b233dcd870 --- /dev/null +++ b/test/box/rtree_array.result @@ -0,0 +1,103 @@ +s = box.schema.create_space('spatial') +--- +... +s:create_index('primary') +--- +- unique: true + parts: + - type: NUM + fieldno: 1 + id: 0 + space_id: 512 + name: primary + type: TREE +... +s:create_index('spatial', { type = 'rtree', unique = false, parts = {2, 'array'}}) +--- +- unique: false + parts: + - type: ARRAY + fieldno: 2 + id: 1 + space_id: 512 + name: spatial + type: RTREE +... +s:insert{1,{0.0,0.0}} +--- +- [1, [0, 0]] +... +s:insert{2,{0.0,10.0}} +--- +- [2, [0, 10]] +... +s:insert{3,{0.0,50.0}} +--- +- [3, [0, 50]] +... +s:insert{4,{10.0,0.0}} +--- +- [4, [10, 0]] +... +s:insert{5,{50.0,0.0}} +--- +- [5, [50, 0]] +... +s:insert{6,{10.0,10.0}} +--- +- [6, [10, 10]] +... +s:insert{7,{10.0,50.0}} +--- +- [7, [10, 50]] +... +s:insert{8,{50.0,10.0}} +--- +- [8, [50, 10]] +... +s:insert{9,{50.0,50.0}} +--- +- [9, [50, 50]] +... +-- select all records +s.index.spatial:select({iterator = 'ALL'}) +--- +- - [1, [0, 0]] + - [2, [0, 10]] + - [3, [0, 50]] + - [4, [10, 0]] + - [5, [50, 0]] + - [6, [10, 10]] + - [7, [10, 50]] + - [8, [50, 10]] + - [9, [50, 50]] +... +-- select records belonging to rectangle (0,0,10,10) +s.index.spatial:select({0.0,0.0,10.0,10.0}, {iterator = 'LE'}) +--- +- - [1, [0, 0]] + - [2, [0, 10]] + - [4, [10, 0]] + - [6, [10, 10]] +... +-- select records with coordinates (10,10) +s.index.spatial:select({10.0,10.0}, {iterator = 'EQ'}) +--- +- - [6, [10, 10]] +... +-- select neighbors of point (5,5) +s.index.spatial:select({5.0,5.0}, {iterator = 'NEIGHBOR'}) +--- +- - [6, [10, 10]] + - [4, [10, 0]] + - [2, [0, 10]] + - [1, [0, 0]] + - [8, [50, 10]] + - [7, [10, 50]] + - [5, [50, 0]] + - [3, [0, 50]] + - [9, [50, 50]] +... +s:drop() +--- +... diff --git a/test/box/rtree_array.test.lua b/test/box/rtree_array.test.lua new file mode 100644 index 0000000000000000000000000000000000000000..1dcecac280e68d8e0d5b02bba4d7e9534b1d9b69 --- /dev/null +++ b/test/box/rtree_array.test.lua @@ -0,0 +1,24 @@ +s = box.schema.create_space('spatial') +s:create_index('primary') +s:create_index('spatial', { type = 'rtree', unique = false, parts = {2, 'array'}}) + +s:insert{1,{0.0,0.0}} +s:insert{2,{0.0,10.0}} +s:insert{3,{0.0,50.0}} +s:insert{4,{10.0,0.0}} +s:insert{5,{50.0,0.0}} +s:insert{6,{10.0,10.0}} +s:insert{7,{10.0,50.0}} +s:insert{8,{50.0,10.0}} +s:insert{9,{50.0,50.0}} + +-- select all records +s.index.spatial:select({iterator = 'ALL'}) +-- select records belonging to rectangle (0,0,10,10) +s.index.spatial:select({0.0,0.0,10.0,10.0}, {iterator = 'LE'}) +-- select records with coordinates (10,10) +s.index.spatial:select({10.0,10.0}, {iterator = 'EQ'}) +-- select neighbors of point (5,5) +s.index.spatial:select({5.0,5.0}, {iterator = 'NEIGHBOR'}) + +s:drop() diff --git a/test/box/rtree_point.result b/test/box/rtree_point.result new file mode 100644 index 0000000000000000000000000000000000000000..c011d852ff01c241c55cfa55efb9f7a13d578ff2 --- /dev/null +++ b/test/box/rtree_point.result @@ -0,0 +1,103 @@ +s = box.schema.create_space('spatial') +--- +... +s:create_index('primary') +--- +- unique: true + parts: + - type: NUM + fieldno: 1 + id: 0 + space_id: 512 + name: primary + type: TREE +... +s:create_index('spatial', { type = 'rtree', unique = false, parts = {2, 'array'}}) +--- +- unique: false + parts: + - type: ARRAY + fieldno: 2 + id: 1 + space_id: 512 + name: spatial + type: RTREE +... +s:insert{1,{0,0}} +--- +- [1, [0, 0]] +... +s:insert{2,{0,10}} +--- +- [2, [0, 10]] +... +s:insert{3,{0,50}} +--- +- [3, [0, 50]] +... +s:insert{4,{10,0}} +--- +- [4, [10, 0]] +... +s:insert{5,{50,0}} +--- +- [5, [50, 0]] +... +s:insert{6,{10,10}} +--- +- [6, [10, 10]] +... +s:insert{7,{10,50}} +--- +- [7, [10, 50]] +... +s:insert{8,{50,10}} +--- +- [8, [50, 10]] +... +s:insert{9,{50,50}} +--- +- [9, [50, 50]] +... +-- select all records +s.index.spatial:select({iterator = 'ALL'}) +--- +- - [1, [0, 0]] + - [2, [0, 10]] + - [3, [0, 50]] + - [4, [10, 0]] + - [5, [50, 0]] + - [6, [10, 10]] + - [7, [10, 50]] + - [8, [50, 10]] + - [9, [50, 50]] +... +-- select records belonging to rectangle (0,0,10,10) +s.index.spatial:select({0,0,10,10}, {iterator = 'LE'}) +--- +- - [1, [0, 0]] + - [2, [0, 10]] + - [4, [10, 0]] + - [6, [10, 10]] +... +-- select records with coordinates (10,10) +s.index.spatial:select({10,10}, {iterator = 'EQ'}) +--- +- - [6, [10, 10]] +... +-- select neighbors of point (5,5) +s.index.spatial:select({5,5}, {iterator = 'NEIGHBOR'}) +--- +- - [6, [10, 10]] + - [4, [10, 0]] + - [2, [0, 10]] + - [1, [0, 0]] + - [8, [50, 10]] + - [7, [10, 50]] + - [5, [50, 0]] + - [3, [0, 50]] + - [9, [50, 50]] +... +s:drop() +--- +... diff --git a/test/box/rtree_point.test.lua b/test/box/rtree_point.test.lua new file mode 100644 index 0000000000000000000000000000000000000000..394a23a5d48d76b99accd906ba229004b93c85e8 --- /dev/null +++ b/test/box/rtree_point.test.lua @@ -0,0 +1,24 @@ +s = box.schema.create_space('spatial') +s:create_index('primary') +s:create_index('spatial', { type = 'rtree', unique = false, parts = {2, 'array'}}) + +s:insert{1,{0,0}} +s:insert{2,{0,10}} +s:insert{3,{0,50}} +s:insert{4,{10,0}} +s:insert{5,{50,0}} +s:insert{6,{10,10}} +s:insert{7,{10,50}} +s:insert{8,{50,10}} +s:insert{9,{50,50}} + +-- select all records +s.index.spatial:select({iterator = 'ALL'}) +-- select records belonging to rectangle (0,0,10,10) +s.index.spatial:select({0,0,10,10}, {iterator = 'LE'}) +-- select records with coordinates (10,10) +s.index.spatial:select({10,10}, {iterator = 'EQ'}) +-- select neighbors of point (5,5) +s.index.spatial:select({5,5}, {iterator = 'NEIGHBOR'}) + +s:drop() diff --git a/test/box/rtree_point_r2.result b/test/box/rtree_point_r2.result new file mode 100644 index 0000000000000000000000000000000000000000..e58a1b45aeadd41caa6edc757bfb44b233dcd870 --- /dev/null +++ b/test/box/rtree_point_r2.result @@ -0,0 +1,103 @@ +s = box.schema.create_space('spatial') +--- +... +s:create_index('primary') +--- +- unique: true + parts: + - type: NUM + fieldno: 1 + id: 0 + space_id: 512 + name: primary + type: TREE +... +s:create_index('spatial', { type = 'rtree', unique = false, parts = {2, 'array'}}) +--- +- unique: false + parts: + - type: ARRAY + fieldno: 2 + id: 1 + space_id: 512 + name: spatial + type: RTREE +... +s:insert{1,{0.0,0.0}} +--- +- [1, [0, 0]] +... +s:insert{2,{0.0,10.0}} +--- +- [2, [0, 10]] +... +s:insert{3,{0.0,50.0}} +--- +- [3, [0, 50]] +... +s:insert{4,{10.0,0.0}} +--- +- [4, [10, 0]] +... +s:insert{5,{50.0,0.0}} +--- +- [5, [50, 0]] +... +s:insert{6,{10.0,10.0}} +--- +- [6, [10, 10]] +... +s:insert{7,{10.0,50.0}} +--- +- [7, [10, 50]] +... +s:insert{8,{50.0,10.0}} +--- +- [8, [50, 10]] +... +s:insert{9,{50.0,50.0}} +--- +- [9, [50, 50]] +... +-- select all records +s.index.spatial:select({iterator = 'ALL'}) +--- +- - [1, [0, 0]] + - [2, [0, 10]] + - [3, [0, 50]] + - [4, [10, 0]] + - [5, [50, 0]] + - [6, [10, 10]] + - [7, [10, 50]] + - [8, [50, 10]] + - [9, [50, 50]] +... +-- select records belonging to rectangle (0,0,10,10) +s.index.spatial:select({0.0,0.0,10.0,10.0}, {iterator = 'LE'}) +--- +- - [1, [0, 0]] + - [2, [0, 10]] + - [4, [10, 0]] + - [6, [10, 10]] +... +-- select records with coordinates (10,10) +s.index.spatial:select({10.0,10.0}, {iterator = 'EQ'}) +--- +- - [6, [10, 10]] +... +-- select neighbors of point (5,5) +s.index.spatial:select({5.0,5.0}, {iterator = 'NEIGHBOR'}) +--- +- - [6, [10, 10]] + - [4, [10, 0]] + - [2, [0, 10]] + - [1, [0, 0]] + - [8, [50, 10]] + - [7, [10, 50]] + - [5, [50, 0]] + - [3, [0, 50]] + - [9, [50, 50]] +... +s:drop() +--- +... diff --git a/test/box/rtree_point_r2.test.lua b/test/box/rtree_point_r2.test.lua new file mode 100644 index 0000000000000000000000000000000000000000..1dcecac280e68d8e0d5b02bba4d7e9534b1d9b69 --- /dev/null +++ b/test/box/rtree_point_r2.test.lua @@ -0,0 +1,24 @@ +s = box.schema.create_space('spatial') +s:create_index('primary') +s:create_index('spatial', { type = 'rtree', unique = false, parts = {2, 'array'}}) + +s:insert{1,{0.0,0.0}} +s:insert{2,{0.0,10.0}} +s:insert{3,{0.0,50.0}} +s:insert{4,{10.0,0.0}} +s:insert{5,{50.0,0.0}} +s:insert{6,{10.0,10.0}} +s:insert{7,{10.0,50.0}} +s:insert{8,{50.0,10.0}} +s:insert{9,{50.0,50.0}} + +-- select all records +s.index.spatial:select({iterator = 'ALL'}) +-- select records belonging to rectangle (0,0,10,10) +s.index.spatial:select({0.0,0.0,10.0,10.0}, {iterator = 'LE'}) +-- select records with coordinates (10,10) +s.index.spatial:select({10.0,10.0}, {iterator = 'EQ'}) +-- select neighbors of point (5,5) +s.index.spatial:select({5.0,5.0}, {iterator = 'NEIGHBOR'}) + +s:drop() diff --git a/test/box/rtree_rect.result b/test/box/rtree_rect.result new file mode 100644 index 0000000000000000000000000000000000000000..c4ee5637cb9b62f4cb3413537e57e38fe1cf8132 --- /dev/null +++ b/test/box/rtree_rect.result @@ -0,0 +1,93 @@ +s = box.schema.create_space('spatial') +--- +... +s:create_index('primary') +--- +- unique: true + parts: + - type: NUM + fieldno: 1 + id: 0 + space_id: 512 + name: primary + type: TREE +... +s:create_index('spatial', { type = 'rtree', unique = false, parts = {2, 'array'}}) +--- +- unique: false + parts: + - type: ARRAY + fieldno: 2 + id: 1 + space_id: 512 + name: spatial + type: RTREE +... +s:insert{1,{0,0,10,10}{ +--- +- error: '[string "s:insert{1,{0,0,10,10}{ "]:1: ''}'' expected near ''{''' +... +s:insert{2,{5,5,10,10}} +--- +- [2, [5, 5, 10, 10]] +... +s:insert{3,{0,0,5,5}} +--- +- [3, [0, 0, 5, 5]] +... +-- select all records +s.index.spatial:select({}, {iterator = 'ALL'}) +--- +- - [2, [5, 5, 10, 10]] + - [3, [0, 0, 5, 5]] +... +-- select records belonging to rectangle (0,0,5,5) +s.index.spatial:select({0,0,5,5}, {iterator = 'LE'}) +--- +- - [3, [0, 0, 5, 5]] +... +-- select records strict belonging to rectangle (0,0,5,5) +s.index.spatial:select({0,0,5,5}, {iterator = 'LT'}) +--- +- [] +... +-- select records strict belonging to rectangle (4,4,11,11) +s.index.spatial:select({4,4,11,11}, {iterator = 'LT'}) +--- +- - [2, [5, 5, 10, 10]] +... +-- select records containing point (5,5) +s.index.spatial:select({5,5}, {iterator = 'GE'}) +--- +- - [2, [5, 5, 10, 10]] + - [3, [0, 0, 5, 5]] +... +-- select records containing rectangle (1,1,2,2) +s.index.spatial:select({1,1,2,2}, {iterator = 'GE'}) +--- +- - [3, [0, 0, 5, 5]] +... +-- select records strict containing rectangle (0,0,5,5) +s.index.spatial:select({0,0,5,5}, {iterator = 'GT'}) +--- +- [] +... +-- select records overlapping rectangle (9,4,11,6) +s.index.spatial:select({9,4,11,6}, {iterator = 'OVERLAPS'}) +--- +- - [2, [5, 5, 10, 10]] +... +-- select records with coordinates (0,0,5,5) +s.index.spatial:select({0,0,5,5}, {iterator = 'EQ'}) +--- +- - [3, [0, 0, 5, 5]] +... +-- select neighbors of point (1,1) +s.index.spatial:select({1,1}, {iterator = 'NEIGHBOR'}) +--- +- - [3, [0, 0, 5, 5]] + - [2, [5, 5, 10, 10]] +... +s:drop() +--- +... diff --git a/test/box/rtree_rect.test.lua b/test/box/rtree_rect.test.lua new file mode 100644 index 0000000000000000000000000000000000000000..3580985e397786e485250532b46c0f4f45ef5083 --- /dev/null +++ b/test/box/rtree_rect.test.lua @@ -0,0 +1,30 @@ +s = box.schema.create_space('spatial') +s:create_index('primary') +s:create_index('spatial', { type = 'rtree', unique = false, parts = {2, 'array'}}) + +s:insert{1,{0,0,10,10}{ +s:insert{2,{5,5,10,10}} +s:insert{3,{0,0,5,5}} + +-- select all records +s.index.spatial:select({}, {iterator = 'ALL'}) +-- select records belonging to rectangle (0,0,5,5) +s.index.spatial:select({0,0,5,5}, {iterator = 'LE'}) +-- select records strict belonging to rectangle (0,0,5,5) +s.index.spatial:select({0,0,5,5}, {iterator = 'LT'}) +-- select records strict belonging to rectangle (4,4,11,11) +s.index.spatial:select({4,4,11,11}, {iterator = 'LT'}) +-- select records containing point (5,5) +s.index.spatial:select({5,5}, {iterator = 'GE'}) +-- select records containing rectangle (1,1,2,2) +s.index.spatial:select({1,1,2,2}, {iterator = 'GE'}) +-- select records strict containing rectangle (0,0,5,5) +s.index.spatial:select({0,0,5,5}, {iterator = 'GT'}) +-- select records overlapping rectangle (9,4,11,6) +s.index.spatial:select({9,4,11,6}, {iterator = 'OVERLAPS'}) +-- select records with coordinates (0,0,5,5) +s.index.spatial:select({0,0,5,5}, {iterator = 'EQ'}) +-- select neighbors of point (1,1) +s.index.spatial:select({1,1}, {iterator = 'NEIGHBOR'}) + +s:drop() diff --git a/test/box/suite.ini b/test/box/suite.ini index adc25b973ed65ff82f0e9d8a061d64bc6197240f..807422123594320de65c110e9f2e8bebdbc5291e 100644 --- a/test/box/suite.ini +++ b/test/box/suite.ini @@ -4,6 +4,6 @@ description = tarantool/box, minimal configuration script = box.lua disabled = valgrind_disabled = admin_coredump.test.lua -release_disabled = errinj.test.lua errinj_index.test.lua +release_disabled = errinj.test.lua errinj_index.test.lua cmdline.test.lua lua_libs = lua/fiber.lua lua/fifo.lua use_unix_sockets = True diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index dc113ed4955596b58e92f319083d3bfde8e54aae..7565250669a8a1f58bf77bab417997034719fe32 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -41,6 +41,10 @@ add_executable(bps_tree.test bps_tree.cc ${CMAKE_SOURCE_DIR}/third_party/qsort_a target_link_libraries(bps_tree.test small) add_executable(bps_tree_itr.test bps_tree_itr.cc ${CMAKE_SOURCE_DIR}/third_party/qsort_arg.c) target_link_libraries(bps_tree_itr.test small) +add_executable(rtree.test rtree.cc ${CMAKE_SOURCE_DIR}/src/lib/salad/rtree.c) +target_link_libraries(rtree.test) +add_executable(rtree_itr.test rtree_itr.cc ${CMAKE_SOURCE_DIR}/src/lib/salad/rtree.c) +target_link_libraries(rtree_itr.test) add_executable(matras.test matras.cc) target_link_libraries(matras.test small) add_executable(vclock.test vclock.cc test.c diff --git a/test/unit/rtree.cc b/test/unit/rtree.cc new file mode 100644 index 0000000000000000000000000000000000000000..9e4960c862b0a49f901150f994eb98212e11396e --- /dev/null +++ b/test/unit/rtree.cc @@ -0,0 +1,289 @@ +#include <stdlib.h> +#include <stdint.h> +#include <stdio.h> +#include <stdbool.h> +#include <inttypes.h> + +#include "unit.h" +#include "salad/rtree.h" + +static int page_count = 0; + +static void * +page_alloc() +{ + page_count++; + return malloc(RTREE_PAGE_SIZE); +} + +static void +page_free(void *page) +{ + page_count--; + free(page); +} + +static void +simple_check() +{ + struct rtree_rect rect; + struct rtree_iterator iterator; + rtree_iterator_init(&iterator); + const size_t rounds = 2000; + + header(); + + struct rtree tree; + rtree_init(&tree, page_alloc, page_free); + + printf("Insert 1..X, remove 1..X\n"); + for (size_t i = 1; i <= rounds; i++) { + record_t rec = (record_t)i; + + rect.lower_point.coords[0] = i; + rect.lower_point.coords[1] = i; + rect.upper_point.coords[0] = i + 0.5; + rect.upper_point.coords[1] = i + 0.5; + + if (rtree_search(&tree, &rect, SOP_EQUALS, &iterator)) { + fail("element already in tree (1)", "true"); + } + rtree_insert(&tree, &rect, rec); + } + if (rtree_number_of_records(&tree) != rounds) { + fail("Tree count mismatch (1)", "true"); + } + for (size_t i = 1; i <= rounds; i++) { + record_t rec = (record_t)i; + + rect.lower_point.coords[0] = i; + rect.lower_point.coords[1] = i; + rect.upper_point.coords[0] = i + 0.5; + rect.upper_point.coords[1] = i + 0.5; + + if (!rtree_search(&tree, &rect, SOP_EQUALS, &iterator)) { + fail("element in tree (1)", "false"); + } + if (rtree_iterator_next(&iterator) != rec) { + fail("right search result (1)", "true"); + } + if (rtree_iterator_next(&iterator)) { + fail("single search result (1)", "true"); + } + if (!rtree_remove(&tree, &rect, rec)) { + fail("delete element in tree (1)", "false"); + } + if (rtree_search(&tree, &rect, SOP_EQUALS, &iterator)) { + fail("element still in tree (1)", "true"); + } + } + if (rtree_number_of_records(&tree) != 0) { + fail("Tree count mismatch (1)", "true"); + } + + printf("Insert 1..X, remove X..1\n"); + for (size_t i = 1; i <= rounds; i++) { + record_t rec = (record_t)i; + + rect.lower_point.coords[0] = i; + rect.lower_point.coords[1] = i; + rect.upper_point.coords[0] = i + 0.5; + rect.upper_point.coords[1] = i + 0.5; + + if (rtree_search(&tree, &rect, SOP_EQUALS, &iterator)) { + fail("element already in tree (2)", "true"); + } + rtree_insert(&tree, &rect, rec); + } + if (rtree_number_of_records(&tree) != rounds) { + fail("Tree count mismatch (2)", "true"); + } + for (size_t i = rounds; i != 0; i--) { + record_t rec = (record_t)i; + + rect.lower_point.coords[0] = i; + rect.lower_point.coords[1] = i; + rect.upper_point.coords[0] = i + 0.5; + rect.upper_point.coords[1] = i + 0.5; + + if (!rtree_search(&tree, &rect, SOP_OVERLAPS, &iterator)) { + fail("element in tree (2)", "false"); + } + if (rtree_iterator_next(&iterator) != rec) { + fail("right search result (2)", "true"); + } + if (rtree_iterator_next(&iterator)) { + fail("single search result (2)", "true"); + } + if (!rtree_remove(&tree, &rect, rec)) { + fail("delete element in tree (2)", "false"); + } + if (rtree_search(&tree, &rect, SOP_OVERLAPS, &iterator)) { + fail("element still in tree (2)", "true"); + } + } + if (rtree_number_of_records(&tree) != 0) { + fail("Tree count mismatch (2)", "true"); + } + + + printf("Insert X..1, remove 1..X\n"); + for (size_t i = rounds; i != 0; i--) { + record_t rec = (record_t)i; + + rect.lower_point.coords[0] = i; + rect.lower_point.coords[1] = i; + rect.upper_point.coords[0] = i + 0.5; + rect.upper_point.coords[1] = i + 0.5; + + if (rtree_search(&tree, &rect, SOP_BELONGS, &iterator)) { + fail("element already in tree (3)", "true"); + } + rtree_insert(&tree, &rect, rec); + } + if (rtree_number_of_records(&tree) != rounds) { + fail("Tree count mismatch (3)", "true"); + } + for (size_t i = 1; i <= rounds; i++) { + record_t rec = (record_t)i; + + rect.lower_point.coords[0] = i; + rect.lower_point.coords[1] = i; + rect.upper_point.coords[0] = i + 0.5; + rect.upper_point.coords[1] = i + 0.5; + + if (!rtree_search(&tree, &rect, SOP_BELONGS, &iterator)) { + fail("element in tree (3)", "false"); + } + if (rtree_iterator_next(&iterator) != rec) { + fail("right search result (3)", "true"); + } + if (rtree_iterator_next(&iterator)) { + fail("single search result (3)", "true"); + } + if (!rtree_remove(&tree, &rect, rec)) { + fail("delete element in tree (3)", "false"); + } + if (rtree_search(&tree, &rect, SOP_BELONGS, &iterator)) { + fail("element still in tree (3)", "true"); + } + } + if (rtree_number_of_records(&tree) != 0) { + fail("Tree count mismatch (3)", "true"); + } + + + printf("Insert X..1, remove X..1\n"); + for (size_t i = rounds; i != 0; i--) { + record_t rec = (record_t)i; + + rect.lower_point.coords[0] = i; + rect.lower_point.coords[1] = i; + rect.upper_point.coords[0] = i + 0.5; + rect.upper_point.coords[1] = i + 0.5; + + if (rtree_search(&tree, &rect, SOP_CONTAINS, &iterator)) { + fail("element already in tree (4)", "true"); + } + rtree_insert(&tree, &rect, rec); + } + if (rtree_number_of_records(&tree) != rounds) { + fail("Tree count mismatch (4)", "true"); + } + for (size_t i = rounds; i != 0; i--) { + record_t rec = (record_t)i; + + rect.lower_point.coords[0] = i; + rect.lower_point.coords[1] = i; + rect.upper_point.coords[0] = i + 0.5; + rect.upper_point.coords[1] = i + 0.5; + + if (!rtree_search(&tree, &rect, SOP_CONTAINS, &iterator)) { + fail("element in tree (4)", "false"); + } + if (rtree_iterator_next(&iterator) != rec) { + fail("right search result (4)", "true"); + } + if (rtree_iterator_next(&iterator)) { + fail("single search result (4)", "true"); + } + if (!rtree_remove(&tree, &rect, rec)) { + fail("delete element in tree (4)", "false"); + } + if (rtree_search(&tree, &rect, SOP_CONTAINS, &iterator)) { + fail("element still in tree (4)", "true"); + } + } + if (rtree_number_of_records(&tree) != 0) { + fail("Tree count mismatch (4)", "true"); + } + + rtree_purge(&tree); + rtree_destroy(&tree); + + rtree_iterator_destroy(&iterator); + + footer(); +} + +static void +rtree_test_build(struct rtree *tree, struct rtree_rect *arr, int count) +{ + for (size_t i = 0; i < count; i++) { + record_t rec = (record_t)(i + 1); + rtree_insert(tree, &arr[i], rec); + } +} + +static void +neighbor_test() +{ + header(); + + const int test_count = 1000; + struct rtree_iterator iterator; + rtree_iterator_init(&iterator); + struct rtree_rect arr[test_count]; + static struct rtree_rect basis; + + for (size_t i = 0; i < test_count; i++) { + arr[i].lower_point.coords[0] = i; + arr[i].lower_point.coords[1] = i; + arr[i].upper_point.coords[0] = i + 1; + arr[i].upper_point.coords[1] = i + 1; + } + + for (size_t i = 0; i <= test_count; i++) { + struct rtree tree; + rtree_init(&tree, page_alloc, page_free); + + rtree_test_build(&tree, arr, i); + + if (!rtree_search(&tree, &basis, SOP_NEIGHBOR, &iterator) && i != 0) { + fail("search is successful", "true"); + } + + for (size_t j = 0; j < i; j++) { + record_t rec = rtree_iterator_next(&iterator); + if (rec != record_t(j+1)) { + fail("wrong search result", "true"); + } + } + rtree_destroy(&tree); + } + + rtree_iterator_destroy(&iterator); + + footer(); +} + + +int +main(void) +{ + simple_check(); + neighbor_test(); + if (page_count != 0) { + fail("memory leak!", "true"); + } +} diff --git a/test/unit/rtree.result b/test/unit/rtree.result new file mode 100644 index 0000000000000000000000000000000000000000..4f56e6513d07b4f03d4c095d0a186e405411329f --- /dev/null +++ b/test/unit/rtree.result @@ -0,0 +1,9 @@ + *** simple_check *** +Insert 1..X, remove 1..X +Insert 1..X, remove X..1 +Insert X..1, remove 1..X +Insert X..1, remove X..1 + *** simple_check: done *** + *** neighbor_test *** + *** neighbor_test: done *** + \ No newline at end of file diff --git a/test/unit/rtree_itr.cc b/test/unit/rtree_itr.cc new file mode 100644 index 0000000000000000000000000000000000000000..fae7c431108f5346f2cdcdcd1126d6337794bf1e --- /dev/null +++ b/test/unit/rtree_itr.cc @@ -0,0 +1,296 @@ +#include <stdlib.h> +#include <stdint.h> +#include <stdio.h> +#include <stdbool.h> + +#include "unit.h" +#include "salad/rtree.h" + +static int page_count = 0; + +static void * +page_alloc() +{ + page_count++; + return malloc(RTREE_PAGE_SIZE); +} + +static void +page_free(void *page) +{ + page_count--; + free(page); +} + +static void +itr_check() +{ + header(); + + struct rtree tree; + rtree_init(&tree, page_alloc, page_free); + + /* Filling tree */ + const size_t count1 = 10000; + const size_t count2 = 5; + struct rtree_rect rect; + size_t count = 0; + record_t rec; + struct rtree_iterator iterator; + rtree_iterator_init(&iterator); + + for (size_t i = 0; i < count1; i++) { + coord_t coord = i * 2 * count2; /* note that filled with even numbers */ + for (size_t j = 0; j < count2; j++) { + rtree_set2d(&rect, coord, coord, coord + j, coord + j); + rtree_insert(&tree, &rect, record_t(++count)); + } + } + printf("Test tree size: %d\n", (int)rtree_number_of_records(&tree)); + + /* Test that tree filled ok */ + for (size_t i = 0; i < count1; i++) { + for (size_t j = 0; j < count2; j++) { + coord_t coord = i * 2 * count2; + rtree_set2d(&rect, coord, coord, coord + j, coord + j); + if (!rtree_search(&tree, &rect, SOP_BELONGS, &iterator)) { + fail("Integrity check failed (1)", "false"); + } + for (size_t k = 0; k <= j; k++) { + if (!rtree_iterator_next(&iterator)) { + fail("Integrity check failed (2)", "false"); + } + } + if (rtree_iterator_next(&iterator)) { + fail("Integrity check failed (3)", "true"); + } + coord = (i * 2 + 1) * count2;; + rtree_set2d(&rect, coord, coord, coord + j, coord + j); + if (rtree_search(&tree, &rect, SOP_BELONGS, &iterator)) { + fail("Integrity check failed (4)", "true"); + } + } + } + + /* Print 7 elems closest to coordinate basis */ + { + static struct rtree_rect basis; + printf("--> "); + if (!rtree_search(&tree, &basis, SOP_NEIGHBOR, &iterator)) { + fail("Integrity check failed (5)", "false"); + } + for (int i = 0; i < 7; i++) { + rec = rtree_iterator_next(&iterator); + if (rec == 0) { + fail("Integrity check failed (6)", "false"); + } + printf("%p ", rec); + } + printf("\n"); + } + /* Print 7 elems closest to the point [(count1-1)*count2*2, (count1-1)*count2*2] */ + { + printf("<-- "); + coord_t coord = (count1 - 1) * count2 * 2; + rtree_set2d(&rect, coord, coord, coord, coord); + if (!rtree_search(&tree, &rect, SOP_NEIGHBOR, &iterator)) { + fail("Integrity check failed (5)", "false"); + } + for (int i = 0; i < 7; i++) { + rec = rtree_iterator_next(&iterator); + if (rec == 0) { + fail("Integrity check failed (6)", "false"); + } + printf("%p ", rec); + } + printf("\n"); + } + + /* Test strict belongs */ + for (size_t i = 0; i < count1; i++) { + for (size_t j = 0; j < count2; j++) { + coord_t coord = i * 2 * count2; + rtree_set2d(&rect, coord - 0.1, coord - 0.1, coord + j, coord + j); + if (!rtree_search(&tree, &rect, SOP_STRICT_BELONGS, &iterator) && j != 0) { + fail("Integrity check failed (7)", "false"); + } + for (size_t k = 0; k < j; k++) { + if (!rtree_iterator_next(&iterator)) { + fail("Integrity check failed (8)", "false"); + } + } + if (rtree_iterator_next(&iterator)) { + fail("Integrity check failed (9)", "true"); + } + coord = (i * 2 + 1) * count2; + rtree_set2d(&rect, coord, coord, coord + j, coord + j); + if (rtree_search(&tree, &rect, SOP_STRICT_BELONGS, &iterator)) { + fail("Integrity check failed (10)", "true"); + } + } + } + + /* Test contains */ + for (size_t i = 0; i < count1; i++) { + for (size_t j = 0; j < count2; j++) { + coord_t coord = i * 2 * count2; + rtree_set2d(&rect, coord, coord, coord + j, coord + j); + if (!rtree_search(&tree, &rect, SOP_CONTAINS, &iterator)) { + fail("Integrity check failed (11)", "false"); + } + for (size_t k = j; k < count2; k++) { + if (!rtree_iterator_next(&iterator)) { + fail("Integrity check failed (12)", "false"); + } + } + if (rtree_iterator_next(&iterator)) { + fail("Integrity check failed (13)", "true"); + } + coord = (i * 2 + 1) * count2; + rtree_set2d(&rect, coord, coord, coord + j, coord + j); + if (rtree_search(&tree, &rect, SOP_CONTAINS, &iterator)) { + fail("Integrity check failed (14)", "true"); + } + } + } + + /* Test strict contains */ + for (size_t i = 0; i < count1; i++) { + for (size_t j = 0; j < count2; j++) { + coord_t coord = i * 2 * count2; + rtree_set2d(&rect, coord + 0.1, coord + 0.1, coord + j, coord + j); + rtree_rect_normalize(&rect); + if (!rtree_search(&tree, &rect, SOP_STRICT_CONTAINS, &iterator) && j != 0 && j != count2 - 1) { + fail("Integrity check failed (11)", "false"); + } + if (j) { + for (size_t k = j; k < count2 - 1; k++) { + if (!rtree_iterator_next(&iterator)) { + fail("Integrity check failed (12)", "false"); + } + } + } + if (rtree_iterator_next(&iterator)) { + fail("Integrity check failed (13)", "true"); + } + coord = (i * 2 + 1) * count2; + rtree_set2d(&rect, coord, coord, coord + j, coord + j); + if (rtree_search(&tree, &rect, SOP_STRICT_CONTAINS, &iterator)) { + fail("Integrity check failed (14)", "true"); + } + } + } + + rtree_purge(&tree); + rtree_destroy(&tree); + rtree_iterator_destroy(&iterator); + + footer(); +} + +static void +itr_invalidate_check() +{ + header(); + + const size_t test_size = 300; + const size_t max_delete_count = 100; + const size_t max_insert_count = 200; + const size_t attempt_count = 100; + struct rtree_iterator iterators[test_size]; + for (size_t i = 0; i < test_size; i++) + rtree_iterator_init(iterators + i); + + struct rtree_rect rect; + + /* invalidation during deletion */ + srand(0); + for (size_t attempt = 0; attempt < attempt_count; attempt++) { + size_t del_pos = rand() % test_size; + size_t del_cnt = rand() % max_delete_count + 1; + if (del_pos + del_cnt > test_size) { + del_cnt = test_size - del_pos; + } + struct rtree tree; + rtree_init(&tree, page_alloc, page_free); + + for (size_t i = 0; i < test_size; i++) { + rtree_set2d(&rect, i, i, i, i); + rtree_insert(&tree, &rect, record_t(i+1)); + } + rtree_set2d(&rect, 0, 0, test_size, test_size); + rtree_search(&tree, &rect, SOP_BELONGS, &iterators[0]); + if (!rtree_iterator_next(&iterators[0])) { + fail("Integrity check failed (15)", "false"); + } + for (size_t i = 1; i < test_size; i++) { + iterators[i] = iterators[i - 1]; + if (!rtree_iterator_next(&iterators[i])) { + fail("Integrity check failed (16)", "false"); + } + } + for (size_t i = del_pos; i < del_pos + del_cnt; i++) { + rtree_set2d(&rect, i, i, i, i); + if (!rtree_remove(&tree, &rect, record_t(i+1))) { + fail("Integrity check failed (17)", "false"); + } + } + for (size_t i = 0; i < test_size; i++) { + if (rtree_iterator_next(&iterators[i])) { + fail("Iterator was not invalidated (18)", "true"); + } + } + rtree_destroy(&tree); + } + + /* invalidation during insertion */ + srand(0); + for (size_t attempt = 0; attempt < attempt_count; attempt++) { + size_t ins_pos = rand() % test_size; + size_t ins_cnt = rand() % max_insert_count + 1; + + struct rtree tree; + rtree_init(&tree, page_alloc, page_free); + + for (size_t i = 0; i < test_size; i++) { + rtree_set2d(&rect, i, i, i, i); + rtree_insert(&tree, &rect, record_t(i+1)); + } + rtree_set2d(&rect, 0, 0, test_size, test_size); + rtree_search(&tree, &rect, SOP_BELONGS, &iterators[0]); + if (!rtree_iterator_next(&iterators[0])) { + fail("Integrity check failed (19)", "false"); + } + for (size_t i = 1; i < test_size; i++) { + iterators[i] = iterators[i - 1]; + if (!rtree_iterator_next(&iterators[0])) { + fail("Integrity check failed (20)", "false"); + } + } + for (size_t i = ins_pos; i < ins_pos + ins_cnt; i++) { + rtree_set2d(&rect, i, i, i, i); + rtree_insert(&tree, &rect, record_t(test_size + i - ins_pos + 1)); + } + for (size_t i = 0; i < test_size; i++) { + if (rtree_iterator_next(&iterators[i])) { + fail("Iterator was not invalidated (22)", "true"); + } + } + rtree_destroy(&tree); + } + + for (size_t i = 0; i < test_size; i++) + rtree_iterator_destroy(iterators + i); + + footer(); +} + +int +main(void) +{ + itr_check(); + itr_invalidate_check(); + if (page_count != 0) { + fail("memory leak!", "false"); + } +} diff --git a/test/unit/rtree_itr.result b/test/unit/rtree_itr.result new file mode 100644 index 0000000000000000000000000000000000000000..4a3762cf11520ff94c45e7e94d41795a43ae52aa --- /dev/null +++ b/test/unit/rtree_itr.result @@ -0,0 +1,8 @@ + *** itr_check *** +Test tree size: 50000 +--> 0x5 0x4 0x3 0x2 0x1 0xa 0x9 +<-- 0xc34c 0xc34d 0xc34e 0xc34f 0xc350 0xc34b 0xc34a + *** itr_check: done *** + *** itr_invalidate_check *** + *** itr_invalidate_check: done *** + \ No newline at end of file diff --git a/test/unit/slab_cache.c b/test/unit/slab_cache.c index 3e3c3ea90d90e652708607b5d739b186eae698bf..81839a0ff621d4d252b9ac60193c5a14beae3de5 100644 --- a/test/unit/slab_cache.c +++ b/test/unit/slab_cache.c @@ -1,4 +1,5 @@ #include "small/slab_cache.h" +#include "small/quota.h" #include <stdio.h> #include <limits.h> #include <stdlib.h> @@ -13,10 +14,13 @@ int main() { srand(time(0)); + struct quota quota; struct slab_arena arena; struct slab_cache cache; - slab_arena_create(&arena, 0, UINT_MAX, 4000000, MAP_PRIVATE); + quota_init("a, UINT_MAX); + + slab_arena_create(&arena, "a, 0, 4000000, MAP_PRIVATE); slab_cache_create(&cache, &arena, 0); int i = 0; diff --git a/test/wal/rtree_benchmark.result b/test/wal/rtree_benchmark.result new file mode 100644 index 0000000000000000000000000000000000000000..3e9256577421aa52c5e878d61153a154d9dd8c9d --- /dev/null +++ b/test/wal/rtree_benchmark.result @@ -0,0 +1,105 @@ +s = box.schema.create_space('rtreebench') +--- +... +s:create_index('primary') +--- +- unique: true + parts: + - type: NUM + fieldno: 1 + id: 0 + space_id: 512 + name: primary + type: TREE +... +s:create_index('spatial', { type = 'rtree', unique = false, parts = {2, 'array'}}) +--- +- unique: false + parts: + - type: ARRAY + fieldno: 2 + id: 1 + space_id: 512 + name: spatial + type: RTREE +... +n_records = 20000 +--- +... +n_iterations = 10000 +--- +... +n_neighbors = 10 +--- +... +file = io.open("rtree_benchmark.res", "w") +--- +... +start = os.clock() +--- +... +--# setopt delimiter ';' +for i = 1, n_records do + s:insert{i,{180*math.random(),180*math.random()}} +end; +--- +... +file:write(string.format("Elapsed time for inserting %d records: %d\n", n_records, os.clock() - start)); +--- +- true +... +start = os.clock(); +--- +... +n = 0; +--- +... +for i = 1, n_iterations do + x = 180*math.random() + y = 180*math.random() + for k,v in s.index.spatial:pairs({x,y,x+1,y+1}, {iterator = 'LE'}) do + n = n + 1 + end +end; +--- +... +file:write(string.format("Elapsed time for %d belongs searches selecting %d records: %d\n", n_iterations, n, os.clock() - start)); +--- +- true +... +start = os.clock(); +--- +... +n = 0 +for i = 1, n_iterations do + x = 180*math.random() + y = 180*math.random() + for k,v in pairs(s.index.spatial:select({x,y }, {limit = n_neighbors, iterator = 'NEIGHBOR'})) do + n = n + 1 + end +end; +--- +... +file:write(string.format("Elapsed time for %d nearest %d neighbors searches selecting %d records: %d\n", n_iterations, n_neighbors, n, os.clock() - start)); +--- +- true +... +start = os.clock(); +--- +... +for i = 1, n_records do + s:delete{i} +end; +--- +... +file:write(string.format("Elapsed time for deleting %d records: %d\n", n_records, os.clock() - start)); +--- +- true +... +file:close(); +--- +- true +... +s:drop(); +--- +... diff --git a/test/wal/rtree_benchmark.test.lua b/test/wal/rtree_benchmark.test.lua new file mode 100644 index 0000000000000000000000000000000000000000..07db874218197ec656a267372b526abae0784a5e --- /dev/null +++ b/test/wal/rtree_benchmark.test.lua @@ -0,0 +1,49 @@ +s = box.schema.create_space('rtreebench') +s:create_index('primary') +s:create_index('spatial', { type = 'rtree', unique = false, parts = {2, 'array'}}) + +n_records = 20000 +n_iterations = 10000 +n_neighbors = 10 + +file = io.open("rtree_benchmark.res", "w") +start = os.clock() + +--# setopt delimiter ';' +for i = 1, n_records do + s:insert{i,{180*math.random(),180*math.random()}} +end; + +file:write(string.format("Elapsed time for inserting %d records: %d\n", n_records, os.clock() - start)); + +start = os.clock(); +n = 0; +for i = 1, n_iterations do + x = 180*math.random() + y = 180*math.random() + for k,v in s.index.spatial:pairs({x,y,x+1,y+1}, {iterator = 'LE'}) do + n = n + 1 + end +end; +file:write(string.format("Elapsed time for %d belongs searches selecting %d records: %d\n", n_iterations, n, os.clock() - start)); + +start = os.clock(); +n = 0 +for i = 1, n_iterations do + x = 180*math.random() + y = 180*math.random() + for k,v in pairs(s.index.spatial:select({x,y }, {limit = n_neighbors, iterator = 'NEIGHBOR'})) do + n = n + 1 + end +end; +file:write(string.format("Elapsed time for %d nearest %d neighbors searches selecting %d records: %d\n", n_iterations, n_neighbors, n, os.clock() - start)); + +start = os.clock(); +for i = 1, n_records do + s:delete{i} +end; +file:write(string.format("Elapsed time for deleting %d records: %d\n", n_records, os.clock() - start)); + +file:close(); +s:drop(); +