Implemented find/find closest for B+ tree.
Added test suite. Fixed some b+ tree bugs.
This commit is contained in:
8
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
8
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="ClangTidyInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="clangTidyChecks" value="*,-android-*,-bugprone-bool-pointer-implicit-conversion,-cert-env33-c,-cert-dcl50-cpp,-cert-dcl59-cpp,-cppcoreguidelines-no-malloc,-cppcoreguidelines-owning-memory,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,-cppcoreguidelines-pro-bounds-constant-array-index,-cppcoreguidelines-pro-bounds-pointer-arithmetic,-cppcoreguidelines-pro-type-const-cast,-cppcoreguidelines-pro-type-cstyle-cast,-cppcoreguidelines-pro-type-reinterpret-cast,-cppcoreguidelines-pro-type-union-access,-cppcoreguidelines-pro-type-vararg,-cppcoreguidelines-special-member-functions,-fuchsia-*,-google-*,google-default-arguments,google-explicit-constructor,google-runtime-member-string-references,google-runtime-operator,-hicpp-braces-around-statements,-hicpp-named-parameter,-hicpp-no-array-decay,-hicpp-no-assembler,-hicpp-no-malloc,-hicpp-function-size,-hicpp-special-member-functions,-hicpp-vararg,-llvm-*,-objc-*,-readability-else-after-return,-readability-implicit-bool-conversion,-readability-named-parameter,-readability-simplify-boolean-expr,-readability-braces-around-statements,-readability-identifier-naming,-readability-function-size,-readability-redundant-member-init,-misc-bool-pointer-implicit-conversion,-misc-definitions-in-headers,-misc-unused-alias-decls,-misc-unused-parameters,-misc-unused-using-decls,-modernize-use-using,-modernize-use-default-member-init,-clang-diagnostic-*,-clang-analyzer-*,-hicpp-signed-bitwise" />
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
</component>
|
||||
6
.idea/misc.xml
generated
6
.idea/misc.xml
generated
@@ -1,6 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
|
||||
<component name="CidrRootsConfiguration">
|
||||
<sourceRoots>
|
||||
<file path="$PROJECT_DIR$/lib" />
|
||||
<file path="$PROJECT_DIR$/src" />
|
||||
</sourceRoots>
|
||||
</component>
|
||||
<component name="JavaScriptSettings">
|
||||
<option name="languageLevel" value="ES6" />
|
||||
</component>
|
||||
|
||||
@@ -1,7 +1,41 @@
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
project(SDB C)
|
||||
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||
|
||||
project(SDB C)
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
|
||||
include_directories(${CMAKE_SOURCE_DIR}/lib)
|
||||
add_executable(SDB src/main.c src/InputBuffer.c src/InputBuffer.h src/SQL.c src/SQL.h src/scanner.c src/scanner.h src/parser.c src/parser.h src/bplus_tree.c src/bplus_tree.h)
|
||||
#Check options
|
||||
include(CheckCSourceCompiles)
|
||||
include(CheckCSourceRuns)
|
||||
include(CheckFunctionExists)
|
||||
include(CheckIncludeFile)
|
||||
include(CheckIncludeFiles)
|
||||
include(CheckLibraryExists)
|
||||
include(CheckSymbolExists)
|
||||
include(CheckTypeSize)
|
||||
set(INCLUDES "")
|
||||
macro(ck_check_include_file header var)
|
||||
check_include_files("${INCLUDES};${header}" ${var})
|
||||
if(${var})
|
||||
set(INCLUDES ${INCLUDES} ${header})
|
||||
endif(${var})
|
||||
endmacro(ck_check_include_file)
|
||||
|
||||
ck_check_include_file("stdlib.h" HAVE_STDLIB_H)
|
||||
check_type_size(intmax_t INTMAX_T)
|
||||
check_type_size(uintmax_t UINTMAX_T)
|
||||
|
||||
check_type_size(pid_t PID_T)
|
||||
if(NOT HAVE_PID_T)
|
||||
if(WIN32)
|
||||
set(pid_t "int")
|
||||
else(WIN32)
|
||||
MESSAGE(FATAL_ERROR "pid_t doesn't exist on this platform?")
|
||||
endif(WIN32)
|
||||
endif(NOT HAVE_PID_T)
|
||||
|
||||
add_subdirectory(src)
|
||||
add_subdirectory(tests)
|
||||
|
||||
enable_testing()
|
||||
add_test(NAME check_SDB COMMAND check_SDB)
|
||||
57
cmake/FindCheck.cmake
Normal file
57
cmake/FindCheck.cmake
Normal file
@@ -0,0 +1,57 @@
|
||||
# - Try to find the CHECK libraries
|
||||
# Once done this will define
|
||||
#
|
||||
# CHECK_FOUND - system has check
|
||||
# CHECK_INCLUDE_DIR - the check include directory
|
||||
# CHECK_LIBRARIES - check library
|
||||
#
|
||||
# This configuration file for finding libcheck is originally from
|
||||
# the opensync project. The originally was downloaded from here:
|
||||
# opensync.org/browser/branches/3rd-party-cmake-modules/modules/FindCheck.cmake
|
||||
#
|
||||
# Copyright (c) 2007 Daniel Gollub <dgollub@suse.de>
|
||||
# Copyright (c) 2007 Bjoern Ricks <b.ricks@fh-osnabrueck.de>
|
||||
#
|
||||
# Redistribution and use is allowed according to the terms of the New
|
||||
# BSD license.
|
||||
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
|
||||
|
||||
|
||||
INCLUDE( FindPkgConfig )
|
||||
|
||||
# Take care about check.pc settings
|
||||
PKG_SEARCH_MODULE( CHECK check )
|
||||
|
||||
# Look for CHECK include dir and libraries
|
||||
IF( NOT CHECK_FOUND )
|
||||
IF ( CHECK_INSTALL_DIR )
|
||||
MESSAGE ( STATUS "Using override CHECK_INSTALL_DIR to find check" )
|
||||
SET ( CHECK_INCLUDE_DIR "${CHECK_INSTALL_DIR}/include" )
|
||||
SET ( CHECK_INCLUDE_DIRS "${CHECK_INCLUDE_DIR}" )
|
||||
FIND_LIBRARY( CHECK_LIBRARY NAMES check PATHS "${CHECK_INSTALL_DIR}/lib" )
|
||||
FIND_LIBRARY( COMPAT_LIBRARY NAMES compat PATHS "${CHECK_INSTALL_DIR}/lib" )
|
||||
SET ( CHECK_LIBRARIES "${CHECK_LIBRARY}" "${COMPAT_LIBRARY}" )
|
||||
ELSE ( CHECK_INSTALL_DIR )
|
||||
FIND_PATH( CHECK_INCLUDE_DIR check.h )
|
||||
FIND_LIBRARY( CHECK_LIBRARIES NAMES check )
|
||||
ENDIF ( CHECK_INSTALL_DIR )
|
||||
|
||||
IF ( CHECK_INCLUDE_DIR AND CHECK_LIBRARIES )
|
||||
SET( CHECK_FOUND 1 )
|
||||
IF ( NOT Check_FIND_QUIETLY )
|
||||
MESSAGE ( STATUS "Found CHECK: ${CHECK_LIBRARIES}" )
|
||||
ENDIF ( NOT Check_FIND_QUIETLY )
|
||||
ELSE ( CHECK_INCLUDE_DIR AND CHECK_LIBRARIES )
|
||||
IF ( Check_FIND_REQUIRED )
|
||||
MESSAGE( FATAL_ERROR "Could NOT find CHECK" )
|
||||
ELSE ( Check_FIND_REQUIRED )
|
||||
IF ( NOT Check_FIND_QUIETLY )
|
||||
MESSAGE( STATUS "Could NOT find CHECK" )
|
||||
ENDIF ( NOT Check_FIND_QUIETLY )
|
||||
ENDIF ( Check_FIND_REQUIRED )
|
||||
ENDIF ( CHECK_INCLUDE_DIR AND CHECK_LIBRARIES )
|
||||
ENDIF( NOT CHECK_FOUND )
|
||||
|
||||
# Hide advanced variables from CMake GUIs
|
||||
MARK_AS_ADVANCED( CHECK_INCLUDE_DIR CHECK_LIBRARIES )
|
||||
|
||||
0
lib/.gitkeep
Normal file
0
lib/.gitkeep
Normal file
7
src/CMakeLists.txt
Normal file
7
src/CMakeLists.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../lib)
|
||||
|
||||
add_library(SDBLib STATIC InputBuffer.c InputBuffer.h SQL.c SQL.h scanner.c scanner.h parser.c parser.h bplus_tree.c bplus_tree.h)
|
||||
|
||||
add_executable(SDB main.c)
|
||||
target_link_libraries(SDB SDBLib)
|
||||
110
src/bplus_tree.c
110
src/bplus_tree.c
@@ -7,6 +7,14 @@
|
||||
#include <assert.h>
|
||||
#include "bplus_tree.h"
|
||||
|
||||
#define APPEND_STR(str, size, _) { \
|
||||
if (strlen(_) + strlen(str) + 1 > (size)) { \
|
||||
(size) *= 2; \
|
||||
(str) = realloc(str, (size) * sizeof(char)); \
|
||||
} \
|
||||
strcat(str, _);\
|
||||
}
|
||||
|
||||
BPlusKV *new_bplus_kv(uint64_t key, void *value, BPlusNode *leftPointer) {
|
||||
BPlusKV *kv = malloc(sizeof(BPlusKV));
|
||||
kv->key = key;
|
||||
@@ -93,6 +101,9 @@ void print_bplus_node(BPlusNode *node, size_t indent) {
|
||||
} else {
|
||||
printf("%sLEAF #%ld (%ld/%ld)\n", indentStr, node->id, node->keyCount, node->order);
|
||||
}
|
||||
if (node->parent != NULL) {
|
||||
printf("%s Parent #%ld\n", indentStr, node->parent->id);
|
||||
}
|
||||
|
||||
if (node->leftPointer != NULL) {
|
||||
print_bplus_node(node->leftPointer, indent + 4);
|
||||
@@ -115,6 +126,24 @@ void print_bplus_node(BPlusNode *node, size_t indent) {
|
||||
free(indentStr);
|
||||
}
|
||||
|
||||
char *debug_bplus_node_str(BPlusNode *node, char *str, size_t *strSize) {
|
||||
APPEND_STR(str, *strSize, "[");
|
||||
if (node->leftPointer != NULL) {
|
||||
str = debug_bplus_node_str(node->leftPointer, str, strSize);
|
||||
}
|
||||
char buffer[32] = {0};
|
||||
for (size_t i = 0; i < node->keyCount; i++) {
|
||||
snprintf(buffer, 32, node->isInternal ? "{%ld}" : "{%ld*}", node->keys[i]->key);
|
||||
APPEND_STR(str, *strSize, buffer);
|
||||
if (node->keys[i]->rightPointer != NULL) {
|
||||
str = debug_bplus_node_str(node->keys[i]->rightPointer, str, strSize);
|
||||
}
|
||||
}
|
||||
|
||||
APPEND_STR(str, *strSize, "]");
|
||||
return str;
|
||||
}
|
||||
|
||||
BPlusTree *new_bplus_tree(size_t order) {
|
||||
BPlusTree *tree = malloc(sizeof(BPlusTree));
|
||||
tree->order = order;
|
||||
@@ -143,6 +172,18 @@ void print_bplus_tree(BPlusTree *tree) {
|
||||
}
|
||||
}
|
||||
|
||||
char *debug_bplus_tree_str(BPlusTree *tree, char *str) {
|
||||
size_t strSize = 32;
|
||||
str = realloc(str, strSize * sizeof(char));
|
||||
memset(str, 0, strSize * sizeof(char));
|
||||
|
||||
snprintf(str, 32, "B+<%ld>", tree->order);
|
||||
|
||||
str = debug_bplus_node_str(tree->root, str, &strSize);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
BPlusNode *bplus_tree_find_leaf(BPlusTree *tree, uint64_t key) {
|
||||
BPlusNode *node = tree->root;
|
||||
|
||||
@@ -232,6 +273,7 @@ bool bplus_tree_insert(BPlusTree *tree, uint64_t newKey, void *newValue) {
|
||||
//Move midpoint up and move keys > midpoint to new node
|
||||
k = 0;
|
||||
for (size_t i = midpointIndex + 1; i < node->order; i++) {
|
||||
node->keys[i]->rightPointer->parent = newNode;
|
||||
newNode->keys[k++] = node->keys[i];
|
||||
node->keys[i] = NULL;
|
||||
newNode->keyCount++;
|
||||
@@ -241,6 +283,7 @@ bool bplus_tree_insert(BPlusTree *tree, uint64_t newKey, void *newValue) {
|
||||
node->keys[midpointIndex] = NULL;
|
||||
node->keyCount--;
|
||||
newNode->leftPointer = ascendingKV->rightPointer;
|
||||
newNode->leftPointer->parent = newNode;
|
||||
ascendingKV->rightPointer = newNode;
|
||||
}
|
||||
}
|
||||
@@ -248,4 +291,71 @@ bool bplus_tree_insert(BPlusTree *tree, uint64_t newKey, void *newValue) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
BPlusKV *bplus_tree_find(BPlusTree *tree, uint64_t key) {
|
||||
BPlusNode *leaf = bplus_tree_find_leaf(tree, key);
|
||||
if (leaf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
for (size_t i = 0; i < leaf->keyCount; i++) {
|
||||
if (leaf->keys[i]->key == key) {
|
||||
return leaf->keys[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BPlusKV *bplus_tree_find_closest(BPlusTree *tree, uint64_t key, BPlusFindComp dir) {
|
||||
//Do regular find if we are only looking for key
|
||||
if (dir == FIND_EQ) {
|
||||
return bplus_tree_find(tree, key);
|
||||
}
|
||||
BPlusNode *leaf = bplus_tree_find_leaf(tree, key);
|
||||
if (leaf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
//Scan right until we find a value >= key
|
||||
ssize_t i;
|
||||
do {
|
||||
bool done = false;
|
||||
for (i = 0; i < leaf->keyCount; i++) {
|
||||
if (leaf->keys[i]->key == key && (dir & FIND_EQ) == FIND_EQ) {
|
||||
//Return the value if we are looking for value = key
|
||||
done = true;
|
||||
break;
|
||||
} else if (leaf->keys[i]->key > key) {
|
||||
//Return the value if we are looking for value > key
|
||||
if ((dir & FIND_GT) == FIND_GT) {
|
||||
return leaf->keys[i];
|
||||
} else {
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (done) {
|
||||
break;
|
||||
}
|
||||
if (leaf->rightLeaf == NULL) {
|
||||
break;
|
||||
}
|
||||
leaf = leaf->rightLeaf;
|
||||
} while (leaf != NULL);
|
||||
|
||||
//Didn't find an acceptable value >= key and we are not looking for value < key
|
||||
if ((dir & FIND_LT) == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//Scan left until we find a value < key
|
||||
do {
|
||||
for (i = leaf->keyCount - 1; i >= 0; i--) {
|
||||
if (leaf->keys[i]->key < key) {
|
||||
return leaf->keys[i];
|
||||
}
|
||||
}
|
||||
leaf = leaf->leftLeaf;
|
||||
} while (leaf != NULL);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -43,6 +43,8 @@ bool bplus_node_insert_kv(BPlusNode *node, BPlusKV *kv);
|
||||
|
||||
void print_bplus_node(BPlusNode *node, size_t indent);
|
||||
|
||||
char * debug_bplus_node_str(BPlusNode *node, char *str, size_t *strSize);
|
||||
|
||||
struct BPlusTree_t {
|
||||
size_t order;
|
||||
size_t minFill;
|
||||
@@ -56,10 +58,21 @@ BPlusTree *new_bplus_tree(size_t order);
|
||||
void free_bplus_tree(BPlusTree *tree);
|
||||
|
||||
void print_bplus_tree(BPlusTree *tree);
|
||||
char *debug_bplus_tree_str(BPlusTree *tree, char *str);
|
||||
|
||||
BPlusNode * bplus_tree_find_leaf(BPlusTree *tree, uint64_t key);
|
||||
|
||||
bool bplus_tree_insert(BPlusTree *tree, uint64_t key, void *value);
|
||||
|
||||
BPlusKV * bplus_tree_find(BPlusTree *tree, uint64_t key);
|
||||
|
||||
enum BPlusFindComp_t {
|
||||
FIND_NONE = 0,
|
||||
FIND_LT = 1,
|
||||
FIND_EQ = 2,
|
||||
FIND_GT = 4
|
||||
};
|
||||
typedef enum BPlusFindComp_t BPlusFindComp;
|
||||
BPlusKV * bplus_tree_find_closest(BPlusTree *tree, uint64_t key, BPlusFindComp dir);
|
||||
|
||||
#endif //SDB_BPLUS_TREE_H
|
||||
|
||||
13
tests/CMakeLists.txt
Normal file
13
tests/CMakeLists.txt
Normal file
@@ -0,0 +1,13 @@
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../src)
|
||||
|
||||
find_package(Check REQUIRED)
|
||||
find_package (Threads REQUIRED)
|
||||
include_directories(${CHECK_INCLUDE_DIRS})
|
||||
link_directories(${CHECK_LIBRARY_DIRS})
|
||||
|
||||
set(TEST_SOURCES
|
||||
check_sdb.c
|
||||
bplus_tree_test.c bplus_tree_test.h)
|
||||
|
||||
add_executable(check_SDB ${TEST_SOURCES})
|
||||
target_link_libraries(check_SDB SDBLib ${CHECK_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
|
||||
76
tests/bplus_tree_test.c
Normal file
76
tests/bplus_tree_test.c
Normal file
@@ -0,0 +1,76 @@
|
||||
//
|
||||
// Created by sam on 23/06/18.
|
||||
//
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "bplus_tree_test.h"
|
||||
#include "../src/bplus_tree.h"
|
||||
|
||||
START_TEST(insert_keys_4)
|
||||
{
|
||||
|
||||
BPlusTree *tree = new_bplus_tree(4);
|
||||
|
||||
char *buffer = NULL;
|
||||
|
||||
ck_assert_str_eq(debug_bplus_tree_str(tree, buffer), "B+<4>[]");
|
||||
|
||||
bplus_tree_insert(tree, 10, NULL);
|
||||
bplus_tree_insert(tree, 20, NULL);
|
||||
|
||||
ck_assert_str_eq(debug_bplus_tree_str(tree, buffer), "B+<4>[{10*}{20*}]");
|
||||
|
||||
bplus_tree_insert(tree, 30, NULL);
|
||||
bplus_tree_insert(tree, 40, NULL);
|
||||
|
||||
ck_assert_str_eq(debug_bplus_tree_str(tree, buffer), "B+<4>[[{10*}{20*}]{30}[{30*}{40*}]]");
|
||||
|
||||
bplus_tree_insert(tree, 35, NULL);
|
||||
bplus_tree_insert(tree, 50, NULL);
|
||||
ck_assert_str_eq(debug_bplus_tree_str(tree, buffer), "B+<4>[[{10*}{20*}]{30}[{30*}{35*}]{40}[{40*}{50*}]]");
|
||||
|
||||
free_bplus_tree(tree);
|
||||
|
||||
if (buffer != NULL) free(buffer);
|
||||
|
||||
}END_TEST
|
||||
|
||||
START_TEST(find_keys_4)
|
||||
{
|
||||
BPlusTree *tree = new_bplus_tree(4);
|
||||
|
||||
bplus_tree_insert(tree, 10, NULL);
|
||||
bplus_tree_insert(tree, 20, NULL);
|
||||
bplus_tree_insert(tree, 30, NULL);
|
||||
bplus_tree_insert(tree, 40, NULL);
|
||||
|
||||
ck_assert(bplus_tree_find(tree, 10) != NULL);
|
||||
ck_assert(bplus_tree_find(tree, 40) != NULL);
|
||||
|
||||
BPlusKV *kv;
|
||||
kv = bplus_tree_find_closest(tree, 15, FIND_LT | FIND_EQ);
|
||||
ck_assert_ptr_ne(kv, NULL);
|
||||
ck_assert_int_eq(kv->key, 10);
|
||||
|
||||
kv = bplus_tree_find_closest(tree, 30, FIND_LT);
|
||||
ck_assert_ptr_ne(kv, NULL);
|
||||
ck_assert_int_eq(kv->key, 20);
|
||||
|
||||
kv = bplus_tree_find_closest(tree, 30, FIND_GT);
|
||||
ck_assert_ptr_ne(kv, NULL);
|
||||
ck_assert_int_eq(kv->key, 40);
|
||||
|
||||
free_bplus_tree(tree);
|
||||
}END_TEST
|
||||
|
||||
Suite *blus_tree_suite(void) {
|
||||
Suite *s = suite_create("B+ Tree");
|
||||
|
||||
TCase *core = tcase_create("Insert");
|
||||
suite_add_tcase(s, core);
|
||||
|
||||
tcase_add_test(core, insert_keys_4);
|
||||
tcase_add_test(core, find_keys_4);
|
||||
|
||||
return s;
|
||||
}
|
||||
12
tests/bplus_tree_test.h
Normal file
12
tests/bplus_tree_test.h
Normal file
@@ -0,0 +1,12 @@
|
||||
//
|
||||
// Created by sam on 23/06/18.
|
||||
//
|
||||
|
||||
#ifndef SDB_BPLUS_TREE_TEST_H
|
||||
#define SDB_BPLUS_TREE_TEST_H
|
||||
|
||||
#include <check.h>
|
||||
|
||||
Suite * blus_tree_suite(void);
|
||||
|
||||
#endif //SDB_BPLUS_TREE_TEST_H
|
||||
21
tests/check_sdb.c
Normal file
21
tests/check_sdb.c
Normal file
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// Created by sam on 22/06/18.
|
||||
//
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <check.h>
|
||||
#include "bplus_tree_test.h"
|
||||
|
||||
int main(void) {
|
||||
int number_failed;
|
||||
Suite *s;
|
||||
SRunner *sr;
|
||||
|
||||
s = blus_tree_suite();
|
||||
sr = srunner_create(s);
|
||||
|
||||
srunner_run_all(sr, CK_NORMAL);
|
||||
number_failed = srunner_ntests_failed(sr);
|
||||
srunner_free(sr);
|
||||
return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
Reference in New Issue
Block a user