diff -Nru librtr-0.6.3/CHANGELOG librtr-0.7.0/CHANGELOG --- librtr-0.6.3/CHANGELOG 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/CHANGELOG 2019-07-18 08:26:08.000000000 +0000 @@ -3,6 +3,18 @@ Release History: +* Version 0.7.0 + - Fix to ensure shadow tables are only freed when initialized before + - Fix SSH transport w/o bind address and add host key verification + - Fix bug in cache groups to ensure uniqueness of cache server preferences + - Fix race condition in rtr_stop + - Remove error PDU null termination check + - Update tommyds implementation to version 2.2 + - tools/rpki-rov: improve validation of command line arguments + - tools/rtrclient: add ROA export with templates, support for CSV and JSON + - Output log messages to stderr instead of stdout + - style: increase max line length to 120 characters + * Version 0.6.3 - Fix IPv4 string conversion to support big endian systems @@ -39,7 +51,7 @@ - Add atomtic reset for pfx table and spki table - Improve code for packets - Add rpm package build infrastructure - - Add cppcheck suppresions for public api functions + - Add cppcheck suppressions for public api functions - Add new test to verify correctness of prefix removal - Remove function rtr_mgr_find_group in RTR Manager because it is not used diff -Nru librtr-0.6.3/cmake/modules/FindLibSSH.cmake librtr-0.7.0/cmake/modules/FindLibSSH.cmake --- librtr-0.6.3/cmake/modules/FindLibSSH.cmake 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/cmake/modules/FindLibSSH.cmake 2019-07-18 08:26:08.000000000 +0000 @@ -12,93 +12,86 @@ # BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. # -set(NOSSH false) -if(CMAKE_BUILD_TYPE STREQUAL NoSSH) - set(NOSSH true) -endif(CMAKE_BUILD_TYPE STREQUAL NoSSH) - -if (NOT NoSSH) - if (LIBSSH_LIBRARIES AND LIBSSH_INCLUDE_DIRS) - # in cache already - set(LIBSSH_FOUND TRUE) - else (LIBSSH_LIBRARIES AND LIBSSH_INCLUDE_DIRS) +if (LIBSSH_LIBRARIES AND LIBSSH_INCLUDE_DIRS) + # in cache already + set(LIBSSH_FOUND TRUE) +else (LIBSSH_LIBRARIES AND LIBSSH_INCLUDE_DIRS) - find_path(LIBSSH_INCLUDE_DIR + find_path(LIBSSH_INCLUDE_DIR NAMES - libssh/libssh.h + libssh/libssh.h PATHS - ${LIBSSH_INCLUDE} - /usr/include - /usr/local/include - /opt/local/include - /sw/include - ${CMAKE_INCLUDE_PATH} - ${CMAKE_INSTALL_PREFIX}/include - ) + ${LIBSSH_INCLUDE} + /usr/include + /usr/local/include + /opt/local/include + /sw/include + ${CMAKE_INCLUDE_PATH} + ${CMAKE_INSTALL_PREFIX}/include + ) - find_library(SSH_LIBRARY + find_library(SSH_LIBRARY NAMES - ssh - libssh + ssh + libssh PATHS - ${LIBSSH_LIBRARY} - /usr/lib - /usr/local/lib - /opt/local/lib - /sw/lib - ${CMAKE_LIBRARY_PATH} - ${CMAKE_INSTALL_PREFIX}/lib - ) + ${LIBSSH_LIBRARY} + /usr/lib + /usr/local/lib + /opt/local/lib + /sw/lib + ${CMAKE_LIBRARY_PATH} + ${CMAKE_INSTALL_PREFIX}/lib + ) - if (LIBSSH_INCLUDE_DIR AND SSH_LIBRARY) + if (LIBSSH_INCLUDE_DIR AND SSH_LIBRARY) set(SSH_FOUND TRUE) - endif (LIBSSH_INCLUDE_DIR AND SSH_LIBRARY) + endif (LIBSSH_INCLUDE_DIR AND SSH_LIBRARY) - set(LIBSSH_INCLUDE_DIRS + set(LIBSSH_INCLUDE_DIRS ${LIBSSH_INCLUDE_DIR} - ) + ) - if (SSH_FOUND) + if (SSH_FOUND) set(LIBSSH_LIBRARIES - ${LIBSSH_LIBRARIES} - ${SSH_LIBRARY} - ) + ${LIBSSH_LIBRARIES} + ${SSH_LIBRARY} + ) if (LibSSH_FIND_VERSION) - file(STRINGS ${LIBSSH_INCLUDE_DIR}/libssh/libssh.h LIBSSH_VERSION_MAJOR - REGEX "#define[ ]+LIBSSH_VERSION_MAJOR[ ]+[0-9]+") - # Older versions of libssh like libssh-0.2 have LIBSSH_VERSION but not LIBSSH_VERSION_MAJOR - if (LIBSSH_VERSION_MAJOR) - string(REGEX MATCH "[0-9]+" LIBSSH_VERSION_MAJOR ${LIBSSH_VERSION_MAJOR}) - file(STRINGS ${LIBSSH_INCLUDE_DIR}/libssh/libssh.h LIBSSH_VERSION_MINOR - REGEX "#define[ ]+LIBSSH_VERSION_MINOR[ ]+[0-9]+") - string(REGEX MATCH "[0-9]+" LIBSSH_VERSION_MINOR ${LIBSSH_VERSION_MINOR}) - file(STRINGS ${LIBSSH_INCLUDE_DIR}/libssh/libssh.h LIBSSH_VERSION_PATCH - REGEX "#define[ ]+LIBSSH_VERSION_MICRO[ ]+[0-9]+") - string(REGEX MATCH "[0-9]+" LIBSSH_VERSION_PATCH ${LIBSSH_VERSION_PATCH}) - - set(LibSSH_VERSION ${LIBSSH_VERSION_MAJOR}.${LIBSSH_VERSION_MINOR}.${LIBSSH_VERSION_PATCH}) - - include(FindPackageVersionCheck) - find_package_version_check(LibSSH DEFAULT_MSG) - else (LIBSSH_VERSION_MAJOR) - message(STATUS "LIBSSH_VERSION_MAJOR not found in ${LIBSSH_INCLUDE_DIR}/libssh/libssh.h, assuming libssh is too old") - set(LIBSSH_FOUND FALSE) - endif (LIBSSH_VERSION_MAJOR) + file(STRINGS ${LIBSSH_INCLUDE_DIR}/libssh/libssh.h LIBSSH_VERSION_MAJOR + REGEX "#define[ ]+LIBSSH_VERSION_MAJOR[ ]+[0-9]+") + # Older versions of libssh like libssh-0.2 have LIBSSH_VERSION but not LIBSSH_VERSION_MAJOR + if (LIBSSH_VERSION_MAJOR) + string(REGEX MATCH "[0-9]+" LIBSSH_VERSION_MAJOR ${LIBSSH_VERSION_MAJOR}) + file(STRINGS ${LIBSSH_INCLUDE_DIR}/libssh/libssh.h LIBSSH_VERSION_MINOR + REGEX "#define[ ]+LIBSSH_VERSION_MINOR[ ]+[0-9]+") + string(REGEX MATCH "[0-9]+" LIBSSH_VERSION_MINOR ${LIBSSH_VERSION_MINOR}) + file(STRINGS ${LIBSSH_INCLUDE_DIR}/libssh/libssh.h LIBSSH_VERSION_PATCH + REGEX "#define[ ]+LIBSSH_VERSION_MICRO[ ]+[0-9]+") + string(REGEX MATCH "[0-9]+" LIBSSH_VERSION_PATCH ${LIBSSH_VERSION_PATCH}) + + set(LibSSH_VERSION ${LIBSSH_VERSION_MAJOR}.${LIBSSH_VERSION_MINOR}.${LIBSSH_VERSION_PATCH}) + + include(FindPackageVersionCheck) + find_package_version_check(LibSSH DEFAULT_MSG) + else (LIBSSH_VERSION_MAJOR) + message(STATUS "LIBSSH_VERSION_MAJOR not found in ${LIBSSH_INCLUDE_DIR}/libssh/libssh.h, assuming libssh is too old") + set(LIBSSH_FOUND FALSE) + endif (LIBSSH_VERSION_MAJOR) endif (LibSSH_FIND_VERSION) - endif (SSH_FOUND) + endif (SSH_FOUND) - # If the version is too old, but libs and includes are set, - # find_package_handle_standard_args will set LIBSSH_FOUND to TRUE again, - # so we need this if() here. - if (LIBSSH_FOUND) + # If the version is too old, but libs and includes are set, + # find_package_handle_standard_args will set LIBSSH_FOUND to TRUE again, + # so we need this if() here. + if (LIBSSH_FOUND) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(LibSSH DEFAULT_MSG LIBSSH_LIBRARIES LIBSSH_INCLUDE_DIRS) - endif (LIBSSH_FOUND) + endif (LIBSSH_FOUND) - # show the LIBSSH_INCLUDE_DIRS and LIBSSH_LIBRARIES variables only in the advanced view - mark_as_advanced(LIBSSH_INCLUDE_DIRS LIBSSH_LIBRARIES) + # show the LIBSSH_INCLUDE_DIRS and LIBSSH_LIBRARIES variables only in the advanced view + mark_as_advanced(LIBSSH_INCLUDE_DIRS LIBSSH_LIBRARIES) - endif (LIBSSH_LIBRARIES AND LIBSSH_INCLUDE_DIRS) -endif(NOT NoSSH) +endif (LIBSSH_LIBRARIES AND LIBSSH_INCLUDE_DIRS) diff -Nru librtr-0.6.3/CMakeLists.txt librtr-0.7.0/CMakeLists.txt --- librtr-0.6.3/CMakeLists.txt 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/CMakeLists.txt 2019-07-18 08:26:08.000000000 +0000 @@ -7,12 +7,24 @@ set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -std=gnu99 -fstack-protector-all") if(CMAKE_BUILD_TYPE STREQUAL Debug) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -Wall -Wextra -Wformat-security -Wmissing-prototypes -Wmissing-declarations -Wdeclaration-after-statement -Winit-self -Waggregate-return -Wmissing-format-attribute -Wundef -Wbad-function-cast -Wwrite-strings -Wformat=2") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -Wall -Wextra -Wformat-security -Winit-self -Wundef -Wwrite-strings") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wformat=2 -Werror=missing-prototypes -Werror=missing-declarations") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wmissing-format-attribute -Werror=implicit-function-declaration") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror=bad-function-cast -Werror=return-type") + execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION) if(GCC_VERSION VERSION_GREATER 4.8 OR GCC_VERSION VERSION_EQUAL 4.8) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wfree-nonheap-object") endif() + if(GCC_VERSION VERSION_GREATER 5.1 OR GCC_VERSION VERSION_EQUAL 5.1) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror=incompatible-pointer-types") + endif() + + if(GCC_VERSION VERSION_GREATER 8.0 OR GCC_VERSION VERSION_EQUAL 8.0) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror=cast-function-type") + endif() + set(DOCS_EXCLUDE_PATTERN "") else() set(DOCS_EXCLUDE_PATTERN "*_private.h") @@ -20,6 +32,8 @@ include_directories(.) +add_subdirectory(third-party) + find_package(codecov) find_package(Threads REQUIRED) @@ -33,20 +47,25 @@ rtrlib/lib/ip.c rtrlib/lib/ipv4.c rtrlib/lib/ipv6.c rtrlib/lib/log.c rtrlib/pfx/trie/trie.c rtrlib/pfx/trie/trie-pfx.c rtrlib/transport/transport.c rtrlib/transport/tcp/tcp_transport.c rtrlib/rtr/rtr.c rtrlib/rtr/packets.c - rtrlib/spki/hashtable/ht-spkitable.c rtrlib/spki/hashtable/tommyds-1.8/tommy.c) + rtrlib/spki/hashtable/ht-spkitable.c ${tommyds}) set(RTRLIB_LINK ${RT_LIB} ${CMAKE_THREAD_LIBS_INIT}) -find_package(LibSSH 0.5.0 QUIET) -if(LIBSSH_FOUND AND NOT NOSSH) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D LIBSSH_VERSION_MAJOR=${LibSSH_VERSION_MAJOR} -D LIBSSH_VERSION_MINOR=${LibSSH_VERSION_MINOR}") - set(RTRLIB_HAVE_LIBSSH 1) - include_directories(${LIBSSH_INCLUDE_DIRS}) - set(RTRLIB_SRC ${RTRLIB_SRC} rtrlib/transport/ssh/ssh_transport.c) - set(RTRLIB_LINK ${RTRLIB_LINK} ${LIBSSH_LIBRARIES}) - message(STATUS "libssh found, building librtr with SSH ${LibSSH_VERSION} support") -else() - message(WARNING "libssh >=0.5.0 not found, building librtr without SSH support") -endif(LIBSSH_FOUND AND NOT NOSSH) +if (NOT DEFINED RTRLIB_TRANSPORT_SSH OR RTRLIB_TRANSPORT_SSH) + find_package(LibSSH 0.5.0 QUIET) + + if(LIBSSH_FOUND) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D LIBSSH_VERSION_MAJOR=${LibSSH_VERSION_MAJOR} -D LIBSSH_VERSION_MINOR=${LibSSH_VERSION_MINOR}") + set(RTRLIB_HAVE_LIBSSH 1) + include_directories(${LIBSSH_INCLUDE_DIRS}) + set(RTRLIB_SRC ${RTRLIB_SRC} rtrlib/transport/ssh/ssh_transport.c) + set(RTRLIB_LINK ${RTRLIB_LINK} ${LIBSSH_LIBRARIES}) + message(STATUS "libssh found, building librtr with SSH ${LibSSH_VERSION} support") + elseif(NOT LIBSSH_FOUND AND RTRLIB_TRANSPORT_SSH) + message(FATAL_ERROR "libssh >= 0.5.0 not found but ssh support was requested. Omit RTRLIB_TRANSPORT_SSH to build without ssh support.") + else() + message(WARNING "libssh >= 0.5.0 not found") + endif(LIBSSH_FOUND) +endif(NOT DEFINED RTRLIB_TRANSPORT_SSH OR RTRLIB_TRANSPORT_SSH) #doxygen target find_package(Doxygen) @@ -92,8 +111,8 @@ #install lib set (RTRLIB_VERSION_MAJOR 0) -set (RTRLIB_VERSION_MINOR 6) -set (RTRLIB_VERSION_PATCH 3) +set (RTRLIB_VERSION_MINOR 7) +set (RTRLIB_VERSION_PATCH 0) CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/rtrlib/rtrlib.h.cmake ${CMAKE_SOURCE_DIR}/rtrlib/rtrlib.h) set(LIBRARY_VERSION ${RTRLIB_VERSION_MAJOR}.${RTRLIB_VERSION_MINOR}.${RTRLIB_VERSION_PATCH}) set(LIBRARY_SOVERSION ${RTRLIB_VERSION_MAJOR}) @@ -118,9 +137,9 @@ endforeach() #pkgconfig file -if(LIBSSH_FOUND) +if(RTRLIB_TRANSPORT_SSH) set (PKG_CONFIG_REQUIRES "libssh >= 0.5.0") -endif(LIBSSH_FOUND) +endif(RTRLIB_TRANSPORT_SSH) # '#include ' includes the "rtrlib/" set (PKG_CONFIG_LIBDIR "\${prefix}/${CMAKE_INSTALL_LIBDIR}") diff -Nru librtr-0.6.3/.codecov.yml librtr-0.7.0/.codecov.yml --- librtr-0.6.3/.codecov.yml 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/.codecov.yml 2019-07-18 08:26:08.000000000 +0000 @@ -8,7 +8,7 @@ range: "50...80" ignore: - - rtrlib/spki/hashtable/tommyds-1.8/.* + - third-party/ - tests/* status: {} diff -Nru librtr-0.6.3/debian/changelog librtr-0.7.0/debian/changelog --- librtr-0.6.3/debian/changelog 2018-12-09 13:04:06.000000000 +0000 +++ librtr-0.7.0/debian/changelog 2021-01-16 16:06:05.000000000 +0000 @@ -1,3 +1,29 @@ +librtr (0.7.0-1) unstable; urgency=medium + + [ Samuel Henrique ] + * Team upload. + * Add salsa-ci.yml. + + [ Debian Janitor ] + * Set debhelper-compat version in Build-Depends. + * Set upstream metadata fields: Bug-Database, Bug-Submit, Repository, + Repository-Browse. + + [ Francisco Vilmar Cardoso Ruviaro ] + * New upstream version 0.7.0. + * Bump DH level to 13. + * Bump Standards-Version to 4.5.1. + * debian/control: + - Add 'Rules-Requires-Root: no'. + - Add pkg-config as build dependency. + * debian/copyright: update upstream and packaging copyright. + * debian/p/Use-FindPkgConfig-to-search-for-libssh-availability.patch: add. + Thanks to Marcel Röthke . (Closes: #975174) + * debian/rules: drop DEB_LDFLAGS_MAINT_APPEND -Wl,--as-needed, + because this is default behavior in bullseye toolchain. + + -- Francisco Vilmar Cardoso Ruviaro Sat, 16 Jan 2021 16:06:05 +0000 + librtr (0.6.3-1) unstable; urgency=medium * Import new upstream release diff -Nru librtr-0.6.3/debian/compat librtr-0.7.0/debian/compat --- librtr-0.6.3/debian/compat 2018-12-09 13:04:06.000000000 +0000 +++ librtr-0.7.0/debian/compat 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -11 diff -Nru librtr-0.6.3/debian/control librtr-0.7.0/debian/control --- librtr-0.6.3/debian/control 2018-12-09 13:04:06.000000000 +0000 +++ librtr-0.7.0/debian/control 2021-01-16 16:06:05.000000000 +0000 @@ -5,10 +5,12 @@ Uploaders: Lukas Schwaighofer Build-Depends: cmake, - debhelper (>= 11), + debhelper-compat (= 13), doxygen, libssh-dev, -Standards-Version: 4.2.1 + pkg-config +Standards-Version: 4.5.1 +Rules-Requires-Root: no Homepage: https://rtrlib.realmv6.org/ Vcs-Browser: https://salsa.debian.org/pkg-security-team/librtr Vcs-Git: https://salsa.debian.org/pkg-security-team/librtr.git diff -Nru librtr-0.6.3/debian/copyright librtr-0.7.0/debian/copyright --- librtr-0.6.3/debian/copyright 2018-12-09 13:04:06.000000000 +0000 +++ librtr-0.7.0/debian/copyright 2021-01-16 16:06:05.000000000 +0000 @@ -8,25 +8,39 @@ Files: debian/* Copyright: 2011-2018 The RTRlib authors - 2018 Lukas Schwaighofer + 2018 Lukas Schwaighofer + 2019 Samuel Henrique + 2020-2021 Francisco Vilmar Cardoso Ruviaro License: Expat -Files: rtrlib/spki/hashtable/tommyds-1.8/* -Copyright: 2010 Andrea Mazzoleni -License: BSD-2-clause - -Files: scripts/checkpatch.pl scripts/spelling.txt -Copyright: 2001, Dave Jones - 2005, Joel Schopp - 2007,2008, Andy Whitcroft +Files: scripts/checkpatch.pl + scripts/spelling.txt +Copyright: 2001 Dave Jones + 2005 Joel Schopp + 2007,2008 Andy Whitcroft 2008-2010 Andy Whitcroft + 2010-2018 Joe Perches License: GPL-2 -Files: cmake/modules/FindGcov.cmake cmake/modules/FindLcov.cmake - cmake/modules/Findcodecov.cmake +Files: cmake/modules/Findcodecov.cmake + cmake/modules/FindLcov.cmake + cmake/modules/FindGcov.cmake Copyright: 2015-2016 RWTH Aachen University, Federal Republic of Germany License: GPL-3+ +Files: cmake/modules/FindPackageVersionCheck.cmake + cmake/modules/FindLibSSH.cmake +Copyright: 2009 Andreas Schneider +License: BSD-3-Clause + +Files: third-party/tommyds/* +Copyright: 2010-2015 Andrea Mazzoleni +License: BSD-2-Clause + +Files: third-party/mustach/* +Copyright: ? José Bollo +License: Apache-2.0 + License: Expat Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -46,30 +60,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -License: BSD-2-clause - 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 ANDREA MAZZOLENI AND CONTRIBUTORS ``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 ANDREA MAZZOLENI 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. - License: GPL-2 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free @@ -102,3 +92,71 @@ . On Debian systems, the full text of the GNU General Public License version 2 can be found in the file `/usr/share/common-licenses/GPL-3'. + +License: BSD-3-Clause + 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. + . + 3. Neither the name of the Andreas Schneider nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + . + THIS SOFTWARE IS PROVIDED BY ANDREAS SCHNEIDER AND CONTRIBUTORS ``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 ANDREAS SCHNEIDER 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. + +License: BSD-2-Clause + 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 ANDREA MAZZOLENI AND CONTRIBUTORS ``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 ANDREA MAZZOLENI 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. + +License: Apache-2.0 + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + . + http://www.apache.org/licenses/LICENSE-2.0 + . + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + . + On Debian systems, the complete text of the Apache version 2.0 license + can be found in "/usr/share/common-licenses/Apache-2.0". diff -Nru librtr-0.6.3/debian/patches/series librtr-0.7.0/debian/patches/series --- librtr-0.6.3/debian/patches/series 2018-12-09 13:04:06.000000000 +0000 +++ librtr-0.7.0/debian/patches/series 2021-01-16 16:06:05.000000000 +0000 @@ -1 +1,2 @@ Disable-tests-requiring-Internet-access.patch +Use-FindPkgConfig-to-search-for-libssh-availability.patch diff -Nru librtr-0.6.3/debian/patches/Use-FindPkgConfig-to-search-for-libssh-availability.patch librtr-0.7.0/debian/patches/Use-FindPkgConfig-to-search-for-libssh-availability.patch --- librtr-0.6.3/debian/patches/Use-FindPkgConfig-to-search-for-libssh-availability.patch 1970-01-01 00:00:00.000000000 +0000 +++ librtr-0.7.0/debian/patches/Use-FindPkgConfig-to-search-for-libssh-availability.patch 2021-01-16 16:06:05.000000000 +0000 @@ -0,0 +1,149 @@ +Description: cmake: use FindPkgConfig to search for libssh availability. +Author: Marcel Röthke +Origin: https://github.com/rtrlib/rtrlib/pull/260/commits/f81b70bf03a52b2e25f7154062c538dc050b3571 +Bug: https://github.com/rtrlib/rtrlib/pull/260 +Bug-Debian: https://bugs.debian.org/975174 +Forwarded: not-needed +Reviewed-By: Francisco Vilmar Cardoso Ruviaro +Last-Update: 2021-01-16 + +--- librtr-0.7.0.orig/CMakeLists.txt ++++ librtr-0.7.0/CMakeLists.txt +@@ -50,11 +50,22 @@ set(RTRLIB_SRC rtrlib/rtr_mgr.c rtrlib/l + rtrlib/spki/hashtable/ht-spkitable.c ${tommyds}) + set(RTRLIB_LINK ${RT_LIB} ${CMAKE_THREAD_LIBS_INIT}) + ++include(FindPkgConfig) ++ + if (NOT DEFINED RTRLIB_TRANSPORT_SSH OR RTRLIB_TRANSPORT_SSH) +- find_package(LibSSH 0.5.0 QUIET) ++ pkg_check_modules(LIBSSH libssh>=0.5.0) + + if(LIBSSH_FOUND) +- set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D LIBSSH_VERSION_MAJOR=${LibSSH_VERSION_MAJOR} -D LIBSSH_VERSION_MINOR=${LibSSH_VERSION_MINOR}") ++ # extract version number components ++ string(REGEX MATCH "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" RTRLIB_LIBSSH_VERSION_MATCH ${LIBSSH_VERSION}) ++ if (NOT RTRLIB_LIBSSH_VERSION_MATCH) ++ message(FATAL_ERROR "Could not parse libssh version ${LIBSSH_VERSION}") ++ endif(NOT RTRLIB_LIBSSH_VERSION_MATCH) ++ SET(LIBSSH_VERSION_MAJOR ${CMAKE_MATCH_1}) ++ SET(LIBSSH_VERSION_MINOR ${CMAKE_MATCH_2}) ++ SET(LIBSSH_VERSION_PATCH ${CMAKE_MATCH_3}) ++ ++ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D LIBSSH_VERSION_MAJOR=${LIBSSH_VERSION_MAJOR} -D LIBSSH_VERSION_MINOR=${LIBSSH_VERSION_MINOR}") + set(RTRLIB_HAVE_LIBSSH 1) + include_directories(${LIBSSH_INCLUDE_DIRS}) + set(RTRLIB_SRC ${RTRLIB_SRC} rtrlib/transport/ssh/ssh_transport.c) +--- librtr-0.7.0.orig/cmake/modules/FindLibSSH.cmake ++++ /dev/null +@@ -1,97 +0,0 @@ +-# - Try to find LibSSH +-# Once done this will define +-# +-# LIBSSH_FOUND - system has LibSSH +-# LIBSSH_INCLUDE_DIRS - the LibSSH include directory +-# LIBSSH_LIBRARIES - Link these to use LibSSH +-# LIBSSH_DEFINITIONS - Compiler switches required for using LibSSH +-# +-# Copyright (c) 2009 Andreas Schneider +-# +-# Redistribution and use is allowed according to the terms of the New +-# BSD license. +-# For details see the accompanying COPYING-CMAKE-SCRIPTS file. +-# +- +-if (LIBSSH_LIBRARIES AND LIBSSH_INCLUDE_DIRS) +- # in cache already +- set(LIBSSH_FOUND TRUE) +-else (LIBSSH_LIBRARIES AND LIBSSH_INCLUDE_DIRS) +- +- find_path(LIBSSH_INCLUDE_DIR +- NAMES +- libssh/libssh.h +- PATHS +- ${LIBSSH_INCLUDE} +- /usr/include +- /usr/local/include +- /opt/local/include +- /sw/include +- ${CMAKE_INCLUDE_PATH} +- ${CMAKE_INSTALL_PREFIX}/include +- ) +- +- find_library(SSH_LIBRARY +- NAMES +- ssh +- libssh +- PATHS +- ${LIBSSH_LIBRARY} +- /usr/lib +- /usr/local/lib +- /opt/local/lib +- /sw/lib +- ${CMAKE_LIBRARY_PATH} +- ${CMAKE_INSTALL_PREFIX}/lib +- ) +- +- if (LIBSSH_INCLUDE_DIR AND SSH_LIBRARY) +- set(SSH_FOUND TRUE) +- endif (LIBSSH_INCLUDE_DIR AND SSH_LIBRARY) +- +- set(LIBSSH_INCLUDE_DIRS +- ${LIBSSH_INCLUDE_DIR} +- ) +- +- if (SSH_FOUND) +- set(LIBSSH_LIBRARIES +- ${LIBSSH_LIBRARIES} +- ${SSH_LIBRARY} +- ) +- +- if (LibSSH_FIND_VERSION) +- file(STRINGS ${LIBSSH_INCLUDE_DIR}/libssh/libssh.h LIBSSH_VERSION_MAJOR +- REGEX "#define[ ]+LIBSSH_VERSION_MAJOR[ ]+[0-9]+") +- # Older versions of libssh like libssh-0.2 have LIBSSH_VERSION but not LIBSSH_VERSION_MAJOR +- if (LIBSSH_VERSION_MAJOR) +- string(REGEX MATCH "[0-9]+" LIBSSH_VERSION_MAJOR ${LIBSSH_VERSION_MAJOR}) +- file(STRINGS ${LIBSSH_INCLUDE_DIR}/libssh/libssh.h LIBSSH_VERSION_MINOR +- REGEX "#define[ ]+LIBSSH_VERSION_MINOR[ ]+[0-9]+") +- string(REGEX MATCH "[0-9]+" LIBSSH_VERSION_MINOR ${LIBSSH_VERSION_MINOR}) +- file(STRINGS ${LIBSSH_INCLUDE_DIR}/libssh/libssh.h LIBSSH_VERSION_PATCH +- REGEX "#define[ ]+LIBSSH_VERSION_MICRO[ ]+[0-9]+") +- string(REGEX MATCH "[0-9]+" LIBSSH_VERSION_PATCH ${LIBSSH_VERSION_PATCH}) +- +- set(LibSSH_VERSION ${LIBSSH_VERSION_MAJOR}.${LIBSSH_VERSION_MINOR}.${LIBSSH_VERSION_PATCH}) +- +- include(FindPackageVersionCheck) +- find_package_version_check(LibSSH DEFAULT_MSG) +- else (LIBSSH_VERSION_MAJOR) +- message(STATUS "LIBSSH_VERSION_MAJOR not found in ${LIBSSH_INCLUDE_DIR}/libssh/libssh.h, assuming libssh is too old") +- set(LIBSSH_FOUND FALSE) +- endif (LIBSSH_VERSION_MAJOR) +- endif (LibSSH_FIND_VERSION) +- endif (SSH_FOUND) +- +- # If the version is too old, but libs and includes are set, +- # find_package_handle_standard_args will set LIBSSH_FOUND to TRUE again, +- # so we need this if() here. +- if (LIBSSH_FOUND) +- include(FindPackageHandleStandardArgs) +- find_package_handle_standard_args(LibSSH DEFAULT_MSG LIBSSH_LIBRARIES LIBSSH_INCLUDE_DIRS) +- endif (LIBSSH_FOUND) +- +- # show the LIBSSH_INCLUDE_DIRS and LIBSSH_LIBRARIES variables only in the advanced view +- mark_as_advanced(LIBSSH_INCLUDE_DIRS LIBSSH_LIBRARIES) +- +-endif (LIBSSH_LIBRARIES AND LIBSSH_INCLUDE_DIRS) +--- librtr-0.7.0.orig/doxygen/examples/CMakeLists.txt ++++ librtr-0.7.0/doxygen/examples/CMakeLists.txt +@@ -1,8 +1,8 @@ + if(CMAKE_BUILD_TYPE STREQUAL Debug) +- if(LIBSSH_FOUND) ++ if(RTRLIB_HAVE_LIBSSH) + add_executable(rtr_mgr rtr_mgr.c) + target_link_libraries(rtr_mgr rtrlib) + add_executable(ssh_tr ssh_tr.c) + target_link_libraries(ssh_tr rtrlib_static) +- endif(LIBSSH_FOUND) ++ endif(RTRLIB_HAVE_LIBSSH) + endif(CMAKE_BUILD_TYPE STREQUAL Debug) diff -Nru librtr-0.6.3/debian/rules librtr-0.7.0/debian/rules --- librtr-0.6.3/debian/rules 2018-12-09 13:04:06.000000000 +0000 +++ librtr-0.7.0/debian/rules 2021-01-16 16:06:05.000000000 +0000 @@ -1,7 +1,6 @@ #!/usr/bin/make -f export DEB_BUILD_MAINT_OPTIONS = hardening=+all -export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed %: dh $@ diff -Nru librtr-0.6.3/debian/salsa-ci.yml librtr-0.7.0/debian/salsa-ci.yml --- librtr-0.6.3/debian/salsa-ci.yml 1970-01-01 00:00:00.000000000 +0000 +++ librtr-0.7.0/debian/salsa-ci.yml 2021-01-16 16:06:05.000000000 +0000 @@ -0,0 +1,4 @@ +--- +include: + - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/salsa-ci.yml + - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/pipeline-jobs.yml diff -Nru librtr-0.6.3/debian/upstream/metadata librtr-0.7.0/debian/upstream/metadata --- librtr-0.6.3/debian/upstream/metadata 1970-01-01 00:00:00.000000000 +0000 +++ librtr-0.7.0/debian/upstream/metadata 2021-01-16 16:06:05.000000000 +0000 @@ -0,0 +1,5 @@ +--- +Bug-Database: https://github.com/rtrlib/rtrlib/issues +Bug-Submit: https://github.com/rtrlib/rtrlib/issues/new +Repository: https://github.com/rtrlib/rtrlib.git +Repository-Browse: https://github.com/rtrlib/rtrlib diff -Nru librtr-0.6.3/doxygen/examples/rtr_mgr.c librtr-0.7.0/doxygen/examples/rtr_mgr.c --- librtr-0.6.3/doxygen/examples/rtr_mgr.c 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/doxygen/examples/rtr_mgr.c 2019-07-18 08:26:08.000000000 +0000 @@ -1,6 +1,7 @@ #include #include #include +#include #include "rtrlib/rtrlib.h" int main(){ @@ -56,7 +57,7 @@ //initialize all rtr_sockets in the server pool with the same settings struct rtr_mgr_config *conf; - int ret = rtr_mgr_init(&conf, groups, 2, 30, 600, 600, NULL, NULL, NULL, NULL); + rtr_mgr_init(&conf, groups, 2, 30, 600, 600, NULL, NULL, NULL, NULL); //start the connection manager rtr_mgr_start(conf); diff -Nru librtr-0.6.3/.github/ISSUE_TEMPLATE/bug_report.md librtr-0.7.0/.github/ISSUE_TEMPLATE/bug_report.md --- librtr-0.6.3/.github/ISSUE_TEMPLATE/bug_report.md 1970-01-01 00:00:00.000000000 +0000 +++ librtr-0.7.0/.github/ISSUE_TEMPLATE/bug_report.md 2019-07-18 08:26:08.000000000 +0000 @@ -0,0 +1,39 @@ +--- +name: Bug report +about: Create a report to help us improve + +--- + +### Description + + +### Steps to reproduce the issue + + +### Expected results + + +### Actual results + + +### Versions + + + + diff -Nru librtr-0.6.3/.github/ISSUE_TEMPLATE/enhancement.md librtr-0.7.0/.github/ISSUE_TEMPLATE/enhancement.md --- librtr-0.6.3/.github/ISSUE_TEMPLATE/enhancement.md 1970-01-01 00:00:00.000000000 +0000 +++ librtr-0.7.0/.github/ISSUE_TEMPLATE/enhancement.md 2019-07-18 08:26:08.000000000 +0000 @@ -0,0 +1,17 @@ +--- +name: Enhancement Request +about: Ask for the enhancement of an existing feature. + +--- + +### Description of current state + + +### Improvement + + +### Useful links + + + + diff -Nru librtr-0.6.3/.github/ISSUE_TEMPLATE/feature_request.md librtr-0.7.0/.github/ISSUE_TEMPLATE/feature_request.md --- librtr-0.6.3/.github/ISSUE_TEMPLATE/feature_request.md 1970-01-01 00:00:00.000000000 +0000 +++ librtr-0.7.0/.github/ISSUE_TEMPLATE/feature_request.md 2019-07-18 08:26:08.000000000 +0000 @@ -0,0 +1,15 @@ +--- +name: Feature Request +about: Ask for missing features. + +--- + +### Description + + +### Useful links + + + + diff -Nru librtr-0.6.3/.github/PULL_REQUEST_TEMPLATE.md librtr-0.7.0/.github/PULL_REQUEST_TEMPLATE.md --- librtr-0.6.3/.github/PULL_REQUEST_TEMPLATE.md 1970-01-01 00:00:00.000000000 +0000 +++ librtr-0.7.0/.github/PULL_REQUEST_TEMPLATE.md 2019-07-18 08:26:08.000000000 +0000 @@ -0,0 +1,37 @@ + + +### Contribution description + + + + +### Testing procedure + + + + +### Issues/PRs references + + + diff -Nru librtr-0.6.3/README librtr-0.7.0/README --- librtr-0.6.3/README 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/README 2019-07-18 08:26:08.000000000 +0000 @@ -42,6 +42,15 @@ -D LIBSSH_LIBRARY= -D LIBSSH_INCLUDE= + If libssh is installed but you do not want to build rtrlib with ssh + support, you can disable it with the following parameter: + + -D RTRLIB_TRANSPORT_SSH=No + + Or to enforce ssh support: + + -D RTRLIB_TRANSPORT_SSH=Yes + To specify another directory where the RTRlib will be installed, you can pass the following argument to cmake: diff -Nru librtr-0.6.3/redhat/SPECS/librtr.spec librtr-0.7.0/redhat/SPECS/librtr.spec --- librtr-0.6.3/redhat/SPECS/librtr.spec 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/redhat/SPECS/librtr.spec 2019-07-18 08:26:08.000000000 +0000 @@ -1,5 +1,5 @@ Name: librtr -Version: 0.6.3 +Version: 0.7.0 Release: 1%{?dist} Summary: Small extensible RPKI-RTR-Client C library Group: Development/Libraries @@ -60,7 +60,7 @@ %prep if [ ! -f %{SOURCE0} ]; then # Build Source Tarball first - pushd `dirname %_topdir`; tar czf %{SOURCE0} . --exclude-vcs --exclude=redhat; popd + pushd `dirname %_topdir`; tar czf %{SOURCE0} --exclude-vcs --exclude=redhat . ; popd fi cd %{_topdir}/BUILD rm -rf %{name}-%{version} @@ -113,4 +113,3 @@ %changelog * Thu Dec 14 2017 Martin Winter - %{version}-%{release} - RPM Packaging added - diff -Nru librtr-0.6.3/rtrlib/lib/alloc_utils.c librtr-0.7.0/rtrlib/lib/alloc_utils.c --- librtr-0.6.3/rtrlib/lib/alloc_utils.c 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/rtrlib/lib/alloc_utils.c 2019-07-18 08:26:08.000000000 +0000 @@ -10,6 +10,7 @@ #include #include #include +#include #include "alloc_utils_private.h" #include "rtrlib/rtrlib_export_private.h" @@ -34,6 +35,31 @@ return MALLOC_PTR(size); } +/* cppcheck-suppress unusedFunction */ +void *lrtr_calloc(size_t nmemb, size_t size) +{ + int bytes = 0; + +#if __GNUC__ >= 5 + if (__builtin_mul_overflow(nmemb, size, &bytes)) { + errno = ENOMEM; + return 0; + } +#else + if (size && nmemb > (size_t)-1 / size) { + errno = ENOMEM; + return 0; + } + bytes = size * nmemb; +#endif + void *p = lrtr_malloc(bytes); + + if (!p) + return p; + + return memset(p, 0, bytes); +} + inline void lrtr_free(void *ptr) { return FREE_PTR(ptr); diff -Nru librtr-0.6.3/rtrlib/lib/alloc_utils_private.h librtr-0.7.0/rtrlib/lib/alloc_utils_private.h --- librtr-0.6.3/rtrlib/lib/alloc_utils_private.h 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/rtrlib/lib/alloc_utils_private.h 2019-07-18 08:26:08.000000000 +0000 @@ -16,6 +16,8 @@ void *lrtr_malloc(size_t size); +void *lrtr_calloc(size_t nmemb, size_t size); + void lrtr_free(void *ptr); void *lrtr_realloc(void *ptr, size_t size); diff -Nru librtr-0.6.3/rtrlib/lib/log.c librtr-0.7.0/rtrlib/lib/log.c --- librtr-0.6.3/rtrlib/lib/log.c 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/rtrlib/lib/log.c 2019-07-18 08:26:08.000000000 +0000 @@ -17,30 +17,34 @@ #include "rtrlib/lib/log_private.h" - void lrtr_dbg(const char *frmt, ...) { #ifndef NDEBUG - va_list argptr; - va_start(argptr, frmt); - struct timeval tv; - struct timezone tz; - - bool fail = true; - if(gettimeofday(&tv, &tz) == 0) { - struct tm tm; - if(localtime_r(&tv.tv_sec, &tm) != NULL) { - printf("(%04d/%02d/%02d %02d:%02d:%02d:%06ld): ", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, tv.tv_usec); - fail = false; - } - } - - if(fail) - printf("(%jd): ", (intmax_t) time(0)); - - vprintf(frmt, argptr); - printf("\n"); - va_end(argptr); + va_list argptr; + struct timeval tv; + struct timezone tz; + + va_start(argptr, frmt); + + bool fail = true; + + if (gettimeofday(&tv, &tz) == 0) { + struct tm tm; + + if (localtime_r(&tv.tv_sec, &tm)) { + fprintf(stderr, + "(%04d/%02d/%02d %02d:%02d:%02d:%06ld): ", + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec, tv.tv_usec); + fail = false; + } + } + + if (fail) + fprintf(stderr, "(%jd): ", (intmax_t)time(0)); + + vfprintf(stderr, frmt, argptr); + fprintf(stderr, "\n"); + va_end(argptr); #endif - return; } diff -Nru librtr-0.6.3/rtrlib/lib/log_private.h librtr-0.7.0/rtrlib/lib/log_private.h --- librtr-0.6.3/rtrlib/lib/log_private.h 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/rtrlib/lib/log_private.h 2019-07-18 08:26:08.000000000 +0000 @@ -14,6 +14,6 @@ * @brief Writes a message to stdout if NDEBUG isn't defined. * @param[in] frmt log message in printf format style. */ -void lrtr_dbg(const char *frmt, ...) __attribute__ ((format (printf, 1, 2))); +void lrtr_dbg(const char *frmt, ...) __attribute__((format(printf, 1, 2))); #endif diff -Nru librtr-0.6.3/rtrlib/pfx/trie/trie-pfx.c librtr-0.7.0/rtrlib/pfx/trie/trie-pfx.c --- librtr-0.6.3/rtrlib/pfx/trie/trie-pfx.c 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/rtrlib/pfx/trie/trie-pfx.c 2019-07-18 08:26:08.000000000 +0000 @@ -86,8 +86,8 @@ pthread_rwlock_wrlock(&(pfx_table->lock)); do { struct node_data *data = (struct node_data *) (root->data); - for(unsigned int i = 0; i < data->len; i++) { - struct pfx_record record = { data->ary[i].asn, (root->prefix), root->len, data->ary[i].max_len, data->ary[i].socket}; + for(unsigned int j = 0; j < data->len; j++) { + struct pfx_record record = { data->ary[j].asn, (root->prefix), root->len, data->ary[j].max_len, data->ary[j].socket}; pfx_table_notify_clients(pfx_table, &record, false); } rm_node = (trie_remove(root, &(root->prefix), root->len, 0)); diff -Nru librtr-0.6.3/rtrlib/rtr/packets.c librtr-0.7.0/rtrlib/rtr/packets.c --- librtr-0.6.3/rtrlib/rtr/packets.c 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/rtrlib/rtr/packets.c 2019-07-18 08:26:08.000000000 +0000 @@ -168,6 +168,15 @@ uint32_t expire_interval; }; +struct recv_loop_cleanup_args { + struct pdu_ipv4 *ipv4_pdus; + struct pdu_ipv6 *ipv6_pdus; + struct pdu_router_key *router_key_pdus; +}; + + +static void recv_loop_cleanup(void *p); + static int rtr_send_error_pdu_from_network(const struct rtr_socket *rtr_socket, const void *erroneous_pdu, const uint32_t erroneous_pdu_len, @@ -486,10 +495,6 @@ break; } - if ((err_msg_len > 0) && (((uint8_t*)err_pdu)[min_size-1] != 0)) { - RTR_DBG1("Error msg is not null terminated!"); - break; - } retval = true; break; case SERIAL_QUERY: @@ -720,16 +725,13 @@ break; } - const uint32_t len_err_txt = ntohl(*((uint32_t *) (pdu->rest + pdu->len_enc_pdu))); + const uint32_t len_err_txt = *((uint32_t *) (pdu->rest + pdu->len_enc_pdu)); if (len_err_txt > 0) { if ((sizeof(pdu->ver) + sizeof(pdu->type) + sizeof(pdu->error_code) + sizeof(pdu->len) + sizeof(pdu->len_enc_pdu) + pdu->len_enc_pdu + 4 + len_err_txt) != pdu->len) RTR_DBG1("error: Length of error text contains an incorrect value"); else { - //assure that the error text contains an terminating \0 char - char txt[len_err_txt + 1]; - char *pdu_txt = (char *) pdu->rest + pdu->len_enc_pdu + 4; - snprintf(txt, len_err_txt + 1, "%s", pdu_txt); - RTR_DBG("Error PDU included the following error msg: \'%s\'", txt); + char *pdu_txt = (char *) pdu->rest + pdu->len_enc_pdu + sizeof(len_err_txt); + RTR_DBG("Error PDU included the following error msg: \'%.*s\'", len_err_txt, pdu_txt); } } @@ -991,7 +993,17 @@ return RTR_SUCCESS; } -int rtr_sync_receive_and_store_pdus(struct rtr_socket *rtr_socket){ + +void recv_loop_cleanup(void *p) { + struct recv_loop_cleanup_args *args = p; + lrtr_free(args->ipv4_pdus); + lrtr_free(args->ipv6_pdus); + lrtr_free(args->router_key_pdus); +} + + +/* WARNING: This Function has cancelable sections*/ +static int rtr_sync_receive_and_store_pdus(struct rtr_socket *rtr_socket){ char pdu[RTR_MAX_PDU_LEN]; enum pdu_type type; int retval = RTR_SUCCESS; @@ -1008,12 +1020,23 @@ unsigned int router_key_pdus_size = 0; unsigned int router_key_pdus_nindex = 0; - struct pfx_table pfx_shadow_table; - struct spki_table spki_shadow_table; + struct pfx_table* pfx_shadow_table = NULL; + struct spki_table* spki_shadow_table = NULL; + + int oldcancelstate; + struct recv_loop_cleanup_args cleanup_args = { + .ipv4_pdus = ipv4_pdus, + .ipv6_pdus = ipv6_pdus, + .router_key_pdus = router_key_pdus}; //receive LRTR_IPV4/IPV6 PDUs till EOD do { + pthread_cleanup_push(recv_loop_cleanup, &cleanup_args); + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldcancelstate); retval = rtr_receive_pdu(rtr_socket, pdu, RTR_MAX_PDU_LEN, RTR_RECV_TIMEOUT); + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldcancelstate); + pthread_cleanup_pop(0); + if (retval == TR_WOULDBLOCK) { rtr_change_socket_state(rtr_socket, RTR_ERROR_TRANSPORT); retval = RTR_ERROR; @@ -1092,8 +1115,15 @@ if (rtr_socket->is_resetting) { RTR_DBG1("Reset in progress creating shadow table for atomic reset"); - pfx_table_init(&pfx_shadow_table, NULL); - pfx_update_table = &pfx_shadow_table; + pfx_shadow_table = lrtr_malloc(sizeof(struct pfx_table)); + if (pfx_shadow_table == NULL) { + RTR_DBG1("Memory allocation for pfx shadow table failed"); + retval = RTR_ERROR; + goto cleanup; + } + + pfx_table_init(pfx_shadow_table, NULL); + pfx_update_table = pfx_shadow_table; if (pfx_table_copy_except_socket(rtr_socket->pfx_table, pfx_update_table, rtr_socket)) { RTR_DBG1("Creation of pfx shadow table failed"); rtr_change_socket_state(rtr_socket, RTR_ERROR_FATAL); @@ -1101,8 +1131,14 @@ goto cleanup; } - spki_table_init(&spki_shadow_table, NULL); - spki_update_table = &spki_shadow_table; + spki_shadow_table = lrtr_malloc(sizeof(struct spki_table)); + if (spki_shadow_table == NULL) { + RTR_DBG1("Memory allocation for spki shadow table failed"); + retval = RTR_ERROR; + goto cleanup; + } + spki_table_init(spki_shadow_table, NULL); + spki_update_table = spki_shadow_table; if (spki_table_copy_except_socket(rtr_socket->spki_table, spki_update_table, rtr_socket) != SPKI_SUCCESS) { RTR_DBG1("Creation of spki shadow table failed"); rtr_change_socket_state(rtr_socket, RTR_ERROR_FATAL); @@ -1116,7 +1152,7 @@ spki_update_table = rtr_socket->spki_table; } - int retval = PFX_SUCCESS; + retval = PFX_SUCCESS; //add all IPv4 prefix pdu to the pfx_table for (unsigned int i = 0; i < ipv4_pdus_nindex; i++) { if (rtr_update_pfx_table(rtr_socket, pfx_update_table, &(ipv4_pdus[i])) == PFX_ERROR) { @@ -1181,19 +1217,19 @@ RTR_DBG1("spki data added"); if (rtr_socket->is_resetting) { RTR_DBG1("Reset finished. Swapping new table in."); - pfx_table_swap(rtr_socket->pfx_table, &pfx_shadow_table); - spki_table_swap(rtr_socket->spki_table, &spki_shadow_table); + pfx_table_swap(rtr_socket->pfx_table, pfx_shadow_table); + spki_table_swap(rtr_socket->spki_table, spki_shadow_table); if (rtr_socket->pfx_table->update_fp) { RTR_DBG1("Calculating and notifying pfx diff"); - pfx_table_notify_diff(rtr_socket->pfx_table, &pfx_shadow_table, rtr_socket); + pfx_table_notify_diff(rtr_socket->pfx_table, pfx_shadow_table, rtr_socket); } else { RTR_DBG1("No pfx update callback. Skipping diff"); } if (rtr_socket->spki_table->update_fp) { RTR_DBG1("Calculating and notifying spki diff"); - spki_table_notify_diff(rtr_socket->spki_table, &spki_shadow_table, rtr_socket); + spki_table_notify_diff(rtr_socket->spki_table, spki_shadow_table, rtr_socket); } else { RTR_DBG1("No spki update callback. Skipping diff"); } @@ -1222,8 +1258,15 @@ if (rtr_socket->is_resetting) { RTR_DBG1("Freeing shadow tables."); - pfx_table_free_without_notify(&pfx_shadow_table); - spki_table_free_without_notify(&spki_shadow_table); + if (pfx_shadow_table != NULL) { + pfx_table_free_without_notify(pfx_shadow_table); + lrtr_free(pfx_shadow_table); + } + + if (spki_shadow_table != NULL) { + spki_table_free_without_notify(spki_shadow_table); + lrtr_free(spki_shadow_table); + } rtr_socket->is_resetting = false; } @@ -1233,13 +1276,18 @@ return retval; } +/* WARNING: This Function has cancelable sections */ int rtr_sync(struct rtr_socket *rtr_socket) { char pdu[RTR_MAX_PDU_LEN]; enum pdu_type type; + int oldcancelstate; + do { + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldcancelstate); int rtval = rtr_receive_pdu(rtr_socket, pdu, RTR_MAX_PDU_LEN, RTR_RECV_TIMEOUT); + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldcancelstate); //If the cache has closed the connection and we don't have a session_id //(no packages where exchanged) we should downgrade. if (rtval == TR_CLOSED && rtr_socket->request_session_id) { diff -Nru librtr-0.6.3/rtrlib/rtr/rtr.c librtr-0.7.0/rtrlib/rtr/rtr.c --- librtr-0.6.3/rtrlib/rtr/rtr.c 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/rtrlib/rtr/rtr.c 2019-07-18 08:26:08.000000000 +0000 @@ -22,9 +22,7 @@ #include "rtrlib/transport/transport_private.h" static void rtr_purge_outdated_records(struct rtr_socket *rtr_socket); -static void rtr_fsm_start(struct rtr_socket *rtr_socket); -static void sighandler(int b); -static int install_sig_handler(); +static void *rtr_fsm_start(struct rtr_socket *rtr_socket); static const char *socket_str_states[] = { [RTR_CONNECTING] = "RTR_CONNECTING", @@ -39,23 +37,6 @@ [RTR_SHUTDOWN] = "RTR_SHUTDOWN" }; -void sighandler(int b __attribute__((unused)) ) -{ - return; -} - -static int install_sig_handler() -{ - struct sigaction sa; - sa.sa_handler = &sighandler; - sigset_t mask; - sigemptyset(&mask); - sa.sa_mask = mask; - sa.sa_flags = 0; - - return sigaction(SIGUSR1, &sa, NULL); -} - int rtr_init(struct rtr_socket *rtr_socket, struct tr_socket *tr, struct pfx_table *pfx_table, @@ -129,13 +110,17 @@ } } -void rtr_fsm_start(struct rtr_socket *rtr_socket) -{ +/* WARNING: This Function has cancelable sections*/ +void *rtr_fsm_start(struct rtr_socket *rtr_socket) +{ if (rtr_socket->state == RTR_SHUTDOWN) - return; - + return NULL; + + // We don't care about the old state, but POSIX demands a non null value for setcancelstate + int oldcancelstate; + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldcancelstate); + rtr_socket->state = RTR_CONNECTING; - install_sig_handler(); while(1) { if(rtr_socket->state == RTR_CONNECTING) { RTR_DBG1("State: RTR_CONNECTING"); @@ -176,8 +161,14 @@ else if(rtr_socket->state == RTR_ESTABLISHED) { RTR_DBG1("State: RTR_ESTABLISHED"); - if(rtr_wait_for_sync(rtr_socket) == RTR_SUCCESS) { //blocks till expire_interval is expired or PDU was received - //send serial query + + // Allow thread cancellation for recv code path only. + // This should be enough since we spend most of the time blocking on recv + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldcancelstate); + int ret = rtr_wait_for_sync(rtr_socket); //blocks till expire_interval is expired or PDU was received + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldcancelstate); + + if(ret == RTR_SUCCESS) { //send serial query if(rtr_send_serial_query(rtr_socket) == RTR_SUCCESS) rtr_change_socket_state(rtr_socket, RTR_SYNC); } @@ -211,7 +202,9 @@ tr_close(rtr_socket->tr_socket); rtr_change_socket_state(rtr_socket, RTR_CONNECTING); RTR_DBG("Waiting %u", rtr_socket->retry_interval); + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldcancelstate); sleep(rtr_socket->retry_interval); + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldcancelstate); } else if(rtr_socket->state == RTR_ERROR_FATAL) { @@ -219,17 +212,13 @@ tr_close(rtr_socket->tr_socket); rtr_change_socket_state(rtr_socket, RTR_CONNECTING); RTR_DBG("Waiting %u", rtr_socket->retry_interval); + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldcancelstate); sleep(rtr_socket->retry_interval); + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldcancelstate); } else if(rtr_socket->state == RTR_SHUTDOWN) { RTR_DBG1("State: RTR_SHUTDOWN"); - tr_close(rtr_socket->tr_socket); - rtr_socket->request_session_id = true; - rtr_socket->serial_number = 0; - rtr_socket->last_update = 0; - pfx_table_src_remove(rtr_socket->pfx_table, rtr_socket); - spki_table_src_remove(rtr_socket->spki_table, rtr_socket); pthread_exit(NULL); } } @@ -240,10 +229,17 @@ RTR_DBG1("rtr_stop()"); rtr_change_socket_state(rtr_socket, RTR_SHUTDOWN); if(rtr_socket->thread_id != 0) { - RTR_DBG1("pthread_kill()"); - pthread_kill(rtr_socket->thread_id, SIGUSR1); + RTR_DBG1("pthread_cancel()"); + pthread_cancel(rtr_socket->thread_id); RTR_DBG1("pthread_join()"); pthread_join(rtr_socket->thread_id, NULL); + + tr_close(rtr_socket->tr_socket); + rtr_socket->request_session_id = true; + rtr_socket->serial_number = 0; + rtr_socket->last_update = 0; + pfx_table_src_remove(rtr_socket->pfx_table, rtr_socket); + spki_table_src_remove(rtr_socket->spki_table, rtr_socket); rtr_socket->thread_id = 0; } RTR_DBG1("Socket shut down"); diff -Nru librtr-0.6.3/rtrlib/rtr_mgr.c librtr-0.7.0/rtrlib/rtr_mgr.c --- librtr-0.6.3/rtrlib/rtr_mgr.c 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/rtrlib/rtr_mgr.c 2019-07-18 08:26:08.000000000 +0000 @@ -262,7 +262,7 @@ if (next_group) rtr_mgr_start_sockets(next_group); else - MGR_DBG1("No other inactive groups found"); + MGR_DBG1("No other inactive groups found"); } } @@ -364,16 +364,16 @@ /* Check that the groups have unique pref and at least one socket */ for (unsigned int i = 0; i < config->len; i++) { - struct rtr_mgr_group cg = groups[i]; - - if ((i > 0) && (cg.preference == last_preference)) { + if ((i > 0) && (groups[i].preference == last_preference)) { MGR_DBG1("Error Same preference for 2 socket groups!"); goto err; } - if (cg.sockets_len == 0) { + if (groups[i].sockets_len == 0) { MGR_DBG1("Error Empty sockets array in socket group!"); goto err; } + + last_preference = groups[i].preference; } /* Init data structures that we need to pass to the sockets */ @@ -458,7 +458,7 @@ RTRLIB_EXPORT int rtr_mgr_start(struct rtr_mgr_config *config) { - MGR_DBG1("rtr_mgr_start()"); + MGR_DBG("%s()", __func__); struct rtr_mgr_group *best_group = rtr_mgr_get_first_group(config); return rtr_mgr_start_sockets(best_group); @@ -490,7 +490,7 @@ RTRLIB_EXPORT void rtr_mgr_free(struct rtr_mgr_config *config) { - MGR_DBG1("rtr_mgr_free()"); + MGR_DBG("%s()", __func__); pthread_mutex_lock(&config->mutex); pfx_table_free(config->pfx_table); @@ -549,7 +549,7 @@ pthread_mutex_lock(&config->mutex); tommy_node *node = tommy_list_head(&config->groups->list); - MGR_DBG1("rtr_mgr_stop()"); + MGR_DBG("%s()", __func__); while (node) { struct rtr_mgr_group_node *group_node = node->data; diff -Nru librtr-0.6.3/rtrlib/rtr_mgr_private.h librtr-0.7.0/rtrlib/rtr_mgr_private.h --- librtr-0.6.3/rtrlib/rtr_mgr_private.h 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/rtrlib/rtr_mgr_private.h 2019-07-18 08:26:08.000000000 +0000 @@ -10,7 +10,7 @@ #ifndef RTR_MGR_PRIVATE #define RTR_MGR_PRIVATE #include "rtrlib/rtr_mgr.h" -#include "rtrlib/spki/hashtable/tommyds-1.8/tommylist.h" +#include "third-party/tommyds/tommylist.h" struct tommy_list_wrapper { tommy_list list; diff -Nru librtr-0.6.3/rtrlib/spki/hashtable/ht-spkitable_private.h librtr-0.7.0/rtrlib/spki/hashtable/ht-spkitable_private.h --- librtr-0.6.3/rtrlib/spki/hashtable/ht-spkitable_private.h 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/rtrlib/spki/hashtable/ht-spkitable_private.h 2019-07-18 08:26:08.000000000 +0000 @@ -10,7 +10,8 @@ #define RTR_HT_SPKITABLE_PRIVATE_H #include "rtrlib/spki/spkitable_private.h" -#include "rtrlib/spki/hashtable/tommyds-1.8/tommy.h" +#include "third-party/tommyds/tommyhashlin.h" +#include "third-party/tommyds/tommylist.h" typedef int (*hash_cmp_fp)(const void *arg, const void *obj); diff -Nru librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/AUTHORS librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/AUTHORS --- librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/AUTHORS 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/AUTHORS 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ -TommyDS AUTHORS -=============== - -The author of TommyDS is Andrea Mazzoleni. - -You can contact me sending an email at: - - amadvance@users.sourceforge.net - diff -Nru librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/HISTORY librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/HISTORY --- librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/HISTORY 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/HISTORY 1970-01-01 00:00:00.000000000 +0000 @@ -1,64 +0,0 @@ -TommyDS HISTORY -=============== - -1.8 2013/12 -=========== - * Fixed build of tommy_arrayblk in C++. - * Changed the default node size of tommy_trie to fit a cache line of 64 bytes. - * Added benchmark comparison with STX BTree. - -1.7 2013/12 -=========== - * Extends tommy_hashlin_done() to work also if the hashtable is not empty. - * Removes the empty tommy_trie_done() because the real deallocation is done - by the allocator. - -1.6 2013/11 -=========== - * Added a new tommy_arrayblk and tommy_arrayblkof types to store elements - in an array minimizing memory occupation. - -1.5 2013/06 -=========== - * Fixed inline declaration to allow building with clang. - * Added a new tommy_arrayof type to store in an array elements of arbitrary - size. - -1.4 2013/03 -=========== - * Added benchmark comparison with Google BTree, and C++ map and unordered_map. - * Benchmark for Linux is now compiled with "-O3 -march=pentium4 -mtune=generic", - and the Windows one with "/Ox /GL /GS- /arch:SSE2". - -1.3 2013/02 -=========== - * Fixed a Segmentation Fault bug in the hashlin container if exact power - of 2 sizes were used. - * Removed some warnings with newer gcc. - * Minor documentation changes. - * Added benchmark comparison with the judy array implementation of Karl Malbrain. - -1.2 2012/05 -=========== - * Minor documentation changes. - * In the check application, added a speed comparison with the C qsort() - implementation. - -1.1 2012/05 -=========== - * Fixed the tommy_hashdyn_remove() function. Now it shrinks the hashtable if required. - * Minor documentation changes. - -1.0 2011/03 -=========== - * First official version of TommyDS. - * Added tommy_list_foreach functions. - -0.2 2011/03 -=========== - * Added tommy_array. A dynamic vector. - -0.1 2011/01 -=========== - * First release of Tommy. - diff -Nru librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/INSTALL librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/INSTALL --- librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/INSTALL 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/INSTALL 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -TommyDS INSTALL -=============== - -TommyDS doesn't need any installation. You have only to import the required .c -and .h files into your program and start using them. - - diff -Nru librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/LICENSE librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/LICENSE --- librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/LICENSE 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/LICENSE 1970-01-01 00:00:00.000000000 +0000 @@ -1,25 +0,0 @@ -Copyright 2010 Andrea Mazzoleni. All rights reserved. - -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 ANDREA MAZZOLENI AND CONTRIBUTORS ``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 ANDREA MAZZOLENI 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. - diff -Nru librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/Makefile librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/Makefile --- librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/Makefile 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/Makefile 1970-01-01 00:00:00.000000000 +0000 @@ -1,172 +0,0 @@ -############################################################################# -# Tommy Makefile - -VERSION=1.8 -CFLAGS=-m32 -O3 -march=pentium4 -mtune=generic -Wall -Wextra -g -# -std=c++11 required by Google btree -CCFLAGS=$(CFLAGS) -fpermissive -std=c++11 -CC=gcc -CXX=g++ -UNAME=$(shell uname) - -# Linux -ifeq ($(UNAME),Linux) -LIB=-lrt benchmark/lib/judy/libJudyL.a benchmark/lib/judy/libJudyMalloc.a -EXE= -O=.o -endif - -# Darwin -ifeq ($(UNAME),Darwin) -LIB= -EXE= -O=.o -endif - -# Windows -ifeq ($(UNAME),) -LIB=benchmark/lib/judy/src/judy.lib -EXE=.exe -O=.obj -endif - -CHECK = ./tommybench -N 1000000 -d tommy-hashlin -#CHECK = ./tommycheck - -DEP = \ - tommyalloc.c \ - tommyalloc.h \ - tommyarray.c \ - tommyarray.h \ - tommyarrayof.c \ - tommyarrayof.h \ - tommyarrayblk.c \ - tommyarrayblk.h \ - tommyarrayblkof.c \ - tommyarrayblkof.h \ - tommy.c \ - tommy.h \ - tommyhash.c \ - tommyhashdyn.c \ - tommyhashdyn.h \ - tommyhash.h \ - tommyhashlin.c \ - tommyhashlin.h \ - tommyhashtbl.c \ - tommyhashtbl.h \ - tommylist.c \ - tommylist.h \ - tommytrie.c \ - tommytrie.h \ - tommytrieinp.c \ - tommytrieinp.h \ - tommytypes.h \ - tommychain.h - -all: tommycheck$(EXE) tommybench$(EXE) - -tommy$(O): $(DEP) - $(CC) $(CFLAGS) -c tommy.c -o tommy$(O) - $(CC) $(CFLAGS) -S -fverbose-asm tommy.c -o tommy.s - objdump -S tommy$(O) > tommy.S - -tommycheck$(EXE): check.c tommy$(O) - $(CC) $(CFLAGS) check.c tommy.o -o tommycheck$(EXE) $(LIB) - -tommybench$(EXE): benchmark.cc $(DEP) - $(CXX) $(CCFLAGS) benchmark.cc -o tommybench$(EXE) $(LIB) - -check: tommycheck$(EXE) tommybench$(EXE) - ./tommycheck$(EXE) - ./tommybench$(EXE) -N 100000 - echo Check completed with success! - -valgrind: - valgrind \ - --tool=memcheck \ - --track-origins=yes \ - --read-var-info=yes \ - -v $(CHECK) \ - 2> valgrind.log - tail valgrind.log - -callgrind: - valgrind \ - --tool=callgrind \ - --dump-instr=yes \ - --trace-jump=yes \ - -v $(CHECK) \ - 2> callgrind.log - tail callgrind.log - -cachegrind: - valgrind \ - --tool=cachegrind \ - -v $(CHECK) \ - 2> cachegrind.log - tail cachegrind.log - -phony: - -graph: phony - cd benchmark && sh gr_all.sh - -doc: phony tommy.doxygen tommy.css $(DEP) - rm -rf doc - mkdir doc - cp -a benchmark/data/* doc/ - rm -f doc/*/*.lst - rm -f doc/*/*.gnu - doxygen tommy.doxygen - rm -f doc/doxygen.png - rm -f doc/tab_*.png - -web: phony tommyweb.doxygen tommy.css $(DEP) - rm -rf web - mkdir web - cp -a benchmark/data/* web/ - rm -f web/*/*.lst - rm -f web/*/*.gnu - doxygen tommyweb.doxygen - rm -f web/doxygen.png - rm -f web/tab_*.png - -clean: - rm -f *.log *.s *.S *.lst *.o - rm -f *.ncb *.suo *.obj - rm -rf Debug Release x64 - rm -f callgrind.out.* - rm -f cachegrind.out.* - -distclean: clean - rm -f tommybench$(EXE) tommycheck$(EXE) - -maintainerclean: distclean - rm -rf doc web - -DIST=tommyds-$(VERSION) - -DISTFILES=\ - Makefile \ - README LICENSE AUTHORS INSTALL HISTORY \ - tommy.doxygen tommy.css tommy-header.html tommy-footer.html \ - benchmark.cc \ - benchmark.vcxproj benchmark.sln \ - benchmark.geany \ - check.c - -dist: - mkdir $(DIST) - cp $(DISTFILES) $(DIST) - cp $(DEP) $(DIST) - cp -R doc $(DIST) - cp -R benchmark $(DIST)/benchmark - rm -f $(DIST)/benchmark/data/*/*.png - rm -rf $(DIST)/benchmark/data/test - rm -f $(DIST)/benchmark/arial.ttf - rm -f $(DIST).tar.gz - tar cfzo $(DIST).tar.gz $(DIST) - rm -f $(DIST).zip - zip -r $(DIST).zip $(DIST) - rm -r $(DIST) - diff -Nru librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/README librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/README --- librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/README 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/README 1970-01-01 00:00:00.000000000 +0000 @@ -1,27 +0,0 @@ -TommyDS -======= - -TommyDS is a C library of hashtables and tries designed for high performance. - -It's faster than all the similar libraries like rbtree, judy, goodledensehash, -khash, uthash, nedtries and others. - -The data structures provided are: - - tommy_list - A double linked list. - tommy_array - A linear array. It doesn't fragment the heap. - tommy_hashtable - A fixed size chained hashtable. - tommy_hashdyn - A dynamic chained hashtable. - tommy_hashlin - A linear chained hashtable. It doesn't have the - problem of the delay when resizing and it doesn't fragment - the heap. - tommy_trie - A trie optimized for cache utilization. - tommy_trie_inplace - A trie completely inplace. - -The documentation is available in HTML format in the doc/index.html file, -and directly in the .h files. - -The official site of TommyDS is: - - http://tommyds.sourceforge.net - diff -Nru librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/tommyalloc.c librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/tommyalloc.c --- librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/tommyalloc.c 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/tommyalloc.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,140 +0,0 @@ -/* - * Copyright 2010 Andrea Mazzoleni. All rights reserved. - * - * 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 ANDREA MAZZOLENI AND CONTRIBUTORS ``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 ANDREA MAZZOLENI 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 "tommyalloc.h" - -/******************************************************************************/ -/* allocator */ - -/** - * Basic allocation segment. - * Smaller of a memory page, to allow also a little heap overread. - * The heap manager may put it in a single memory page. - */ -#define TOMMY_ALLOCATOR_BLOCK_SIZE (4096-64) - -void tommy_allocator_init(tommy_allocator* alloc, unsigned block_size, unsigned align_size) -{ - /* setup the minimal alignment */ - if (align_size < sizeof(void*)) - align_size = sizeof(void*); - - /* ensure that the block_size keeps the alignment */ - if (block_size % align_size != 0) { - block_size += align_size - block_size % align_size; - } - - alloc->block_size = block_size; - alloc->align_size = align_size; - - alloc->count = 0; - alloc->free_block = 0; - alloc->used_segment = 0; -} - -/** - * Reset the allocator and free all. - */ -static void allocator_reset(tommy_allocator* alloc) -{ - tommy_allocator_entry* block = alloc->used_segment; - while (block) { - tommy_allocator_entry* block_next = block->next; - tommy_free(block); - block = block_next; - } - - alloc->count = 0; - alloc->free_block = 0; - alloc->used_segment = 0; -} - -void tommy_allocator_done(tommy_allocator* alloc) -{ - allocator_reset(alloc); -} - -void* tommy_allocator_alloc(tommy_allocator* alloc) -{ - void* ptr; - - /* if no free block available */ - if (!alloc->free_block) { - tommy_uintptr_t off, mis; - tommy_size_t size = TOMMY_ALLOCATOR_BLOCK_SIZE; - char* data = tommy_cast(char*, tommy_malloc(size)); - tommy_allocator_entry* segment = (tommy_allocator_entry*)data; - - /* put in the segment list */ - segment->next = alloc->used_segment; - alloc->used_segment = segment; - data += sizeof(tommy_allocator_entry); - - /* align if not aligned */ - off = (tommy_uintptr_t)data; - mis = off % alloc->align_size; - if (mis != 0) { - data += alloc->align_size - mis; - size -= alloc->align_size - mis; - } - - /* insert in free list */ - while (size >= alloc->block_size) { - tommy_allocator_entry* free_block = (tommy_allocator_entry*)data; - free_block->next = alloc->free_block; - alloc->free_block = free_block; - - data += alloc->block_size; - size -= alloc->block_size; - } - } - - /* remove one from the free list */ - ptr = alloc->free_block; - alloc->free_block = alloc->free_block->next; - - ++alloc->count; - - return ptr; -} - -void tommy_allocator_free(tommy_allocator* alloc, void* ptr) -{ - tommy_allocator_entry* free_block = tommy_cast(tommy_allocator_entry*, ptr); - - /* put it in the free list */ - free_block->next = alloc->free_block; - alloc->free_block = free_block; - - --alloc->count; -} - -tommy_size_t tommy_allocator_memory_usage(tommy_allocator* alloc) -{ - return alloc->count * (tommy_size_t)alloc->block_size; -} - diff -Nru librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/tommyalloc.h librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/tommyalloc.h --- librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/tommyalloc.h 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/tommyalloc.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,95 +0,0 @@ -/* - * Copyright 2010 Andrea Mazzoleni. All rights reserved. - * - * 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 ANDREA MAZZOLENI AND CONTRIBUTORS ``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 ANDREA MAZZOLENI 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. - */ - -/** \file - * Allocator of fixed size blocks. - */ - -#ifndef __TOMMYALLOC_H -#define __TOMMYALLOC_H - -#include "tommytypes.h" - -/******************************************************************************/ -/* allocator */ - -/** \internal - * Allocator entry. - */ -struct tommy_allocator_entry_struct { - struct tommy_allocator_entry_struct* next; /**< Pointer at the next entry. 0 for last. */ -}; -typedef struct tommy_allocator_entry_struct tommy_allocator_entry; - -/** - * Allocator of fixed size blocks. - */ -typedef struct tommy_allocator_struct { - struct tommy_allocator_entry_struct* free_block; /**< List of free blocks. */ - struct tommy_allocator_entry_struct* used_segment; /**< List of allocated segments. */ - unsigned block_size; /**< Block size. */ - unsigned align_size; /**< Alignment size. */ - unsigned count; /**< Number of allocated elements. */ -} tommy_allocator; - -/** - * Initializes the allocator. - * \param alloc Allocator to initialize. - * \param block_size Size of the block to allocate. - * \param align_size Minimum alignment requirement. No less than sizeof(void*). - */ -void tommy_allocator_init(tommy_allocator* alloc, unsigned block_size, unsigned align_size); - -/** - * Deinitialize the allocator. - * It also releases all the allocated memory to the heap. - * \param alloc Allocator to deinitialize. - */ -void tommy_allocator_done(tommy_allocator* alloc); - -/** - * Allocates a block. - * \param alloc Allocator to use. - */ -void* tommy_allocator_alloc(tommy_allocator* alloc); - -/** - * Deallocates a block. - * You must use the same allocator used in the tommy_allocator_alloc() call. - * \param alloc Allocator to use. - * \param ptr Block to free. - */ -void tommy_allocator_free(tommy_allocator* alloc, void* ptr); - -/** - * Gets the size of allocated memory. - * \param alloc Allocator to use. - */ -tommy_size_t tommy_allocator_memory_usage(tommy_allocator* alloc); - -#endif - diff -Nru librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/tommyarrayblk.c librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/tommyarrayblk.c --- librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/tommyarrayblk.c 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/tommyarrayblk.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,83 +0,0 @@ -/* - * Copyright 2013 Andrea Mazzoleni. All rights reserved. - * - * 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 ANDREA MAZZOLENI AND CONTRIBUTORS ``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 ANDREA MAZZOLENI 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 "tommyarrayblk.h" - -#include /* for memset */ - -/******************************************************************************/ -/* array */ - -void tommy_arrayblk_init(tommy_arrayblk* array) -{ - tommy_array_init(&array->block); - - array->size = 0; -} - -void tommy_arrayblk_done(tommy_arrayblk* array) -{ - unsigned i; - - for(i=0;iblock);++i) - tommy_free(tommy_array_get(&array->block, i)); - - tommy_array_done(&array->block); -} - -void tommy_arrayblk_grow(tommy_arrayblk* array, unsigned size) -{ - unsigned block_max = (size + TOMMY_ARRAYBLK_SIZE - 1) / TOMMY_ARRAYBLK_SIZE; - unsigned block_mac = tommy_array_size(&array->block); - - if (block_mac < block_max) { - /* grow the block array */ - tommy_array_grow(&array->block, block_max); - - /* allocate new blocks */ - while (block_mac < block_max) { - void* ptr = tommy_malloc(TOMMY_ARRAYBLK_SIZE * sizeof(void*)); - - /* initializes it with zeros */ - memset(ptr, 0, TOMMY_ARRAYBLK_SIZE * sizeof(void*)); - - /* set the new block */ - tommy_array_set(&array->block, block_mac, ptr); - - ++block_mac; - } - } - - if (array->size < size) - array->size = size; -} - -tommy_size_t tommy_arrayblk_memory_usage(tommy_arrayblk* array) -{ - return tommy_array_memory_usage(&array->block) + tommy_array_size(&array->block) * TOMMY_ARRAYBLK_SIZE * sizeof(void*); -} - diff -Nru librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/tommyarrayblk.h librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/tommyarrayblk.h --- librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/tommyarrayblk.h 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/tommyarrayblk.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,143 +0,0 @@ -/* - * Copyright 2013 Andrea Mazzoleni. All rights reserved. - * - * 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 ANDREA MAZZOLENI AND CONTRIBUTORS ``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 ANDREA MAZZOLENI 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. - */ - -/** \file - * Dynamic array based on blocks of fixed sizes. - * - * This array is able to grow dynamically upon request. - * - * The resize involve an allocation of a new array block, without reallocating - * the already allocated memory, and then not increasing the heap fragmentation, - * and minimize the space occupation. - * This means that the address of the allocated blocks never change. - * - * Allocated blocks are always of the same fixed size of 64 Ki pointers. - */ - -#ifndef __TOMMYARRAYBLK_H -#define __TOMMYARRAYBLK_H - -#include "tommytypes.h" -#include "tommyarray.h" - -#include /* for assert */ - -/******************************************************************************/ -/* array */ - -/** - * Elements for each block. - */ -#define TOMMY_ARRAYBLK_SIZE (64 * 1024) - -/** - * Array. - */ -typedef struct tommy_arrayblk_struct { - tommy_array block; /**< Array of blocks. */ - unsigned size; /**< Currently allocated and initialized size. */ -} tommy_arrayblk; - -/** - * Initializes the array. - */ -void tommy_arrayblk_init(tommy_arrayblk* array); - -/** - * Deinitializes the array. - */ -void tommy_arrayblk_done(tommy_arrayblk* array); - -/** - * Grow the size up to the specified value. - * All the new elements in the array are initialized with the 0 value. - */ -void tommy_arrayblk_grow(tommy_arrayblk* array, unsigned size); - -/** - * Gets a reference of the element at the specified position. - * You must be sure that space for this position is already - * allocated calling tommy_arrayblk_grow(). - */ -tommy_inline void** tommy_arrayblk_ref(tommy_arrayblk* array, unsigned pos) -{ - void** ptr; - - assert(pos < array->size); - - ptr = tommy_cast(void**, tommy_array_get(&array->block, pos / TOMMY_ARRAYBLK_SIZE)); - - return &ptr[pos % TOMMY_ARRAYBLK_SIZE]; -} - -/** - * Sets the element at the specified position. - * You must be sure that space for this position is already - * allocated calling tommy_arrayblk_grow(). - */ -tommy_inline void tommy_arrayblk_set(tommy_arrayblk* array, unsigned pos, void* element) -{ - *tommy_arrayblk_ref(array, pos) = element; -} - -/** - * Gets the element at the specified position. - * You must be sure that space for this position is already - * allocated calling tommy_arrayblk_grow(). - */ -tommy_inline void* tommy_arrayblk_get(tommy_arrayblk* array, unsigned pos) -{ - return *tommy_arrayblk_ref(array, pos); -} - -/** - * Grows and inserts a new element at the end of the array. - */ -tommy_inline void tommy_arrayblk_insert(tommy_arrayblk* array, void* element) -{ - unsigned pos = array->size; - - tommy_arrayblk_grow(array, pos + 1); - - tommy_arrayblk_set(array, pos, element); -} - -/** - * Gets the initialized size of the array. - */ -tommy_inline unsigned tommy_arrayblk_size(tommy_arrayblk* array) -{ - return array->size; -} - -/** - * Gets the size of allocated memory. - */ -tommy_size_t tommy_arrayblk_memory_usage(tommy_arrayblk* array); - -#endif - diff -Nru librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/tommyarrayblkof.c librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/tommyarrayblkof.c --- librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/tommyarrayblkof.c 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/tommyarrayblkof.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,83 +0,0 @@ -/* - * Copyright 2013 Andrea Mazzoleni. All rights reserved. - * - * 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 ANDREA MAZZOLENI AND CONTRIBUTORS ``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 ANDREA MAZZOLENI 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 "tommyarrayblkof.h" - -#include /* for memset */ - -/******************************************************************************/ -/* array */ - -void tommy_arrayblkof_init(tommy_arrayblkof* array, unsigned element_size) -{ - tommy_array_init(&array->block); - array->element_size = element_size; - array->size = 0; -} - -void tommy_arrayblkof_done(tommy_arrayblkof* array) -{ - unsigned i; - - for(i=0;iblock);++i) - tommy_free(tommy_array_get(&array->block, i)); - - tommy_array_done(&array->block); -} - -void tommy_arrayblkof_grow(tommy_arrayblkof* array, unsigned size) -{ - unsigned block_max = (size + TOMMY_ARRAYBLK_SIZE - 1) / TOMMY_ARRAYBLK_SIZE; - unsigned block_mac = tommy_array_size(&array->block); - - if (block_mac < block_max) { - /* grow the block array */ - tommy_array_grow(&array->block, block_max); - - /* allocate new blocks */ - while (block_mac < block_max) { - void* ptr = tommy_malloc(TOMMY_ARRAYBLK_SIZE * array->element_size); - - /* initializes it with zeros */ - memset(ptr, 0, TOMMY_ARRAYBLK_SIZE * array->element_size); - - /* set the new block */ - tommy_array_set(&array->block, block_mac, ptr); - - ++block_mac; - } - } - - if (array->size < size) - array->size = size; -} - -tommy_size_t tommy_arrayblkof_memory_usage(tommy_arrayblkof* array) -{ - return tommy_array_memory_usage(&array->block) + tommy_array_size(&array->block) * TOMMY_ARRAYBLKOF_SIZE * array->element_size; -} - diff -Nru librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/tommyarrayblkof.h librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/tommyarrayblkof.h --- librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/tommyarrayblkof.h 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/tommyarrayblkof.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,113 +0,0 @@ -/* - * Copyright 2013 Andrea Mazzoleni. All rights reserved. - * - * 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 ANDREA MAZZOLENI AND CONTRIBUTORS ``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 ANDREA MAZZOLENI 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. - */ - -/** \file - * Dynamic array based on blocks of fixed sizes. - * - * This array is able to grow dynamically upon request. - * - * This is very similar at ::tommy_arrayblk, but it allows to store elements of any - * size and not just pointers. - * - * Note that in this case tommy_arrayblkof_ref() returns a pointer at the element, - * and it should be used for both get() and set() operation. - */ - -#ifndef __TOMMYARRAYBLKOF_H -#define __TOMMYARRAYBLKOF_H - -#include "tommytypes.h" -#include "tommyarray.h" - -#include /* for assert */ - -/******************************************************************************/ -/* array */ - -/** - * Elements for each block. - */ -#define TOMMY_ARRAYBLKOF_SIZE (64 * 1024) - -/** - * Array. - */ -typedef struct tommy_arrayblkof_struct { - tommy_array block; /**< Array of blocks. */ - unsigned element_size; /**< Size of the stored element in bytes. */ - unsigned size; /**< Currently allocated and initialized size. */ -} tommy_arrayblkof; - -/** - * Initializes the array. - * \param element_size Size in byte of the element to store in the array. - */ -void tommy_arrayblkof_init(tommy_arrayblkof* array, unsigned element_size); - -/** - * Deinitializes the array. - */ -void tommy_arrayblkof_done(tommy_arrayblkof* array); - -/** - * Grow the size up to the specified value. - * All the new elements in the array are initialized with the 0 value. - */ -void tommy_arrayblkof_grow(tommy_arrayblkof* array, unsigned size); - -/** - * Gets a reference of the element at the specified position. - * You must be sure that space for this position is already - * allocated calling tommy_arrayblkof_grow(). - */ -tommy_inline void* tommy_arrayblkof_ref(tommy_arrayblkof* array, unsigned pos) -{ - unsigned char* base; - - assert(pos < array->size); - - base = tommy_cast(unsigned char*, tommy_array_get(&array->block, pos / TOMMY_ARRAYBLKOF_SIZE)); - -// cppcheck-suppress CastIntegerToAddressAtReturn - return base + (pos % TOMMY_ARRAYBLKOF_SIZE) * array->element_size; -} - -/** - * Gets the initialized size of the array. - */ -tommy_inline unsigned tommy_arrayblkof_size(tommy_arrayblkof* array) -{ - return array->size; -} - -/** - * Gets the size of allocated memory. - */ -tommy_size_t tommy_arrayblkof_memory_usage(tommy_arrayblkof* array); - -#endif - diff -Nru librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/tommyarray.c librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/tommyarray.c --- librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/tommyarray.c 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/tommyarray.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,78 +0,0 @@ -/* - * Copyright 2011 Andrea Mazzoleni. All rights reserved. - * - * 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 ANDREA MAZZOLENI AND CONTRIBUTORS ``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 ANDREA MAZZOLENI 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 "tommyarray.h" - -#include /* for memset */ - -/******************************************************************************/ -/* array */ - -void tommy_array_init(tommy_array* array) -{ - /* fixed initial size */ - array->bucket_bit = TOMMY_ARRAY_BIT; - array->bucket_max = 1 << array->bucket_bit; - array->bucket[0] = tommy_cast(void**, tommy_malloc(array->bucket_max * sizeof(void*))); - - /* initializes it with zeros */ - memset(array->bucket[0], 0, array->bucket_max * sizeof(void*)); - - array->bucket_mac = 1; - array->size = 0; -} - -void tommy_array_done(tommy_array* array) -{ - unsigned i; - for(i=0;ibucket_mac;++i) - tommy_free(array->bucket[i]); -} - -void tommy_array_grow(tommy_array* array, unsigned size) -{ - while (size > array->bucket_max) { - /* allocate one more bucket */ - array->bucket[array->bucket_mac] = tommy_cast(void**, tommy_malloc(array->bucket_max * sizeof(void*))); - - /* initializes it with zeros */ - memset(array->bucket[array->bucket_mac], 0, array->bucket_max * sizeof(void*)); - - ++array->bucket_mac; - ++array->bucket_bit; - array->bucket_max = 1 << array->bucket_bit; - } - - if (array->size < size) - array->size = size; -} - -tommy_size_t tommy_array_memory_usage(tommy_array* array) -{ - return array->bucket_max * (tommy_size_t)sizeof(void*); -} - diff -Nru librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/tommyarray.h librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/tommyarray.h --- librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/tommyarray.h 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/tommyarray.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,159 +0,0 @@ -/* - * Copyright 2011 Andrea Mazzoleni. All rights reserved. - * - * 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 ANDREA MAZZOLENI AND CONTRIBUTORS ``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 ANDREA MAZZOLENI 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. - */ - -/** \file - * Dynamic array based on segments of exponential growing size. - * - * This array is able to grow dynamically upon request. - * - * The resize involve an allocation of a new array segment, without reallocating - * the already allocated memory, and then not increasing the heap fragmentation. - * This means that the address of the allocated segments never change. - * - * Allocated segments grow in size exponentially. - */ - -#ifndef __TOMMYARRAY_H -#define __TOMMYARRAY_H - -#include "tommytypes.h" - -#include /* for assert */ - -/******************************************************************************/ -/* array */ - -/** - * Initial and minimal size of the array expressed as a power of 2. - * The initial size is 2^TOMMY_ARRAY_BIT. - */ -#define TOMMY_ARRAY_BIT 6 - -/** \internal - * Max number of elements as a power of 2. - */ -#define TOMMY_ARRAY_BIT_MAX 32 - -/** - * Array. - */ -typedef struct tommy_array_struct { - void** bucket[TOMMY_ARRAY_BIT_MAX]; /**< Dynamic array of buckets. */ - unsigned bucket_bit; /**< Bits used in the bit mask. */ - unsigned bucket_max; /**< Number of buckets. */ - unsigned bucket_mac; /**< Number of vectors allocated. */ - unsigned size; /**< Currently allocated and initialized size. */ -} tommy_array; - -/** - * Initializes the array. - */ -void tommy_array_init(tommy_array* array); - -/** - * Deinitializes the array. - */ -void tommy_array_done(tommy_array* array); - -/** - * Grow the size up to the specified value. - * All the new elements in the array are initialized with the 0 value. - */ -void tommy_array_grow(tommy_array* array, unsigned size); - -/** - * Gets a reference of the element at the specified position. - * You must be sure that space for this position is already - * allocated calling tommy_array_grow(). - */ -tommy_inline void** tommy_array_ref(tommy_array* array, unsigned pos) -{ - unsigned bsr; - - assert(pos < array->size); - - /* special case for the first bucket */ - if (pos < (1 << TOMMY_ARRAY_BIT)) { - return &array->bucket[0][pos]; - } - - /* get the highest bit set */ - bsr = tommy_ilog2_u32(pos); - - /* clear the highest bit */ - pos -= 1 << bsr; - - return &array->bucket[bsr - TOMMY_ARRAY_BIT + 1][pos]; -} - -/** - * Sets the element at the specified position. - * You must be sure that space for this position is already - * allocated calling tommy_array_grow(). - */ -tommy_inline void tommy_array_set(tommy_array* array, unsigned pos, void* element) -{ - *tommy_array_ref(array, pos) = element; -} - -/** - * Gets the element at the specified position. - * You must be sure that space for this position is already - * allocated calling tommy_array_grow(). - */ -tommy_inline void* tommy_array_get(tommy_array* array, unsigned pos) -{ - return *tommy_array_ref(array, pos); -} - -/** - * Grows and inserts a new element at the end of the array. - */ -tommy_inline void tommy_array_insert(tommy_array* array, void* element) -{ - unsigned pos = array->size; - - tommy_array_grow(array, pos + 1); - - tommy_array_set(array, pos, element); -} - -/** - * Gets the initialized size of the array. - */ -tommy_inline unsigned tommy_array_size(tommy_array* array) -{ - return array->size; -} - -/** - * Gets the size of allocated memory. - */ -tommy_size_t tommy_array_memory_usage(tommy_array* array); - -#endif - diff -Nru librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/tommyarrayof.c librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/tommyarrayof.c --- librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/tommyarrayof.c 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/tommyarrayof.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,79 +0,0 @@ -/* - * Copyright 2013 Andrea Mazzoleni. All rights reserved. - * - * 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 ANDREA MAZZOLENI AND CONTRIBUTORS ``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 ANDREA MAZZOLENI 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 "tommyarrayof.h" - -#include /* for memset */ - -/******************************************************************************/ -/* array */ - -void tommy_arrayof_init(tommy_arrayof* array, unsigned element_size) -{ - /* fixed initial size */ - array->element_size = element_size; - array->bucket_bit = TOMMY_ARRAYOF_BIT; - array->bucket_max = 1 << array->bucket_bit; - array->bucket[0] = tommy_malloc(array->bucket_max * array->element_size); - - /* initializes it with zeros */ - memset(array->bucket[0], 0, array->bucket_max * array->element_size); - - array->bucket_mac = 1; - array->size = 0; -} - -void tommy_arrayof_done(tommy_arrayof* array) -{ - unsigned i; - for(i=0;ibucket_mac;++i) - tommy_free(array->bucket[i]); -} - -void tommy_arrayof_grow(tommy_arrayof* array, unsigned size) -{ - while (size > array->bucket_max) { - /* allocate one more bucket */ - array->bucket[array->bucket_mac] = tommy_malloc(array->bucket_max * array->element_size); - - /* initializes it with zeros */ - memset(array->bucket[array->bucket_mac], 0, array->bucket_max * array->element_size); - - ++array->bucket_mac; - ++array->bucket_bit; - array->bucket_max = 1 << array->bucket_bit; - } - - if (array->size < size) - array->size = size; -} - -tommy_size_t tommy_arrayof_memory_usage(tommy_arrayof* array) -{ - return array->bucket_max * (tommy_size_t)array->element_size; -} - diff -Nru librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/tommyarrayof.h librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/tommyarrayof.h --- librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/tommyarrayof.h 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/tommyarrayof.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,132 +0,0 @@ -/* - * Copyright 2013 Andrea Mazzoleni. All rights reserved. - * - * 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 ANDREA MAZZOLENI AND CONTRIBUTORS ``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 ANDREA MAZZOLENI 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. - */ - -/** \file - * Dynamic array based on segments of exponential growing size. - * - * This array is able to grow dynamically upon request. - * - * This is very similar at ::tommy_array, but it allows to store elements of any - * size and not just pointers. - * - * Note that in this case tommy_arrayof_ref() returns a pointer at the element, - * and it should be used for both get() and set() operation. - */ - -#ifndef __TOMMYARRAYOF_H -#define __TOMMYARRAYOF_H - -#include "tommytypes.h" - -#include /* for assert */ - -/******************************************************************************/ -/* array */ - -/** - * Initial and minimal size of the array expressed as a power of 2. - * The initial size is 2^TOMMY_ARRAYOF_BIT. - */ -#define TOMMY_ARRAYOF_BIT 6 - -/** \internal - * Max number of elements as a power of 2. - */ -#define TOMMY_ARRAYOF_BIT_MAX 32 - -/** - * Array. - */ -typedef struct tommy_arrayof_struct { - void* bucket[TOMMY_ARRAYOF_BIT_MAX]; /**< Dynamic array of buckets. */ - unsigned element_size; /**< Size of the stored element in bytes. */ - unsigned bucket_bit; /**< Bits used in the bit mask. */ - unsigned bucket_max; /**< Number of buckets. */ - unsigned bucket_mac; /**< Number of vectors allocated. */ - unsigned size; /**< Currently allocated and initialized size. */ -} tommy_arrayof; - -/** - * Initializes the array. - * \param element_size Size in byte of the element to store in the array. - */ -void tommy_arrayof_init(tommy_arrayof* array, unsigned element_size); - -/** - * Deinitializes the array. - */ -void tommy_arrayof_done(tommy_arrayof* array); - -/** - * Grow the size up to the specified value. - * All the new elements in the array are initialized with the 0 value. - */ -void tommy_arrayof_grow(tommy_arrayof* array, unsigned size); - -/** - * Gets a reference of the element at the specified position. - * You must be sure that space for this position is already - * allocated calling tommy_arrayof_grow(). - */ -tommy_inline void* tommy_arrayof_ref(tommy_arrayof* array, unsigned pos) -{ - unsigned char* base; - - assert(pos < array->size); - - /* special case for the first bucket */ - if (pos < (1 << TOMMY_ARRAYOF_BIT)) { - base = tommy_cast(unsigned char*, array->bucket[0]); - } else { - /* get the highest bit set */ - unsigned bsr = tommy_ilog2_u32(pos); - - /* clear the highest bit */ - pos -= 1 << bsr; - - base = tommy_cast(unsigned char*, array->bucket[bsr - TOMMY_ARRAYOF_BIT + 1]); - } - -// cppcheck-suppress CastIntegerToAddressAtReturn - return base + pos * array->element_size; -} - -/** - * Gets the initialized size of the array. - */ -tommy_inline unsigned tommy_arrayof_size(tommy_arrayof* array) -{ - return array->size; -} - -/** - * Gets the size of allocated memory. - */ -tommy_size_t tommy_arrayof_memory_usage(tommy_arrayof* array); - -#endif - diff -Nru librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/tommy.c librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/tommy.c --- librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/tommy.c 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/tommy.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,40 +0,0 @@ -/* - * Copyright 2010 Andrea Mazzoleni. All rights reserved. - * - * 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 ANDREA MAZZOLENI AND CONTRIBUTORS ``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 ANDREA MAZZOLENI 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 "tommyhash.c" -#include "tommyalloc.c" -#include "tommyarray.c" -#include "tommyarrayof.c" -#include "tommyarrayblk.c" -#include "tommyarrayblkof.c" -#include "tommylist.c" -#include "tommytrie.c" -#include "tommytrieinp.c" -#include "tommyhashtbl.c" -#include "tommyhashdyn.c" -#include "tommyhashlin.c" - diff -Nru librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/tommychain.h librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/tommychain.h --- librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/tommychain.h 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/tommychain.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,225 +0,0 @@ -/* - * Copyright 2010 Andrea Mazzoleni. All rights reserved. - * - * 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 ANDREA MAZZOLENI AND CONTRIBUTORS ``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 ANDREA MAZZOLENI 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. - */ - -/** \file - * Chain of nodes. - * A chain of nodes is an abstraction used to implements complex list operations - * like sorting. - * - * Do not use this directly. Use lists instead. - */ - -#ifndef __TOMMYCHAIN_H -#define __TOMMYCHAIN_H - -#include "tommytypes.h" - -/******************************************************************************/ -/* chain */ - -/** - * Chain of nodes. - * A chain of nodes is a sequence of nodes with the following properties: - * - It contains at least one node. A chains of zero nodes cannot exist. - * - The next field of the tail is of *undefined* value. - * - The prev field of the head is of *undefined* value. - * - All the other inner prev and next fields are correctly set. - */ -typedef struct tommy_chain_struct { - tommy_node* head; /**< Pointer at the head of the chain. */ - tommy_node* tail; /**< Pointer at the tail of the chain. */ -} tommy_chain; - -/** - * Splices a chain in the middle of another chain. - */ -tommy_inline void tommy_chain_splice(tommy_node* first_before, tommy_node* first_after, tommy_node* second_head, tommy_node* second_tail) -{ - /* set the prev list */ - first_after->prev = second_tail; - second_head->prev = first_before; - - /* set the next list */ - first_before->next = second_head; - second_tail->next = first_after; -} - -/** - * Concats two chains. - */ -tommy_inline void tommy_chain_concat(tommy_node* first_tail, tommy_node* second_head) -{ - /* set the prev list */ - second_head->prev = first_tail; - - /* set the next list */ - first_tail->next = second_head; -} - -/** - * Merges two chains. - */ -tommy_inline void tommy_chain_merge(tommy_chain* first, tommy_chain* second, tommy_compare_func* cmp) -{ - tommy_node* first_i = first->head; - tommy_node* second_i = second->head; - - /* merge */ - while (1) { - if (cmp(first_i->data, second_i->data) > 0) { - tommy_node* next = second_i->next; - if (first_i == first->head) { - tommy_chain_concat(second_i, first_i); - first->head = second_i; - } else { - tommy_chain_splice(first_i->prev, first_i, second_i, second_i); - } - if (second_i == second->tail) - break; - second_i = next; - } else { - if (first_i == first->tail) { - tommy_chain_concat(first_i, second_i); - first->tail = second->tail; - break; - } - first_i = first_i->next; - } - } -} - -/** - * Merges two chains managing special degenerated cases. - * It's funtionally equivalent at tommy_chain_merge() but faster with already ordered chains. - */ -tommy_inline void tommy_chain_merge_degenerated(tommy_chain* first, tommy_chain* second, tommy_compare_func* cmp) -{ - /* identify the condition first <= second */ - if (cmp(first->tail->data, second->head->data) <= 0) { - tommy_chain_concat(first->tail, second->head); - first->tail = second->tail; - return; - } - - /* identify the condition second < first */ - /* here we must be strict on comparison to keep the sort stable */ - if (cmp(second->tail->data, first->head->data) < 0) { - tommy_chain_concat(second->tail, first->head); - first->head = second->head; - return; - } - - tommy_chain_merge(first, second, cmp); -} - -/** - * Max number of elements as a power of 2. - */ -#define TOMMY_CHAIN_BIT_MAX 32 - -/** - * Sorts a chain. - * It's a stable merge sort using power of 2 buckets, with O(N*log(N)) complexity, - * similar at the one used in the SGI STL libraries and in the Linux Kernel, - * but faster on degenerated cases like already ordered lists. - * - * SGI STL stl_list.h - * http://www.sgi.com/tech/stl/stl_list.h - * - * Linux Kernel lib/list_sort.c - * http://lxr.linux.no/#linux+v2.6.36/lib/list_sort.c - */ -tommy_inline void tommy_chain_mergesort(tommy_chain* chain, tommy_compare_func* cmp) -{ - /* - * Bit buckets of chains. - * Each bucket contains 2^i nodes or it's empty. - * The chain at address TOMMY_CHAIN_BIT_MAX is an independet variable operating as "carry". - * We keep it in the same "bit" vector to avoid reports from the valgrind tool sgcheck. - */ - tommy_chain bit[TOMMY_CHAIN_BIT_MAX + 1]; - - /** - * Value stored inside the bit bucket. - * It's used to know which bucket is empty of full. - */ - unsigned counter; - tommy_node* node = chain->head; - tommy_node* tail = chain->tail; - unsigned mask; - unsigned i; - - counter = 0; - while (1) { - tommy_node* next; - tommy_chain* last; - - /* carry bit to add */ - last = &bit[TOMMY_CHAIN_BIT_MAX]; - bit[TOMMY_CHAIN_BIT_MAX].head = node; - bit[TOMMY_CHAIN_BIT_MAX].tail = node; - next = node->next; - - /* add the bit, propagating the carry */ - i = 0; - mask = counter; - while ((mask & 1) != 0) { - tommy_chain_merge_degenerated(&bit[i], last, cmp); - mask >>= 1; - last = &bit[i]; - ++i; - } - - /* copy the carry in the first empty bit */ - bit[i] = *last; - - /* add the carry in the counter */ - ++counter; - - if (node == tail) - break; - node = next; - } - - /* merge the buckets */ - i = tommy_ctz_u32(counter); - mask = counter >> i; - while (mask != 1) { - mask >>= 1; - if (mask & 1) { - tommy_chain_merge_degenerated(&bit[i+1], &bit[i], cmp); - } else { - bit[i+1] = bit[i]; - } - ++i; - } - - *chain = bit[i]; -} - -#endif - diff -Nru librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/tommy.h librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/tommy.h --- librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/tommy.h 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/tommy.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,704 +0,0 @@ -/* - * Copyright 2010 Andrea Mazzoleni. All rights reserved. - * - * 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 ANDREA MAZZOLENI AND CONTRIBUTORS ``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 ANDREA MAZZOLENI 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. - */ - -/** \mainpage - * \section Introduction - * Tommy is a C library of hashtables and tries designed to store objects with high performance. - * - * It's faster than all the similar libraries like - * rbtree, - * judy, - * googledensehash, - * googlebtree, - * stxbtree, - * khash, - * uthash, - * nedtrie, - * judyarray and others. - * - * The data structures provided are: - * - * - ::tommy_list - A double linked list. - * - ::tommy_array, ::tommy_arrayof - A linear array. - * It doesn't fragment the heap. - * - ::tommy_arrayblk, ::tommy_arrayblkof - A blocked linear array. - * It doesn't fragment the heap and minimize the space occupation. - * - ::tommy_hashtable - A fixed size chained hashtable. - * - ::tommy_hashdyn - A dynamic chained hashtable. - * - ::tommy_hashlin - A linear chained hashtable. - * It doesn't have the problem of the delay when resizing and - * it doesn't fragment the heap. - * - ::tommy_trie - A trie optimized for cache utilization. - * - ::tommy_trie_inplace - A trie completely inplace. - * - * The most interesting are ::tommy_array, ::tommy_hashdyn, ::tommy_hashlin, ::tommy_trie and ::tommy_trie_inplace. - * - * Tommy is released with a \ref license "2-clause BSD license". - * - * The official site of TommyDS is http://tommyds.sourceforge.net/, - * - * \section Use - * - * All the data structures share the same interface that needs to embedded in the object to store - * a node of type ::tommy_node. - * - * Inside this node is stored a pointer to the object itself in the tommy_node::data field, - * and the key used to identify the object in the tommy_node::key field. - * - * \code - * struct object { - * tommy_node node; - * // other fields - * int value; - * }; - * \endcode - * - * To insert an object you have to provide the address of the embedded node, the address of the - * object and the value of the key. - * \code - * struct object* obj = malloc(sizeof(struct object)); - * - * tommy_trie_insert(..., &obj->node, obj, obj->value); - * \endcode - * - * To search an object you have to provide the key and call the search function. - * \code - * int value_to_find = 1; - * struct object* obj = tommy_trie_search(..., value_to_find); - * if (!obj) { - * // not found - * } else { - * // found - * } - * \endcode - * - * To access all the objects with the same keys you have to iterate over the bucket - * assigned at the specified key. - * \code - * int value_to_find = 1; - * tommy_trie_node* i = tommy_trie_bucket(..., value_to_find); - * - * while (i) { - * struct object* obj = i->data; // gets the object pointer - * - * printf("%d\n", obj->value); // process the object - * - * i = i->next; // goes to the next element - * } - * \endcode - * - * To remove an object you have to provide the key and call the remove function. - * \code - * struct object* obj = tommy_trie_remove(..., value); - * if (obj) { - * free(obj); // frees the object allocated memory - * } - * \endcode - * - * Dealing with hashtables, instead of the key, you have to provide the hash value of the object, - * and a compare function able to differentiate objects with the same hash value. - * - * To compute the hash value, you can use the generic tommy_hash_u32() function, or the - * specialized integer hash function tommy_inthash_u32(). - * - * \section Performance - * Here you can see some timings compared to other common implementations in Hit - * and Change performance. Hit is searching an object with success, and Change is searching, removing - * and reinsert it with a different key value. - * - * Times are expressed in nanoseconds for element, and lower is better. - * - * A complete analysis is available in the \ref benchmark page. - * - * - * - * - * - * \section Features - * - * Tommy is 100% portable in all the platforms and operating systems. - * - * Tommy containers support multiple elements with the same key. - * - * \section Limitations - * - * Tommy is not thread safe. You have always to provide thread safety using - * locks before calling any Tommy functions. - * - * Tommy doesn't provide iterators over the implicit order defined by the data - * structures. To iterate on elements you must insert them also into a ::tommy_list, - * and use the list as iterator. See the \ref multiindex example for more details. - * Note that this is a real limitation only for ::tommy_trie, as it's the only - * data structure defining an useable order. - * - * Tommy doesn't provide an error reporting mechanism for a malloc() failure. - * You have to provide it redefining malloc() if you expect it to fail. - * - * Tommy assumes to never have more than 2^32 elements in a container. - * - * \page benchmark Tommy Benchmarks - * - * To evaluate Tommy performances, an extensive benchmark was done, - * comparing it to the best libraries of data structures available: - * - * Specifically we have tested: - * - ::tommy_hashtable - Fixed size chained hashtable. - * - ::tommy_hashdyn - Dynamic chained hashtable. - * - ::tommy_hashlin - Linear chained hashtable. - * - ::tommy_trie - Trie optimized for cache usage. - * - ::tommy_trie_inplace - Trie completely inplace. - * - rbtree - Red-black tree by Jason Evans. - * - nedtrie - Binary trie inplace by Niall Douglas. - * - khash - Dynamic open addressing hashtable by Attractive Chaos. - * - uthash - Dynamic chaining hashtable by Troy D. Hanson. - * - judy - Burst trie (JudyL) by Doug Baskins. - * - judyarray - Burst trie by Karl Malbrain. - * - googledensehash - Dynamic open addressing hashtable by Craig Silverstein at Google. - * - googlebtree - Btree by Google. - * - stxbtree - STX Btree by Timo Bingmann. - * - c++unordered_map - C++ STL unordered_map<> template. - * - c++map - C++ STL map<> template. - * - * \section thebenchmark The Benchmark - * - * The benchmark consists in storing a set of N pointers to objects and - * searching them using an integer key. - * - * It's different than the simpler case of mapping integers to integers, - * as pointers to objects are also dereferenced resulting in additional cache - * misses. This benchmark gives a boost to implementations that store information - * in the objects itself, as the additional cache misses are already implicit. - * - * The test done are: - * - Insert Insert all the objects starting with an empty container. - * - Change Find and remove one object and reinsert it with a different key, repeated for all the objects. - * - Hit Find with success all the objects and derefence them. - * - Miss Find with failure all the objects. - * - Remove Remove all the objects and dereference them. - * - * The Change, Hit and Miss tests operate always with N - * objects in the containers. - * The Insert test starts with an empty container, and the Remove - * test ends with an empty container. - * The objects are always dereferenced, as we are supposing to use them. This - * happens even in the remove case, as we are supposing to deallocate them. - * - * All the objects are preallocated in the heap, and this allocation time is not - * included in the test. - * - * The objects contain an integer value field used for consistency checks, - * an unused payload field of 16 bytes, and any other data required by the - * data structure. - * - * The objects are identified and stored using integer and unique keys. - * The key domain used is dense, and it's defined by the set - * of N even numbers starting from 0x80000000 to 0x80000000+2*N. - * - * The use of only even numbers allows to have missing keys inside the domain for - * the Change test. - * In such tests it's used the key domain defined by the set of N odd numbers - * starting from 0x80000000+1 to 0x80000000+2*N+1. - * Note that using additional keys at the corners of the domain would have pushed - * tries and trees as they implicitly keep track of the maximum and minimum key values - * inserted. - * - * The use of the 0x80000000 base, allow to test a key domain not necessarily - * starting at 0. Using a 0 base would have pushed some tries managing it as - * a special case. - * - * The tests are repeated using keys in Random mode and in Forward mode. - * In the forward mode the key values are used in order from the lowest to the highest. - * In the random mode the key values are used in a completely random order. - * In the Change test in forward mode, each object is reinserted using the previous - * key incremented by 1. In random mode each object is reinserted using a completely - * different and uncorrelated key. - * - * The forward order pushes tries and trees as they use the key directly and they have a - * cache advantage on using consecutive keys. - * The random order pushes hashtables, as the hash function already randomizes the key. - * Usually real uses case are in between, and the random one is the worst one. - * - * \section result Results - * - * The most significant tests depend on your data usage model, but if in doubt, - * you should mostly look at Random Hit and Random Change. - * - * They are the most significant, because operating always with N elements - * in the data structure and with a random patterns. - * They represent the real world worst condition. - * - * - * - * In the Random Hit graph you can see a vertical split at the 100.000 elements limit. - * Before this limit the cache of modern processor is able to contains most of the data, and it allow a very fast access with almost all data structures. - * After this limit, the number of cache misses is the dominant factor, and the curve depends mainly on the number of cache-miss - * required. - * - * For rbtree and nedtrie, it's log2(N) as they have two branches on each node, log4(N) for ::tommy_trie_inplace, log8(N) for ::tommy_trie and 1 for hashtables. - * For ::tommy_trie_inplace and ::tommy_trie you can change the slope configuring a different number of branches for node. - * - * - * - * The Random Change graph confirms the vertical split at the 100.000 elements limit. - * It also show that hashtables are almost unbeatable with a random access. - * - * \section random Random order - * Here you can see the whole Random test results in different platforms. - * - * In the Random test, hashtables are almost always winning, seconds are - * tries, and as last trees. - * - * The best choices are ::tommy_hashdyn, ::tommy_hashlin, and googledensehash, with - * ::tommy_hashlin having the advantage to be real-time friendly and not - * increasing the heap fragmentation. - * - * - *
- * - * - * - *
- * - * - * - *
- * - * - * - *
- * - * - * - *
- * - * - * - *
- * - * \section forward Forward order - * Here you can see the whole Forward test results in different platforms. - * - * In the Forward test, tries are the winners. Hashtables are competitive - * until the cache limit, then they lose against tries. Trees are the slowest. - * - * The best choices are ::tommy_trie and ::tommy_trie_inplace, where ::tommy_trie is - * a bit faster, and ::tommy_trie_inplace doesn't require a custom allocator. - * - * Note that also hashtables are faster in forward order than random. This may - * seem a bit surprising as the hash function randomizes the access even with - * consecutive keys. This happens because the objects are allocated in consecutive - * memory, and accessing them in order, improves the cache utilization, even if - * the hashed key is random. - * - * Note that you can make hashtables to reach tries performance tweaking - * the hash function to put near keys allocated nearby. - * This is possible if you know in advance the distribution of keys. - * For example, in the benchmark you could use something like: - * \code - * #define hash(v) tommy_inthash32(v & ~0xF) + (v & 0xF) - * \endcode - * and make keys that differ only by the lowest bits - * to have hashes with the same property, resulting in - * objects stored nearby, and improving cache utilization. - * - * - * - *
- * - * - * - *
- * - * - * - *
- * - * - * - *
- * - * - * - *
- * - * - * - *
- * - * \section size Size - * Here you can see the memory usage of the different data structures. - * - * - *
- * - * - * - *
- * - * \section code Code - * - * The compilers used in the benchmark are: - * - gcc 4.7.1 in Linux with options: -O3 -march=pentium4 -mtune=generic - * - Visual C 2012 in Windows with options: /Ox /GL /GS- - * - * The following is pseudo code of the benchmark used. In this case it's written for the C++ unordered_map. - * - * \code - * #define N 10000000 // Number of elements - * #define PAYLOAD 16 // Size of the object - * - * // Basic object inserted in the colletion - * struct obj { - * unsigned value; // Key used for searching - * char payload[PAYLOAD]; - * }; - * - * // Custom hash function to avoid to use the STL one - * class custom_hash { - * public: - * unsigned operator()(unsigned key) const { return tommy_inthash_u32(key); } - * }; - * - * // Map collection from "unsigned" to "pointer to object" - * typedef std::unordered_map bag_t; - * bag_t bag; - * - * // Preallocate objects - * obj* OBJ = new obj[N]; - * - * // Keys used for inserting and searching elements - * unsigned INSERT[N]; - * unsigned SEARCH[N]; - * - * // Initialize the keys - * for(i=0;ivalue = key; - * - * // Insert it - * bag[key] = element; - * } - * \endcode - * - * \subsection change Change benchmark - * \code - * for(i=0;isecond; - * bag.erase(j); - * - * // Reinsert the element with a new key - * // Use +1 in the key to ensure that the new key is unique - * key = INSERT[i] + 1; - * element->value = key; - * bag[key] = element; - * } - * \endcode - * - * \subsection hit Hit benchmark - * \code - * for(i=0;isecond; - * if (element->value != key) - * abort(); - * } - * \endcode - * - * \subsection miss Miss benchmark - * \code - * for(i=0;isecond; - * if (element->value != key) - * abort(); - * } - * \endcode - * - * \section others Other benchmarks - * Here some links to other performance comparison: - * - * Comparison of Hash Table Libraries - * - * Hash Table Benchmarks - * - * \section notes Notes - * - * Here some notes about the data structure tested not part of Tommy. - * - * \subsection cgoogledensehash Google C densehash - * It's the C implementation located in the experimental/ directory of the googlesparsehash archive. - * It has very bad performances in the Change test for some N values. - * See for example this graph with a lot of spikes. - * The C++ version doesn't suffer of this problem. - * - * \subsection googledensehash Google C++ densehash - * It doesn't release memory on deletion. - * To avoid an unfair advantage in the Remove test, we force a periodic - * resize calling resize(0) after any deallocation. - * The resize is executed when the load factor is lower than 20%. - * - * \subsection khash khash - * It doesn't release memory on deletion. This gives an unfair advantage on the Remove test. - * - * \subsection nedtrie nedtrie - * I've found a crash bug when inserting keys with the 0 value. - * The fix of this issue is now in the nedtries github. - * We do not use the C++ implementation as it doesn't compile with gcc 4.4.3. - * - * \subsection judy Judy - * Sometimes it has bad performances in some specific platform - * and for some specific input data size. - * This makes difficult to predict the performance, as it is usually good until - * you get one of these cases. - * See for example this graph with a big replicable spike at 50.000 elements. - * - * \page multiindex Tommy Multi Indexing - * - * Tommy doesn't provide iterators support to iterate over all the elements inside - * a data structure. To do that you have to manually insert any objects also in a - * ::tommy_list, and use the list as iterator. - * - * This technique allows to keep track of the insertion order with the list, - * and provide more search possibilities using different data structures for - * different search keys. - * - * See the next example, for an object that is inserted in a ::tommy_list, and in - * two ::tommy_hashdyn using different keys. - * - * \code - * struct object { - * // data fields - * int value_0; - * int value_1; - * - * // for containers - * tommy_node list_node; // node for the list - * tommy_node hash_node_0; // node for the first hash - * tommy_node hash_node_1; // node for the second hash - * }; - * - * // search function for value_1 - * int search_1(const void* arg, const void* obj) - * { - * return *(const int*)arg != ((const struct object*)obj)->value_1; - * } - * - * tommy_hashdyn hash_0; - * tommy_hashdyn hash_1; - * tommy_list list; - * - * // initializes the the hash tables and the list - * tommy_hashdyn_init(&hash_0); - * tommy_hashdyn_init(&hash_1); - * tommy_list_init(&list); - * - * ... - * - * // creates an object and inserts it - * struct object* obj = malloc(sizeof(struct object)); - * obj->value_0 = ...; - * obj->value_1 = ...; - * tommy_hashdyn_insert(&hash_0, &obj->hash_node_0, obj, tommy_inthash_u32(obj->value0)); // inserts in the first hash table - * tommy_hashdyn_insert(&hash_1, &obj->hash_node_1, obj, tommy_inthash_u32(obj->value1)); // inserts in the second hash table - * tommy_list_insert_tail(&list, &obj->list_node, obj); // inserts in the list - * - * ... - * - * // searches an object by value_1 and deletes it - * int value_to_find = ...; - * struct object* obj = tommy_hashdyn_search(&hash_1, search_1, &value_to_find, tommy_inthash_u32(value_to_find)); - * if (obj) { - * // if found removes all the references - * tommy_hashdyn_remove_existing(&hash_0, &obj->hash_node_0); - * tommy_hashdyn_remove_existing(&hash_1, &obj->hash_node_1); - * tommy_list_remove_existing(&list, &obj->list_node); - * } - * - * ... - * - * // deallocates all the objects iterating the list - * tommy_list_foreach(&list, free); - * - * // deallocates all the hash tables - * tommy_hashdyn_done(&hash_0); - * tommy_hashdyn_done(&hash_1); - * \endcode - * - * \page design Tommy Design - * - * Tommy is mainly designed to provide high performance, but also much care was - * given in the definition of an useable API. In some case, even at the cost of - * some efficency. - * - * \section datapointer Data pointer - * The tommy_node::data field is present only to provide a simpler API. - * - * A more memory conservative approach is to do not store this pointer, and - * computing it from the node pointer every time considering that they are - * always at a constant offset. - * - * See for example the Linux Kernel declaration of container_of() at - * http://lxr.linux.no/#linux+v2.6.37/include/linux/kernel.h#L526 - * - * Although, it would have required more complexity for the user to require - * a manual conversion from a node to the object containing the node. - * - * \section zero_list Zero terminated next list - * The half 0 terminated format of ::tommy_list is present only to provide - * a forward iterator terminating in 0. - * - * A more efficient approach is to use a double circular list, as operating on - * nodes in a circular list doesn't requires to manage the special terminating - * case. - * - * Although, it would have required too much complexity at the user for a simple - * iteration. - * - * \section double_linked Double linked list for collisions - * The linked list used for collision is a double linked list to allow - * insertion of elements at the end of the list to keep the insertion order - * of equal elements. - * - * A more memory conservative approach is to use a single linked list, - * inserting elements only at the start of the list. - * On the other hand, with with a double linked list we can concatenate - * two lists in constant time, as using the previous circular element we - * can get a tail pointer. - * - * \page license Tommy License - * Tommy is released with a 2-clause BSD license. - * - * \code - * Copyright 2010 Andrea Mazzoleni. All rights reserved. - * - * 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 ANDREA MAZZOLENI AND CONTRIBUTORS ``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 ANDREA MAZZOLENI 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. - * \endcode - */ - -/** \file - * All in one include for Tommy. - */ - -#ifdef __cplusplus -extern "C" { -#endif - -#include "tommytypes.h" -#include "tommyhash.h" -#include "tommyalloc.h" -#include "tommyarray.h" -#include "tommyarrayof.h" -#include "tommyarrayblk.h" -#include "tommyarrayblkof.h" -#include "tommylist.h" -#include "tommytrie.h" -#include "tommytrieinp.h" -#include "tommyhashtbl.h" -#include "tommyhashdyn.h" -#include "tommyhashlin.h" - -#ifdef __cplusplus -} -#endif - diff -Nru librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/tommyhash.c librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/tommyhash.c --- librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/tommyhash.c 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/tommyhash.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,167 +0,0 @@ -/* - * Copyright 2010 Andrea Mazzoleni. All rights reserved. - * - * 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 ANDREA MAZZOLENI AND CONTRIBUTORS ``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 ANDREA MAZZOLENI 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 "tommyhash.h" - -/******************************************************************************/ -/* hash */ - -tommy_inline tommy_uint32_t tommy_le_uint32_read(const void* ptr) -{ - /* allow unaligned read on Intel x86 and x86_64 platforms */ -#if defined(__i386__) || defined(_M_IX86) || defined(_X86_) || defined(__x86_64__) || defined(_M_X64) - /* defines from http://predef.sourceforge.net/ */ - return *(tommy_uint32_t*)ptr; -#else - const unsigned char* ptr8 = tommy_cast(const unsigned char*, ptr); - return ptr8[0] + ((tommy_uint32_t)ptr8[1] << 8) + ((tommy_uint32_t)ptr8[2] << 16) + ((tommy_uint32_t)ptr8[3] << 24); -#endif -} - -#define tommy_rot(x,k) \ - (((x)<<(k)) | ((x)>>(32-(k)))) - -#define tommy_mix(a,b,c) \ - do { \ - a -= c; a ^= tommy_rot(c, 4); c += b; \ - b -= a; b ^= tommy_rot(a, 6); a += c; \ - c -= b; c ^= tommy_rot(b, 8); b += a; \ - a -= c; a ^= tommy_rot(c,16); c += b; \ - b -= a; b ^= tommy_rot(a,19); a += c; \ - c -= b; c ^= tommy_rot(b, 4); b += a; \ - } while (0) - -#define tommy_final(a,b,c) \ - do { \ - c ^= b; c -= tommy_rot(b,14); \ - a ^= c; a -= tommy_rot(c,11); \ - b ^= a; b -= tommy_rot(a,25); \ - c ^= b; c -= tommy_rot(b,16); \ - a ^= c; a -= tommy_rot(c,4); \ - b ^= a; b -= tommy_rot(a,14); \ - c ^= b; c -= tommy_rot(b,24); \ - } while (0) - -tommy_uint32_t tommy_hash_u32(tommy_uint32_t init_val, const void* void_key, tommy_size_t key_len) -{ - const unsigned char* key = tommy_cast(const unsigned char*,void_key); - tommy_uint32_t a, b, c; - - a = b = c = 0xdeadbeef + ((tommy_uint32_t)key_len) + init_val; - - while (key_len > 12) { - a += tommy_le_uint32_read(key + 0); - b += tommy_le_uint32_read(key + 4); - c += tommy_le_uint32_read(key + 8); - - tommy_mix(a,b,c); - - key_len -= 12; - key += 12; - } - - switch (key_len) { - case 0 : - return c; /* used only when called with a zero length */ - case 12 : - c += tommy_le_uint32_read(key + 8); - b += tommy_le_uint32_read(key + 4); - a += tommy_le_uint32_read(key + 0); - break; - case 11 : c += ((tommy_uint32_t)key[10]) << 16; - case 10 : c += ((tommy_uint32_t)key[9]) << 8; - case 9 : c += key[8]; - case 8 : - b += tommy_le_uint32_read(key + 4); - a += tommy_le_uint32_read(key + 0); - break; - case 7 : b += ((tommy_uint32_t)key[6]) << 16; - case 6 : b += ((tommy_uint32_t)key[5]) << 8; - case 5 : b += key[4]; - case 4 : - a += tommy_le_uint32_read(key + 0); - break; - case 3 : a += ((tommy_uint32_t)key[2]) << 16; - case 2 : a += ((tommy_uint32_t)key[1]) << 8; - case 1 : a += key[0]; - } - - tommy_final(a,b,c); - - return c; -} - -tommy_uint64_t tommy_hash_u64(tommy_uint64_t init_val, const void* void_key, tommy_size_t key_len) -{ - const unsigned char* key = tommy_cast(const unsigned char*,void_key); - tommy_uint32_t a, b, c; - - a = b = c = 0xdeadbeef + ((tommy_uint32_t)key_len) + (init_val & 0xffffffff); - c += init_val >> 32; - - while (key_len > 12) { - a += tommy_le_uint32_read(key + 0); - b += tommy_le_uint32_read(key + 4); - c += tommy_le_uint32_read(key + 8); - - tommy_mix(a,b,c); - - key_len -= 12; - key += 12; - } - - switch (key_len) { - case 0 : - return c + ((tommy_uint64_t)b << 32); /* used only when called with a zero length */ - case 12 : - c += tommy_le_uint32_read(key + 8); - b += tommy_le_uint32_read(key + 4); - a += tommy_le_uint32_read(key + 0); - break; - case 11 : c += ((tommy_uint32_t)key[10]) << 16; - case 10 : c += ((tommy_uint32_t)key[9]) << 8; - case 9 : c += key[8]; - case 8 : - b += tommy_le_uint32_read(key + 4); - a += tommy_le_uint32_read(key + 0); - break; - case 7 : b += ((tommy_uint32_t)key[6]) << 16; - case 6 : b += ((tommy_uint32_t)key[5]) << 8; - case 5 : b += key[4]; - case 4 : - a += tommy_le_uint32_read(key + 0); - break; - case 3 : a += ((tommy_uint32_t)key[2]) << 16; - case 2 : a += ((tommy_uint32_t)key[1]) << 8; - case 1 : a += key[0]; - } - - tommy_final(a,b,c); - - return c + ((tommy_uint64_t)b << 32); -} - diff -Nru librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/tommyhashdyn.c librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/tommyhashdyn.c --- librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/tommyhashdyn.c 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/tommyhashdyn.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,192 +0,0 @@ -/* - * Copyright 2010 Andrea Mazzoleni. All rights reserved. - * - * 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 ANDREA MAZZOLENI AND CONTRIBUTORS ``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 ANDREA MAZZOLENI 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 "tommyhashdyn.h" -#include "tommylist.h" - -#include /* for memset */ - -/******************************************************************************/ -/* hashdyn */ - -void tommy_hashdyn_init(tommy_hashdyn* hashdyn) -{ - /* fixed initial size */ - hashdyn->bucket_bit = TOMMY_HASHDYN_BIT; - hashdyn->bucket_max = 1 << hashdyn->bucket_bit; - hashdyn->bucket_mask = hashdyn->bucket_max - 1; - hashdyn->bucket = tommy_cast(tommy_hashdyn_node**, tommy_malloc(hashdyn->bucket_max * sizeof(tommy_hashdyn_node*))); - memset(hashdyn->bucket, 0, hashdyn->bucket_max * sizeof(tommy_hashdyn_node*)); - - hashdyn->count = 0; -} - -void tommy_hashdyn_done(tommy_hashdyn* hashdyn) -{ - tommy_free(hashdyn->bucket); -} - -/** - * Resize the bucket vector. - */ -static void tommy_hashdyn_resize(tommy_hashdyn* hashdyn, unsigned new_bucket_bit) -{ - unsigned bucket_bit; - unsigned bucket_max; - unsigned new_bucket_max; - unsigned new_bucket_mask; - tommy_hashdyn_node** new_bucket; - - bucket_bit = hashdyn->bucket_bit; - bucket_max = hashdyn->bucket_max; - - new_bucket_max = 1 << new_bucket_bit; - new_bucket_mask = new_bucket_max - 1; - new_bucket = tommy_cast(tommy_hashdyn_node**, tommy_malloc(new_bucket_max * sizeof(tommy_hashdyn_node*))); - - /* reinsert all the elements */ - if (new_bucket_bit > bucket_bit) { - unsigned i; - - /* grow */ - for(i=0;ibucket[i]; - while (j) { - tommy_hashdyn_node* j_next = j->next; - unsigned index = j->key & new_bucket_mask; - if (new_bucket[index]) - tommy_list_insert_tail_not_empty(new_bucket[index], j); - else - tommy_list_insert_first(&new_bucket[index], j); - j = j_next; - } - } - } else { - unsigned i; - - /* shrink */ - for(i=0;ibucket[i]; - - /* concat the upper bucket */ - tommy_list_concat(&new_bucket[i], &hashdyn->bucket[i + new_bucket_max]); - } - } - - tommy_free(hashdyn->bucket); - - /* setup */ - hashdyn->bucket_bit = new_bucket_bit; - hashdyn->bucket_max = new_bucket_max; - hashdyn->bucket_mask = new_bucket_mask; - hashdyn->bucket = new_bucket; -} - -/** - * Grow. - */ -tommy_inline void hashdyn_grow_step(tommy_hashdyn* hashdyn) -{ - /* grow if more than 50% full */ - if (hashdyn->count >= hashdyn->bucket_max / 2) { - tommy_hashdyn_resize(hashdyn, hashdyn->bucket_bit + 1); - } -} - -/** - * Shrink. - */ -tommy_inline void hashdyn_shrink_step(tommy_hashdyn* hashdyn) -{ - /* shrink if less than 12.5% full */ - if (hashdyn->count <= hashdyn->bucket_max / 8 && hashdyn->bucket_bit > TOMMY_HASHDYN_BIT) { - tommy_hashdyn_resize(hashdyn, hashdyn->bucket_bit - 1); - } -} - -void tommy_hashdyn_insert(tommy_hashdyn* hashdyn, tommy_hashdyn_node* node, void* data, tommy_hash_t hash) -{ - unsigned pos = hash & hashdyn->bucket_mask; - - tommy_list_insert_tail(&hashdyn->bucket[pos], node, data); - - node->key = hash; - - ++hashdyn->count; - - hashdyn_grow_step(hashdyn); -} - -void* tommy_hashdyn_remove_existing(tommy_hashdyn* hashdyn, tommy_hashdyn_node* node) -{ - unsigned pos = node->key & hashdyn->bucket_mask; - - tommy_list_remove_existing(&hashdyn->bucket[pos], node); - - --hashdyn->count; - - hashdyn_shrink_step(hashdyn); - - return node->data; -} - -void* tommy_hashdyn_remove(tommy_hashdyn* hashdyn, tommy_search_func* cmp, const void* cmp_arg, tommy_hash_t hash) -{ - unsigned pos = hash % hashdyn->bucket_max; - tommy_hashdyn_node* i = hashdyn->bucket[pos]; - - while (i) { - /* we first check if the hash matches, as in the same bucket we may have multiples hash values */ - if (i->key == hash && cmp(cmp_arg, i->data) == 0) { - tommy_list_remove_existing(&hashdyn->bucket[pos], i); - - --hashdyn->count; - - hashdyn_shrink_step(hashdyn); - - return i->data; - } - i = i->next; - } - - return 0; -} - -tommy_size_t tommy_hashdyn_memory_usage(tommy_hashdyn* hashdyn) -{ - return hashdyn->bucket_max * (tommy_size_t)sizeof(hashdyn->bucket[0]) - + tommy_hashdyn_count(hashdyn) * (tommy_size_t)sizeof(tommy_hashdyn_node); -} - diff -Nru librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/tommyhashdyn.h librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/tommyhashdyn.h --- librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/tommyhashdyn.h 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/tommyhashdyn.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,255 +0,0 @@ -/* - * Copyright 2010 Andrea Mazzoleni. All rights reserved. - * - * 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 ANDREA MAZZOLENI AND CONTRIBUTORS ``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 ANDREA MAZZOLENI 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. - */ - -/** \file - * Dynamic chained hashtable. - * - * This hashtable resizes dynamically. It starts with the minimal size of 16 buckets, it doubles - * the size then it reaches a load factor greater than 0.5 and it halves the size with a load - * factor lower than 0.125. - * - * All the elements are reallocated in a single resize operation done inside - * tommy_hashdyn_insert() or tommy_hashdyn_remove(). - * - * Note that the resize operation takes approximatively 100 [ms] with 1 million of elements, - * and 1 [second] with 10 millions. This could be a problem in real-time applications. - * - * The resize also fragment the heap, as it involves allocating a double-sized table, copy elements, - * and deallocating the older table. Leaving a big hole in the heap. - * - * The ::tommy_hashlin hashtable fixes both problems. - * - * To initialize the hashtable you have to call tommy_hashdyn_init(). - * - * \code - * tommy_hashslin hashdyn; - * - * tommy_hashdyn_init(&hashdyn); - * \endcode - * - * To insert elements in the hashtable you have to call tommy_hashdyn_insert() for - * each element. - * In the insertion call you have to specify the address of the node, the - * address of the object, and the hash value of the key to use. - * The address of the object is used to initialize the tommy_node::data field - * of the node, and the hash to initialize the tommy_node::key field. - * - * \code - * struct object { - * tommy_node node; - * // other fields - * int value; - * }; - * - * struct object* obj = malloc(sizeof(struct object)); // creates the object - * - * obj->value = ...; // initializes the object - * - * tommy_hashdyn_insert(&hashdyn, &obj->node, obj, tommy_inthash_u32(obj->value)); // inserts the object - * \endcode - * - * To find and element in the hashtable you have to call tommy_hashtable_search() - * providing a comparison function, its argument, and the hash of the key to search. - * - * \code - * int compare(const void* arg, const void* obj) - * { - * return *(const int*)arg != ((const struct object*)obj)->value; - * } - * - * int value_to_find = 1; - * struct object* obj = tommy_hashdyn_search(&hashdyn, compare, &value_to_find, tommy_inthash_u32(value_to_find)); - * if (!obj) { - * // not found - * } else { - * // found - * } - * \endcode - * - * To iterate over all the elements in the hashtable with the same key, you have to - * use tommy_hashdyn_bucket() and follow the tommy_node::next pointer until NULL. - * You have also to check explicitely for the key, as the bucket may contains - * different keys. - * - * \code - * int value_to_find = 1; - * tommy_node* i = tommy_hashdyn_bucket(&hashdyn, tommy_inthash_u32(value_to_find)); - * while (i) { - * struct object* obj = i->data; // gets the object pointer - * - * if (obj->value == value_to_find) { - * printf("%d\n", obj->value); // process the object - * } - * - * i = i->next; // goes to the next element - * } - * \endcode - * - * To remove an element from the hashtable you have to call tommy_hashdyn_remove() - * providing a comparison function, its argument, and the hash of the key to search - * and remove. - * - * \code - * struct object* obj = tommy_trie_remove(&hashtable, compare, &value_to_remove, tommy_inthash_u32(value_to_remove)); - * if (obj) { - * free(obj); // frees the object allocated memory - * } - * \endcode - * - * To destroy the hashtable you have to remove all the elements, and deinitialize - * the hashtable calling tommy_hashdyn_done(). - * - * \code - * tommy_hashdyn_done(&hashdyn); - * \endcode - * - * Note that you cannot iterates over all the elements in the hashtable using the - * hashtable itself. You have to insert all the elements also in a ::tommy_list, - * and use the list to iterate. See the \ref multiindex example for more detail. - */ - -#ifndef __TOMMYHASHDYN_H -#define __TOMMYHASHDYN_H - -#include "tommyhash.h" - -/******************************************************************************/ -/* hashdyn */ - -/** \internal - * Initial and minimal size of the hashtable expressed as a power of 2. - * The initial size is 2^TOMMY_HASHDYN_BIT. - */ -#define TOMMY_HASHDYN_BIT 4 - -/** - * Dynamic hashtable node. - * This is the node that you have to include inside your objects. - */ -typedef tommy_node tommy_hashdyn_node; - -/** - * Dynamic chained hashtable. - */ -typedef struct tommy_hashdyn_struct { - tommy_hashdyn_node** bucket; /**< Hash buckets. One list for each hash modulus. */ - unsigned bucket_bit; /**< Bits used in the bit mask. */ - unsigned bucket_max; /**< Number of buckets. */ - unsigned bucket_mask; /**< Bit mask to access the buckets. */ - unsigned count; /**< Number of elements. */ -} tommy_hashdyn; - -/** - * Initializes the hashtable. - */ -void tommy_hashdyn_init(tommy_hashdyn* hashdyn); - -/** - * Deinitializes the hashtable. - * - * You can call this function with elements still contained, - * but such elements are not going to be freed by this call. - */ -void tommy_hashdyn_done(tommy_hashdyn* hashdyn); - -/** - * Inserts an element in the hashtable. - */ -void tommy_hashdyn_insert(tommy_hashdyn* hashdyn, tommy_hashdyn_node* node, void* data, tommy_hash_t hash); - -/** - * Searches and removes an element from the hashtable. - * You have to provide a compare function and the hash of the element you want to remove. - * If the element is not found, 0 is returned. - * If more equal elements are present, the first one is removed. - * This operation is faster than calling tommy_hashdyn_bucket() and tommy_hashdyn_remove_existing() separately. - * \param cmp Compare function called with cmp_arg as first argument and with the element to compare as a second one. - * The function should return 0 for equal elements, anything other for different elements. - * \param cmp_arg Compare argument passed as first argument of the compare function. - * \param hash Hash of the element to find and remove. - * \return The removed element, or 0 if not found. - */ -void* tommy_hashdyn_remove(tommy_hashdyn* hashdyn, tommy_search_func* cmp, const void* cmp_arg, tommy_hash_t hash); - -/** - * Gets the bucket of the specified hash. - * The bucket is guaranteed to contain ALL the elements with the specified hash, - * but it can contain also others. - * You can access elements in the bucket following the ::next pointer until 0. - * \param hash Hash of the element to find. - * \return The head of the bucket, or 0 if empty. - */ -tommy_inline tommy_hashdyn_node* tommy_hashdyn_bucket(tommy_hashdyn* hashdyn, tommy_hash_t hash) -{ - return hashdyn->bucket[hash & hashdyn->bucket_mask]; -} - -/** - * Searches an element in the hashtable. - * You have to provide a compare function and the hash of the element you want to find. - * If more equal elements are present, the first one is returned. - * \param cmp Compare function called with cmp_arg as first argument and with the element to compare as a second one. - * The function should return 0 for equal elements, anything other for different elements. - * \param cmp_arg Compare argument passed as first argument of the compare function. - * \param hash Hash of the element to find. - * \return The first element found, or 0 if none. - */ -tommy_inline void* tommy_hashdyn_search(tommy_hashdyn* hashdyn, tommy_search_func* cmp, const void* cmp_arg, tommy_hash_t hash) -{ - tommy_hashdyn_node* i = tommy_hashdyn_bucket(hashdyn, hash); - while (i) { - /* we first check if the hash matches, as in the same bucket we may have multiples hash values */ - if (i->key == hash && cmp(cmp_arg, i->data) == 0) - return i->data; - i = i->next; - } - return 0; -} - -/** - * Removes an element from the hashtable. - * You must already have the address of the element to remove. - * \return The tommy_node::data field of the node removed. - */ -void* tommy_hashdyn_remove_existing(tommy_hashdyn* hashdyn, tommy_hashdyn_node* node); - -/** - * Gets the number of elements. - */ -tommy_inline unsigned tommy_hashdyn_count(tommy_hashdyn* hashdyn) -{ - return hashdyn->count; -} - -/** - * Gets the size of allocated memory. - * It includes the size of the ::tommy_hashdyn_node of the stored elements. - */ -tommy_size_t tommy_hashdyn_memory_usage(tommy_hashdyn* hashdyn); - -#endif - diff -Nru librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/tommyhash.h librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/tommyhash.h --- librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/tommyhash.h 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/tommyhash.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,112 +0,0 @@ -/* - * Copyright 2010 Andrea Mazzoleni. All rights reserved. - * - * 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 ANDREA MAZZOLENI AND CONTRIBUTORS ``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 ANDREA MAZZOLENI 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. - */ - -/** \file - * Hash functions for the use with ::tommy_hashtable, ::tommy_hashdyn and ::tommy_hashlin. - */ - -#ifndef __TOMMYHASH_H -#define __TOMMYHASH_H - -#include "tommytypes.h" - -/******************************************************************************/ -/* hash */ - -/** - * Hash type used in hashtables. - */ -typedef tommy_key_t tommy_hash_t; - -/** - * Hash function with a 32 bits result. - * Implementation of the Robert Jenkins "lookup3" hash 32 bits version, - * from http://www.burtleburtle.net/bob/hash/doobs.html, function hashlittle(). - * \param init_val Initialization value. - * Using a different initialization value, you can generate a completely different set of hash values. - * Use 0 if not relevalt. - * \param void_key Pointer at the data to hash. - * \param key_len Size of the data to hash. - * \note - * This function is endianess independent. - * \return The hash value of 32 bits. - */ -tommy_uint32_t tommy_hash_u32(tommy_uint32_t init_val, const void* void_key, tommy_size_t key_len); - -/** - * Hash function with a 64 bits result. - * Implementation of the Robert Jenkins "lookup3" hash 64 bits versions, - * from http://www.burtleburtle.net/bob/hash/doobs.html, function hashlittle2(). - * \param init_val Initialization value. - * Using a different initialization value, you can generate a completely different set of hash values. - * Use 0 if not relevalt. - * \param void_key Pointer at the data to hash. - * \param key_len Size of the data to hash. - * \note - * This function is endianess independent. - * \return The hash value of 64 bits. - */ -tommy_uint64_t tommy_hash_u64(tommy_uint64_t init_val, const void* void_key, tommy_size_t key_len); - -/** - * Integer hash of 32 bits. - * Implementation of the Robert Jenkins "4-byte Integer Hashing", - * from http://burtleburtle.net/bob/hash/integer.html - */ -tommy_inline tommy_uint32_t tommy_inthash_u32(tommy_uint32_t key) -{ - key -= key << 6; - key ^= key >> 17; - key -= key << 9; - key ^= key << 4; - key -= key << 3; - key ^= key << 10; - key ^= key >> 15; - - return key; -} - -/** - * Integer hash of 64 bits. - * Implementation of the Thomas Wang "Integer Hash Function", - * from http://www.cris.com/~Ttwang/tech/inthash.htm - */ -tommy_inline tommy_uint64_t tommy_inthash_u64(tommy_uint64_t key) -{ - key = ~key + (key << 21); - key = key ^ (key >> 24); - key = key + (key << 3) + (key << 8); - key = key ^ (key >> 14); - key = key + (key << 2) + (key << 4); - key = key ^ (key >> 28); - key = key + (key << 31); - - return key; -} - -#endif - diff -Nru librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/tommyhashlin.c librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/tommyhashlin.c --- librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/tommyhashlin.c 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/tommyhashlin.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,321 +0,0 @@ -/* - * Copyright 2010 Andrea Mazzoleni. All rights reserved. - * - * 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 ANDREA MAZZOLENI AND CONTRIBUTORS ``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 ANDREA MAZZOLENI 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 "tommyhashlin.h" -#include "tommylist.h" - -#include /* for memset */ -#include /* for assert */ - -/******************************************************************************/ -/* hashlin */ - -/** - * Reallocation states. - */ -#define TOMMY_HASHLIN_STATE_STABLE 0 -#define TOMMY_HASHLIN_STATE_GROW 1 -#define TOMMY_HASHLIN_STATE_SHRINK 2 - -void tommy_hashlin_init(tommy_hashlin* hashlin) -{ - /* fixed initial size */ - hashlin->bucket_bit = TOMMY_HASHLIN_BIT; - hashlin->bucket_max = 1 << hashlin->bucket_bit; - hashlin->bucket_mask = hashlin->bucket_max - 1; - hashlin->bucket[0] = tommy_cast(tommy_hashlin_node**, tommy_malloc(hashlin->bucket_max * sizeof(tommy_hashlin_node*))); - memset(hashlin->bucket[0], 0, hashlin->bucket_max * sizeof(tommy_hashlin_node*)); - hashlin->bucket_mac = 1; - - /* stable state */ - hashlin->state = TOMMY_HASHLIN_STATE_STABLE; - - hashlin->count = 0; -} - -void tommy_hashlin_done(tommy_hashlin* hashlin) -{ - unsigned i; - for(i=0;ibucket_mac;++i) - tommy_free(hashlin->bucket[i]); -} - -/** - * Return the bucket at the specified pos. - */ -tommy_inline tommy_hashlin_node** tommy_hashlin_pos(tommy_hashlin* hashlin, tommy_hash_t pos) -{ - unsigned bsr; - - /* special case for the first bucket */ - if (pos < (1 << TOMMY_HASHLIN_BIT)) { - return &hashlin->bucket[0][pos]; - } - - /* get the highest bit set */ - bsr = tommy_ilog2_u32(pos); - - /* clear the highest bit */ - pos -= 1 << bsr; - - return &hashlin->bucket[bsr - TOMMY_HASHLIN_BIT + 1][pos]; -} - -/** - * Return the bucket to use. - */ -tommy_inline tommy_hashlin_node** tommy_hashlin_bucket_ptr(tommy_hashlin* hashlin, tommy_hash_t hash) -{ - unsigned pos; - - /* if we are reallocating */ - if (hashlin->state != TOMMY_HASHLIN_STATE_STABLE) { - /* compute the old position */ - pos = hash & hashlin->low_mask; - - /* if we have not reallocated this position yet */ - if (pos >= hashlin->split) { - - /* use it as it was before */ - return tommy_hashlin_pos(hashlin, pos); - } - } - - /* otherwise operates normally */ - pos = hash & hashlin->bucket_mask; - - return tommy_hashlin_pos(hashlin, pos); -} - -/** - * Grow one step. - */ -tommy_inline void hashlin_grow_step(tommy_hashlin* hashlin) -{ - /* grow if more than 50% full */ - if (hashlin->state != TOMMY_HASHLIN_STATE_GROW - && hashlin->count > hashlin->bucket_max / 2 - ) { - /* if we are stable, setup a new grow state */ - /* otherwise continue with the already setup shrink one */ - /* but in backward direction */ - if (hashlin->state == TOMMY_HASHLIN_STATE_STABLE) { - /* set the lower size */ - hashlin->low_max = hashlin->bucket_max; - hashlin->low_mask = hashlin->bucket_mask; - - /* grow the hash size and allocate */ - ++hashlin->bucket_bit; - hashlin->bucket_max = 1 << hashlin->bucket_bit; - hashlin->bucket_mask = hashlin->bucket_max - 1; - hashlin->bucket[hashlin->bucket_mac] = tommy_cast(tommy_hashlin_node**, tommy_malloc(hashlin->low_max * sizeof(tommy_hashlin_node*))); - ++hashlin->bucket_mac; - - /* start from the beginning going forward */ - hashlin->split = 0; - } - - /* grow state */ - hashlin->state = TOMMY_HASHLIN_STATE_GROW; - } - - /* if we are growing */ - if (hashlin->state == TOMMY_HASHLIN_STATE_GROW) { - /* compute the split target required to finish the reallocation before the next resize */ - unsigned split_target = 2 * hashlin->count; - - /* reallocate buckets until the split target */ - while (hashlin->split + hashlin->low_max < split_target) { - tommy_hashlin_node** split[2]; - tommy_hashlin_node* j; - unsigned mask; - - /* get the low bucket */ - split[0] = tommy_hashlin_pos(hashlin, hashlin->split); - - /* get the high bucket */ - /* it's always in the second half, so we can index it directly */ - /* without calling tommy_hashlin_pos() */ - split[1] = &hashlin->bucket[hashlin->bucket_mac-1][hashlin->split]; - - /* save the low bucket */ - j = *split[0]; - - /* reinitialize the buckets */ - *split[0] = 0; - *split[1] = 0; - - /* compute the bit to identify the bucket */ - mask = hashlin->bucket_mask & ~hashlin->low_mask; - - /* flush the bucket */ - while (j) { - tommy_hashlin_node* j_next = j->next; - unsigned index = (j->key & mask) != 0; - if (*split[index]) - tommy_list_insert_tail_not_empty(*split[index], j); - else - tommy_list_insert_first(split[index], j); - j = j_next; - } - - /* go forward */ - ++hashlin->split; - - /* if we have finished, change the state */ - if (hashlin->split == hashlin->low_max) { - hashlin->state = TOMMY_HASHLIN_STATE_STABLE; - break; - } - } - } -} - -/** - * Shrink one step. - */ -tommy_inline void hashlin_shrink_step(tommy_hashlin* hashlin) -{ - /* shrink if less than 12.5% full */ - if (hashlin->state != TOMMY_HASHLIN_STATE_SHRINK - && hashlin->count < hashlin->bucket_max / 8 - ) { - /* avoid to shrink the first bucket */ - if (hashlin->bucket_bit > TOMMY_HASHLIN_BIT) { - /* if we are stable, setup a new shrink state */ - /* otherwise continue with the already setup grow one */ - /* but in backward direction */ - if (hashlin->state == TOMMY_HASHLIN_STATE_STABLE) { - /* set the lower size */ - hashlin->low_max = hashlin->bucket_max / 2; - hashlin->low_mask = hashlin->bucket_mask / 2; - - /* start from the half going backward */ - hashlin->split = hashlin->low_max; - } - - /* start reallocation */ - hashlin->state = TOMMY_HASHLIN_STATE_SHRINK; - } - } - - /* if we are shrinking */ - if (hashlin->state == TOMMY_HASHLIN_STATE_SHRINK) { - /* compute the split target required to finish the reallocation before the next resize */ - unsigned split_target = 8 * hashlin->count; - - /* reallocate buckets until the split target */ - while (hashlin->split + hashlin->low_max > split_target) { - tommy_hashlin_node** split[2]; - - /* go backward position */ - --hashlin->split; - - /* get the low bucket */ - split[0] = tommy_hashlin_pos(hashlin, hashlin->split); - - /* get the high bucket */ - /* it's always in the second half, so we can index it directly */ - /* without calling tommy_hashlin_pos() */ - split[1] = &hashlin->bucket[hashlin->bucket_mac-1][hashlin->split]; - - /* concat the high bucket into the low one */ - tommy_list_concat(split[0], split[1]); - - /* if we have finished, clean up and change the state */ - if (hashlin->split == 0) { - hashlin->state = TOMMY_HASHLIN_STATE_STABLE; - - /* shrink the hash size */ - --hashlin->bucket_bit; - hashlin->bucket_max = 1 << hashlin->bucket_bit; - hashlin->bucket_mask = hashlin->bucket_max - 1; - - /* free the last segment */ - --hashlin->bucket_mac; - tommy_free(hashlin->bucket[hashlin->bucket_mac]); - break; - } - } - } -} - -void tommy_hashlin_insert(tommy_hashlin* hashlin, tommy_hashlin_node* node, void* data, tommy_hash_t hash) -{ - tommy_list_insert_tail(tommy_hashlin_bucket_ptr(hashlin, hash), node, data); - - node->key = hash; - - ++hashlin->count; - - hashlin_grow_step(hashlin); -} - -void* tommy_hashlin_remove_existing(tommy_hashlin* hashlin, tommy_hashlin_node* node) -{ - tommy_list_remove_existing(tommy_hashlin_bucket_ptr(hashlin, node->key), node); - - --hashlin->count; - - hashlin_shrink_step(hashlin); - - return node->data; -} - -tommy_hashlin_node* tommy_hashlin_bucket(tommy_hashlin* hashlin, tommy_hash_t hash) -{ - return *tommy_hashlin_bucket_ptr(hashlin, hash); -} - -void* tommy_hashlin_remove(tommy_hashlin* hashlin, tommy_search_func* cmp, const void* cmp_arg, tommy_hash_t hash) -{ - tommy_hashlin_node** let_ptr = tommy_hashlin_bucket_ptr(hashlin, hash); - tommy_hashlin_node* i = *let_ptr; - - while (i) { - /* we first check if the hash matches, as in the same bucket we may have multiples hash values */ - if (i->key == hash && cmp(cmp_arg, i->data) == 0) { - tommy_list_remove_existing(let_ptr, i); - - --hashlin->count; - - hashlin_shrink_step(hashlin); - - return i->data; - } - i = i->next; - } - - return 0; -} - -tommy_size_t tommy_hashlin_memory_usage(tommy_hashlin* hashlin) -{ - return hashlin->bucket_max * (tommy_size_t)sizeof(hashlin->bucket[0][0]) - + hashlin->count * (tommy_size_t)sizeof(tommy_hashlin_node); -} - diff -Nru librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/tommyhashlin.h librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/tommyhashlin.h --- librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/tommyhashlin.h 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/tommyhashlin.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,265 +0,0 @@ -/* - * Copyright 2010 Andrea Mazzoleni. All rights reserved. - * - * 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 ANDREA MAZZOLENI AND CONTRIBUTORS ``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 ANDREA MAZZOLENI 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. - */ - -/** \file - * Linear chained hashtable. - * - * This hashtable resizes dynamically and progressively using a variation of the - * linear hashing algorithm described in http://en.wikipedia.org/wiki/Linear_hashing - * - * It starts with the minimal size of 16 buckets, it doubles the size then it - * reaches a load factor greater than 0.5 and it halves the size with a load - * factor lower than 0.125. - * - * The progressive resize is good for real-time and interactive applications - * as it makes insert and delete operations taking always the same time. - * - * For resizing it's used a dynamic array that supports access to not contigous - * segments. - * In this way we only allocate additional table segments on the heap, without - * freeing the previous table, and then not increasing the heap fragmentation. - * - * The resize takes place inside tommy_hashlin_insert() and tommy_hashlin_remove(). - * No resize is done in the tommy_hashlin_search() operation. - * - * To initialize the hashtable you have to call tommy_hashlin_init(). - * - * \code - * tommy_hashslin hashlin; - * - * tommy_hashlin_init(&hashlin); - * \endcode - * - * To insert elements in the hashtable you have to call tommy_hashlin_insert() for - * each element. - * In the insertion call you have to specify the address of the node, the - * address of the object, and the hash value of the key to use. - * The address of the object is used to initialize the tommy_node::data field - * of the node, and the hash to initialize the tommy_node::key field. - * - * \code - * struct object { - * tommy_node node; - * // other fields - * int value; - * }; - * - * struct object* obj = malloc(sizeof(struct object)); // creates the object - * - * obj->value = ...; // initializes the object - * - * tommy_hashlin_insert(&hashlin, &obj->node, obj, tommy_inthash_u32(obj->value)); // inserts the object - * \endcode - * - * To find and element in the hashtable you have to call tommy_hashtable_search() - * providing a comparison function, its argument, and the hash of the key to search. - * - * \code - * int compare(const void* arg, const void* obj) - * { - * return *(const int*)arg != ((const struct object*)obj)->value; - * } - * - * int value_to_find = 1; - * struct object* obj = tommy_hashlin_search(&hashlin, compare, &value_to_find, tommy_inthash_u32(value_to_find)); - * if (!obj) { - * // not found - * } else { - * // found - * } - * \endcode - * - * To iterate over all the elements in the hashtable with the same key, you have to - * use tommy_hashlin_bucket() and follow the tommy_node::next pointer until NULL. - * You have also to check explicitely for the key, as the bucket may contains - * different keys. - * - * \code - * int value_to_find = 1; - * tommy_node* i = tommy_hashlin_bucket(&hashlin, tommy_inthash_u32(value_to_find)); - * while (i) { - * struct object* obj = i->data; // gets the object pointer - * - * if (obj->value == value_to_find) { - * printf("%d\n", obj->value); // process the object - * } - * - * i = i->next; // goes to the next element - * } - * \endcode - * - * To remove an element from the hashtable you have to call tommy_hashlin_remove() - * providing a comparison function, its argument, and the hash of the key to search - * and remove. - * - * \code - * struct object* obj = tommy_trie_remove(&hashtable, compare, &value_to_remove, tommy_inthash_u32(value_to_remove)); - * if (obj) { - * free(obj); // frees the object allocated memory - * } - * \endcode - * - * To destroy the hashtable you have to remove all the elements, and deinitialize - * the hashtable calling tommy_hashlin_done(). - * - * \code - * tommy_hashlin_done(&hashlin); - * \endcode - * - * Note that you cannot iterates over all the elements in the hashtable using the - * hashtable itself. You have to insert all the elements also in a ::tommy_list, - * and use the list to iterate. See the \ref multiindex example for more detail. - */ - -#ifndef __TOMMYHASHLIN_H -#define __TOMMYHASHLIN_H - -#include "tommyhash.h" - -/******************************************************************************/ -/* hashlin */ - -/** \internal - * Initial and minimal size of the hashtable expressed as a power of 2. - * The initial size is 2^TOMMY_HASHLIN_BIT. - */ -#define TOMMY_HASHLIN_BIT 6 - -/** - * Linear hashtable node. - * This is the node that you have to include inside your objects. - */ -typedef tommy_node tommy_hashlin_node; - -/** \internal - * Max number of elements as a power of 2. - */ -#define TOMMY_HASHLIN_BIT_MAX 32 - -/** - * Linear chained hashtable. - */ -typedef struct tommy_hashlin_struct { - tommy_hashlin_node** bucket[TOMMY_HASHLIN_BIT_MAX]; /**< Dynamic array of hash buckets. One list for each hash modulus. */ - unsigned bucket_bit; /**< Bits used in the bit mask. */ - unsigned bucket_max; /**< Number of buckets. */ - unsigned bucket_mask; /**< Bit mask to access the buckets. */ - unsigned bucket_mac; /**< Number of vectors allocated. */ - unsigned low_max; /**< Low order max value. */ - unsigned low_mask; /**< Low order mask value. */ - unsigned split; /**< Split position. */ - unsigned state; /**< Reallocation state. */ - unsigned count; /**< Number of elements. */ -} tommy_hashlin; - -/** - * Initializes the hashtable. - */ -void tommy_hashlin_init(tommy_hashlin* hashlin); - -/** - * Deinitializes the hashtable. - * - * You can call this function with elements still contained, - * but such elements are not going to be freed by this call. - */ -void tommy_hashlin_done(tommy_hashlin* hashlin); - -/** - * Inserts an element in the the hashtable. - */ -void tommy_hashlin_insert(tommy_hashlin* hashlin, tommy_hashlin_node* node, void* data, tommy_hash_t hash); - -/** - * Searches and removes an element from the hashtable. - * You have to provide a compare function and the hash of the element you want to remove. - * If the element is not found, 0 is returned. - * If more equal elements are present, the first one is removed. - * This operation is faster than calling tommy_hashlin_bucket() and tommy_hashlin_remove_existing() separately. - * \param cmp Compare function called with cmp_arg as first argument and with the element to compare as a second one. - * The function should return 0 for equal elements, anything other for different elements. - * \param cmp_arg Compare argument passed as first argument of the compare function. - * \param hash Hash of the element to find and remove. - * \return The removed element, or 0 if not found. - */ -void* tommy_hashlin_remove(tommy_hashlin* hashlin, tommy_search_func* cmp, const void* cmp_arg, tommy_hash_t hash); - -/** - * Gets the bucket of the specified hash. - * The bucket is guaranteed to contain ALL the elements with the specified hash, - * but it can contain also others. - * You can access elements in the bucket following the ::next pointer until 0. - * \param hash Hash of the element to find. - * \return The head of the bucket, or 0 if empty. - */ -tommy_hashlin_node* tommy_hashlin_bucket(tommy_hashlin* hashlin, tommy_hash_t hash); - -/** - * Searches an element in the hashtable. - * You have to provide a compare function and the hash of the element you want to find. - * If more equal elements are present, the first one is returned. - * \param cmp Compare function called with cmp_arg as first argument and with the element to compare as a second one. - * The function should return 0 for equal elements, anything other for different elements. - * \param cmp_arg Compare argument passed as first argument of the compare function. - * \param hash Hash of the element to find. - * \return The first element found, or 0 if none. - */ -tommy_inline void* tommy_hashlin_search(tommy_hashlin* hashlin, tommy_search_func* cmp, const void* cmp_arg, tommy_hash_t hash) -{ - tommy_hashlin_node* i = tommy_hashlin_bucket(hashlin, hash); - while (i) { - /* we first check if the hash matches, as in the same bucket we may have multiples hash values */ - if (i->key == hash && cmp(cmp_arg, i->data) == 0) - return i->data; - i = i->next; - } - return 0; -} - -/** - * Removes an element from the hashtable. - * You must already have the address of the element to remove. - * \return The tommy_node::data field of the node removed. - */ -void* tommy_hashlin_remove_existing(tommy_hashlin* hashlin, tommy_hashlin_node* node); - -/** - * Gets the number of elements. - */ -tommy_inline unsigned tommy_hashlin_count(tommy_hashlin* hashlin) -{ - return hashlin->count; -} - -/** - * Gets the size of allocated memory. - * It includes the size of the ::tommy_hashlin_node of the stored elements. - */ -tommy_size_t tommy_hashlin_memory_usage(tommy_hashlin* hashlin); - -#endif - diff -Nru librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/tommyhashtbl.c librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/tommyhashtbl.c --- librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/tommyhashtbl.c 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/tommyhashtbl.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,105 +0,0 @@ -/* - * Copyright 2010 Andrea Mazzoleni. All rights reserved. - * - * 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 ANDREA MAZZOLENI AND CONTRIBUTORS ``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 ANDREA MAZZOLENI 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 "tommyhashtbl.h" -#include "tommylist.h" - -#include /* for memset */ - -/******************************************************************************/ -/* hashtable */ - -void tommy_hashtable_init(tommy_hashtable* hashtable, unsigned bucket_max) -{ - if (bucket_max < 16) { - bucket_max = 16; - } else { - bucket_max = tommy_roundup_pow2_u32(bucket_max); - } - - hashtable->bucket_max = bucket_max; - hashtable->bucket_mask = hashtable->bucket_max - 1; - hashtable->bucket = tommy_cast(tommy_hashtable_node**, tommy_malloc(hashtable->bucket_max * sizeof(tommy_hashtable_node*))); - memset(hashtable->bucket, 0, hashtable->bucket_max * sizeof(tommy_hashtable_node*)); - - hashtable->count = 0; -} - -void tommy_hashtable_done(tommy_hashtable* hashtable) -{ - tommy_free(hashtable->bucket); -} - -void tommy_hashtable_insert(tommy_hashtable* hashtable, tommy_hashtable_node* node, void* data, tommy_hash_t hash) -{ - unsigned pos = hash & hashtable->bucket_mask; - - tommy_list_insert_tail(&hashtable->bucket[pos], node, data); - - node->key = hash; - - ++hashtable->count; -} - -void* tommy_hashtable_remove_existing(tommy_hashtable* hashtable, tommy_hashtable_node* node) -{ - unsigned pos = node->key & hashtable->bucket_mask; - - tommy_list_remove_existing(&hashtable->bucket[pos], node); - - --hashtable->count; - - return node->data; -} - -void* tommy_hashtable_remove(tommy_hashtable* hashtable, tommy_search_func* cmp, const void* cmp_arg, tommy_hash_t hash) -{ - unsigned pos = hash & hashtable->bucket_mask; - tommy_hashtable_node* i = hashtable->bucket[pos]; - - while (i) { - /* we first check if the hash matches, as in the same bucket we may have multiples hash values */ - if (i->key == hash && cmp(cmp_arg, i->data) == 0) { - tommy_list_remove_existing(&hashtable->bucket[pos], i); - - --hashtable->count; - - return i->data; - } - /* we assume that i->next is still valid also after removing */ - i = i->next; - } - - return 0; -} - -tommy_size_t tommy_hashtable_memory_usage(tommy_hashtable* hashtable) -{ - return hashtable->bucket_max * (tommy_size_t)sizeof(hashtable->bucket[0]) - + tommy_hashtable_count(hashtable) * (tommy_size_t)sizeof(tommy_hashtable_node); -} - diff -Nru librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/tommyhashtbl.h librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/tommyhashtbl.h --- librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/tommyhashtbl.h 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/tommyhashtbl.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,239 +0,0 @@ -/* - * Copyright 2010 Andrea Mazzoleni. All rights reserved. - * - * 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 ANDREA MAZZOLENI AND CONTRIBUTORS ``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 ANDREA MAZZOLENI 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. - */ - -/** \file - * Fixed size chained hashtable. - * - * This hashtable is a standard implementation of a chained hashtable with a fixed size. - * - * Note that performances starts to degenerate after reaching a load factor greater than 0.75. - * The ::tommy_hashdyn and ::tommy_hashlin hashtables fix this problem growing dynamically. - * - * To initialize the hashtable you have to call tommy_hashtable_init() specifing - * the fixed bucket size. - * - * \code - * tommy_hashslin hashtable; - * - * tommy_hashtable_init(&hashtable, 1024); - * \endcode - * - * To insert elements in the hashtable you have to call tommy_hashtable_insert() for - * each element. - * In the insertion call you have to specify the address of the node, the - * address of the object, and the hash value of the key to use. - * The address of the object is used to initialize the tommy_node::data field - * of the node, and the hash to initialize the tommy_node::key field. - * - * \code - * struct object { - * tommy_node node; - * // other fields - * int value; - * }; - * - * struct object* obj = malloc(sizeof(struct object)); // creates the object - * - * obj->value = ...; // initializes the object - * - * tommy_hashtable_insert(&hashtable, &obj->node, obj, tommy_inthash_u32(obj->value)); // inserts the object - * \endcode - * - * To find and element in the hashtable you have to call tommy_hashtable_search() - * providing a comparison function, its argument, and the hash of the key to search. - * - * \code - * int compare(const void* arg, const void* obj) - * { - * return *(const int*)arg != ((const struct object*)obj)->value; - * } - * - * int value_to_find = 1; - * struct object* obj = tommy_hashtable_search(&hashtable, compare, &value_to_find, tommy_inthash_u32(value_to_find)); - * if (!obj) { - * // not found - * } else { - * // found - * } - * \endcode - * - * To iterate over all the elements in the hashtable with the same key, you have to - * use tommy_hashtable_bucket() and follow the tommy_node::next pointer until NULL. - * You have also to check explicitely for the key, as the bucket may contains - * different keys. - * - * \code - * tommy_node* i = tommy_hashtable_bucket(&hashtable, tommy_inthash_u32(value_to_find)); - * while (i) { - * struct object* obj = i->data; // gets the object pointer - * - * if (obj->value == value_to_find) { - * printf("%d\n", obj->value); // process the object - * } - * - * i = i->next; // goes to the next element - * } - * \endcode - * - * To remove an element from the hashtable you have to call tommy_hashtable_remove() - * providing a comparison function, its argument, and the hash of the key to search - * and remove. - * - * \code - * struct object* obj = tommy_trie_remove(&hashtable, compare, &value_to_remove, tommy_inthash_u32(value_to_remove)); - * if (obj) { - * free(obj); // frees the object allocated memory - * } - * \endcode - * - * To destroy the hashtable you have to remove all the elements, and deinitialize - * the hashtable calling tommy_hashtable_done(). - * - * \code - * tommy_hashtable_done(&hashtable); - * \endcode - * - * Note that you cannot iterates over all the elements in the hashtable using the - * hashtable itself. You have to insert all the elements also in a ::tommy_list, - * and use the list to iterate. See the \ref multiindex example for more detail. - */ - -#ifndef __TOMMYHASHTBL_H -#define __TOMMYHASHTBL_H - -#include "tommyhash.h" - -/******************************************************************************/ -/* hashtable */ - -/** - * Hashtable node. - * This is the node that you have to include inside your objects. - */ -typedef tommy_node tommy_hashtable_node; - -/** - * Fixed size chained hashtable. - */ -typedef struct tommy_hashtable_struct { - tommy_hashtable_node** bucket; /**< Hash buckets. One list for each hash modulus. */ - unsigned bucket_max; /**< Number of buckets. */ - unsigned bucket_mask; /**< Bit mask to access the buckets. */ - unsigned count; /**< Number of elements. */ -} tommy_hashtable; - -/** - * Initializes the hashtable. - * \param buckets Minimum number of buckets to allocate. The effective number used is the next power of 2. - */ -void tommy_hashtable_init(tommy_hashtable* hashtable, unsigned bucket_max); - -/** - * Deinitializes the hashtable. - * - * You can call this function with elements still contained, - * but such elements are not going to be freed by this call. - */ -void tommy_hashtable_done(tommy_hashtable* hashtable); - -/** - * Inserts an element in the hashtable. - */ -void tommy_hashtable_insert(tommy_hashtable* hashtable, tommy_hashtable_node* node, void* data, tommy_hash_t hash); - -/** - * Searches and removes an element from the hashtable. - * You have to provide a compare function and the hash of the element you want to remove. - * If the element is not found, 0 is returned. - * If more equal elements are present, the first one is removed. - * This operation is faster than calling tommy_hashtable_bucket() and tommy_hashtable_remove_existing() separately. - * \param cmp Compare function called with cmp_arg as first argument and with the element to compare as a second one. - * The function should return 0 for equal elements, anything other for different elements. - * \param cmp_arg Compare argument passed as first argument of the compare function. - * \param hash Hash of the element to find and remove. - * \return The removed element, or 0 if not found. - */ -void* tommy_hashtable_remove(tommy_hashtable* hashtable, tommy_search_func* cmp, const void* cmp_arg, tommy_hash_t hash); - -/** - * Gets the bucket of the specified hash. - * The bucket is guaranteed to contain ALL the elements with the specified hash, - * but it can contain also others. - * You can access elements in the bucket following the ::next pointer until 0. - * \param hash Hash of the element to find. - * \return The head of the bucket, or 0 if empty. - */ -tommy_inline tommy_hashtable_node* tommy_hashtable_bucket(tommy_hashtable* hashtable, tommy_hash_t hash) -{ - return hashtable->bucket[hash & hashtable->bucket_mask]; -} - -/** - * Searches an element in the hashtable. - * You have to provide a compare function and the hash of the element you want to find. - * If more equal elements are present, the first one is returned. - * \param cmp Compare function called with cmp_arg as first argument and with the element to compare as a second one. - * The function should return 0 for equal elements, anything other for different elements. - * \param cmp_arg Compare argument passed as first argument of the compare function. - * \param hash Hash of the element to find. - * \return The first element found, or 0 if none. - */ -tommy_inline void* tommy_hashtable_search(tommy_hashtable* hashtable, tommy_search_func* cmp, const void* cmp_arg, tommy_hash_t hash) -{ - tommy_hashtable_node* i = tommy_hashtable_bucket(hashtable, hash); - while (i) { - /* we first check if the hash matches, as in the same bucket we may have multiples hash values */ - if (i->key == hash && cmp(cmp_arg, i->data) == 0) - return i->data; - i = i->next; - } - return 0; -} - -/** - * Removes an element from the hashtable. - * You must already have the address of the element to remove. - * \return The tommy_node::data field of the node removed. - */ -void* tommy_hashtable_remove_existing(tommy_hashtable* hashtable, tommy_hashtable_node* node); - -/** - * Gets the number of elements. - */ -tommy_inline unsigned tommy_hashtable_count(tommy_hashtable* hashtable) -{ - return hashtable->count; -} - -/** - * Gets the size of allocated memory. - * It includes the size of the ::tommy_hashtable_node of the stored elements. - */ -tommy_size_t tommy_hashtable_memory_usage(tommy_hashtable* hashtable); - -#endif - diff -Nru librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/tommylist.c librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/tommylist.c --- librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/tommylist.c 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/tommylist.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,87 +0,0 @@ -/* - * Copyright 2010 Andrea Mazzoleni. All rights reserved. - * - * 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 ANDREA MAZZOLENI AND CONTRIBUTORS ``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 ANDREA MAZZOLENI 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 "tommylist.h" -#include "tommychain.h" - -void tommy_list_concat(tommy_list* first, tommy_list* second) -{ - tommy_node* first_head; - tommy_node* first_tail; - tommy_node* second_head; - - if (tommy_list_empty(second)) { - return; - } - - if (tommy_list_empty(first)) { - *first = *second; - return; - } - - first_head = tommy_list_head(first); - second_head = tommy_list_head(second); - first_tail = tommy_list_tail(first); - - /* set the "circular" prev list */ - first_head->prev = second_head->prev; - second_head->prev = first_tail; - - /* set the "0 terminated" next list */ - first_tail->next = second_head; -} - -/** \internal - * Setup a list. - */ -tommy_inline void tommy_list_set(tommy_list* list, tommy_node* head, tommy_node* tail) -{ - head->prev = tail; - tail->next = 0; - *list = head; -} - -void tommy_list_sort(tommy_list* list, tommy_compare_func* cmp) -{ - tommy_chain chain; - tommy_node* head; - - if (tommy_list_empty(list)) - return; - - head = tommy_list_head(list); - - /* create a chain from the list */ - chain.head = head; - chain.tail = head->prev; - - tommy_chain_mergesort(&chain, cmp); - - /* restore the list */ - tommy_list_set(list, chain.head, chain.tail); -} - diff -Nru librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/tommylist.h librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/tommylist.h --- librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/tommylist.h 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/tommylist.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,337 +0,0 @@ -/* - * Copyright 2010 Andrea Mazzoleni. All rights reserved. - * - * 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 ANDREA MAZZOLENI AND CONTRIBUTORS ``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 ANDREA MAZZOLENI 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. - */ - -/** \file - * Double linked list for collisions into hashtables. - * - * This list is a double linked list mainly targetted for collisions into an hashtables, - * but useable also as a generic list. - * - * The main feature of this list is to require only one pointer to represent the - * list, compared to a classic implementation requiring an head an a tail pointers. - * This reduces the memory usage in hashtables. - * - * Another feature is to support the insertion at the end of the list. This allow to store - * collisions in a stable order. Where for stable order we mean that equal elements keep - * their insertion order. - * - * To initialize the list, you have to call tommy_list_init(), or simply assign - * to it NULL, as an empty list is represented by the NULL value. - * - * \code - * tommy_list list; - * - * tommy_list_init(&list); // initializes the list - * \endcode - * - * To insert elements in the list you have to call tommy_list_insert_tail() - * or tommy_list_insert_head() for each element. - * In the insertion call you have to specify the address of the node and the - * address of the object. - * The address of the object is used to initialize the tommy_node::data field - * of the node. - * - * \code - * struct object { - * tommy_node node; - * // other fields - * int value; - * }; - * - * struct object* obj = malloc(sizeof(struct object)); // creates the object - * - * obj->value = ...; // initializes the object - * - * tommy_list_insert_tail(&list, &obj->node, obj); // inserts the object - * \endcode - * - * To iterates over all the elements in the list you have to call - * tommy_list_head() to get the head of the list and follow the - * tommy_node::next pointer until NULL. - * - * \code - * tommy_node* i = tommy_list_head(&list); - * while (i) { - * struct object* obj = i->data; // gets the object pointer - * - * printf("%d\n", obj->value); // process the object - * - * i = i->next; // go to the next element - * } - * \endcode - * - * To destroy the list you have only to remove all the elements, as the list is - * completely inplace and it doesn't allocate memory. - * - * \code - * tommy_node* i = tommy_list_head(&list); - * while (i) { - * tommy_node* i_next = i->next; // saves the next element before freeing - * - * free(i->data); // frees the object allocated memory - * - * i = i_next; // goes to the next element - * } - * \endcode - */ - -#ifndef __TOMMYLIST_H -#define __TOMMYLIST_H - -#include "tommytypes.h" - -/******************************************************************************/ -/* list */ - -/** - * Double linked list for collisions into hashtables. - */ -typedef tommy_node* tommy_list; - -/** - * Initializes the list. - * The list is completely inplace, so it doesn't need to be deinitialized. - */ -tommy_inline void tommy_list_init(tommy_list* list) -{ - *list = 0; -} - -/** - * Gets the head of the list. - * \return The head node. For empty lists 0 is returned. - */ -tommy_inline tommy_node* tommy_list_head(tommy_list* list) -{ - return *list; -} - -/** - * Gets the tail of the list. - * \return The tail node. For empty lists 0 is returned. - */ -tommy_inline tommy_node* tommy_list_tail(tommy_list* list) -{ - tommy_node* head = tommy_list_head(list); - - if (!head) - return 0; - - return head->prev; -} - -/** \internal - * Creates a new list with a single element. - * \param list The list to initialize. - * \param node The node to insert. - */ -tommy_inline void tommy_list_insert_first(tommy_list* list, tommy_node* node) -{ - /* one element "circular" prev list */ - node->prev = node; - - /* one element "0 terminated" next list */ - node->next = 0; - - *list = node; -} - -/** \internal - * Inserts an element at the head of a not empty list. - * The element is inserted at the head of the list. The list cannot be empty. - * \param list The list. The list cannot be empty. - * \param node The node to insert. - */ -tommy_inline void tommy_list_insert_head_not_empty(tommy_list* list, tommy_node* node) -{ - tommy_node* head = tommy_list_head(list); - - /* insert in the "circular" prev list */ - node->prev = head->prev; - head->prev = node; - - /* insert in the "0 terminated" next list */ - node->next = head; - - *list = node; -} - -/** \internal - * Inserts an element at the tail of a not empty list. - * The element is inserted at the tail of the list. The list cannot be empty. - * \param head The node at the list head. It cannot be 0. - * \param node The node to insert. - */ -tommy_inline void tommy_list_insert_tail_not_empty(tommy_node* head, tommy_node* node) -{ - /* insert in the "circular" prev list */ - node->prev = head->prev; - head->prev = node; - - /* insert in the "0 terminated" next list */ - node->next = 0; - node->prev->next = node; -} - -/** - * Inserts an element at the head of a list. - * \param node The node to insert. - * \param data The object containing the node. It's used to set the tommy_node::data field of the node. - */ -tommy_inline void tommy_list_insert_head(tommy_list* list, tommy_node* node, void* data) -{ - tommy_node* head = tommy_list_head(list); - - if (head) { - tommy_list_insert_head_not_empty(list, node); - } else { - tommy_list_insert_first(list, node); - } - - node->data = data; -} - -/** - * Inserts an element at the tail of a list. - * \param node The node to insert. - * \param data The object containing the node. It's used to set the tommy_node::data field of the node. - */ -tommy_inline void tommy_list_insert_tail(tommy_list* list, tommy_node* node, void* data) -{ - tommy_node* head = tommy_list_head(list); - - if (head) { - tommy_list_insert_tail_not_empty(head, node); - } else { - tommy_list_insert_first(list, node); - } - - node->data = data; -} - -/** \internal - * Removes an element from the head of a not empty list. - * \param list The list. The list cannot be empty. - * \return The node removed. - */ -tommy_inline tommy_node* tommy_list_remove_head_not_empty(tommy_list* list) -{ - tommy_node* head = tommy_list_head(list); - - /* remove from the "circular" prev list */ - head->next->prev = head->prev; - - /* remove from the "0 terminated" next list */ - *list = head->next; /* the new head, in case 0 */ - - return head; -} - -/** - * Removes an element from the list. - * You must already have the address of the element to remove. - * \note The node content is left unchanged, including the tommy_node::next - * and tommy_node::prev fields that still contain pointers at the list. - * \param node The node to remove. The node must be in the list. - * \return The tommy_node::data field of the node removed. - */ -tommy_inline void* tommy_list_remove_existing(tommy_list* list, tommy_node* node) -{ - tommy_node* head = tommy_list_head(list); - - /* remove from the "circular" prev list */ - if (node->next) { - node->next->prev = node->prev; - } else { - head->prev = node->prev; /* the last */ - } - - /* remove from the "0 terminated" next list */ - if (head == node) { - *list = node->next; /* the new head, in case 0 */ - } else { - node->prev->next = node->next; - } - - return node->data; -} - -/** - * Concats two lists. - * The second list is concatenated at the first list. - * \param first The first list. - * \param second The second list. After this call the list content is undefined, - * and you should not use it anymore. - */ -void tommy_list_concat(tommy_list* first, tommy_list* second); - -/** - * Sorts a list. - * It's a stable merge sort with O(N*log(N)) worst complexity. - * It's faster on degenerated cases like partially ordered lists. - * \param cmp Compare function called with two elements. - * The function should return <0 if the first element is less than the second, == 0 if equal, and > 0 if greather. - */ -void tommy_list_sort(tommy_list* list, tommy_compare_func* cmp); - -/** - * Checks if empty. - * \return If the list is empty. - */ -tommy_inline tommy_bool_t tommy_list_empty(tommy_list* list) -{ - return tommy_list_head(list) == 0; -} - -/** - * Calls the specified function for each element in the list. - */ -tommy_inline void tommy_list_foreach(tommy_list* list, tommy_foreach_func* func) -{ - tommy_node* node = tommy_list_head(list); - while (node) { - void* data = node->data; - node = node->next; - func(data); - } -} - -/** - * Calls the specified function with argument for each element in the list. - */ -tommy_inline void tommy_list_foreach_arg(tommy_list* list, tommy_foreach_arg_func* func, void* arg) -{ - tommy_node* node = tommy_list_head(list); - while (node) { - void* data = node->data; - node = node->next; - func(arg, data); - } -} - -#endif - diff -Nru librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/tommytrie.c librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/tommytrie.c --- librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/tommytrie.c 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/tommytrie.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,323 +0,0 @@ -/* - * Copyright 2010 Andrea Mazzoleni. All rights reserved. - * - * 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 ANDREA MAZZOLENI AND CONTRIBUTORS ``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 ANDREA MAZZOLENI 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 "tommytrie.h" -#include "tommylist.h" - -#include /* for assert */ - -/******************************************************************************/ -/* trie */ - -/** - * Mask for the inner branches. - */ -#define TOMMY_TRIE_TREE_MASK (TOMMY_TRIE_TREE_MAX-1) - -/** - * Shift for the first level of branches. - */ -#define TOMMY_TRIE_BUCKET_SHIFT (TOMMY_KEY_BIT - TOMMY_TRIE_BUCKET_BIT) - -/** - * Max number of levels. - */ -#define TOMMY_TRIE_LEVEL_MAX ((TOMMY_KEY_BIT - TOMMY_TRIE_BUCKET_BIT) / TOMMY_TRIE_TREE_BIT) - -/** - * Hashtrie tree. - * A tree contains TOMMY_TRIE_TREE_MAX ordered pointers to . - * - * Each tree level uses exactly TOMMY_TRIE_TREE_BIT bits from the key. - */ -struct tommy_trie_tree_struct { - tommy_trie_node* map[TOMMY_TRIE_TREE_MAX]; -}; -typedef struct tommy_trie_tree_struct tommy_trie_tree; - -/** - * Kinds of an trie node. - */ -#define TOMMY_TRIE_TYPE_NODE 0 /**< The node is of type ::tommy_trie_node. */ -#define TOMMY_TRIE_TYPE_TREE 1 /**< The node is of type ::tommy_trie_tree. */ - -/** - * Get and set pointer of trie nodes. - * - * The pointer type is stored in the lower bit. - */ -#define trie_get_type(ptr) (((tommy_uintptr_t)(ptr)) & 1) -#define trie_get_tree(ptr) ((tommy_trie_tree*)(((tommy_uintptr_t)(ptr)) - TOMMY_TRIE_TYPE_TREE)) -#define trie_set_tree(ptr) (void*)(((tommy_uintptr_t)(ptr)) + TOMMY_TRIE_TYPE_TREE) - -void tommy_trie_init(tommy_trie* trie, tommy_allocator* alloc) -{ - unsigned i; - - for(i=0;ibucket[i] = 0; - - trie->count = 0; - trie->node_count = 0; - - trie->alloc = alloc; -} - -static void trie_bucket_insert(tommy_trie* trie, unsigned shift, tommy_trie_node** let_ptr, tommy_trie_node* insert, tommy_key_t key) -{ - tommy_trie_tree* tree; - tommy_trie_node* node; - void* ptr; - unsigned i, j; - -recurse: - ptr = *let_ptr; - - /* if null, just insert the node */ - if (!ptr) { - /* setup the node as a list */ - tommy_list_insert_first(let_ptr, insert); - return; - } - - if (trie_get_type(ptr) == TOMMY_TRIE_TYPE_TREE) { - /* repeat the process one level down */ - let_ptr = &trie_get_tree(ptr)->map[(key >> shift) & TOMMY_TRIE_TREE_MASK]; - shift -= TOMMY_TRIE_TREE_BIT; - goto recurse; - } - - node = tommy_cast(tommy_trie_node*, ptr); - - /* if it's the same key, insert in the list */ - if (node->key == key) { - tommy_list_insert_tail_not_empty(node, insert); - return; - } - -expand: - /* convert to a tree */ - tree = tommy_cast(tommy_trie_tree*, tommy_allocator_alloc(trie->alloc)); - ++trie->node_count; - *let_ptr = tommy_cast(tommy_trie_node*, trie_set_tree(tree)); - - /* initialize it */ - for(i=0;imap[i] = 0; - } - - /* get the position of the two elements */ - i = (node->key >> shift) & TOMMY_TRIE_TREE_MASK; - j = (key >> shift) & TOMMY_TRIE_TREE_MASK; - - /* if they don't collide */ - if (i != j) { - /* insert the already existing element */ - tree->map[i] = node; - - /* insert the new node */ - tommy_list_insert_first(&tree->map[j], insert); - return; - } - - /* expand one more level */ - let_ptr = &tree->map[i]; - shift -= TOMMY_TRIE_TREE_BIT; - goto expand; -} - -void tommy_trie_insert(tommy_trie* trie, tommy_trie_node* node, void* data, tommy_key_t key) -{ - tommy_trie_node** let_ptr; - - node->data = data; - node->key = key; - - let_ptr = &trie->bucket[key >> TOMMY_TRIE_BUCKET_SHIFT]; - - trie_bucket_insert(trie, TOMMY_TRIE_BUCKET_SHIFT, let_ptr, node, key); - - ++trie->count; -} - -static tommy_trie_node* trie_bucket_remove_existing(tommy_trie* trie, unsigned shift, tommy_trie_node** let_ptr, tommy_trie_node* remove, tommy_key_t key) -{ - tommy_trie_node* node; - tommy_trie_tree* tree; - void* ptr; - tommy_trie_node** let_back[TOMMY_TRIE_LEVEL_MAX+1]; - unsigned level; - unsigned i; - unsigned count; - unsigned last; - - level = 0; -recurse: - ptr = *let_ptr; - - if (!ptr) - return 0; - - if (trie_get_type(ptr) == TOMMY_TRIE_TYPE_TREE) { - tree = trie_get_tree(ptr); - - /* save the path */ - let_back[level++] = let_ptr; - - /* go down one level */ - let_ptr = &tree->map[(key >> shift) & TOMMY_TRIE_TREE_MASK]; - shift -= TOMMY_TRIE_TREE_BIT; - - goto recurse; - } - - node = tommy_cast(tommy_trie_node*, ptr); - - /* if the node to remove is not specified */ - if (!remove) { - /* remove the first */ - remove = node; - - /* check if it's really the element to remove */ - if (remove->key != key) - return 0; - } - - tommy_list_remove_existing(let_ptr, remove); - - /* if the list is not empty, try to reduce */ - if (*let_ptr || !level) - return remove; - -reduce: - /* go one level up */ - let_ptr = let_back[--level]; - - tree = trie_get_tree(*let_ptr); - - /* check if there is only one child node */ - count = 0; - last = 0; - for(i=0;imap[i]) { - /* if we have a sub tree, we cannot reduce */ - if (trie_get_type(tree->map[i]) != TOMMY_TRIE_TYPE_NODE) - return remove; - /* if more than one node, we cannot reduce */ - if (++count > 1) - return remove; - last = i; - } - } - - /* here count is never 0, as we cannot have a tree with only one sub node */ - assert(count == 1); - - *let_ptr = tree->map[last]; - - tommy_allocator_free(trie->alloc, tree); - --trie->node_count; - - /* repeat until more level */ - if (level) - goto reduce; - - return remove; -} - -void* tommy_trie_remove(tommy_trie* trie, tommy_key_t key) -{ - tommy_trie_node* ret; - tommy_trie_node** let_ptr; - - let_ptr = &trie->bucket[key >> TOMMY_TRIE_BUCKET_SHIFT]; - - ret = trie_bucket_remove_existing(trie, TOMMY_TRIE_BUCKET_SHIFT, let_ptr, 0, key); - - if (!ret) - return 0; - - --trie->count; - - return ret->data; -} - -void* tommy_trie_remove_existing(tommy_trie* trie, tommy_trie_node* node) -{ - tommy_trie_node* ret; - tommy_key_t key = node->key; - tommy_trie_node** let_ptr; - - let_ptr = &trie->bucket[key >> TOMMY_TRIE_BUCKET_SHIFT]; - - ret = trie_bucket_remove_existing(trie, TOMMY_TRIE_BUCKET_SHIFT, let_ptr, node, key); - - /* the element removed must match the one passed */ - assert(ret == node); - - --trie->count; - - return ret->data; -} - -tommy_trie_node* tommy_trie_bucket(tommy_trie* trie, tommy_key_t key) -{ - tommy_trie_node* node; - void* ptr; - unsigned type; - unsigned shift; - - ptr = trie->bucket[key >> TOMMY_TRIE_BUCKET_SHIFT]; - - shift = TOMMY_TRIE_BUCKET_SHIFT; - -recurse: - if (!ptr) - return 0; - - type = trie_get_type(ptr); - - switch (type) { - case TOMMY_TRIE_TYPE_NODE : - node = tommy_cast(tommy_trie_node*, ptr); - if (node->key != key) - return 0; - return node; - default: - case TOMMY_TRIE_TYPE_TREE : - ptr = trie_get_tree(ptr)->map[(key >> shift) & TOMMY_TRIE_TREE_MASK]; - shift -= TOMMY_TRIE_TREE_BIT; - goto recurse; - } -} - -tommy_size_t tommy_trie_memory_usage(tommy_trie* trie) -{ - return tommy_trie_count(trie) * (tommy_size_t)sizeof(tommy_trie_node) - + trie->node_count * (tommy_size_t)TOMMY_TRIE_BLOCK_SIZE; -} - diff -Nru librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/tommytrie.h librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/tommytrie.h --- librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/tommytrie.h 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/tommytrie.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,260 +0,0 @@ -/* - * Copyright 2010 Andrea Mazzoleni. All rights reserved. - * - * 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 ANDREA MAZZOLENI AND CONTRIBUTORS ``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 ANDREA MAZZOLENI 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. - */ - -/** \file - * Trie optimized for cache utilization. - * - * This trie is a standard implementation that stores elements in the order defined - * by the key. - * - * It needs an external allocator for the inner nodes in the trie. - * - * You can control the number of branches of each node using the ::TOMMY_TRIE_TREE_MAX - * define. More branches imply more speed, but a bigger memory occupation. - * - * Compared to ::tommy_trie_inplace you have to provide a ::tommy_allocator allocator. - * Note that the C malloc() is too slow to futfill this role. - * - * To initialize the trie you have to call tommy_allocator_init() to initialize - * the allocator, and tommy_trie_init() for the trie. - * - * \code - * tommy_allocator alloc; - * tommy_trie trie; - * - * tommy_allocator_init(&alloc, TOMMY_TRIE_BLOCK_SIZE, TOMMY_TRIE_BLOCK_SIZE); - * - * tommy_trie_init(&trie, &alloc); - * \endcode - * - * To insert elements in the trie you have to call tommy_trie_insert() for - * each element. - * In the insertion call you have to specify the address of the node, the - * address of the object, and the key value to use. - * The address of the object is used to initialize the tommy_node::data field - * of the node, and the key to initialize the tommy_node::key field. - * - * \code - * struct object { - * tommy_node node; - * // other fields - * int value; - * }; - * - * struct object* obj = malloc(sizeof(struct object)); // creates the object - * - * obj->value = ...; // initializes the object - * - * tommy_trie_insert(&trie, &obj->node, obj, obj->value); // inserts the object - * \endcode - * - * To find and element in the trie you have to call tommy_trie_search() providing - * the key to search. - * - * \code - * int value_to_find = 1; - * struct object* obj = tommy_trie_search(&trie, value_to_find); - * if (!obj) { - * // not found - * } else { - * // found - * } - * \endcode - * - * To iterate over all the elements in the trie with the same key, you have to - * use tommy_trie_bucket() and follow the tommy_node::next pointer until NULL. - * - * \code - * int value_to_find = 1; - * tommy_node* i = tommy_trie_bucket(&trie, value_to_find); - * while (i) { - * struct object* obj = i->data; // gets the object pointer - * - * printf("%d\n", obj->value); // process the object - * - * i = i->next; // goes to the next element - * } - * \endcode - * - * To remove an element from the trie you have to call tommy_trie_remove() - * providing the key to search and remove. - * - * \code - * struct object* obj = tommy_trie_remove(&trie, value_to_remove); - * if (obj) { - * free(obj); // frees the object allocated memory - * } - * \endcode - * - * To destroy the trie you have to remove all the elements, and deinitialize - * the trie calling tommy_trie_done() and the allocator using tommy_allocator_done(). - * - * \code - * tommy_trie_done(&trie); - * tommy_allocator_done(&alloc); - * \endcode - * - * Note that you cannot iterates over all the elements in the trie using the - * trie itself. You have to insert all the elements also in a ::tommy_list, - * and use the list to iterate. See the \ref multiindex example for more detail. - */ - -#ifndef __TOMMYTRIE_H -#define __TOMMYTRIE_H - -#include "tommytypes.h" -#include "tommyalloc.h" - -/******************************************************************************/ -/* trie */ - -/** - * Number of branches on each inner node. It must be a power of 2. - * Suggested values are 8, 16 and 32. - * Any inner node, excluding leafs, contains a pointer to each branch. - * - * The default size is choosen to exactly fit a typical cache line of 64 bytes. - */ -#define TOMMY_TRIE_TREE_MAX (64 / sizeof(void*)) - -/** - * Trie node. - * This is the node that you have to include inside your objects. - */ -typedef tommy_node tommy_trie_node; - -/** - * Trie block size. - * You must use this value to initialize the allocator. - */ -#define TOMMY_TRIE_BLOCK_SIZE (TOMMY_TRIE_TREE_MAX * sizeof(void*)) - -/** \internal - * Number of bits for each branch. - */ -#define TOMMY_TRIE_TREE_BIT TOMMY_ILOG2(TOMMY_TRIE_TREE_MAX) - -/** \internal - * Number of bits of the first level. - */ -#define TOMMY_TRIE_BUCKET_BIT ((TOMMY_KEY_BIT % TOMMY_TRIE_TREE_BIT) + TOMMY_TRIE_TREE_BIT) - -/** \internal - * Number of branches of the first level. - * It's like a inner branch, but bigger to get any remainder bits. - */ -#define TOMMY_TRIE_BUCKET_MAX (1 << TOMMY_TRIE_BUCKET_BIT) - -/** - * Trie optimized for cache utilization. - */ -typedef struct tommy_trie_struct { - tommy_trie_node* bucket[TOMMY_TRIE_BUCKET_MAX]; /**< First tree level. */ - unsigned count; /**< Number of elements. */ - unsigned node_count; /**< Number of nodes. */ - tommy_allocator* alloc; /**< Allocator for internal nodes. */ -} tommy_trie; - -/** - * Initializes the trie. - * You have to provide an allocator initialized with *both* the size and align with TOMMY_TRIE_BLOCK_SIZE. - * You can share this allocator with other tries. - * - * The tries is completely allocated through the allocator, and it doesn't need to be deinitialized. - * \param alloc Allocator initialized with *both* the size and align with TOMMY_TRIE_BLOCK_SIZE. - */ -void tommy_trie_init(tommy_trie* trie, tommy_allocator* alloc); - -/** - * Inserts an element in the trie. - * You have to provide the pointer of the node embedded into the object, - * the pointer at the object and the key to use. - * \param node Pointer at the node embedded into the object to insert. - * \param data Pointer at the object to insert. - * \param key Key to use to insert the object. - */ -void tommy_trie_insert(tommy_trie* trie, tommy_trie_node* node, void* data, tommy_key_t key); - -/** - * Searches and removes the first element with the specified key. - * If the element is not found, 0 is returned. - * If more equal elements are present, the first one is removed. - * This operation is faster than calling tommy_trie_bucket() and tommy_trie_remove_existing() separately. - * \param key Key of the element to find and remove. - * \return The removed element, or 0 if not found. - */ -void* tommy_trie_remove(tommy_trie* trie, tommy_key_t key); - -/** - * Gets the bucket of the specified key. - * The bucket is guaranteed to contain ALL and ONLY the elements with the specified key. - * You can access elements in the bucket following the ::next pointer until 0. - * \param key Key of the element to find. - * \return The head of the bucket, or 0 if empty. - */ -tommy_trie_node* tommy_trie_bucket(tommy_trie* trie, tommy_key_t key); - -/** - * Searches an element in the trie. - * You have to provide the key of the element you want to find. - * If more elements with the same key are present, the first one is returned. - * \param key Key of the element to find. - * \return The first element found, or 0 if none. - */ -tommy_inline void* tommy_trie_search(tommy_trie* trie, tommy_key_t key) -{ - tommy_trie_node* i = tommy_trie_bucket(trie, key); - - if (!i) - return 0; - - return i->data; -} - -/** - * Removes an element from the trie. - * You must already have the address of the element to remove. - * \return The tommy_node::data field of the node removed. - */ -void* tommy_trie_remove_existing(tommy_trie* trie, tommy_trie_node* node); - -/** - * Returns the number of elements. - */ -tommy_inline unsigned tommy_trie_count(tommy_trie* trie) -{ - return trie->count; -} - -/** - * Gets the size of allocated memory. - * It includes the size of the ::tommy_trie_node of the stored elements. - */ -tommy_size_t tommy_trie_memory_usage(tommy_trie* trie); - -#endif - diff -Nru librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/tommytrieinp.c librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/tommytrieinp.c --- librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/tommytrieinp.c 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/tommytrieinp.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,272 +0,0 @@ -/* - * Copyright 2010 Andrea Mazzoleni. All rights reserved. - * - * 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 ANDREA MAZZOLENI AND CONTRIBUTORS ``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 ANDREA MAZZOLENI 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 "tommytrieinp.h" - -#include /* for assert */ - -/******************************************************************************/ -/* trie_inplace */ - -/** - * Mask for the inner branches. - */ -#define TOMMY_TRIE_INPLACE_TREE_MASK (TOMMY_TRIE_INPLACE_TREE_MAX-1) - -/** - * Shift for the first level of branches. - */ -#define TOMMY_TRIE_INPLACE_BUCKET_SHIFT (TOMMY_KEY_BIT - TOMMY_TRIE_INPLACE_BUCKET_BIT) - -/** - * Create a new list with a single element. - */ -tommy_inline tommy_trie_inplace_node* tommy_trie_inplace_list_insert_first(tommy_trie_inplace_node* node) -{ - /* one element "circular" prev list */ - node->prev = node; - - /* one element "0 terminated" next list */ - node->next = 0; - - return node; -} - -/** - * Add an element to an existing list. - * \note The element is inserted at the end of the list. - */ -tommy_inline void tommy_trie_inplace_list_insert_tail_not_empty(tommy_trie_inplace_node* head, tommy_trie_inplace_node* node) -{ - /* insert in the list in the last position */ - - /* insert in the "circular" prev list */ - node->prev = head->prev; - head->prev = node; - - /* insert in the "0 terminated" next list */ - node->next = 0; - node->prev->next = node; -} - -/** - * Remove an element from the list. - */ -tommy_inline void tommy_trie_inplace_list_remove(tommy_trie_inplace_node** let_ptr, tommy_trie_inplace_node* node) -{ - tommy_trie_inplace_node* head = *let_ptr; - - /* remove from the "circular" prev list */ - if (node->next) { - node->next->prev = node->prev; - } else { - head->prev = node->prev; /* the last */ - } - - /* remove from the "0 terminated" next list */ - if (head == node) { - *let_ptr = node->next; /* the new first */ - } else { - node->prev->next = node->next; - } -} - -void tommy_trie_inplace_init(tommy_trie_inplace* trie_inplace) -{ - unsigned i; - - for(i=0;ibucket[i] = 0; - - trie_inplace->count = 0; -} - -static void trie_inplace_bucket_insert(unsigned shift, tommy_trie_inplace_node** let_ptr, tommy_trie_inplace_node* insert, tommy_key_t key) -{ - tommy_trie_inplace_node* node; - - node = *let_ptr; - while (node && node->key != key) { - let_ptr = &node->map[(key >> shift) & TOMMY_TRIE_INPLACE_TREE_MASK]; - node = *let_ptr; - shift -= TOMMY_TRIE_INPLACE_TREE_BIT; - } - - /* if null, just insert the node */ - if (!node) { - /* setup the node as a list */ - *let_ptr = tommy_trie_inplace_list_insert_first(insert); - } else { - /* if it's the same key, insert in the list */ - tommy_trie_inplace_list_insert_tail_not_empty(node, insert); - } -} - -void tommy_trie_inplace_insert(tommy_trie_inplace* trie_inplace, tommy_trie_inplace_node* node, void* data, tommy_key_t key) -{ - tommy_trie_inplace_node** let_ptr; - unsigned i; - - node->data = data; - node->key = key; - /* clear the child pointers */ - for(i=0;imap[i] = 0; - - let_ptr = &trie_inplace->bucket[key >> TOMMY_TRIE_INPLACE_BUCKET_SHIFT]; - - trie_inplace_bucket_insert(TOMMY_TRIE_INPLACE_BUCKET_SHIFT, let_ptr, node, key); - - ++trie_inplace->count; -} - -static tommy_trie_inplace_node* trie_inplace_bucket_remove(unsigned shift, tommy_trie_inplace_node** let_ptr, tommy_trie_inplace_node* remove, tommy_key_t key) -{ - tommy_trie_inplace_node* node; - int i; - tommy_trie_inplace_node** leaf_let_ptr; - tommy_trie_inplace_node* leaf; - - node = *let_ptr; - while (node && node->key != key) { - let_ptr = &node->map[(key >> shift) & TOMMY_TRIE_INPLACE_TREE_MASK]; - node = *let_ptr; - shift -= TOMMY_TRIE_INPLACE_TREE_BIT; - } - - if (!node) - return 0; - - /* if the node to remove is not specified */ - if (!remove) { - /* remove the first */ - remove = node; - } - - tommy_trie_inplace_list_remove(let_ptr, remove); - - /* if we have a substitute */ - if (*let_ptr != 0) { - /* copy the child pointers to the new one */ - node = *let_ptr; - for(i=0;imap[i] = remove->map[i]; - } - return remove; - } - - /* find a leaf */ - leaf_let_ptr = 0; - leaf = remove; - - /* search backward, statistically we have more zeros than ones */ - i = TOMMY_TRIE_INPLACE_TREE_MAX-1; - while (i >= 0) { - if (leaf->map[i]) { - leaf_let_ptr = &leaf->map[i]; - leaf = *leaf_let_ptr; - i = TOMMY_TRIE_INPLACE_TREE_MAX-1; - continue; - } - --i; - } - - /* if it's itself a leaf */ - if (!leaf_let_ptr) { - return remove; - } - - /* remove the leaf */ - *leaf_let_ptr = 0; - - /* copy the child pointers */ - for(i=0;imap[i] = remove->map[i]; - } - - /* put it in place */ - *let_ptr = leaf; - - return remove; -} - -void* tommy_trie_inplace_remove(tommy_trie_inplace* trie_inplace, tommy_key_t key) -{ - tommy_trie_inplace_node* ret; - tommy_trie_inplace_node** let_ptr; - - let_ptr = &trie_inplace->bucket[key >> TOMMY_TRIE_INPLACE_BUCKET_SHIFT]; - - ret = trie_inplace_bucket_remove(TOMMY_TRIE_INPLACE_BUCKET_SHIFT, let_ptr, 0, key); - - if (!ret) - return 0; - - --trie_inplace->count; - - return ret->data; -} - -void* tommy_trie_inplace_remove_existing(tommy_trie_inplace* trie_inplace, tommy_trie_inplace_node* node) -{ - tommy_trie_inplace_node* ret; - tommy_key_t key = node->key; - tommy_trie_inplace_node** let_ptr; - - let_ptr = &trie_inplace->bucket[key >> TOMMY_TRIE_INPLACE_BUCKET_SHIFT]; - - ret = trie_inplace_bucket_remove(TOMMY_TRIE_INPLACE_BUCKET_SHIFT, let_ptr, node, key); - - /* the element removed must match the one passed */ - assert(ret == node); - - --trie_inplace->count; - - return ret->data; -} - -tommy_trie_inplace_node* tommy_trie_inplace_bucket(tommy_trie_inplace* trie_inplace, tommy_key_t key) -{ - tommy_trie_inplace_node* node; - unsigned shift; - - node = trie_inplace->bucket[key >> TOMMY_TRIE_INPLACE_BUCKET_SHIFT]; - shift = TOMMY_TRIE_INPLACE_BUCKET_SHIFT; - - while (node && node->key != key) { - node = node->map[(key >> shift) & TOMMY_TRIE_INPLACE_TREE_MASK]; - shift -= TOMMY_TRIE_INPLACE_TREE_BIT; - } - - return node; -} - -tommy_size_t tommy_trie_inplace_memory_usage(tommy_trie_inplace* trie_inplace) -{ - return tommy_trie_inplace_count(trie_inplace) * (tommy_size_t)sizeof(tommy_trie_inplace_node); -} - diff -Nru librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/tommytrieinp.h librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/tommytrieinp.h --- librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/tommytrieinp.h 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/tommytrieinp.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,238 +0,0 @@ -/* - * Copyright 2010 Andrea Mazzoleni. All rights reserved. - * - * 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 ANDREA MAZZOLENI AND CONTRIBUTORS ``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 ANDREA MAZZOLENI 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. - */ - -/** \file - * Inplace trie. - * - * This trie is a inplace implementation not needing any external allocation. - * - * Elements are not stored in order, like ::tommy_trie, because some elements - * should be used to represent the inner nodes in the trie. - * - * You can control the number of branches of each node using the ::TOMMY_TRIE_INPLACE_TREE_MAX define. - * More branches imply more speed, but a bigger memory occupation. - * - * Compared to ::tommy_trie you should use a lower number of branches to limit the unused memory - * occupation of the leaf nodes. This imply a lower speed, but without the need of an external allocator. - * - * To initialize the trie you have to call tommy_trie_inplace_init(). - * - * \code - * tommy_trie_inplace trie_inplace; - * - * tommy_trie_inplace_init(&trie_inplace); - * \endcode - * - * To insert elements in the trie you have to call tommy_trie_inplace_insert() for - * each element. - * In the insertion call you have to specify the address of the node, the - * address of the object, and the key value to use. - * The address of the object is used to initialize the tommy_node::data field - * of the node, and the key to initialize the tommy_node::key field. - * - * \code - * struct object { - * tommy_node node; - * // other fields - * int value; - * }; - * - * struct object* obj = malloc(sizeof(struct object)); // creates the object - * - * obj->value = ...; // initializes the object - * - * tommy_trie_inplace_insert(&trie_inplace, &obj->node, obj, obj->value); // inserts the object - * \endcode - * - * To find and element in the trie you have to call tommy_trie_inplace_search() providing - * the key to search. - * - * \code - * int value_to_find = 1; - * struct object* obj = tommy_trie_inplace_search(&trie_inplace, value_to_find); - * if (!obj) { - * // not found - * } else { - * // found - * } - * \endcode - * - * To iterate over all the elements in the trie with the same key, you have to - * use tommy_trie_inplace_bucket() and follow the tommy_node::next pointer until NULL. - * - * \code - * int value_to_find = 1; - * tommy_node* i = tommy_trie_inplace_bucket(&trie_inplace, value_to_find); - * while (i) { - * struct object* obj = i->data; // gets the object pointer - * - * printf("%d\n", obj->value); // process the object - * - * i = i->next; // goes to the next element - * } - * \endcode - * - * To remove an element from the trie you have to call tommy_trie_inplace_remove() - * providing the key to search and remove. - * - * \code - * struct object* obj = tommy_trie_inplace_remove(&trie_inplace, value_to_remove); - * if (obj) { - * free(obj); // frees the object allocated memory - * } - * \endcode - * - * To destroy the trie you have only to remove all the elements, as the trie is - * completely inplace and it doesn't allocate memory. - * - * Note that you cannot iterates over all the elements in the trie using the - * trie itself. You have to insert all the elements also in a ::tommy_list, - * and use the list to iterate. See the \ref multiindex example for more detail. - */ - -#ifndef __TOMMYTRIEINP_H -#define __TOMMYTRIEINP_H - -#include "tommytypes.h" - -/******************************************************************************/ -/* trie_inplace */ - -/** - * Number of branches on each node. It must be a power of 2. - * Suggested values are 2, 4 and 8. - * Any node, including leafs, contains a pointer to each branch. - */ -#define TOMMY_TRIE_INPLACE_TREE_MAX 4 - -/** \internal - * Number of bits for each branch. - */ -#define TOMMY_TRIE_INPLACE_TREE_BIT TOMMY_ILOG2(TOMMY_TRIE_INPLACE_TREE_MAX) - -/** \internal - * Number of bits of the first level. - */ -#define TOMMY_TRIE_INPLACE_BUCKET_BIT ((TOMMY_KEY_BIT % TOMMY_TRIE_INPLACE_TREE_BIT) + 3 * TOMMY_TRIE_INPLACE_TREE_BIT) - -/** \internal - * Number of branches of the first level. - * It's like a inner branch, but bigger to get any remainder bits. - */ -#define TOMMY_TRIE_INPLACE_BUCKET_MAX (1 << TOMMY_TRIE_INPLACE_BUCKET_BIT) - -/** - * Inplace trie node. - * This is the node that you have to include inside your objects. - */ -typedef struct tommy_trie_inplace_node_struct { - struct tommy_trie_inplace_node_struct* next; /**< Next element. 0 if it's the last. */ - struct tommy_trie_inplace_node_struct* prev; /**< Circular previous element. */ - void* data; /**< Pointer at the data. */ - tommy_key_t key; /**< Used to store the key or the hash. */ - struct tommy_trie_inplace_node_struct* map[TOMMY_TRIE_INPLACE_TREE_MAX]; /** Branches of the node. */ -} tommy_trie_inplace_node; - -/** - * Inplace trie. - */ -typedef struct tommy_trie_inplace_struct { - tommy_trie_inplace_node* bucket[TOMMY_TRIE_INPLACE_BUCKET_MAX]; /**< First tree level. */ - unsigned count; /**< Number of elements. */ -} tommy_trie_inplace; - -/** - * Initializes the trie. - * - * The tries is completely inplace, and it doesn't need to be deinitialized. - */ -void tommy_trie_inplace_init(tommy_trie_inplace* trie_inplace); - -/** - * Inserts an element in the trie. - */ -void tommy_trie_inplace_insert(tommy_trie_inplace* trie_inplace, tommy_trie_inplace_node* node, void* data, tommy_key_t key); - -/** - * Searches and removes the first element with the specified key. - * If the element is not found, 0 is returned. - * If more equal elements are present, the first one is removed. - * This operation is faster than calling tommy_trie_inplace_bucket() and tommy_trie_inplace_remove_existing() separately. - * \param key Key of the element to find and remove. - * \return The removed element, or 0 if not found. - */ -void* tommy_trie_inplace_remove(tommy_trie_inplace* trie_inplace, tommy_key_t key); - -/** - * Gets the bucket of the specified key. - * The bucket is guaranteed to contain ALL and ONLY the elements with the specified key. - * You can access elements in the bucket following the ::next pointer until 0. - * \param key Key of the element to find. - * \return The head of the bucket, or 0 if empty. - */ -tommy_trie_inplace_node* tommy_trie_inplace_bucket(tommy_trie_inplace* trie_inplace, tommy_key_t key); - -/** - * Searches an element in the trie. - * You have to provide the key of the element you want to find. - * If more elements with the same key are present, the first one is returned. - * \param key Key of the element to find. - * \return The first element found, or 0 if none. - */ -tommy_inline void* tommy_trie_inplace_search(tommy_trie_inplace* trie_inplace, tommy_key_t key) -{ - tommy_trie_inplace_node* i = tommy_trie_inplace_bucket(trie_inplace, key); - - if (!i) - return 0; - - return i->data; -} - -/** - * Removes an element from the trie. - * You must already have the address of the element to remove. - * \return The tommy_node::data field of the node removed. - */ -void* tommy_trie_inplace_remove_existing(tommy_trie_inplace* trie_inplace, tommy_trie_inplace_node* node); - -/** - * Returns the number of elements. - */ -tommy_inline unsigned tommy_trie_inplace_count(tommy_trie_inplace* trie_inplace) -{ - return trie_inplace->count; -} - -/** - * Gets the size of allocated memory. - * It includes the size of the ::tommy_inplace_node of the stored elements. - */ -tommy_size_t tommy_trie_inplace_memory_usage(tommy_trie_inplace* trie_inplace); - -#endif - diff -Nru librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/tommytypes.h librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/tommytypes.h --- librtr-0.6.3/rtrlib/spki/hashtable/tommyds-1.8/tommytypes.h 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/rtrlib/spki/hashtable/tommyds-1.8/tommytypes.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,384 +0,0 @@ -/* - * Copyright 2010 Andrea Mazzoleni. All rights reserved. - * - * 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 ANDREA MAZZOLENI AND CONTRIBUTORS ``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 ANDREA MAZZOLENI 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. - */ - -/** \file - * Generic types. - */ - -#ifndef __TOMMYTYPES_H -#define __TOMMYTYPES_H - -/******************************************************************************/ -/* types */ - -#if defined(_MSC_VER) -#include -typedef unsigned tommy_uint32_t; /**< Generic uint32_t type. */ -typedef unsigned _int64 tommy_uint64_t; /**< Generic uint64_t type. */ -typedef size_t tommy_size_t; /**< Generic size_t type. */ -typedef size_t tommy_uintptr_t; /**< Generic uintptr_t type. */ -#else -#include -typedef uint32_t tommy_uint32_t; /**< Generic uint32_t type. */ -typedef uint64_t tommy_uint64_t; /**< Generic uint64_t type. */ -typedef uintptr_t tommy_size_t; /**< Generic size_t type. */ -typedef uintptr_t tommy_uintptr_t; /**< Generic uintptr_t type. */ -#endif -typedef int tommy_bool_t; /**< Generic boolean type. */ - -/** \internal - * Type cast required for the C++ compilation. - * When compiling in C++ we cannot convert a void* pointer to another pointer. - * In such case we need an explicit cast. - */ -#ifdef __cplusplus -#define tommy_cast(type, value) static_cast(value) -#else -#define tommy_cast(type, value) (value) -#endif - -/******************************************************************************/ -/* heap */ - -/* by default uses malloc/realloc/free */ - -/** - * Generic malloc(), realloc() and free() functions. - * Redefine them to what you need. By default they map to the C malloc(), realloc() and free(). - */ -#if !defined(tommy_malloc) && !defined(tommy_realloc) && !defined(tommy_free) -#include "rtrlib/lib/alloc_utils_private.h" -#define tommy_malloc lrtr_malloc -#define tommy_realloc lrtr_realloc -#define tommy_free lrtr_free -#endif - -/******************************************************************************/ -/* modificators */ - -/** \internal - * Definition of the inline keyword if available. - */ -#if !defined(tommy_inline) -#if defined(_MSC_VER) || defined(__GNUC__) -#define tommy_inline static __inline -#else -#define tommy_inline static -#endif -#endif - -/** \internal - * Definition of the restrict keyword if available. - */ -#if !defined(tommy_restrict) -#if __STDC_VERSION__ >= 199901L -#define tommy_restrict restrict -#elif defined(_MSC_VER) || defined(__GNUC__) -#define tommy_restrict __restrict -#else -#define tommy_restrict -#endif -#endif - -/** \internal - * Hints the compiler that a condition is likely true. - */ -#if !defined(tommy_likely) -#if defined(__GNUC__) -#define tommy_likely(x) __builtin_expect(!!(x),1) -#else -#define tommy_likely(x) (x) -#endif -#endif - -/** \internal - * Hints the compiler that a condition is likely false. - */ -#if !defined(tommy_unlikely) -#if defined(__GNUC__) -#define tommy_unlikely(x) __builtin_expect(!!(x),0) -#else -#define tommy_unlikely(x) (x) -#endif -#endif - -/******************************************************************************/ -/* key */ - -/** - * Key type used in indexed data structures to store the key or the hash value. - */ -typedef tommy_uint32_t tommy_key_t; - -/** - * Bits into the ::tommy_key_t type. - */ -#define TOMMY_KEY_BIT (sizeof(tommy_key_t) * 8) - -/******************************************************************************/ -/* node */ - -/** - * Data structure node. - * This node type is shared between all the data structures and used to store some - * info directly into the objects you want to store. - * - * A typical declaration is: - * \code - * struct object { - * tommy_node node; - * // other fields - * }; - * \endcode - */ -typedef struct tommy_node_struct { - /** - * Next node. - * The tail node has it at 0, like a 0 terminated list. - */ - struct tommy_node_struct* next; - - /** - * Previous node. - * The head node points to the tail node, like a circular list. - */ - struct tommy_node_struct* prev; - - /** - * Pointer at the object containing the node. - * This field is initialized when inserting nodes into a data structure. - */ - void* data; - - /** - * Key used to store the node. - * With hashtables this field is used to store the hash value. - * With lists this field is not used. - */ - tommy_key_t key; -} tommy_node; - -/******************************************************************************/ -/* compare */ - -/** - * Compare function for elements. - * \param obj_a Pointer at the first object to compare. - * \param obj_b Pointer at the second object to compare. - * \return <0 if the first element is less than the second, ==0 equal, >0 if greather. - * - * This function is like the C strcmp(). - * - * \code - * struct object { - * tommy_node node; - * int value; - * }; - * - * int compare(const void* obj_a, const void* obj_b) - * { - * if (((const struct object*)obj_a)->value < ((const struct object*)obj_b)->value) - * return -1; - * if (((const struct object*)obj_a)->value > ((const struct object*)obj_b)->value) - * return 1; - * return 0; - * } - * - * tommy_list_sort(&list, compare); - * \endcode - * - */ -typedef int tommy_compare_func(const void* obj_a, const void* obj_b); - -/** - * Search function for elements. - * \param arg Pointer at the value to search. - * \param obj Pointer at the object to compare to. - * \return ==0 if the value matches the element. != 0 if different. - * - * Note that the first argument is a pointer to the value to search and - * the second one is a pointer to the object to compare. - * They are pointers of two different types. - * - * \code - * struct object { - * tommy_node node; - * int value; - * }; - * - * int compare(const void* arg, const void* obj) - * { - * return *(const int*)arg != ((const struct object*)obj)->value; - * } - * - * int value_to_find = 1; - * struct object* obj = tommy_hashtable_search(&hashtable, compare, &value_to_find, tommy_inthash_u32(value_to_find)); - * if (!obj) { - * // not found - * } else { - * // found - * } - * \endcode - * - */ -typedef int tommy_search_func(const void* arg, const void* obj); - -/** - * Foreach function. - * \param obj Pointer at the object to iterate. - * - * A typical example is to use free() to deallocate all the objects in a list. - * \code - * tommy_list_foreach(&list, (tommy_foreach_func*)free); - * \endcode - */ -typedef void tommy_foreach_func(void* obj); - -/** - * Foreach function with an argument. - * \param arg Pointer at a generic argument. - * \param obj Pointer at the object to iterate. - */ -typedef void tommy_foreach_arg_func(void* arg, void* obj); - -/******************************************************************************/ -/* bit hacks */ - -#if defined(_MSC_VER) && !defined(__cplusplus) -#include -#pragma intrinsic(_BitScanReverse) -#pragma intrinsic(_BitScanForward) -#endif - -/** \internal - * Integer log2 for constants. - * You can use it only for exact power of 2 up to 256. - */ -#define TOMMY_ILOG2(value) ((value) == 256 ? 8 : (value) == 128 ? 7 :(value) == 64 ? 6 : (value) == 32 ? 5 : (value) == 16 ? 4 : (value) == 8 ? 3 : (value) == 4 ? 2 : (value) == 2 ? 1 : 0) - -/** - * Bit scan reverse or integer log2. - * Return the bit index of the most significant 1 bit. - * - * If no bit is set, the result is undefined. - * To force a return 0 in this case, you can use tommy_ilog2(value | 1). - * - * Other interesting ways for bitscan can be found at: - * - * Bit Twiddling Hacks - * http://graphics.stanford.edu/~seander/bithacks.html - * - * Chess Programming BitScan - * http://chessprogramming.wikispaces.com/BitScan - * - * \param value Value to scan. 0 is not allowed. - * \return The index of the most significan bit set. - */ -tommy_inline unsigned tommy_ilog2_u32(tommy_uint32_t value) -{ -#if defined(_MSC_VER) - unsigned long count; - _BitScanReverse(&count, value); - return count; -#elif defined(__GNUC__) - /* - * GCC implements __builtin_clz(x) as "__builtin_clz(x) = bsr(x) ^ 31" - * - * Where "x ^ 31 = 31 - x", but gcc does not optimize "31 - __builtin_clz(x)" to bsr(x), - * but generates 31 - (bsr(x) xor 31). - * - * So we write "__builtin_clz(x) ^ 31" instead of "31 - __builtin_clz(x)". - */ - return __builtin_clz(value) ^ 31; -#else - /* Find the log base 2 of an N-bit integer in O(lg(N)) operations with multiply and lookup */ - /* from http://graphics.stanford.edu/~seander/bithacks.html */ - static const int TOMMY_DE_BRUIJN_INDEX_ILOG2[32] = { - 0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30, - 8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31 - }; - - value |= value >> 1; - value |= value >> 2; - value |= value >> 4; - value |= value >> 8; - value |= value >> 16; - - return TOMMY_DE_BRUIJN_INDEX_ILOG2[(tommy_uint32_t)(value * 0x07C4ACDDU) >> 27]; -#endif -} - -/** - * Bit scan forward or trailing zero count. - * Return the bit index of the least significant 1 bit. - * - * If no bit is set, the result is undefined. - * \param value Value to scan. 0 is not allowed. - * \return The index of the least significant bit set. - */ -tommy_inline unsigned tommy_ctz_u32(tommy_uint32_t value) -{ -#if defined(_MSC_VER) - unsigned long count; - _BitScanForward(&count, value); - return count; -#elif defined(__GNUC__) - return __builtin_ctz(value); -#else - /* Count the consecutive zero bits (trailing) on the right with multiply and lookup */ - /* from http://graphics.stanford.edu/~seander/bithacks.html */ - static const tommy_uint32_t TOMMY_DE_BRUIJN_INDEX_CTZ[32] = { - 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, - 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9 - }; - - return TOMMY_DE_BRUIJN_INDEX_CTZ[(tommy_uint32_t)(((value & -value) * 0x077CB531U)) >> 27]; -#endif -} - -/** - * Rounds up to the next power of 2. - * For the value 0, the result is undefined. - * \return The smallest power of 2 not less than the specified value. - */ -tommy_inline tommy_uint32_t tommy_roundup_pow2_u32(tommy_uint32_t value) -{ - /* Round up to the next highest power of 2 */ - /* from http://www-graphics.stanford.edu/~seander/bithacks.html */ - - --value; - value |= value >> 1; - value |= value >> 2; - value |= value >> 4; - value |= value >> 8; - value |= value >> 16; - ++value; - - return value; -} -#endif - diff -Nru librtr-0.6.3/rtrlib/transport/ssh/ssh_transport.c librtr-0.7.0/rtrlib/transport/ssh/ssh_transport.c --- librtr-0.6.3/rtrlib/transport/ssh/ssh_transport.c 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/rtrlib/transport/ssh/ssh_transport.c 2019-07-18 08:26:08.000000000 +0000 @@ -231,14 +231,14 @@ ssh_socket->config.bindaddr = NULL; } - if (config->bindaddr) { + if (config->client_privkey_path) { ssh_socket->config.client_privkey_path = lrtr_strdup(config->client_privkey_path); } else { ssh_socket->config.client_privkey_path = NULL; } - if (config->bindaddr) { + if (config->server_hostkey_path) { ssh_socket->config.server_hostkey_path = lrtr_strdup(config->server_hostkey_path); } else { diff -Nru librtr-0.6.3/rtrlib/transport/tcp/tcp_transport.c librtr-0.7.0/rtrlib/transport/tcp/tcp_transport.c --- librtr-0.6.3/rtrlib/transport/tcp/tcp_transport.c 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/rtrlib/transport/tcp/tcp_transport.c 2019-07-18 08:26:08.000000000 +0000 @@ -181,7 +181,7 @@ size_t len; struct tr_tcp_socket *sock = socket; - assert(socket != NULL); + assert(sock != NULL); if (sock->ident != NULL) return sock->ident; diff -Nru librtr-0.6.3/rtrlib/transport/transport.c librtr-0.7.0/rtrlib/transport/transport.c --- librtr-0.6.3/rtrlib/transport/transport.c 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/rtrlib/transport/transport.c 2019-07-18 08:26:08.000000000 +0000 @@ -35,6 +35,7 @@ return socket->recv_fp(socket->socket, buf, len, timeout); } +/* cppcheck-suppress unusedFunction */ inline const char *tr_ident(struct tr_socket *sock) { return sock->ident_fp(sock->socket); diff -Nru librtr-0.6.3/scripts/check-coding-files.txt librtr-0.7.0/scripts/check-coding-files.txt --- librtr-0.6.3/scripts/check-coding-files.txt 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/scripts/check-coding-files.txt 2019-07-18 08:26:08.000000000 +0000 @@ -10,6 +10,8 @@ rtrlib/lib/ipv4_private.h rtrlib/lib/ipv6.h rtrlib/lib/ipv6_private.h +rtrlib/lib/log.c +rtrlib/lib/log_private.h rtrlib/pfx/trie/trie_private.h rtrlib/pfx/trie/trie.c tests/test_ht_spkitable.c diff -Nru librtr-0.6.3/scripts/check-coding-style.sh librtr-0.7.0/scripts/check-coding-style.sh --- librtr-0.6.3/scripts/check-coding-style.sh 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/scripts/check-coding-style.sh 2019-07-18 08:26:08.000000000 +0000 @@ -29,12 +29,12 @@ cd $SCRIPT_DIR/.. for i in $CHECKSOURCE; do echo "> check coding style of $i ..." - IGNORE="PREFER_KERNEL_TYPES,CONST_STRUCT,OPEN_BRACE" + IGNORE="PREFER_KERNEL_TYPES,CONST_STRUCT,OPEN_BRACE,SPDX_LICENSE_TAG,OPEN_ENDED_LINE,UNNECESSARY_PARENTHESES,PREFER_PRINTF,GLOBAL_INITIALISERS,PREFER_PACKED" if [[ $i == *"unittest"* ]]; then IGNORE="${IGNORE},CAMELCASE" fi $SCRIPT_DIR/checkpatch.pl -f --strict --no-tree --terse --show-types \ - --ignore ${IGNORE} $i + --max-line-length 120 --ignore ${IGNORE} $i if [ $? -ne "0" ]; then EXIT_CODE=1 diff -Nru librtr-0.6.3/scripts/check-exports.sh librtr-0.7.0/scripts/check-exports.sh --- librtr-0.6.3/scripts/check-exports.sh 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/scripts/check-exports.sh 2019-07-18 08:26:08.000000000 +0000 @@ -5,21 +5,20 @@ # Must be run from rtrlibs root dir. # ---HELP--- -SO_SYMBOL_WHITELIST=(__bss_start _edata _end _fini _init) +SO_SYMBOL_WHITELIST=(__bss_start _edata _end _fini _init __gcov_master __gcov_sort_n_vals __gcov_var) ERROR=0 # Functions annoted with RTRLIB_EXPORT EXPORTS=$( - grep -r '^RTRLIB_EXPORT' rtrlib | - grep -Po '(?<= ).*?(?=\(.*?)' | grep -o '\w*$' | # extract function name + ctags -x --c-kinds=fp $(find rtrlib -iname '*.c' ! -name '*tommy*') | + grep 'RTRLIB_EXPORT' | awk '{ print $1 }' | sort) # Functions found in public headers HEADER_SYMBOLS=$( - cat $(find rtrlib -iname '*.h' -type f ! -name '*_private.h' ! -name '*tommy*') | # cat all public headers - gcc -o - -xc -fpreprocessed -dD -E -P - | # strip comments via gcc - grep -Po '(?<= ).*?(?=\(.*?)' | grep -o '\w*$' | # extract function name + ctags -x --c-kinds=fp $(find rtrlib -iname '*.h' -type f ! -name '*_private.h' ! -name '*tommy*') | + awk '{ print $1 }' | sort) # Symbols found in librtrs dynamic export table diff -Nru librtr-0.6.3/scripts/checkpatch.pl librtr-0.7.0/scripts/checkpatch.pl --- librtr-0.6.3/scripts/checkpatch.pl 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/scripts/checkpatch.pl 2019-07-18 08:26:08.000000000 +0000 @@ -1,15 +1,19 @@ -#!/usr/bin/perl -w +#!/usr/bin/env perl +# SPDX-License-Identifier: GPL-2.0 +# # (c) 2001, Dave Jones. (the file handling bit) # (c) 2005, Joel Schopp (the ugly bit) # (c) 2007,2008, Andy Whitcroft (new conditions, test suite) # (c) 2008-2010 Andy Whitcroft -# Licensed under the terms of the GNU GPL License version 2 +# (c) 2010-2018 Joe Perches use strict; +use warnings; use POSIX; use File::Basename; use Cwd 'abs_path'; use Term::ANSIColor qw(:constants); +use Encode qw(decode encode); my $P = $0; my $D = dirname(abs_path($P)); @@ -55,7 +59,8 @@ my $codespell = 0; my $codespellfile = "/usr/share/codespell/dictionary.txt"; my $conststructsfile = "$D/const_structs.checkpatch"; -my $color = 1; +my $typedefsfile = ""; +my $color = "auto"; my $allow_c99_comments = 1; sub help { @@ -113,7 +118,9 @@ --codespell Use the codespell dictionary for spelling/typos (default:/usr/share/codespell/dictionary.txt) --codespellfile Use this codespell dictionary - --color Use colors when output is STDOUT (default: on) + --typedefsfile Read additional types from this file + --color[=WHEN] Use colors 'always', 'never', or only when output + is a terminal ('auto'). Default is 'auto'. -h, --help, --version display this help and exit When FILE is - read standard input. @@ -141,7 +148,8 @@ close($script); my @types = (); - for ($text =~ /\b(?:(?:CHK|WARN|ERROR)\s*\(\s*"([^"]+)")/g) { + # Also catch when type or level is passed through a variable + for ($text =~ /(?:(?:\bCHK|\bWARN|\bERROR|&\{\$msg_level})\s*\(|\$msg_type\s*=)\s*"([^"]+)"/g) { push (@types, $_); } @types = sort(uniq(@types)); @@ -179,6 +187,14 @@ unshift(@ARGV, @conf_args) if @conf_args; } +# Perl's Getopt::Long allows options to take optional arguments after a space. +# Prevent --color by itself from consuming other arguments +foreach (@ARGV) { + if ($_ eq "--color" || $_ eq "-color") { + $_ = "--color=$color"; + } +} + GetOptions( 'q|quiet+' => \$quiet, 'tree!' => \$tree, @@ -208,7 +224,10 @@ 'test-only=s' => \$tst_only, 'codespell!' => \$codespell, 'codespellfile=s' => \$codespellfile, - 'color!' => \$color, + 'typedefsfile=s' => \$typedefsfile, + 'color=s' => \$color, + 'no-color' => \$color, #keep old behaviors of -nocolor + 'nocolor' => \$color, #keep old behaviors of -nocolor 'h|help' => \$help, 'version' => \$help ) or help(1); @@ -222,11 +241,11 @@ my $exit = 0; +my $perl_version_ok = 1; if ($^V && $^V lt $minimum_perl_version) { + $perl_version_ok = 0; printf "$P: requires at least perl version %vd\n", $minimum_perl_version; - if (!$ignore_perl_version) { - exit(1); - } + exit(1) if (!$ignore_perl_version); } #if no filenames are given, push '-' to read patch from stdin @@ -234,6 +253,18 @@ push(@ARGV, '-'); } +if ($color =~ /^[01]$/) { + $color = !$color; +} elsif ($color =~ /^always$/i) { + $color = 1; +} elsif ($color =~ /^never$/i) { + $color = 0; +} elsif ($color =~ /^auto$/i) { + $color = (-t STDOUT); +} else { + die "Invalid color mode: $color\n"; +} + sub hash_save_array_words { my ($hashRef, $arrayRef) = @_; @@ -316,9 +347,10 @@ __force| __iomem| __must_check| - __init_refok| __kprobes| __ref| + __refconst| + __refdata| __rcu| __private }x; @@ -348,6 +380,7 @@ __noclone| __deprecated| __read_mostly| + __ro_after_init| __kprobes| $InitAttribute| ____cacheline_aligned| @@ -424,8 +457,9 @@ our $zero_initializer = qr{(?:(?:0[xX])?0+$Int_type?|NULL|false)\b}; our $logFunctions = qr{(?x: - printk(?:_ratelimited|_once|)| + printk(?:_ratelimited|_once|_deferred_once|_deferred|)| (?:[a-z0-9]+_){1,2}(?:printk|emerg|alert|crit|err|warning|warn|notice|info|debug|dbg|vdbg|devel|cont|WARN)(?:_ratelimited|_once|)| + TP_printk| WARN(?:_RATELIMIT|_ONCE|)| panic| MODULE_[A-Z_]+| @@ -434,6 +468,7 @@ our $signature_tags = qr{(?xi: Signed-off-by:| + Co-developed-by:| Acked-by:| Tested-by:| Reviewed-by:| @@ -537,6 +572,28 @@ $mode_perms_search .= '|' if ($mode_perms_search ne ""); $mode_perms_search .= $entry->[0]; } +$mode_perms_search = "(?:${mode_perms_search})"; + +our %deprecated_apis = ( + "synchronize_rcu_bh" => "synchronize_rcu", + "synchronize_rcu_bh_expedited" => "synchronize_rcu_expedited", + "call_rcu_bh" => "call_rcu", + "rcu_barrier_bh" => "rcu_barrier", + "synchronize_sched" => "synchronize_rcu", + "synchronize_sched_expedited" => "synchronize_rcu_expedited", + "call_rcu_sched" => "call_rcu", + "rcu_barrier_sched" => "rcu_barrier", + "get_state_synchronize_sched" => "get_state_synchronize_rcu", + "cond_synchronize_sched" => "cond_synchronize_rcu", +); + +#Create a search pattern for all these strings to speed up a loop below +our $deprecated_apis_search = ""; +foreach my $entry (keys %deprecated_apis) { + $deprecated_apis_search .= '|' if ($deprecated_apis_search ne ""); + $deprecated_apis_search .= $entry; +} +$deprecated_apis_search = "(?:${deprecated_apis_search})"; our $mode_perms_world_writable = qr{ S_IWUGO | @@ -571,6 +628,37 @@ $mode_perms_string_search .= '|' if ($mode_perms_string_search ne ""); $mode_perms_string_search .= $entry; } +our $single_mode_perms_string_search = "(?:${mode_perms_string_search})"; +our $multi_mode_perms_string_search = qr{ + ${single_mode_perms_string_search} + (?:\s*\|\s*${single_mode_perms_string_search})* +}x; + +sub perms_to_octal { + my ($string) = @_; + + return trim($string) if ($string =~ /^\s*0[0-7]{3,3}\s*$/); + + my $val = ""; + my $oval = ""; + my $to = 0; + my $curpos = 0; + my $lastpos = 0; + while ($string =~ /\b(($single_mode_perms_string_search)\b(?:\s*\|\s*)?\s*)/g) { + $curpos = pos($string); + my $match = $2; + my $omatch = $1; + last if ($lastpos > 0 && ($curpos - length($omatch) != $lastpos)); + $lastpos = $curpos; + $to |= $mode_permission_string_types{$match}; + $val .= '\s*\|\s*' if ($val ne ""); + $val .= $match; + $oval .= $omatch; + } + $oval =~ s/^\s*\|\s*//; + $oval =~ s/\s*\|\s*$//; + return sprintf("%04o", $to); +} our $allowed_asm_includes = qr{(?x: irq| @@ -629,29 +717,44 @@ $misspellings = join("|", sort keys %spelling_fix) if keys %spelling_fix; -my $const_structs = ""; -if (open(my $conststructs, '<', $conststructsfile)) { - while (<$conststructs>) { - my $line = $_; +sub read_words { + my ($wordsRef, $file) = @_; - $line =~ s/\s*\n?$//g; - $line =~ s/^\s*//g; + if (open(my $words, '<', $file)) { + while (<$words>) { + my $line = $_; - next if ($line =~ m/^\s*#/); - next if ($line =~ m/^\s*$/); - if ($line =~ /\s/) { - print("$conststructsfile: '$line' invalid - ignored\n"); - next; - } + $line =~ s/\s*\n?$//g; + $line =~ s/^\s*//g; - $const_structs .= '|' if ($const_structs ne ""); - $const_structs .= $line; + next if ($line =~ m/^\s*#/); + next if ($line =~ m/^\s*$/); + if ($line =~ /\s/) { + print("$file: '$line' invalid - ignored\n"); + next; + } + + $$wordsRef .= '|' if ($$wordsRef ne ""); + $$wordsRef .= $line; + } + close($file); + return 1; } - close($conststructsfile); -} else { - warn "No structs that should be const will be found - file '$conststructsfile': $!\n"; + + return 0; } +my $const_structs = ""; +read_words(\$const_structs, $conststructsfile) + or warn "No structs that should be const will be found - file '$conststructsfile': $!\n"; + +my $typeOtherTypedefs = ""; +if (length($typedefsfile)) { + read_words(\$typeOtherTypedefs, $typedefsfile) + or warn "No additional types will be considered - file '$typedefsfile': $!\n"; +} +$typeTypedefs .= '|' . $typeOtherTypedefs if ($typeOtherTypedefs ne ""); + sub build_types { my $mods = "(?x: \n" . join("|\n ", (@modifierList, @modifierListFile)) . "\n)"; my $all = "(?x: \n" . join("|\n ", (@typeList, @typeListFile)) . "\n)"; @@ -714,8 +817,9 @@ our $declaration_macros = qr{(?x: (?:$Storage\s+)?(?:[A-Z_][A-Z0-9]*_){0,2}(?:DEFINE|DECLARE)(?:_[A-Z0-9]+){1,6}\s*\(| - (?:$Storage\s+)?LIST_HEAD\s*\(| - (?:$Storage\s+)?${Type}\s+uninitialized_var\s*\( + (?:$Storage\s+)?[HLP]?LIST_HEAD\s*\(| + (?:$Storage\s+)?${Type}\s+uninitialized_var\s*\(| + (?:SKCIPHER_REQUEST|SHASH_DESC|AHASH_REQUEST)_ON_STACK\s*\( )}; sub deparenthesize { @@ -768,6 +872,17 @@ return $status =~ /obsolete/i; } +sub is_SPDX_License_valid { + my ($license) = @_; + + return 1 if (!$tree || which("python") eq "" || !(-e "$root/scripts/spdxcheck.py") || !(-e "$root/.git")); + + my $root_path = abs_path($root); + my $status = `cd "$root_path"; echo "$license" | python scripts/spdxcheck.py -`; + return 0 if ($status ne ""); + return 1; +} + my $camelcase_seeded = 0; sub seed_camelcase_includes { return if ($camelcase_seeded); @@ -848,6 +963,7 @@ # echo "commit $(cut -c 1-12,41-)" # done } elsif ($lines[0] =~ /^fatal: ambiguous argument '$commit': unknown revision or path not in the working tree\./) { + $id = undef; } else { $id = substr($lines[0], 0, 12); $desc = substr($lines[0], 41); @@ -946,11 +1062,11 @@ hash_show_words(\%use_type, "Used"); hash_show_words(\%ignore_type, "Ignored"); - if ($^V lt 5.10.0) { + if (!$perl_version_ok) { print << "EOM" NOTE: perl $^V is not modern enough to detect all possible issues. - An upgrade to at least perl v5.10.0 is suggested. + An upgrade to at least perl $minimum_perl_version is suggested. EOM } if ($exit) { @@ -998,7 +1114,7 @@ } elsif ($formatted_email =~ /(\S+\@\S+)(.*)$/) { $address = $1; $comment = $2 if defined $2; - $formatted_email =~ s/$address.*$//; + $formatted_email =~ s/\Q$address\E.*$//; $name = $formatted_email; $name = trim($name); $name =~ s/^\"|\"$//g; @@ -1140,7 +1256,7 @@ for ($off = 1; $off < length($line); $off++) { $c = substr($line, $off, 1); - # Comments we are wacking completly including the begin + # Comments we are whacking completely including the begin # and end, all to $;. if ($sanitise_quote eq '' && substr($line, $off, 2) eq '/*') { $sanitise_quote = '*/'; @@ -1220,6 +1336,7 @@ sub get_quoted_string { my ($line, $rawline) = @_; + return "" if (!defined($line) || !defined($rawline)); return "" if ($line !~ m/($String)/g); return substr($rawline, $-[0], $+[0] - $-[0]); } @@ -1567,6 +1684,28 @@ return $line; } +sub get_stat_real { + my ($linenr, $lc) = @_; + + my $stat_real = raw_line($linenr, 0); + for (my $count = $linenr + 1; $count <= $lc; $count++) { + $stat_real = $stat_real . "\n" . raw_line($count, 0); + } + + return $stat_real; +} + +sub get_stat_here { + my ($linenr, $cnt, $here) = @_; + + my $herectx = $here . "\n"; + for (my $n = 0; $n < $cnt; $n++) { + $herectx .= raw_line($linenr, $n) . "\n"; + } + + return $herectx; +} + sub cat_vet { my ($vet) = @_; my ($res, $coded); @@ -1848,6 +1987,8 @@ sub show_type { my ($type) = @_; + $type =~ tr/[a-z]/[A-Z]/; + return defined $use_type{$type} if (scalar keys %use_type > 0); return !defined $ignore_type{$type}; @@ -1861,7 +2002,7 @@ return 0; } my $output = ''; - if (-t STDOUT && $color) { + if ($color) { if ($level eq 'ERROR') { $output .= RED; } elsif ($level eq 'WARNING') { @@ -1872,10 +2013,10 @@ } $output .= $prefix . $level . ':'; if ($show_types) { - $output .= BLUE if (-t STDOUT && $color); + $output .= BLUE if ($color); $output .= "$type:"; } - $output .= RESET if (-t STDOUT && $color); + $output .= RESET if ($color); $output .= ' ' . $msg . "\n"; if ($showfile) { @@ -2130,11 +2271,15 @@ our $clean = 1; my $signoff = 0; + my $author = ''; + my $authorsignoff = 0; my $is_patch = 0; + my $is_binding_patch = -1; my $in_header_lines = $file ? 0 : 1; my $in_commit_log = 0; #Scanning lines before patch my $has_commit_log = 0; #Encountered lines before patch - my $commit_log_possible_stack_dump = 0; + my $commit_log_lines = 0; #Number of commit log lines + my $commit_log_possible_stack_dump = 0; my $commit_log_long_line = 0; my $commit_log_has_diff = 0; my $reported_maintainer_file = 0; @@ -2154,6 +2299,7 @@ my $realline = 0; my $realcnt = 0; my $here = ''; + my $context_function; #undef'd unless there's a known function my $in_comment = 0; my $comment_edge = 0; my $first_line = 0; @@ -2177,6 +2323,8 @@ my $camelcase_file_seeded = 0; + my $checklicenseline = 1; + sanitise_line_reset(); my $line; foreach my $rawline (@rawlines) { @@ -2192,7 +2340,7 @@ } #next; } - if ($rawline=~/^\@\@ -\d+(?:,\d+)? \+(\d+)(,(\d+))? \@\@/) { + if ($rawline =~ /^\@\@ -\d+(?:,\d+)? \+(\d+)(,(\d+))? \@\@/) { $realline=$1-1; if (defined $2) { $realcnt=$3+1; @@ -2269,9 +2417,18 @@ my $rawline = $rawlines[$linenr - 1]; +# check if it's a mode change, rename or start of a patch + if (!$in_commit_log && + ($line =~ /^ mode change [0-7]+ => [0-7]+ \S+\s*$/ || + ($line =~ /^rename (?:from|to) \S+\s*$/ || + $line =~ /^diff --git a\/[\w\/\.\_\-]+ b\/\S+\s*$/))) { + $is_patch = 1; + } + #extract the line range in the file after the patch is applied if (!$in_commit_log && - $line =~ /^\@\@ -\d+(?:,\d+)? \+(\d+)(,(\d+))? \@\@/) { + $line =~ /^\@\@ -\d+(?:,\d+)? \+(\d+)(,(\d+))? \@\@(.*)/) { + my $context = $4; $is_patch = 1; $first_line = $linenr + 1; $realline=$1-1; @@ -2287,6 +2444,11 @@ %suppress_whiletrailers = (); %suppress_export = (); $suppress_statement = 0; + if ($context =~ /\b(\w+)\s*\(/) { + $context_function = $1; + } else { + undef $context_function; + } next; # track the line number as we move through the hunk, note that @@ -2362,6 +2524,20 @@ } else { $check = $check_orig; } + $checklicenseline = 1; + + if ($realfile !~ /^MAINTAINERS/) { + my $last_binding_patch = $is_binding_patch; + + $is_binding_patch = () = $realfile =~ m@^(?:Documentation/devicetree/|include/dt-bindings/)@; + + if (($last_binding_patch != -1) && + ($last_binding_patch ^ $is_binding_patch)) { + WARN("DT_SPLIT_BINDING_PATCH", + "DT binding docs and includes should be a separate patch. See: Documentation/devicetree/bindings/submitting-patches.txt\n"); + } + } + next; } @@ -2373,6 +2549,18 @@ $cnt_lines++ if ($realcnt != 0); +# Verify the existence of a commit log if appropriate +# 2 is used because a $signature is counted in $commit_log_lines + if ($in_commit_log) { + if ($line !~ /^\s*$/) { + $commit_log_lines++; #could be a $signature + } + } elsif ($has_commit_log && $commit_log_lines < 2) { + WARN("COMMIT_MESSAGE", + "Missing commit description - Add an appropriate one\n"); + $commit_log_lines = 2; #warn only once + } + # Check if the commit log has what seems like a diff which can confuse patch if ($in_commit_log && !$commit_log_has_diff && (($line =~ m@^\s+diff\b.*a/[\w/]+@ && @@ -2394,10 +2582,24 @@ } } +# Check the patch for a From: + if (decode("MIME-Header", $line) =~ /^From:\s*(.*)/) { + $author = $1; + $author = encode("utf8", $author) if ($line =~ /=\?utf-8\?/i); + $author =~ s/"//g; + } + # Check the patch for a signoff: if ($line =~ /^\s*signed-off-by:/i) { $signoff++; $in_commit_log = 0; + if ($author ne '') { + my $l = $line; + $l =~ s/"//g; + if ($l =~ /^\s*signed-off-by:\s*\Q$author\E/i) { + $authorsignoff = 1; + } + } } # Check if MAINTAINERS is being updated. If so, there's probably no need to @@ -2483,12 +2685,6 @@ "A patch subject line should describe the change not the tool that found it\n" . $herecurr); } -# Check for old stable address - if ($line =~ /^\s*cc:\s*.*?.*$/i) { - ERROR("STABLE_ADDRESS", - "The 'stable' address should be 'stable\@vger.kernel.org'\n" . $herecurr); - } - # Check for unwanted Gerrit info if ($in_commit_log && $line =~ /^\s*change-id:/i) { ERROR("GERRIT_CHANGE_ID", @@ -2529,6 +2725,7 @@ # Check for git id commit length and improperly formed commit descriptions if ($in_commit_log && !$commit_log_possible_stack_dump && $line !~ /^\s*(?:Link|Patchwork|http|https|BugLink):/i && + $line !~ /^This reverts commit [0-9a-f]{7,40}/ && ($line =~ /\bcommit\s+[0-9a-f]{5,}\b/i || ($line =~ /(?:\s|^)[0-9a-f]{12,40}(?:[\s"'\(\[]|$)/i && $line !~ /[\<\[][0-9a-f]{12,40}[\>\]]/i && @@ -2577,7 +2774,8 @@ ($id, $description) = git_commit_info($orig_commit, $id, $orig_desc); - if ($short || $long || $space || $case || ($orig_desc ne $description) || !$hasparens) { + if (defined($id) && + ($short || $long || $space || $case || ($orig_desc ne $description) || !$hasparens)) { ERROR("GIT_COMMIT_ID", "Please use git commit description style 'commit <12+ chars of sha1> (\"\")' - ie: '${init_char}ommit $id (\"$description\")'\n" . $herecurr); } @@ -2618,8 +2816,8 @@ # Check if it's the start of a commit log # (not a header line and we haven't seen the patch filename) if ($in_header_lines && $realfile =~ /^$/ && - !($rawline =~ /^\s+\S/ || - $rawline =~ /^(commit\b|from\b|[\w-]+:).*$/i)) { + !($rawline =~ /^\s+(?:\S|$)/ || + $rawline =~ /^(?:commit\b|from\b|[\w-]+:)/i)) { $in_header_lines = 0; $in_commit_log = 1; $has_commit_log = 1; @@ -2661,10 +2859,10 @@ my $typo_fix = $spelling_fix{lc($typo)}; $typo_fix = ucfirst($typo_fix) if ($typo =~ /^[A-Z]/); $typo_fix = uc($typo_fix) if ($typo =~ /^[A-Z]+$/); - my $msg_type = \&WARN; - $msg_type = \&CHK if ($file); - if (&{$msg_type}("TYPO_SPELLING", - "'$typo' may be misspelled - perhaps '$typo_fix'?\n" . $herecurr) && + my $msg_level = \&WARN; + $msg_level = \&CHK if ($file); + if (&{$msg_level}("TYPO_SPELLING", + "'$typo' may be misspelled - perhaps '$typo_fix'?\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/(^|[^A-Za-z@])($typo)($|[^A-Za-z@])/$1$typo_fix$3/; } @@ -2695,20 +2893,24 @@ # Check for FSF mailing addresses. if ($rawline =~ /\bwrite to the Free/i || + $rawline =~ /\b675\s+Mass\s+Ave/i || $rawline =~ /\b59\s+Temple\s+Pl/i || $rawline =~ /\b51\s+Franklin\s+St/i) { my $herevet = "$here\n" . cat_vet($rawline) . "\n"; - my $msg_type = \&ERROR; - $msg_type = \&CHK if ($file); - &{$msg_type}("FSF_MAILING_ADDRESS", - "Do not include the paragraph about writing to the Free Software Foundation's mailing address from the sample GPL notice. The FSF has changed addresses in the past, and may do so again. Linux already includes a copy of the GPL.\n" . $herevet) + my $msg_level = \&ERROR; + $msg_level = \&CHK if ($file); + &{$msg_level}("FSF_MAILING_ADDRESS", + "Do not include the paragraph about writing to the Free Software Foundation's mailing address from the sample GPL notice. The FSF has changed addresses in the past, and may do so again. Linux already includes a copy of the GPL.\n" . $herevet) } # check for Kconfig help text having a real description # Only applies when adding the entry originally, after that we do not have # sufficient context to determine whether it is indeed long enough. if ($realfile =~ /Kconfig/ && - $line =~ /^\+\s*config\s+/) { + # 'choice' is usually the last thing on the line (though + # Kconfig supports named choices), so use a word boundary + # (\b) rather than a whitespace character (\s) + $line =~ /^\+\s*(?:config|menuconfig|choice)\b/) { my $length = 0; my $cnt = $realcnt; my $ln = $linenr + 1; @@ -2723,9 +2925,13 @@ next if ($f =~ /^-/); last if (!$file && $f =~ /^\@\@/); - if ($lines[$ln - 1] =~ /^\+\s*(?:bool|tristate)\s*\"/) { + if ($lines[$ln - 1] =~ /^\+\s*(?:bool|tristate|prompt)\s*["']/) { $is_start = 1; - } elsif ($lines[$ln - 1] =~ /^\+\s*(?:---)?help(?:---)?$/) { + } elsif ($lines[$ln - 1] =~ /^\+\s*(?:help|---help---)\s*$/) { + if ($lines[$ln - 1] =~ "---help---") { + WARN("CONFIG_DESCRIPTION", + "prefer 'help' over '---help---' for new help texts\n" . $herecurr); + } $length = -1; } @@ -2733,7 +2939,13 @@ $f =~ s/#.*//; $f =~ s/^\s+//; next if ($f =~ /^$/); - if ($f =~ /^\s*config\s/) { + + # This only checks context lines in the patch + # and so hopefully shouldn't trigger false + # positives, even though some of these are + # common words in help texts + if ($f =~ /^\s*(?:config|menuconfig|choice|endchoice| + if|endif|menu|endmenu|source)\b/x) { $is_end = 1; last; } @@ -2746,11 +2958,15 @@ #print "is_start<$is_start> is_end<$is_end> length<$length>\n"; } -# discourage the addition of CONFIG_EXPERIMENTAL in Kconfig. - if ($realfile =~ /Kconfig/ && - $line =~ /.\s*depends on\s+.*\bEXPERIMENTAL\b/) { - WARN("CONFIG_EXPERIMENTAL", - "Use of CONFIG_EXPERIMENTAL is deprecated. For alternatives, see https://lkml.org/lkml/2012/10/23/580\n"); +# check for MAINTAINERS entries that don't have the right form + if ($realfile =~ /^MAINTAINERS$/ && + $rawline =~ /^\+[A-Z]:/ && + $rawline !~ /^\+[A-Z]:\t\S/) { + if (WARN("MAINTAINERS_STYLE", + "MAINTAINERS entries use one tab after TYPE:\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/^(\+[A-Z]):\s*/$1:\t/; + } } # discourage the use of boolean for type definition attributes of Kconfig options @@ -2805,6 +3021,36 @@ } } +# check for using SPDX license tag at beginning of files + if ($realline == $checklicenseline) { + if ($rawline =~ /^[ \+]\s*\#\!\s*\//) { + $checklicenseline = 2; + } elsif ($rawline =~ /^\+/) { + my $comment = ""; + if ($realfile =~ /\.(h|s|S)$/) { + $comment = '/*'; + } elsif ($realfile =~ /\.(c|dts|dtsi)$/) { + $comment = '//'; + } elsif (($checklicenseline == 2) || $realfile =~ /\.(sh|pl|py|awk|tc)$/) { + $comment = '#'; + } elsif ($realfile =~ /\.rst$/) { + $comment = '..'; + } + + if ($comment !~ /^$/ && + $rawline !~ /^\+\Q$comment\E SPDX-License-Identifier: /) { + WARN("SPDX_LICENSE_TAG", + "Missing or malformed SPDX-License-Identifier tag in line $checklicenseline\n" . $herecurr); + } elsif ($rawline =~ /(SPDX-License-Identifier: .*)/) { + my $spdx_license = $1; + if (!is_SPDX_License_valid($spdx_license)) { + WARN("SPDX_LICENSE_TAG", + "'$spdx_license' is not supported in LICENSES/...\n" . $herecurr); + } + } + } + } + # check we are in a valid source file if not then ignore this hunk next if ($realfile !~ /\.(h|c|s|S|sh|dtsi|dts)$/); @@ -2814,9 +3060,10 @@ # logging functions like pr_info that end in a string # lines with a single string # #defines that are a single string +# lines with an RFC3986 like URL # # There are 3 different line length message types: -# LONG_LINE_COMMENT a comment starts before but extends beyond $max_linelength +# LONG_LINE_COMMENT a comment starts before but extends beyond $max_line_length # LONG_LINE_STRING a string starts before but extends beyond $max_line_length # LONG_LINE all other lines longer than $max_line_length # @@ -2840,8 +3087,13 @@ $line =~ /^\+\s*#\s*define\s+\w+\s+$String$/) { $msg_type = ""; - # EFI_GUID is another special case - } elsif ($line =~ /^\+.*\bEFI_GUID\s*\(/) { + # More special cases + } elsif ($line =~ /^\+.*\bEFI_GUID\s*\(/ || + $line =~ /^\+\s*(?:\w+)?\s*DEFINE_PER_CPU/) { + $msg_type = ""; + + # URL ($rawline is used in case the URL is in a comment) + } elsif ($rawline =~ /^\+.*\b[a-z][\w\.\+\-]*:\/\/\S+/i) { $msg_type = ""; # Otherwise set the alternate message types @@ -2870,20 +3122,6 @@ "adding a line without newline at end of file\n" . $herecurr); } -# Blackfin: use hi/lo macros - if ($realfile =~ m@arch/blackfin/.*\.S$@) { - if ($line =~ /\.[lL][[:space:]]*=.*&[[:space:]]*0x[fF][fF][fF][fF]/) { - my $herevet = "$here\n" . cat_vet($line) . "\n"; - ERROR("LO_MACRO", - "use the LO() macro, not (... & 0xFFFF)\n" . $herevet); - } - if ($line =~ /\.[hH][[:space:]]*=.*>>[[:space:]]*16/) { - my $herevet = "$here\n" . cat_vet($line) . "\n"; - ERROR("HI_MACRO", - "use the HI() macro, not (... >> 16)\n" . $herevet); - } - } - # check we are in a valid source file C or perl if not then ignore this hunk next if ($realfile !~ /\.(h|c|pl|dtsi|dts)$/); @@ -2913,6 +3151,12 @@ } } +# check for assignments on the start of a line + if ($sline =~ /^\+\s+($Assignment)[^=]/) { + CHK("ASSIGNMENT_CONTINUATIONS", + "Assignment operator '$1' should be on the previous line\n" . $hereprev); + } + # check for && or || at the start of a line if ($rawline =~ /^\+\s*(&&|\|\|)/) { CHK("LOGICAL_CONTINUATIONS", @@ -2920,8 +3164,8 @@ } # check indentation starts on a tab stop - if ($^V && $^V ge 5.10.0 && - $sline =~ /^\+\t+( +)(?:$c90_Keywords\b|\{\s*$|\}\s*(?:else\b|while\b|\s*$))/) { + if ($perl_version_ok && + $sline =~ /^\+\t+( +)(?:$c90_Keywords\b|\{\s*$|\}\s*(?:else\b|while\b|\s*$)|$Declare\s*$Ident\s*[;=])/) { my $indent = length($1); if ($indent % 8) { if (WARN("TABSTOP", @@ -2933,8 +3177,8 @@ } # check multi-line statement indentation matches previous line - if ($^V && $^V ge 5.10.0 && - $prevline =~ /^\+([ \t]*)((?:$c90_Keywords(?:\s+if)\s*)|(?:$Declare\s*)?(?:$Ident|\(\s*\*\s*$Ident\s*\))\s*|$Ident\s*=\s*$Ident\s*)\(.*(\&\&|\|\||,)\s*$/) { + if ($perl_version_ok && + $prevline =~ /^\+([ \t]*)((?:$c90_Keywords(?:\s+if)\s*)|(?:$Declare\s*)?(?:$Ident|\(\s*\*\s*$Ident\s*\))\s*|(?:\*\s*)*$Lval\s*=\s*$Ident\s*)\(.*(\&\&|\|\||,)\s*$/) { $prevline =~ /^\+(\t*)(.*)$/; my $oldindent = $1; my $rest = $2; @@ -3043,6 +3287,7 @@ $line =~ /^\+[a-z_]*init/ || $line =~ /^\+\s*(?:static\s+)?[A-Z_]*ATTR/ || $line =~ /^\+\s*DECLARE/ || + $line =~ /^\+\s*builtin_[\w_]*driver/ || $line =~ /^\+\s*__setup/)) { if (CHK("LINE_SPACING", "Please use a blank line after function/struct/union/enum declarations\n" . $hereprev) && @@ -3089,7 +3334,7 @@ # known declaration macros $sline =~ /^\+\s+$declaration_macros/ || # start of struct or union or enum - $sline =~ /^\+\s+(?:union|struct|enum|typedef)\b/ || + $sline =~ /^\+\s+(?:static\s+)?(?:const\s+)?(?:union|struct|enum|typedef)\b/ || # start or end of block or continuation of declaration $sline =~ /^\+\s+(?:$|[\{\}\.\#\"\?\:\(\[])/ || # bitfield continuation @@ -3122,6 +3367,23 @@ # check we are in a valid C source file if not then ignore this hunk next if ($realfile !~ /\.(h|c)$/); +# check for unusual line ending [ or ( + if ($line =~ /^\+.*([\[\(])\s*$/) { + CHK("OPEN_ENDED_LINE", + "Lines should not end with a '$1'\n" . $herecurr); + } + +# check if this appears to be the start function declaration, save the name + if ($sline =~ /^\+\{\s*$/ && + $prevline =~ /^\+(?:(?:(?:$Storage|$Inline)\s*)*\s*$Type\s*)?($Ident)\(/) { + $context_function = $1; + } + +# check if this appears to be the end of function declaration + if ($sline =~ /^\+\}\s*$/) { + undef $context_function; + } + # check indentation of any line with a bare else # (but not if it is a multiple line "if (foo) return bar; else return baz;") # if the previous line is a break or return and is indented 1 tab more... @@ -3146,30 +3408,12 @@ } } -# discourage the addition of CONFIG_EXPERIMENTAL in #if(def). - if ($line =~ /^\+\s*\#\s*if.*\bCONFIG_EXPERIMENTAL\b/) { - WARN("CONFIG_EXPERIMENTAL", - "Use of CONFIG_EXPERIMENTAL is deprecated. For alternatives, see https://lkml.org/lkml/2012/10/23/580\n"); - } - # check for RCS/CVS revision markers if ($rawline =~ /^\+.*\$(Revision|Log|Id)(?:\$|)/) { WARN("CVS_KEYWORD", "CVS style keyword markers, these will _not_ be updated\n". $herecurr); } -# Blackfin: don't use __builtin_bfin_[cs]sync - if ($line =~ /__builtin_bfin_csync/) { - my $herevet = "$here\n" . cat_vet($line) . "\n"; - ERROR("CSYNC", - "use the CSYNC() macro in asm/blackfin.h\n" . $herevet); - } - if ($line =~ /__builtin_bfin_ssync/) { - my $herevet = "$here\n" . cat_vet($line) . "\n"; - ERROR("SSYNC", - "use the SSYNC() macro in asm/blackfin.h\n" . $herevet); - } - # check for old HOTPLUG __dev<foo> section markings if ($line =~ /\b(__dev(init|exit)(data|const|))\b/) { WARN("HOTPLUG_SECTION", @@ -3180,7 +3424,7 @@ my ($stat, $cond, $line_nr_next, $remain_next, $off_next, $realline_next); #print "LINE<$line>\n"; - if ($linenr >= $suppress_statement && + if ($linenr > $suppress_statement && $realcnt && $sline =~ /.\s*\S/) { ($stat, $cond, $line_nr_next, $remain_next, $off_next) = ctx_statement_block($linenr, $realcnt, 0); @@ -3327,7 +3571,7 @@ } # Check relative indent for conditionals and blocks. - if ($line =~ /\b(?:(?:if|while|for|(?:[a-z_]+|)for_each[a-z_]+)\s*\(|do\b)/ && $line !~ /^.\s*#/ && $line !~ /\}\s*while\s*/) { + if ($line =~ /\b(?:(?:if|while|for|(?:[a-z_]+|)for_each[a-z_]+)\s*\(|(?:do|else)\b)/ && $line !~ /^.\s*#/ && $line !~ /\}\s*while\s*/) { ($stat, $cond, $line_nr_next, $remain_next, $off_next) = ctx_statement_block($linenr, $realcnt, 0) if (!defined $stat); @@ -3419,6 +3663,8 @@ if ($check && $s ne '' && (($sindent % 8) != 0 || ($sindent < $indent) || + ($sindent == $indent && + ($s !~ /^\s*(?:\}|\{|else\b)/)) || ($sindent > $indent + 8))) { WARN("SUSPECT_CODE_INDENT", "suspect code indent for conditional statements ($indent, $sindent)\n" . $herecurr . "$stat_real\n"); @@ -3512,7 +3758,7 @@ $fixedline =~ s/\s*=\s*$/ = {/; fix_insert_line($fixlinenr, $fixedline); $fixedline = $line; - $fixedline =~ s/^(.\s*){\s*/$1/; + $fixedline =~ s/^(.\s*)\{\s*/$1/; fix_insert_line($fixlinenr, $fixedline); } } @@ -3620,19 +3866,48 @@ "type '$tmp' should be specified in [[un]signed] [short|int|long|long long] order\n" . $herecurr); } +# check for unnecessary <signed> int declarations of short/long/long long + while ($sline =~ m{\b($TypeMisordered(\s*\*)*|$C90_int_types)\b}g) { + my $type = trim($1); + next if ($type !~ /\bint\b/); + next if ($type !~ /\b(?:short|long\s+long|long)\b/); + my $new_type = $type; + $new_type =~ s/\b\s*int\s*\b/ /; + $new_type =~ s/\b\s*(?:un)?signed\b\s*/ /; + $new_type =~ s/^const\s+//; + $new_type = "unsigned $new_type" if ($type =~ /\bunsigned\b/); + $new_type = "const $new_type" if ($type =~ /^const\b/); + $new_type =~ s/\s+/ /g; + $new_type = trim($new_type); + if (WARN("UNNECESSARY_INT", + "Prefer '$new_type' over '$type' as the int is unnecessary\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\b\Q$type\E\b/$new_type/; + } + } + # check for static const char * arrays. if ($line =~ /\bstatic\s+const\s+char\s*\*\s*(\w+)\s*\[\s*\]\s*=\s*/) { WARN("STATIC_CONST_CHAR_ARRAY", "static const char * array should probably be static const char * const\n" . $herecurr); - } + } + +# check for initialized const char arrays that should be static const + if ($line =~ /^\+\s*const\s+(char|unsigned\s+char|_*u8|(?:[us]_)?int8_t)\s+\w+\s*\[\s*(?:\w+\s*)?\]\s*=\s*"/) { + if (WARN("STATIC_CONST_CHAR_ARRAY", + "const array should probably be static const\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/(^.\s*)const\b/${1}static const/; + } + } # check for static char foo[] = "bar" declarations. if ($line =~ /\bstatic\s+char\s+(\w+)\s*\[\s*\]\s*=\s*"/) { WARN("STATIC_CONST_CHAR_ARRAY", "static char array declaration should probably be static const char\n" . $herecurr); - } + } # check for const <foo> const where <foo> is not a pointer or array type if ($sline =~ /\bconst\s+($BasicType)\s+const\b/) { @@ -3744,10 +4019,10 @@ # avoid BUG() or BUG_ON() if ($line =~ /\b(?:BUG|BUG_ON)\b/) { - my $msg_type = \&WARN; - $msg_type = \&CHK if ($file); - &{$msg_type}("AVOID_BUG", - "Avoid crashing the kernel - try using WARN_ON & recovery code rather than BUG() or BUG_ON()\n" . $herecurr); + my $msg_level = \&WARN; + $msg_level = \&CHK if ($file); + &{$msg_level}("AVOID_BUG", + "Avoid crashing the kernel - try using WARN_ON & recovery code rather than BUG() or BUG_ON()\n" . $herecurr); } # avoid LINUX_VERSION_CODE @@ -3762,28 +4037,10 @@ "Prefer printk_ratelimited or pr_<level>_ratelimited to printk_ratelimit\n" . $herecurr); } -# printk should use KERN_* levels. Note that follow on printk's on the -# same line do not need a level, so we use the current block context -# to try and find and validate the current printk. In summary the current -# printk includes all preceding printk's which have no newline on the end. -# we assume the first bad printk is the one to report. - if ($line =~ /\bprintk\((?!KERN_)\s*"/) { - my $ok = 0; - for (my $ln = $linenr - 1; $ln >= $first_line; $ln--) { - #print "CHECK<$lines[$ln - 1]\n"; - # we have a preceding printk if it ends - # with "\n" ignore it, else it is to blame - if ($lines[$ln - 1] =~ m{\bprintk\(}) { - if ($rawlines[$ln - 1] !~ m{\\n"}) { - $ok = 1; - } - last; - } - } - if ($ok == 0) { - WARN("PRINTK_WITHOUT_KERN_LEVEL", - "printk() should include KERN_ facility level\n" . $herecurr); - } +# printk should use KERN_* levels + if ($line =~ /\bprintk\s*\(\s*(?!KERN_[A-Z]+\b)/) { + WARN("PRINTK_WITHOUT_KERN_LEVEL", + "printk() should include KERN_<LEVEL> facility level\n" . $herecurr); } if ($line =~ /\bprintk\s*\(\s*KERN_([A-Z]+)/) { @@ -3824,10 +4081,12 @@ # function brace can't be on same line, except for #defines of do while, # or if closed on same line - if (($line=~/$Type\s*$Ident\(.*\).*\s*{/) and - !($line=~/\#\s*define.*do\s\{/) and !($line=~/}/)) { + if ($perl_version_ok && + $sline =~ /$Type\s*$Ident\s*$balanced_parens\s*\{/ && + $sline !~ /\#\s*define\b.*do\s*\{/ && + $sline !~ /}/) { if (ERROR("OPEN_BRACE", - "open brace '{' following function declarations go on the next line\n" . $herecurr) && + "open brace '{' following function definitions go on the next line\n" . $herecurr) && $fix) { fix_delete_line($fixlinenr, $rawline); my $fixed_line = $rawline; @@ -3853,7 +4112,7 @@ my $fixedline = rtrim($prevrawline) . " {"; fix_insert_line($fixlinenr, $fixedline); $fixedline = $rawline; - $fixedline =~ s/^(.\s*){\s*/$1\t/; + $fixedline =~ s/^(.\s*)\{\s*/$1\t/; if ($fixedline !~ /^\+\s*$/) { fix_insert_line($fixlinenr, $fixedline); } @@ -3948,7 +4207,7 @@ my ($where, $prefix) = ($-[1], $1); if ($prefix !~ /$Type\s+$/ && ($where != 0 || $prefix !~ /^.\s+$/) && - $prefix !~ /[{,]\s+$/) { + $prefix !~ /[{,:]\s+$/) { if (ERROR("BRACKET_SPACE", "space prohibited before open square bracket '['\n" . $herecurr) && $fix) { @@ -4273,11 +4532,11 @@ # messages are ERROR, but ?: are CHK if ($ok == 0) { - my $msg_type = \&ERROR; - $msg_type = \&CHK if (($op eq '?:' || $op eq '?' || $op eq ':') && $ctx =~ /VxV/); + my $msg_level = \&ERROR; + $msg_level = \&CHK if (($op eq '?:' || $op eq '?' || $op eq ':') && $ctx =~ /VxV/); - if (&{$msg_type}("SPACING", - "spaces required around that '$op' $at\n" . $hereptr)) { + if (&{$msg_level}("SPACING", + "spaces required around that '$op' $at\n" . $hereptr)) { $good = rtrim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]) . " "; if (defined $fix_elements[$n + 2]) { $fix_elements[$n + 2] =~ s/^\s+//; @@ -4338,11 +4597,11 @@ #need space before brace following if, while, etc if (($line =~ /\(.*\)\{/ && $line !~ /\($Type\)\{/) || - $line =~ /do\{/) { + $line =~ /\b(?:else|do)\{/) { if (ERROR("SPACING", "space required before the open brace '{'\n" . $herecurr) && $fix) { - $fixed[$fixlinenr] =~ s/^(\+.*(?:do|\))){/$1 {/; + $fixed[$fixlinenr] =~ s/^(\+.*(?:do|else|\)))\{/$1 {/; } } @@ -4430,6 +4689,32 @@ } } +# check for unnecessary parentheses around comparisons in if uses +# when !drivers/staging or command-line uses --strict + if (($realfile !~ m@^(?:drivers/staging/)@ || $check_orig) && + $perl_version_ok && defined($stat) && + $stat =~ /(^.\s*if\s*($balanced_parens))/) { + my $if_stat = $1; + my $test = substr($2, 1, -1); + my $herectx; + while ($test =~ /(?:^|[^\w\&\!\~])+\s*\(\s*([\&\!\~]?\s*$Lval\s*(?:$Compare\s*$FuncArg)?)\s*\)/g) { + my $match = $1; + # avoid parentheses around potential macro args + next if ($match =~ /^\s*\w+\s*$/); + if (!defined($herectx)) { + $herectx = $here . "\n"; + my $cnt = statement_rawlines($if_stat); + for (my $n = 0; $n < $cnt; $n++) { + my $rl = raw_line($linenr, $n); + $herectx .= $rl . "\n"; + last if $rl =~ /^[ \+].*\{/; + } + } + CHK("UNNECESSARY_PARENTHESES", + "Unnecessary parentheses around '$match'\n" . $herectx); + } + } + #goto labels aren't indented, allow a single space however if ($line=~/^.\s+[A-Za-z\d_]+:(?![0-9]+)/ and !($line=~/^. [A-Za-z\d_]+:/) and !($line=~/^.\s+default:/)) { @@ -4444,7 +4729,7 @@ # return is not a function if (defined($stat) && $stat =~ /^.\s*return(\s*)\(/s) { my $spacing = $1; - if ($^V && $^V ge 5.10.0 && + if ($perl_version_ok && $stat =~ /^.\s*return\s*($balanced_parens)\s*;\s*$/) { my $value = $1; $value = deparenthesize($value); @@ -4471,7 +4756,7 @@ } # if statements using unnecessary parentheses - ie: if ((foo == bar)) - if ($^V && $^V ge 5.10.0 && + if ($perl_version_ok && $line =~ /\bif\s*((?:\(\s*){2,})/) { my $openparens = $1; my $count = $openparens =~ tr@\(@\(@; @@ -4488,7 +4773,7 @@ # avoid cases like "foo + BAR < baz" # only fix matches surrounded by parentheses to avoid incorrect # conversions like "FOO < baz() + 5" being "misfixed" to "baz() > FOO + 5" - if ($^V && $^V ge 5.10.0 && + if ($perl_version_ok && $line =~ /^\+(.*)\b($Constant|[A-Z_][A-Z0-9_]*)\s*($Compare)\s*($LvalOrFunc)/) { my $lead = $1; my $const = $2; @@ -4680,17 +4965,6 @@ while ($line =~ m{($Constant|$Lval)}g) { my $var = $1; -#gcc binary extension - if ($var =~ /^$Binary$/) { - if (WARN("GCC_BINARY_CONSTANT", - "Avoid gcc v4.3+ binary constant extension: <$var>\n" . $herecurr) && - $fix) { - my $hexval = sprintf("0x%x", oct($var)); - $fixed[$fixlinenr] =~ - s/\b$var\b/$hexval/; - } - } - #CamelCase if ($var !~ /^$Constant$/ && $var =~ /[A-Z][a-z]|[a-z][A-Z]/ && @@ -4778,6 +5052,7 @@ if (defined $define_args && $define_args ne "") { $define_args = substr($define_args, 1, length($define_args) - 2); $define_args =~ s/\s*//g; + $define_args =~ s/\\\+?//g; @def_args = split(",", $define_args); } @@ -4818,12 +5093,8 @@ #print "REST<$rest> dstat<$dstat> ctx<$ctx>\n"; $ctx =~ s/\n*$//; - my $herectx = $here . "\n"; my $stmt_cnt = statement_rawlines($ctx); - - for (my $n = 0; $n < $stmt_cnt; $n++) { - $herectx .= raw_line($linenr, $n) . "\n"; - } + my $herectx = get_stat_here($linenr, $stmt_cnt, $here); if ($dstat ne '' && $dstat !~ /^(?:$Ident|-?$Constant),$/ && # 10, // foo(), @@ -4840,8 +5111,10 @@ $dstat !~ /^\(\{/ && # ({... $ctx !~ /^.\s*#\s*define\s+TRACE_(?:SYSTEM|INCLUDE_FILE|INCLUDE_PATH)\b/) { - - if ($dstat =~ /;/) { + if ($dstat =~ /^\s*if\b/) { + ERROR("MULTISTATEMENT_MACRO_USE_DO_WHILE", + "Macros starting with if should be enclosed by a do - while loop to avoid possible if/else logic defects\n" . "$herectx"); + } elsif ($dstat =~ /;/) { ERROR("MULTISTATEMENT_MACRO_USE_DO_WHILE", "Macros with multiple statements should be enclosed in a do - while loop\n" . "$herectx"); } else { @@ -4872,17 +5145,17 @@ foreach my $arg (@def_args) { next if ($arg =~ /\.\.\./); next if ($arg =~ /^type$/i); - my $tmp = $define_stmt; - $tmp =~ s/\b(typeof|__typeof__|__builtin\w+|typecheck\s*\(\s*$Type\s*,|\#+)\s*\(*\s*$arg\s*\)*\b//g; - $tmp =~ s/\#+\s*$arg\b//g; - $tmp =~ s/\b$arg\s*\#\#//g; - my $use_cnt = $tmp =~ s/\b$arg\b//g; + my $tmp_stmt = $define_stmt; + $tmp_stmt =~ s/\b(typeof|__typeof__|__builtin\w+|typecheck\s*\(\s*$Type\s*,|\#+)\s*\(*\s*$arg\s*\)*\b//g; + $tmp_stmt =~ s/\#+\s*$arg\b//g; + $tmp_stmt =~ s/\b$arg\s*\#\#//g; + my $use_cnt = () = $tmp_stmt =~ /\b$arg\b/g; if ($use_cnt > 1) { CHK("MACRO_ARG_REUSE", "Macro argument reuse '$arg' - possible side-effects?\n" . "$herectx"); } # check if any macro arguments may have other precedence issues - if ($define_stmt =~ m/($Operators)?\s*\b$arg\b\s*($Operators)?/m && + if ($tmp_stmt =~ m/($Operators)?\s*\b$arg\b\s*($Operators)?/m && ((defined($1) && $1 ne ',') || (defined($2) && $2 ne ','))) { CHK("MACRO_ARG_PRECEDENCE", @@ -4893,12 +5166,9 @@ # check for macros with flow control, but without ## concatenation # ## concatenation is commonly a macro that defines a function so ignore those if ($has_flow_statement && !$has_arg_concat) { - my $herectx = $here . "\n"; my $cnt = statement_rawlines($ctx); + my $herectx = get_stat_here($linenr, $cnt, $here); - for (my $n = 0; $n < $cnt; $n++) { - $herectx .= raw_line($linenr, $n) . "\n"; - } WARN("MACRO_WITH_FLOW_CONTROL", "Macros with flow control statements should be avoided\n" . "$herectx"); } @@ -4918,7 +5188,7 @@ # do {} while (0) macro tests: # single-statement macros do not need to be enclosed in do while (0) loop, # macro should not end with a semicolon - if ($^V && $^V ge 5.10.0 && + if ($perl_version_ok && $realfile !~ m@/vmlinux.lds.h$@ && $line =~ /^.\s*\#\s*define\s+$Ident(\()?/) { my $ln = $linenr; @@ -4938,11 +5208,7 @@ $ctx =~ s/\n*$//; my $cnt = statement_rawlines($ctx); - my $herectx = $here . "\n"; - - for (my $n = 0; $n < $cnt; $n++) { - $herectx .= raw_line($linenr, $n) . "\n"; - } + my $herectx = get_stat_here($linenr, $cnt, $here); if (($stmts =~ tr/;/;/) == 1 && $stmts !~ /^\s*(if|while|for|switch)\b/) { @@ -4956,27 +5222,13 @@ } elsif ($dstat =~ /^\+\s*#\s*define\s+$Ident.*;\s*$/) { $ctx =~ s/\n*$//; my $cnt = statement_rawlines($ctx); - my $herectx = $here . "\n"; - - for (my $n = 0; $n < $cnt; $n++) { - $herectx .= raw_line($linenr, $n) . "\n"; - } + my $herectx = get_stat_here($linenr, $cnt, $here); WARN("TRAILING_SEMICOLON", "macros should not use a trailing semicolon\n" . "$herectx"); } } -# make sure symbols are always wrapped with VMLINUX_SYMBOL() ... -# all assignments may have only one of the following with an assignment: -# . -# ALIGN(...) -# VMLINUX_SYMBOL(...) - if ($realfile eq 'vmlinux.lds.h' && $line =~ /(?:(?:^|\s)$Ident\s*=|=\s*$Ident(?:\s|$))/) { - WARN("MISSING_VMLINUX_SYMBOL", - "vmlinux.lds.h needs VMLINUX_SYMBOL() around C-visible symbols\n" . $herecurr); - } - # check for redundant bracing round if etc if ($line =~ /(^.*)\bif\b/ && $1 !~ /else\s*$/) { my ($level, $endln, @chunks) = @@ -5083,18 +5335,20 @@ } } if ($level == 0 && $block =~ /^\s*\{/ && !$allowed) { - my $herectx = $here . "\n"; my $cnt = statement_rawlines($block); - - for (my $n = 0; $n < $cnt; $n++) { - $herectx .= raw_line($linenr, $n) . "\n"; - } + my $herectx = get_stat_here($linenr, $cnt, $here); WARN("BRACES", "braces {} are not necessary for single statement blocks\n" . $herectx); } } +# check for single line unbalanced braces + if ($sline =~ /^.\s*\}\s*else\s*$/ || + $sline =~ /^.\s*else\s*\{\s*$/) { + CHK("BRACES", "Unbalanced braces around else statement\n" . $herecurr); + } + # check for unnecessary blank lines around braces if (($line =~ /^.\s*}\s*$/ && $prevrawline =~ /^.\s*$/)) { if (CHK("BRACES", @@ -5157,6 +5411,18 @@ "break quoted strings at a space character\n" . $hereprev); } +# check for an embedded function name in a string when the function is known +# This does not work very well for -f --file checking as it depends on patch +# context providing the function name or a single line form for in-file +# function declarations + if ($line =~ /^\+.*$String/ && + defined($context_function) && + get_quoted_string($line, $rawline) =~ /\b$context_function\b/ && + length(get_quoted_string($line, $rawline)) != (length($context_function) + 2)) { + WARN("EMBEDDED_FUNCTION_NAME", + "Prefer using '\"%s...\", __func__' to using '$context_function', this function's name, in a string\n" . $herecurr); + } + # check for spaces before a quoted newline if ($rawline =~ /^.*\".*\s\\n/) { if (WARN("QUOTED_WHITESPACE_BEFORE_NEWLINE", @@ -5168,44 +5434,71 @@ } # concatenated string without spaces between elements - if ($line =~ /$String[A-Z_]/ || $line =~ /[A-Za-z0-9_]$String/) { - CHK("CONCATENATED_STRING", - "Concatenated strings should use spaces between elements\n" . $herecurr); + if ($line =~ /$String[A-Za-z0-9_]/ || $line =~ /[A-Za-z0-9_]$String/) { + if (CHK("CONCATENATED_STRING", + "Concatenated strings should use spaces between elements\n" . $herecurr) && + $fix) { + while ($line =~ /($String)/g) { + my $extracted_string = substr($rawline, $-[0], $+[0] - $-[0]); + $fixed[$fixlinenr] =~ s/\Q$extracted_string\E([A-Za-z0-9_])/$extracted_string $1/; + $fixed[$fixlinenr] =~ s/([A-Za-z0-9_])\Q$extracted_string\E/$1 $extracted_string/; + } + } } # uncoalesced string fragments if ($line =~ /$String\s*"/) { - WARN("STRING_FRAGMENTS", - "Consecutive strings are generally better as a single string\n" . $herecurr); + if (WARN("STRING_FRAGMENTS", + "Consecutive strings are generally better as a single string\n" . $herecurr) && + $fix) { + while ($line =~ /($String)(?=\s*")/g) { + my $extracted_string = substr($rawline, $-[0], $+[0] - $-[0]); + $fixed[$fixlinenr] =~ s/\Q$extracted_string\E\s*"/substr($extracted_string, 0, -1)/e; + } + } } -# check for %L{u,d,i} and 0x%[udi] in strings - my $string; +# check for non-standard and hex prefixed decimal printf formats + my $show_L = 1; #don't show the same defect twice + my $show_Z = 1; while ($line =~ /(?:^|")([X\t]*)(?:"|$)/g) { - $string = substr($rawline, $-[1], $+[1] - $-[1]); + my $string = substr($rawline, $-[1], $+[1] - $-[1]); $string =~ s/%%/__/g; - if ($string =~ /(?<!%)%[\*\d\.\$]*L[udi]/) { + # check for %L + if ($show_L && $string =~ /%[\*\d\.\$]*L([diouxX])/) { WARN("PRINTF_L", - "\%Ld/%Lu are not-standard C, use %lld/%llu\n" . $herecurr); - last; + "\%L$1 is non-standard C, use %ll$1\n" . $herecurr); + $show_L = 0; } - if ($string =~ /0x%[\*\d\.\$\Llzth]*[udi]/) { - ERROR("PRINTF_0xDECIMAL", + # check for %Z + if ($show_Z && $string =~ /%[\*\d\.\$]*Z([diouxX])/) { + WARN("PRINTF_Z", + "%Z$1 is non-standard C, use %z$1\n" . $herecurr); + $show_Z = 0; + } + # check for 0x<decimal> + if ($string =~ /0x%[\*\d\.\$\Llzth]*[diou]/) { + ERROR("PRINTF_0XDECIMAL", "Prefixing 0x with decimal output is defective\n" . $herecurr); } } # check for line continuations in quoted strings with odd counts of " - if ($rawline =~ /\\$/ && $rawline =~ tr/"/"/ % 2) { + if ($rawline =~ /\\$/ && $sline =~ tr/"/"/ % 2) { WARN("LINE_CONTINUATIONS", "Avoid line continuations in quoted strings\n" . $herecurr); } # warn about #if 0 if ($line =~ /^.\s*\#\s*if\s+0\b/) { - CHK("REDUNDANT_CODE", - "if this code is redundant consider removing it\n" . - $herecurr); + WARN("IF_0", + "Consider removing the code enclosed by this #if 0 and its #endif\n" . $herecurr); + } + +# warn about #if 1 + if ($line =~ /^.\s*\#\s*if\s+1\b/) { + WARN("IF_1", + "Consider removing the #if 1 and its #endif\n" . $herecurr); } # check for needless "if (<foo>) fn(<foo>)" uses @@ -5252,7 +5545,7 @@ my ($s, $c) = ctx_statement_block($linenr - 3, $realcnt, 0); # print("line: <$line>\nprevline: <$prevline>\ns: <$s>\nc: <$c>\n\n\n"); - if ($c =~ /(?:^|\n)[ \+]\s*(?:$Type\s*)?\Q$testval\E\s*=\s*(?:\([^\)]*\)\s*)?\s*(?:devm_)?(?:[kv][czm]alloc(?:_node|_array)?\b|kstrdup|(?:dev_)?alloc_skb)/) { + if ($s =~ /(?:^|\n)[ \+]\s*(?:$Type\s*)?\Q$testval\E\s*=\s*(?:\([^\)]*\)\s*)?\s*(?:devm_)?(?:[kv][czm]alloc(?:_node|_array)?\b|kstrdup|kmemdup|(?:dev_)?alloc_skb)/) { WARN("OOM_MESSAGE", "Possible unnecessary 'out of memory' message\n" . $hereprev); } @@ -5269,8 +5562,14 @@ } } +# check for logging continuations + if ($line =~ /\bprintk\s*\(\s*KERN_CONT\b|\bpr_cont\s*\(/) { + WARN("LOGGING_CONTINUATION", + "Avoid logging continuation uses where feasible\n" . $herecurr); + } + # check for mask then right shift without a parentheses - if ($^V && $^V ge 5.10.0 && + if ($perl_version_ok && $line =~ /$LvalOrFunc\s*\&\s*($LvalOrFunc)\s*>>/ && $4 !~ /^\&/) { # $LvalOrFunc may be &foo, ignore if so WARN("MASK_THEN_SHIFT", @@ -5278,7 +5577,7 @@ } # check for pointer comparisons to NULL - if ($^V && $^V ge 5.10.0) { + if ($perl_version_ok) { while ($line =~ /\b$LvalOrFunc\s*(==|\!=)\s*NULL\b/g) { my $val = $1; my $equal = "!"; @@ -5469,21 +5768,10 @@ } } -# Check for expedited grace periods that interrupt non-idle non-nohz -# online CPUs. These expedited can therefore degrade real-time response -# if used carelessly, and should be avoided where not absolutely -# needed. It is always OK to use synchronize_rcu_expedited() and -# synchronize_sched_expedited() at boot time (before real-time applications -# start) and in error situations where real-time response is compromised in -# any case. Note that synchronize_srcu_expedited() does -not- interrupt -# other CPUs, so don't warn on uses of synchronize_srcu_expedited(). -# Of course, nothing comes for free, and srcu_read_lock() and -# srcu_read_unlock() do contain full memory barriers in payment for -# synchronize_srcu_expedited() non-interruption properties. - if ($line =~ /\b(synchronize_rcu_expedited|synchronize_sched_expedited)\(/) { - WARN("EXPEDITED_RCU_GRACE_PERIOD", - "expedited RCU grace periods should be avoided where they can degrade real-time response\n" . $herecurr); - +# check for smp_read_barrier_depends and read_barrier_depends + if (!$file && $line =~ /\b(smp_|)read_barrier_depends\s*\(/) { + WARN("READ_BARRIER_DEPENDS", + "$1read_barrier_depends should only be used in READ_ONCE or DEC Alpha code\n" . $herecurr); } # check of hardware specific defines @@ -5492,10 +5780,18 @@ "architecture specific defines should be avoided\n" . $herecurr); } +# check that the storage class is not after a type + if ($line =~ /\b($Type)\s+($Storage)\b/) { + WARN("STORAGE_CLASS", + "storage class '$2' should be located before type '$1'\n" . $herecurr); + } # Check that the storage class is at the beginning of a declaration - if ($line =~ /\b$Storage\b/ && $line !~ /^.\s*$Storage\b/) { + if ($line =~ /\b$Storage\b/ && + $line !~ /^.\s*$Storage/ && + $line =~ /^.\s*(.+?)\$Storage\s/ && + $1 !~ /[\,\)]\s*$/) { WARN("STORAGE_CLASS", - "storage class should be at the beginning of the declaration\n" . $herecurr) + "storage class should be at the beginning of the declaration\n" . $herecurr); } # check the location of the inline attribute, that it is between @@ -5553,7 +5849,7 @@ } # Check for __attribute__ weak, or __weak declarations (may have link issues) - if ($^V && $^V ge 5.10.0 && + if ($perl_version_ok && $line =~ /(?:$Declare|$DeclareMisordered)\s*$Ident\s*$balanced_parens\s*(?:$Attribute)?\s*;/ && ($line =~ /\b__attribute__\s*\(\s*\(.*\bweak\b/ || $line =~ /\b__weak\b/)) { @@ -5634,8 +5930,55 @@ } } +# check for vsprintf extension %p<foo> misuses + if ($perl_version_ok && + defined $stat && + $stat =~ /^\+(?![^\{]*\{\s*).*\b(\w+)\s*\(.*$String\s*,/s && + $1 !~ /^_*volatile_*$/) { + my $stat_real; + + my $lc = $stat =~ tr@\n@@; + $lc = $lc + $linenr; + for (my $count = $linenr; $count <= $lc; $count++) { + my $specifier; + my $extension; + my $bad_specifier = ""; + my $fmt = get_quoted_string($lines[$count - 1], raw_line($count, 0)); + $fmt =~ s/%%//g; + + while ($fmt =~ /(\%[\*\d\.]*p(\w))/g) { + $specifier = $1; + $extension = $2; + if ($extension !~ /[SsBKRraEhMmIiUDdgVCbGNOx]/) { + $bad_specifier = $specifier; + last; + } + if ($extension eq "x" && !defined($stat_real)) { + if (!defined($stat_real)) { + $stat_real = get_stat_real($linenr, $lc); + } + WARN("VSPRINTF_SPECIFIER_PX", + "Using vsprintf specifier '\%px' potentially exposes the kernel memory layout, if you don't really need the address please consider using '\%p'.\n" . "$here\n$stat_real\n"); + } + } + if ($bad_specifier ne "") { + my $stat_real = get_stat_real($linenr, $lc); + my $ext_type = "Invalid"; + my $use = ""; + if ($bad_specifier =~ /p[Ff]/) { + $ext_type = "Deprecated"; + $use = " - use %pS instead"; + $use =~ s/pS/ps/ if ($bad_specifier =~ /pf/); + } + + WARN("VSPRINTF_POINTER_EXTENSION", + "$ext_type vsprintf pointer extension '$bad_specifier'$use\n" . "$here\n$stat_real\n"); + } + } + } + # Check for misused memsets - if ($^V && $^V ge 5.10.0 && + if ($perl_version_ok && defined $stat && $stat =~ /^\+(?:.*?)\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*$FuncArg\s*\)/) { @@ -5653,7 +5996,7 @@ } # Check for memcpy(foo, bar, ETH_ALEN) that could be ether_addr_copy(foo, bar) -# if ($^V && $^V ge 5.10.0 && +# if ($perl_version_ok && # defined $stat && # $stat =~ /^\+(?:.*?)\bmemcpy\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/) { # if (WARN("PREFER_ETHER_ADDR_COPY", @@ -5664,7 +6007,7 @@ # } # Check for memcmp(foo, bar, ETH_ALEN) that could be ether_addr_equal*(foo, bar) -# if ($^V && $^V ge 5.10.0 && +# if ($perl_version_ok && # defined $stat && # $stat =~ /^\+(?:.*?)\bmemcmp\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/) { # WARN("PREFER_ETHER_ADDR_EQUAL", @@ -5673,7 +6016,7 @@ # check for memset(foo, 0x0, ETH_ALEN) that could be eth_zero_addr # check for memset(foo, 0xFF, ETH_ALEN) that could be eth_broadcast_addr -# if ($^V && $^V ge 5.10.0 && +# if ($perl_version_ok && # defined $stat && # $stat =~ /^\+(?:.*?)\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/) { # @@ -5695,7 +6038,7 @@ # } # typecasts on min/max could be min_t/max_t - if ($^V && $^V ge 5.10.0 && + if ($perl_version_ok && defined $stat && $stat =~ /^\+(?:.*?)\b(min|max)\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\)/) { if (defined $2 || defined $7) { @@ -5719,7 +6062,7 @@ } # check usleep_range arguments - if ($^V && $^V ge 5.10.0 && + if ($perl_version_ok && defined $stat && $stat =~ /^\+(?:.*?)\busleep_range\s*\(\s*($FuncArg)\s*,\s*($FuncArg)\s*\)/) { my $min = $1; @@ -5735,7 +6078,7 @@ } # check for naked sscanf - if ($^V && $^V ge 5.10.0 && + if ($perl_version_ok && defined $stat && $line =~ /\bsscanf\b/ && ($stat !~ /$Ident\s*=\s*sscanf\s*$balanced_parens/ && @@ -5743,24 +6086,18 @@ $stat !~ /(?:$Compare)\s*\bsscanf\s*$balanced_parens/)) { my $lc = $stat =~ tr@\n@@; $lc = $lc + $linenr; - my $stat_real = raw_line($linenr, 0); - for (my $count = $linenr + 1; $count <= $lc; $count++) { - $stat_real = $stat_real . "\n" . raw_line($count, 0); - } + my $stat_real = get_stat_real($linenr, $lc); WARN("NAKED_SSCANF", "unchecked sscanf return value\n" . "$here\n$stat_real\n"); } # check for simple sscanf that should be kstrto<foo> - if ($^V && $^V ge 5.10.0 && + if ($perl_version_ok && defined $stat && $line =~ /\bsscanf\b/) { my $lc = $stat =~ tr@\n@@; $lc = $lc + $linenr; - my $stat_real = raw_line($linenr, 0); - for (my $count = $linenr + 1; $count <= $lc; $count++) { - $stat_real = $stat_real . "\n" . raw_line($count, 0); - } + my $stat_real = get_stat_real($linenr, $lc); if ($stat_real =~ /\bsscanf\b\s*\(\s*$FuncArg\s*,\s*("[^"]+")/) { my $format = $6; my $count = $format =~ tr@%@%@; @@ -5812,8 +6149,9 @@ "externs should be avoided in .c files\n" . $herecurr); } - if ($realfile =~ /\.[ch]$/ && defined $stat && - $stat =~ /^.\s*(?:extern\s+)?$Type\s*$Ident\s*\(\s*([^{]+)\s*\)\s*;/s && +# check for function declarations that have arguments without identifier names + if (defined $stat && + $stat =~ /^.\s*(?:extern\s+)?$Type\s*(?:$Ident|\(\s*\*\s*$Ident\s*\))\s*\(\s*([^{]+)\s*\)\s*;/s && $1 ne "void") { my $args = trim($1); while ($args =~ m/\s*($Type\s*(?:$Ident|\(\s*\*\s*$Ident?\s*\)\s*$balanced_parens)?)/g) { @@ -5825,6 +6163,29 @@ } } +# check for function definitions + if ($perl_version_ok && + defined $stat && + $stat =~ /^.\s*(?:$Storage\s+)?$Type\s*($Ident)\s*$balanced_parens\s*{/s) { + $context_function = $1; + +# check for multiline function definition with misplaced open brace + my $ok = 0; + my $cnt = statement_rawlines($stat); + my $herectx = $here . "\n"; + for (my $n = 0; $n < $cnt; $n++) { + my $rl = raw_line($linenr, $n); + $herectx .= $rl . "\n"; + $ok = 1 if ($rl =~ /^[ \+]\{/); + $ok = 1 if ($rl =~ /\{/ && $n == 0); + last if $rl =~ /^[ \+].*\{/; + } + if (!$ok) { + ERROR("OPEN_BRACE", + "open brace '{' following function definitions go on the next line\n" . $herectx); + } + } + # checks for new __setup's if ($rawline =~ /\b__setup\("([^"]*)"/) { my $name = $1; @@ -5843,15 +6204,16 @@ # alloc style # p = alloc(sizeof(struct foo), ...) should be p = alloc(sizeof(*p), ...) - if ($^V && $^V ge 5.10.0 && + if ($perl_version_ok && $line =~ /\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*([kv][mz]alloc(?:_node)?)\s*\(\s*(sizeof\s*\(\s*struct\s+$Lval\s*\))/) { CHK("ALLOC_SIZEOF_STRUCT", "Prefer $3(sizeof(*$1)...) over $3($4...)\n" . $herecurr); } # check for k[mz]alloc with multiplies that could be kmalloc_array/kcalloc - if ($^V && $^V ge 5.10.0 && - $line =~ /\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*(k[mz]alloc)\s*\(\s*($FuncArg)\s*\*\s*($FuncArg)\s*,/) { + if ($perl_version_ok && + defined $stat && + $stat =~ /^\+\s*($Lval)\s*\=\s*(?:$balanced_parens)?\s*(k[mz]alloc)\s*\(\s*($FuncArg)\s*\*\s*($FuncArg)\s*,/) { my $oldfunc = $3; my $a1 = $4; my $a2 = $10; @@ -5865,18 +6227,22 @@ } if ($r1 !~ /^sizeof\b/ && $r2 =~ /^sizeof\s*\S/ && !($r1 =~ /^$Constant$/ || $r1 =~ /^[A-Z_][A-Z0-9_]*$/)) { + my $cnt = statement_rawlines($stat); + my $herectx = get_stat_here($linenr, $cnt, $here); + if (WARN("ALLOC_WITH_MULTIPLY", - "Prefer $newfunc over $oldfunc with multiply\n" . $herecurr) && + "Prefer $newfunc over $oldfunc with multiply\n" . $herectx) && + $cnt == 1 && $fix) { $fixed[$fixlinenr] =~ s/\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*(k[mz]alloc)\s*\(\s*($FuncArg)\s*\*\s*($FuncArg)/$1 . ' = ' . "$newfunc(" . trim($r1) . ', ' . trim($r2)/e; - } } } # check for krealloc arg reuse - if ($^V && $^V ge 5.10.0 && - $line =~ /\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*krealloc\s*\(\s*\1\s*,/) { + if ($perl_version_ok && + $line =~ /\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*krealloc\s*\(\s*($Lval)\s*,/ && + $1 eq $3) { WARN("KREALLOC_ARG_REUSE", "Reusing the krealloc arg is almost always a bug\n" . $herecurr); } @@ -5935,7 +6301,7 @@ next if ($fline =~ /^.[\s$;]*$/); $has_statement = 1; $count++; - $has_break = 1 if ($fline =~ /\bswitch\b|\b(?:break\s*;[\s$;]*$|return\b|goto\b|continue\b)/); + $has_break = 1 if ($fline =~ /\bswitch\b|\b(?:break\s*;[\s$;]*$|exit\s*\(\b|return\b|goto\b|continue\b)/); } if (!$has_break && $has_statement) { WARN("MISSING_BREAK", @@ -5944,15 +6310,12 @@ } # check for switch/default statements without a break; - if ($^V && $^V ge 5.10.0 && + if ($perl_version_ok && defined $stat && $stat =~ /^\+[$;\s]*(?:case[$;\s]+\w+[$;\s]*:[$;\s]*|)*[$;\s]*\bdefault[$;\s]*:[$;\s]*;/g) { - my $ctx = ''; - my $herectx = $here . "\n"; my $cnt = statement_rawlines($stat); - for (my $n = 0; $n < $cnt; $n++) { - $herectx .= raw_line($linenr, $n) . "\n"; - } + my $herectx = get_stat_here($linenr, $cnt, $here); + WARN("DEFAULT_NO_BREAK", "switch default: should use break\n" . $herectx); } @@ -6005,6 +6368,19 @@ } } +# check for bool bitfields + if ($sline =~ /^.\s+bool\s*$Ident\s*:\s*\d+\s*;/) { + WARN("BOOL_BITFIELD", + "Avoid using bool as bitfield. Prefer bool bitfields as unsigned int or u<8|16|32>\n" . $herecurr); + } + +# check for bool use in .h files + if ($realfile =~ /\.h$/ && + $sline =~ /^.\s+bool\s*$Ident\s*(?::\s*d+\s*)?;/) { + CHK("BOOL_MEMBER", + "Avoid using bool structure members because of possible alignment issues - see: https://lkml.org/lkml/2017/11/21/384\n" . $herecurr); + } + # check for semaphores initialized locked if ($line =~ /^.\s*sema_init.+,\W?0\W?\)/) { WARN("CONSIDER_COMPLETION", @@ -6023,12 +6399,26 @@ "please use device_initcall() or more appropriate function instead of __initcall() (see include/linux/init.h)\n" . $herecurr); } +# check for spin_is_locked(), suggest lockdep instead + if ($line =~ /\bspin_is_locked\(/) { + WARN("USE_LOCKDEP", + "Where possible, use lockdep_assert_held instead of assertions based on spin_is_locked\n" . $herecurr); + } + +# check for deprecated apis + if ($line =~ /\b($deprecated_apis_search)\b\s*\(/) { + my $deprecated_api = $1; + my $new_api = $deprecated_apis{$deprecated_api}; + WARN("DEPRECATED_API", + "Deprecated use of '$deprecated_api', prefer '$new_api' instead\n" . $herecurr); + } + # check for various structs that are normally const (ops, kgdb, device_tree) +# and avoid what seem like struct definitions 'struct foo {' if ($line !~ /\bconst\b/ && - $line =~ /\bstruct\s+($const_structs)\b/) { + $line =~ /\bstruct\s+($const_structs)\b(?!\s*\{)/) { WARN("CONST_STRUCT", - "struct $1 should normally be const\n" . - $herecurr); + "struct $1 should normally be const\n" . $herecurr); } # use of NR_CPUS is usually wrong @@ -6051,7 +6441,7 @@ } # likely/unlikely comparisons similar to "(likely(foo) > 0)" - if ($^V && $^V ge 5.10.0 && + if ($perl_version_ok && $line =~ /\b((?:un)?likely)\s*\(\s*$FuncArg\s*\)\s*$Compare/) { WARN("LIKELY_MISUSE", "Using $1 should generally have parentheses around the comparison\n" . $herecurr); @@ -6068,28 +6458,6 @@ } } -# whine about ACCESS_ONCE - if ($^V && $^V ge 5.10.0 && - $line =~ /\bACCESS_ONCE\s*$balanced_parens\s*(=(?!=))?\s*($FuncArg)?/) { - my $par = $1; - my $eq = $2; - my $fun = $3; - $par =~ s/^\(\s*(.*)\s*\)$/$1/; - if (defined($eq)) { - if (WARN("PREFER_WRITE_ONCE", - "Prefer WRITE_ONCE(<FOO>, <BAR>) over ACCESS_ONCE(<FOO>) = <BAR>\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\bACCESS_ONCE\s*\(\s*\Q$par\E\s*\)\s*$eq\s*\Q$fun\E/WRITE_ONCE($par, $fun)/; - } - } else { - if (WARN("PREFER_READ_ONCE", - "Prefer READ_ONCE(<FOO>) over ACCESS_ONCE(<FOO>)\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\bACCESS_ONCE\s*\(\s*\Q$par\E\s*\)/READ_ONCE($par)/; - } - } - } - # check for mutex_trylock_recursive usage if ($line =~ /mutex_trylock_recursive/) { ERROR("LOCKING", @@ -6113,9 +6481,70 @@ "Exporting world writable files is usually an error. Consider more restrictive permissions.\n" . $herecurr); } +# check for DEVICE_ATTR uses that could be DEVICE_ATTR_<FOO> +# and whether or not function naming is typical and if +# DEVICE_ATTR permissions uses are unusual too + if ($perl_version_ok && + defined $stat && + $stat =~ /\bDEVICE_ATTR\s*\(\s*(\w+)\s*,\s*\(?\s*(\s*(?:${multi_mode_perms_string_search}|0[0-7]{3,3})\s*)\s*\)?\s*,\s*(\w+)\s*,\s*(\w+)\s*\)/) { + my $var = $1; + my $perms = $2; + my $show = $3; + my $store = $4; + my $octal_perms = perms_to_octal($perms); + if ($show =~ /^${var}_show$/ && + $store =~ /^${var}_store$/ && + $octal_perms eq "0644") { + if (WARN("DEVICE_ATTR_RW", + "Use DEVICE_ATTR_RW\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\bDEVICE_ATTR\s*\(\s*$var\s*,\s*\Q$perms\E\s*,\s*$show\s*,\s*$store\s*\)/DEVICE_ATTR_RW(${var})/; + } + } elsif ($show =~ /^${var}_show$/ && + $store =~ /^NULL$/ && + $octal_perms eq "0444") { + if (WARN("DEVICE_ATTR_RO", + "Use DEVICE_ATTR_RO\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\bDEVICE_ATTR\s*\(\s*$var\s*,\s*\Q$perms\E\s*,\s*$show\s*,\s*NULL\s*\)/DEVICE_ATTR_RO(${var})/; + } + } elsif ($show =~ /^NULL$/ && + $store =~ /^${var}_store$/ && + $octal_perms eq "0200") { + if (WARN("DEVICE_ATTR_WO", + "Use DEVICE_ATTR_WO\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\bDEVICE_ATTR\s*\(\s*$var\s*,\s*\Q$perms\E\s*,\s*NULL\s*,\s*$store\s*\)/DEVICE_ATTR_WO(${var})/; + } + } elsif ($octal_perms eq "0644" || + $octal_perms eq "0444" || + $octal_perms eq "0200") { + my $newshow = "$show"; + $newshow = "${var}_show" if ($show ne "NULL" && $show ne "${var}_show"); + my $newstore = $store; + $newstore = "${var}_store" if ($store ne "NULL" && $store ne "${var}_store"); + my $rename = ""; + if ($show ne $newshow) { + $rename .= " '$show' to '$newshow'"; + } + if ($store ne $newstore) { + $rename .= " '$store' to '$newstore'"; + } + WARN("DEVICE_ATTR_FUNCTIONS", + "Consider renaming function(s)$rename\n" . $herecurr); + } else { + WARN("DEVICE_ATTR_PERMS", + "DEVICE_ATTR unusual permissions '$perms' used\n" . $herecurr); + } + } + # Mode permission misuses where it seems decimal should be octal # This uses a shortcut match to avoid unnecessary uses of a slow foreach loop - if ($^V && $^V ge 5.10.0 && +# o Ignore module_param*(...) uses with a decimal 0 permission as that has a +# specific definition of not visible in sysfs. +# o Ignore proc_create*(...) uses with a decimal 0 permission as that means +# use the default permissions + if ($perl_version_ok && defined $stat && $line =~ /$mode_perms_search/) { foreach my $entry (@mode_permission_funcs) { @@ -6124,10 +6553,7 @@ my $lc = $stat =~ tr@\n@@; $lc = $lc + $linenr; - my $stat_real = raw_line($linenr, 0); - for (my $count = $linenr + 1; $count <= $lc; $count++) { - $stat_real = $stat_real . "\n" . raw_line($count, 0); - } + my $stat_real = get_stat_real($linenr, $lc); my $skip_args = ""; if ($arg_pos > 1) { @@ -6138,8 +6564,9 @@ if ($stat =~ /$test/) { my $val = $1; $val = $6 if ($skip_args ne ""); - if (($val =~ /^$Int$/ && $val !~ /^$Octal$/) || - ($val =~ /^$Octal$/ && length($val) ne 4)) { + if (!($func =~ /^(?:module_param|proc_create)/ && $val eq "0") && + (($val =~ /^$Int$/ && $val !~ /^$Octal$/) || + ($val =~ /^$Octal$/ && length($val) ne 4))) { ERROR("NON_OCTAL_PERMISSIONS", "Use 4 digit octal (0777) not decimal permissions\n" . "$here\n" . $stat_real); } @@ -6152,30 +6579,13 @@ } # check for uses of S_<PERMS> that could be octal for readability - if ($line =~ /\b$mode_perms_string_search\b/) { - my $val = ""; - my $oval = ""; - my $to = 0; - my $curpos = 0; - my $lastpos = 0; - while ($line =~ /\b(($mode_perms_string_search)\b(?:\s*\|\s*)?\s*)/g) { - $curpos = pos($line); - my $match = $2; - my $omatch = $1; - last if ($lastpos > 0 && ($curpos - length($omatch) != $lastpos)); - $lastpos = $curpos; - $to |= $mode_permission_string_types{$match}; - $val .= '\s*\|\s*' if ($val ne ""); - $val .= $match; - $oval .= $omatch; - } - $oval =~ s/^\s*\|\s*//; - $oval =~ s/\s*\|\s*$//; - my $octal = sprintf("%04o", $to); + while ($line =~ m{\b($multi_mode_perms_string_search)\b}g) { + my $oval = $1; + my $octal = perms_to_octal($oval); if (WARN("SYMBOLIC_PERMS", "Symbolic permissions '$oval' are not preferred. Consider using octal permissions '$octal'.\n" . $herecurr) && $fix) { - $fixed[$fixlinenr] =~ s/$val/$octal/; + $fixed[$fixlinenr] =~ s/\Q$oval\E/$octal/; } } @@ -6216,13 +6626,18 @@ exit(0); } - if (!$is_patch && $file !~ /cover-letter\.patch$/) { + if (!$is_patch && $filename !~ /cover-letter\.patch$/) { ERROR("NOT_UNIFIED_DIFF", "Does not appear to be a unified-diff format patch\n"); } - if ($is_patch && $has_commit_log && $chk_signoff && $signoff == 0) { - ERROR("MISSING_SIGN_OFF", - "Missing Signed-off-by: line(s)\n"); + if ($is_patch && $has_commit_log && $chk_signoff) { + if ($signoff == 0) { + ERROR("MISSING_SIGN_OFF", + "Missing Signed-off-by: line(s)\n"); + } elsif (!$authorsignoff) { + WARN("NO_AUTHOR_SIGN_OFF", + "Missing Signed-off-by: line by nominal patch author '$author'\n"); + } } print report_dump(); diff -Nru librtr-0.6.3/scripts/cppcheck.sh librtr-0.7.0/scripts/cppcheck.sh --- librtr-0.6.3/scripts/cppcheck.sh 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/scripts/cppcheck.sh 2019-07-18 08:26:08.000000000 +0000 @@ -2,4 +2,4 @@ cppcheck --error-exitcode=1 --enable=all --inline-suppr \ --template='{id}:{file}({line}):({severity}) {message}' \ - -i rtrlib/spki/hashtable/tommyds-1.8/ tools/ rtrlib/ + -i rtrlib/spki/hashtable/tommyds-2.2/ tools/ rtrlib/ diff -Nru librtr-0.6.3/scripts/travis.sh librtr-0.7.0/scripts/travis.sh --- librtr-0.6.3/scripts/travis.sh 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/scripts/travis.sh 2019-07-18 08:26:08.000000000 +0000 @@ -4,30 +4,44 @@ GREEN='\033[0;32m' NC='\033[0m' # No Color +BUILDSTEP_FAILED=0 + function run_command { eval $@ ret=$? if [ $ret != 0 ]; then colour=$RED + BUILDSTEP_FAILED=1 else colour=$GREEN fi echo -e "\n${colour}The command \"$@\" exited with $ret.$NC\n\n" + return $ret +} + +function checkpatch { + git diff $TRAVIS_BRANCH {rtrlib,tools,tests}/**/*.[ch] > /tmp/patch + run_command scripts/checkpatch.pl --ignore FILE_PATH_CHANGES,PREFER_KERNEL_TYPES,CONST_STRUCT,OPEN_BRACE,SPDX_LICENSE_TAG,OPEN_ENDED_LINE,UNNECESSARY_PARENTHESES,PREFER_PRINTF,GLOBAL_INITIALISERS,PREFER_PACKED --terse --no-tree --strict --show-types --max-line-length 120 /tmp/patch + ret=$? if [ $ret != 0 ]; then - exit $ret + cat -n /tmp/patch fi } +[[ $TRAVIS = "true" ]] && run_command git fetch --unshallow run_command scripts/cppcheck.sh run_command scripts/check-coding-style.sh -run_command cmake -D CMAKE_BUILD_TYPE=NoSSH . +[[ $TRAVIS_EVENT_TYPE = "pull_request" ]] && run_command checkpatch +run_command cmake -D RTRLIB_TRANSPORT_SSH=Off . run_command make run_command make test run_command make clean -run_command cmake -D CMAKE_BUILD_TYPE=Release -DENABLE_COVERAGE=On -DUNIT_TESTING=On . +run_command cmake -D CMAKE_BUILD_TYPE=Release -DENABLE_COVERAGE=On -DUNIT_TESTING=On -DRTRLIB_TRANSPORT_SSH=On . run_command make run_command make test run_command make gcov run_command scripts/check-exports.sh + +exit $BUILDSTEP_FAILED diff -Nru librtr-0.6.3/tests/test_dynamic_groups.c librtr-0.7.0/tests/test_dynamic_groups.c --- librtr-0.6.3/tests/test_dynamic_groups.c 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/tests/test_dynamic_groups.c 2019-07-18 08:26:08.000000000 +0000 @@ -5,7 +5,7 @@ #include "rtrlib/rtrlib.h" #include "rtrlib/rtr_mgr_private.h" -#include "rtrlib/spki/hashtable/tommyds-1.8/tommylist.h" +#include "third-party/tommyds/tommylist.h" const int connection_timeout = 20; enum rtr_mgr_status connection_status = -1; @@ -24,7 +24,7 @@ int retval = 0; struct tr_socket tr_tcp; char tcp_host[] = "rpki-validator.realmv6.org"; - char tcp_port[] = "8282"; + char tcp_port[] = "8283"; struct tr_tcp_config tcp_config = { tcp_host, //IP diff -Nru librtr-0.6.3/tests/test_ht_spkitable.c librtr-0.7.0/tests/test_ht_spkitable.c --- librtr-0.6.3/tests/test_ht_spkitable.c 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/tests/test_ht_spkitable.c 2019-07-18 08:26:08.000000000 +0000 @@ -7,6 +7,7 @@ * Website: http://rtrlib.realmv6.org/ */ +#include <assert.h> #include <stdint.h> #include <stdio.h> #include <string.h> @@ -173,7 +174,7 @@ free(result); free(socket_one); free(socket_two); - printf("test_h1_1() complete\n"); + printf("%s() complete\n", __func__); } /** @@ -223,7 +224,7 @@ free(record1); free(record2); spki_table_free(&table); - printf("test_ht_2() complete\n"); + printf("%s() complete\n", __func__); } /** @@ -335,7 +336,7 @@ free(record2); free(record3); free(record4); - printf("test_ht_3() complete\n"); + printf("%s complete\n", __func__); } /** @@ -385,7 +386,7 @@ /* cleanup: free memory */ spki_table_free(&table); - printf("test_ht_4() complete\n"); + printf("%s() complete\n", __func__); } /** @@ -417,7 +418,7 @@ spki_table_free(&table); free(record1); free(record2); - printf("test_ht_5() complete\n"); + printf("%s() complete\n", __func__); } /** @@ -447,7 +448,7 @@ /* cleanup: free memory */ spki_table_free(&table); - printf("test_ht_6() complete\n"); + printf("%s() complete\n", __func__); } /** @@ -538,10 +539,10 @@ /* cleanup: free memory */ spki_table_free(&table); - printf("test_ht_7() complete\n"); + printf("%s() complete\n", __func__); } -void test_table_swap(void) +static void test_table_swap(void) { struct spki_table table1; struct spki_table table2; @@ -604,7 +605,7 @@ free(test_record1); free(test_record2); - printf("test_table_swap() complete\n"); + printf("%s() complete\n", __func__); } static void update_spki(struct spki_table *s __attribute__((unused)), @@ -645,7 +646,7 @@ assert(added); } -void test_table_diff(void) +static void test_table_diff(void) { struct spki_table table1; struct spki_table table2; @@ -680,7 +681,7 @@ free(test_record2); free(test_record3); - printf("test_table_diff() complete\n"); + printf("%s() complete\n", __func__); } int main(void) diff -Nru librtr-0.6.3/tests/test_ht_spkitable_locks.c librtr-0.7.0/tests/test_ht_spkitable_locks.c --- librtr-0.6.3/tests/test_ht_spkitable_locks.c 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/tests/test_ht_spkitable_locks.c 2019-07-18 08:26:08.000000000 +0000 @@ -8,6 +8,7 @@ */ #include "rtrlib/spki/hashtable/ht-spkitable_private.h" +#include "assert.h" #include <pthread.h> #include <stdio.h> #include <string.h> @@ -78,7 +79,7 @@ * Add 'args->count' records to the spki table 'args->table', start with * ASN 'args->start_asn'. */ -static void add_records(struct add_records_args *args) +static void *add_records(struct add_records_args *args) { printf("Add %i records: ASN [%i..%i]\n", args->count, args->start_asn, args->count + args->start_asn - 1); @@ -89,13 +90,15 @@ assert(ret == SPKI_SUCCESS); free(record); } + + return NULL; } /** * @brief remove records from spki table * Remove 'args->count' records from the spki table 'args->table'. */ -static void remove_records(struct remove_records_args *args) +static void *remove_records(struct remove_records_args *args) { printf("Remove %i records: ASN [%i..%i]\n", args->count, args->start_asn, args->count + args->start_asn - 1); @@ -106,6 +109,8 @@ assert(ret == SPKI_SUCCESS); free(record); } + + return NULL; } /** @@ -171,7 +176,7 @@ /* cleanup: free spki_table */ spki_table_free(&spkit); - printf("lock_test1() complete\n"); + printf("%s() complete\n", __func__); } int main(void) diff -Nru librtr-0.6.3/tests/test_ipaddr.c librtr-0.7.0/tests/test_ipaddr.c --- librtr-0.6.3/tests/test_ipaddr.c 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/tests/test_ipaddr.c 2019-07-18 08:26:08.000000000 +0000 @@ -224,7 +224,7 @@ /* * @brief test ip comparsions */ -void test_cmp(void) +static void test_cmp(void) { struct lrtr_ip_addr addr1, addr2; diff -Nru librtr-0.6.3/tests/test_live_validation.c librtr-0.7.0/tests/test_live_validation.c --- librtr-0.6.3/tests/test_live_validation.c 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/tests/test_live_validation.c 2019-07-18 08:26:08.000000000 +0000 @@ -14,7 +14,7 @@ #include "rtrlib/rtrlib.h" #define RPKI_CACHE_HOST "rpki-validator.realmv6.org" -#define RPKI_CACHE_POST "8282" +#define RPKI_CACHE_POST "8283" struct test_validity_query { char *pfx; diff -Nru librtr-0.6.3/tests/test_pfx.c librtr-0.7.0/tests/test_pfx.c --- librtr-0.6.3/tests/test_pfx.c 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/tests/test_pfx.c 2019-07-18 08:26:08.000000000 +0000 @@ -18,6 +18,7 @@ #include "rtrlib/lib/ip_private.h" #include "rtrlib/lib/utils_private.h" #include "rtrlib/pfx/pfx.h" +#include "rtrlib/pfx/pfx_private.h" #include "rtrlib/pfx/trie/trie_private.h" #include "rtrlib/rtr/rtr.h" @@ -95,7 +96,7 @@ /* cleanup: free table */ pfx_table_free(&pfxt); - printf("remove_src_test successful\n"); + printf("%s() successful\n", __func__); } /** @@ -185,7 +186,7 @@ /* cleanup: free table */ pfx_table_free(&pfxt); - printf("mass_test successful\n"); + printf("%s() successful\n", __func__); } /** @@ -330,7 +331,7 @@ /* cleanup: free record and table */ pfx_table_free(&pfxt); - printf("pfx_table_test successful\n"); + printf("%s() successful\n", __func__); } static void create_ip4_pfx_record(struct pfx_record *pfx, uint32_t asn, diff -Nru librtr-0.6.3/tests/test_pfx_locks.c librtr-0.7.0/tests/test_pfx_locks.c --- librtr-0.6.3/tests/test_pfx_locks.c 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/tests/test_pfx_locks.c 2019-07-18 08:26:08.000000000 +0000 @@ -26,7 +26,7 @@ /** * @brief Add records to prefix table */ -static void rec_add(struct pfx_table *pfxt) +static void *rec_add(struct pfx_table *pfxt) { const int tid = getpid(); struct pfx_record rec; @@ -56,12 +56,14 @@ pfx_table_add(pfxt, &rec); usleep(rand() / (RAND_MAX / 20)); } + + return NULL; } /** * @brief Validate records in prefix table */ -static void rec_val(struct pfx_table *pfxt) +static void *rec_val(struct pfx_table *pfxt) { const int tid = getpid(); struct pfx_record rec; @@ -92,12 +94,14 @@ &rec.prefix, rec.min_len, &res); usleep(rand() / (RAND_MAX / 20)); } + + return NULL; } /** * @brief Delete records from prefix table */ -static void rec_del(struct pfx_table *pfxt) +static void *rec_del(struct pfx_table *pfxt) { const int tid = getpid(); struct pfx_record rec; @@ -128,6 +132,8 @@ usleep(rand() / (RAND_MAX / 20)); } printf("Done\n"); + + return NULL; } /** diff -Nru librtr-0.6.3/tests/unittests/test_packets_static.c librtr-0.7.0/tests/unittests/test_packets_static.c --- librtr-0.6.3/tests/unittests/test_packets_static.c 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/tests/unittests/test_packets_static.c 2019-07-18 08:26:08.000000000 +0000 @@ -271,12 +271,6 @@ error->len = 24; error->rest[11] = 0xA; assert_false(rtr_pdu_check_size((struct pdu_header *)error)); - - /* test error pdu error string termination */ - error->len = 25; - error->rest[11] = 0x1; - error->rest[12] = 0x20; - assert_false(rtr_pdu_check_size((struct pdu_header *)error)); } static void test_rtr_send_error_pdu(void **state) diff -Nru librtr-0.6.3/tests/unittests/test_packets_static.h librtr-0.7.0/tests/unittests/test_packets_static.h --- librtr-0.6.3/tests/unittests/test_packets_static.h 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/tests/unittests/test_packets_static.h 2019-07-18 08:26:08.000000000 +0000 @@ -8,3 +8,7 @@ */ int __wrap_lrtr_get_monotonic_time(time_t *seconds); + +int __wrap_tr_send_all(const struct tr_socket *socket, + const void *pdu, const size_t len, + const time_t timeout); diff -Nru librtr-0.6.3/third-party/CMakeLists.txt librtr-0.7.0/third-party/CMakeLists.txt --- librtr-0.6.3/third-party/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 +++ librtr-0.7.0/third-party/CMakeLists.txt 2019-07-18 08:26:08.000000000 +0000 @@ -0,0 +1,4 @@ +set(tommyds "${CMAKE_CURRENT_SOURCE_DIR}/tommyds/tommy.c" PARENT_SCOPE) + +set(mustach "${CMAKE_CURRENT_SOURCE_DIR}/mustach/mustach.c" PARENT_SCOPE) +set_source_files_properties("${CMAKE_CURRENT_SOURCE_DIR}/mustach/mustach.c" PROPERTIES COMPILE_FLAGS "-DNO_EXTENSION_FOR_MUSTACH") diff -Nru librtr-0.6.3/third-party/mustach/AUTHORS librtr-0.7.0/third-party/mustach/AUTHORS --- librtr-0.6.3/third-party/mustach/AUTHORS 1970-01-01 00:00:00.000000000 +0000 +++ librtr-0.7.0/third-party/mustach/AUTHORS 2019-07-18 08:26:08.000000000 +0000 @@ -0,0 +1,15 @@ +Main author: + José Bollo <jobol@nonadev.net> + +Contributors: + Harold L Marzan + Atlas + Sami Kerola + Tomasz Sieprawski + Lailton Fernando Mariano + +Thanks to issue submitters: + Thierry Fournier + Dante Torres + Mark Bucciarelli + Paul Wisehart diff -Nru librtr-0.6.3/third-party/mustach/LICENSE-2.0.txt librtr-0.7.0/third-party/mustach/LICENSE-2.0.txt --- librtr-0.6.3/third-party/mustach/LICENSE-2.0.txt 1970-01-01 00:00:00.000000000 +0000 +++ librtr-0.7.0/third-party/mustach/LICENSE-2.0.txt 2019-07-18 08:26:08.000000000 +0000 @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff -Nru librtr-0.6.3/third-party/mustach/mustach.c librtr-0.7.0/third-party/mustach/mustach.c --- librtr-0.6.3/third-party/mustach/mustach.c 1970-01-01 00:00:00.000000000 +0000 +++ librtr-0.7.0/third-party/mustach/mustach.c 2019-07-18 08:26:08.000000000 +0000 @@ -0,0 +1,327 @@ +/* + Author: José Bollo <jobol@nonadev.net> + Author: José Bollo <jose.bollo@iot.bzh> + + https://gitlab.com/jobol/mustach + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#define _GNU_SOURCE + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> + +#include "mustach.h" + +#define NAME_LENGTH_MAX 1024 +#define DEPTH_MAX 256 + +#if !defined(NO_OPEN_MEMSTREAM) +static FILE *memfile_open(char **buffer, size_t *size) +{ + return open_memstream(buffer, size); +} +static void memfile_abort(FILE *file, char **buffer, size_t *size) +{ + fclose(file); + free(*buffer); + *buffer = NULL; + *size = 0; +} +static int memfile_close(FILE *file, char **buffer, size_t *size) +{ + int rc; + + /* adds terminating null */ + rc = fputc(0, file) ? MUSTACH_ERROR_SYSTEM : 0; + fclose(file); + if (rc == 0) + /* removes terminating null of the length */ + (*size)--; + else { + free(*buffer); + *buffer = NULL; + *size = 0; + } + return rc; +} +#else +static FILE *memfile_open(char **buffer, size_t *size) +{ + return tmpfile(); +} +static void memfile_abort(FILE *file, char **buffer, size_t *size) +{ + fclose(file); + *buffer = NULL; + *size = 0; +} +static int memfile_close(FILE *file, char **buffer, size_t *size) +{ + int rc; + size_t s; + char *b; + + s = (size_t)ftell(file); + b = malloc(s + 1); + if (b == NULL) { + rc = MUSTACH_ERROR_SYSTEM; + errno = ENOMEM; + s = 0; + } else { + rewind(file); + if (1 == fread(b, s, 1, file)) { + rc = 0; + b[s] = 0; + } else { + rc = MUSTACH_ERROR_SYSTEM; + free(b); + b = NULL; + s = 0; + } + } + *buffer = b; + *size = s; + return rc; +} +#endif + +static int getpartial(struct mustach_itf *itf, void *closure, const char *name, char **result) +{ + int rc; + FILE *file; + size_t size; + + *result = NULL; + file = memfile_open(result, &size); + if (file == NULL) + rc = MUSTACH_ERROR_SYSTEM; + else { + rc = itf->put(closure, name, 0, file); + if (rc < 0) + memfile_abort(file, result, &size); + else + rc = memfile_close(file, result, &size); + } + return rc; +} + +static int process(const char *template, struct mustach_itf *itf, void *closure, FILE *file, const char *opstr, const char *clstr) +{ + char name[NAME_LENGTH_MAX + 1], *partial, c, *tmp; + const char *beg, *term; + struct { const char *name, *again; size_t length; int emit, entered; } stack[DEPTH_MAX]; + size_t oplen, cllen, len, l; + int depth, rc, emit; + + emit = 1; + oplen = strlen(opstr); + cllen = strlen(clstr); + depth = 0; + for(;;) { + beg = strstr(template, opstr); + if (beg == NULL) { + /* no more mustach */ + if (emit) + fwrite(template, strlen(template), 1, file); + return depth ? MUSTACH_ERROR_UNEXPECTED_END : 0; + } + if (emit) + fwrite(template, (size_t)(beg - template), 1, file); + beg += oplen; + term = strstr(beg, clstr); + if (term == NULL) + return MUSTACH_ERROR_UNEXPECTED_END; + template = term + cllen; + len = (size_t)(term - beg); + c = *beg; + switch(c) { + case '!': + case '=': + break; + case '{': + for (l = 0 ; clstr[l] == '}' ; l++); + if (clstr[l]) { + if (!len || beg[len-1] != '}') + return MUSTACH_ERROR_BAD_UNESCAPE_TAG; + len--; + } else { + if (term[l] != '}') + return MUSTACH_ERROR_BAD_UNESCAPE_TAG; + template++; + } + c = '&'; + /*@fallthrough@*/ + case '^': + case '#': + case '/': + case '&': + case '>': +#if !defined(NO_EXTENSION_FOR_MUSTACH) && !defined(NO_COLON_EXTENSION_FOR_MUSTACH) + case ':': +#endif + beg++; len--; + default: + while (len && isspace(beg[0])) { beg++; len--; } + while (len && isspace(beg[len-1])) len--; +#if defined(NO_EXTENSION_FOR_MUSTACH) || defined(NO_ALLOW_EMPTY_TAG) + if (len == 0) + return MUSTACH_ERROR_EMPTY_TAG; +#endif + if (len > NAME_LENGTH_MAX) + return MUSTACH_ERROR_TAG_TOO_LONG; + memcpy(name, beg, len); + name[len] = 0; + break; + } + switch(c) { + case '!': + /* comment */ + /* nothing to do */ + break; + case '=': + /* defines separators */ + if (len < 5 || beg[len - 1] != '=') + return MUSTACH_ERROR_BAD_SEPARATORS; + beg++; + len -= 2; + for (l = 0; l < len && !isspace(beg[l]) ; l++); + if (l == len) + return MUSTACH_ERROR_BAD_SEPARATORS; + oplen = l; + tmp = alloca(oplen + 1); + memcpy(tmp, beg, oplen); + tmp[oplen] = 0; + opstr = tmp; + while (l < len && isspace(beg[l])) l++; + if (l == len) + return MUSTACH_ERROR_BAD_SEPARATORS; + cllen = len - l; + tmp = alloca(cllen + 1); + memcpy(tmp, beg + l, cllen); + tmp[cllen] = 0; + clstr = tmp; + break; + case '^': + case '#': + /* begin section */ + if (depth == DEPTH_MAX) + return MUSTACH_ERROR_TOO_DEEP; + rc = emit; + if (rc) { + rc = itf->enter(closure, name); + if (rc < 0) + return rc; + } + + if (template[0] == '\n') + ++template; + + stack[depth].name = beg; + stack[depth].again = template; + stack[depth].length = len; + stack[depth].emit = emit; + stack[depth].entered = rc; + if ((c == '#') == (rc == 0)) + emit = 0; + depth++; + break; + case '/': + /* end section */ + if (depth-- == 0 || len != stack[depth].length || memcmp(stack[depth].name, name, len)) + return MUSTACH_ERROR_CLOSING; + rc = emit && stack[depth].entered ? itf->next(closure) : 0; + if (rc < 0) + return rc; + if (rc) { + template = stack[depth++].again; + } else { + emit = stack[depth].emit; + if (emit && stack[depth].entered) + itf->leave(closure); + } + break; + case '>': + /* partials */ + if (emit) { + rc = getpartial(itf, closure, name, &partial); + if (rc == 0) { + rc = process(partial, itf, closure, file, opstr, clstr); + free(partial); + } + if (rc < 0) + return rc; + } + break; + default: + /* replacement */ + if (emit) { + rc = itf->put(closure, name, c != '&', file); + if (rc < 0) + return rc; + } + break; + } + } +} + +int fmustach(const char *template, struct mustach_itf *itf, void *closure, FILE *file) +{ + int rc = itf->start ? itf->start(closure) : 0; + if (rc == 0) + rc = process(template, itf, closure, file, "{{", "}}"); + return rc; +} + +int fdmustach(const char *template, struct mustach_itf *itf, void *closure, int fd) +{ + int rc; + FILE *file; + + file = fdopen(fd, "w"); + if (file == NULL) { + rc = MUSTACH_ERROR_SYSTEM; + errno = ENOMEM; + } else { + rc = fmustach(template, itf, closure, file); + fclose(file); + } + return rc; +} + +int mustach(const char *template, struct mustach_itf *itf, void *closure, char **result, size_t *size) +{ + int rc; + FILE *file; + size_t s; + + *result = NULL; + if (size == NULL) + size = &s; + file = memfile_open(result, size); + if (file == NULL) + rc = MUSTACH_ERROR_SYSTEM; + else { + rc = fmustach(template, itf, closure, file); + if (rc < 0) + memfile_abort(file, result, size); + else + rc = memfile_close(file, result, size); + } + return rc; +} + diff -Nru librtr-0.6.3/third-party/mustach/mustach.h librtr-0.7.0/third-party/mustach/mustach.h --- librtr-0.6.3/third-party/mustach/mustach.h 1970-01-01 00:00:00.000000000 +0000 +++ librtr-0.7.0/third-party/mustach/mustach.h 2019-07-18 08:26:08.000000000 +0000 @@ -0,0 +1,120 @@ +/* + Author: José Bollo <jobol@nonadev.net> + Author: José Bollo <jose.bollo@iot.bzh> + + https://gitlab.com/jobol/mustach + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#ifndef _mustach_h_included_ +#define _mustach_h_included_ + +/** + * mustach_itf - interface for callbacks + * + * All of this function should return a negative value to stop + * the mustache processing. The returned negative value will be + * then returned to the caller of mustach as is. + * + * The functions enter and next should return 0 or 1. + * + * All other functions should normally return 0. If it returns + * a negative value, it means an error that stop the process + * and that is reported to the caller. + * + * @start: Starts the mustach processing of the closure + * 'start' is optional (can be NULL) + * + * @put: Writes the value of 'name' to 'file' with 'escape' or not + * As an extension (see NO_ALLOW_EMPTY_TAG), the 'name' can be + * the empty string. In that later case an implemntation can + * return MUSTACH_ERROR_EMPTY_TAG to refuse empty names. + * + * @enter: Enters the section of 'name' if possible. + * Musts return 1 if entered or 0 if not entered. + * When 1 is returned, the function 'leave' will always be called. + * Conversely 'leave' is never called when enter returns 0 or + * a negative value. + * When 1 is returned, the function must activate the first + * item of the section. + * + * @next: Activates the next item of the section if it exists. + * Musts return 1 when the next item is activated. + * Musts return 0 when there is no item to activate. + * + * @leave: Leaves the last entered section + */ +struct mustach_itf { + int (*start)(void *closure); + int (*put)(void *closure, const char *name, int escape, FILE *file); + int (*enter)(void *closure, const char *name); + int (*next)(void *closure); + int (*leave)(void *closure); +}; + +#define MUSTACH_OK 0 +#define MUSTACH_ERROR_SYSTEM -1 +#define MUSTACH_ERROR_UNEXPECTED_END -2 +#define MUSTACH_ERROR_EMPTY_TAG -3 +#define MUSTACH_ERROR_TAG_TOO_LONG -4 +#define MUSTACH_ERROR_BAD_SEPARATORS -5 +#define MUSTACH_ERROR_TOO_DEEP -6 +#define MUSTACH_ERROR_CLOSING -7 +#define MUSTACH_ERROR_BAD_UNESCAPE_TAG -8 + +/* compatibility with older bad name */ +#define MUSTACH_ERROR_TOO_DEPTH MUSTACH_ERROR_TOO_DEEP + +/** + * fmustach - Renders the mustache 'template' in 'file' for 'itf' and 'closure'. + * + * @template: the template string to instanciate + * @itf: the interface to the functions that mustach calls + * @closure: the closure to pass to functions called + * @file: the file where to write the result + * + * Returns 0 in case of success, -1 with errno set in case of system error + * a other negative value in case of error. + */ +extern int fmustach(const char *template, struct mustach_itf *itf, void *closure, FILE *file); + +/** + * fmustach - Renders the mustache 'template' in 'fd' for 'itf' and 'closure'. + * + * @template: the template string to instanciate + * @itf: the interface to the functions that mustach calls + * @closure: the closure to pass to functions called + * @fd: the file descriptor number where to write the result + * + * Returns 0 in case of success, -1 with errno set in case of system error + * a other negative value in case of error. + */ +extern int fdmustach(const char *template, struct mustach_itf *itf, void *closure, int fd); + +/** + * fmustach - Renders the mustache 'template' in 'result' for 'itf' and 'closure'. + * + * @template: the template string to instanciate + * @itf: the interface to the functions that mustach calls + * @closure: the closure to pass to functions called + * @result: the pointer receiving the result when 0 is returned + * @size: the size of the returned result + * + * Returns 0 in case of success, -1 with errno set in case of system error + * a other negative value in case of error. + */ +extern int mustach(const char *template, struct mustach_itf *itf, void *closure, char **result, size_t *size); + +#endif + diff -Nru librtr-0.6.3/third-party/mustach/README.md librtr-0.7.0/third-party/mustach/README.md --- librtr-0.6.3/third-party/mustach/README.md 1970-01-01 00:00:00.000000000 +0000 +++ librtr-0.7.0/third-party/mustach/README.md 2019-07-18 08:26:08.000000000 +0000 @@ -0,0 +1,117 @@ +Introduction to Mustach +======================= + +`mustach` is a C implementation of the [mustache](http://mustache.github.io "main site for mustache") template library. + +The main site for `mustach` is on [gitlab](https://gitlab.com/jobol/mustach). + +The best way to use mustach is to copy the files **mustach.h** and **mustach.c** +directly into your project and use it. + +The current source files are: + +- **mustach.c** core implementation of mustache in C +- **mustach.h** header file for core definitions +- **mustach-json-c.c** tiny json wrapper of mustach using [json-c](https://github.com/json-c/json-c) +- **mustach-json-c.h** header file for using the tiny JSON wrapper +- **mustach-tool.c** simple tool for applying template files to a JSON file + +The file **mustach-json-c.c** is the main example of use of **mustach** core +and it is also a practical implementation that can be used. It uses the library +json-c. (NOTE for Mac OS: available through homebrew). + +HELP REQUESTED TO GIVE EXAMPLE BASED ON OTHER LIBRARIES (ex: janson, ...). + +The tool **mustach** is build using `make`, its usage is: + + mustach json template [template]... + +It then outputs the result of applying the templates files to the JSON file. + +Portability +=========== + +Some system does not provide *open_memstream*. In that case, tell your +prefered compiler to declare the preprocessor symbol **NO_OPEN_MEMSTREAM**. +Example: + + gcc -DNO_OPEN_MEMSTREAM + +Extensions +========== + +By default, the current implementation provides the following extensions: + +Explicit Substitution +--------------------- + +This is a core extension implemented in file **mustach.c**. + +In somecases the name of the key used for substition begins with a +character reserved for mustach: one of '#', '^', '/', '&', '{', '>' and '='. +This extension introduces the special character ':' to explicitly +tell mustach to just substitute the value. So ':' becomes a new special +character. + +Value Testing +------------- + +This is a tool extension implmented in file **mustach-json-c.c**. + +This extension allows you to test the value of the selected key. +It is allowed to write key=value (matching test) or key=!value +(not matching test) in any query. + +Removing Extensions +------------------- + +When compiling mustach.c or mustach-json-c.c, +extensions can be removed by defining macros +using option -D. + +The possible macros are: + +- `NO_COLON_EXTENSION_FOR_MUSTACH` + + This macro remove the ability to use colon (:) + as explicit command for variable substituion. + This extension allows to have name starting + with one of the mustach character :#^/&{=< + +- `NO_EQUAL_VALUE_EXTENSION_FOR_MUSTACH` + + This macro allows the program to check the whether + the actual value is equal to an expected value. + This is useful in `{{#key=val}}` or `{{^key=val}}` + with the corresponding `{{/key=val}}`. + It can also be used in `{{key=val}}` but this + doesn't seem to be useful. + +- `NO_JSON_POINTER_EXTENSION_FOR_MUSTACH` + + This macro removes the possible use of JSON pointers. + JSON pointers are defined in IETF RFC 6901. + If not set, any key starting with "/" is a JSON pointer. + This implies to use the colon to introduce keys. + So `NO_COLON_EXTENSION_FOR_MUSTACH` implies + `NO_JSON_POINTER_EXTENSION_FOR_MUSTACH`. + A special escaping is used for "=" signs when + values comparison is enabled: "~=" leaves "=" in the key. + +- `NO_ALLOW_EMPTY_TAG` + + Generate the error MUSTACH_ERROR_EMPTY_TAG automatically. + +- NO_OBJECT_ITERATION_FOR_MUSTACH + + Disable the object iteration extension. That extension allows + to iterate over the keys of an object. The iteration on object + is selected by using the selector `{{#key.*}}`. In the context + of iterating over object keys, the single key `{{*}}` returns the + key and `{{.}}` returns the value. + +- `NO_EXTENSION_FOR_MUSTACH` + + This macro disables any current or future + extensions. + diff -Nru librtr-0.6.3/third-party/tommyds/AUTHORS librtr-0.7.0/third-party/tommyds/AUTHORS --- librtr-0.6.3/third-party/tommyds/AUTHORS 1970-01-01 00:00:00.000000000 +0000 +++ librtr-0.7.0/third-party/tommyds/AUTHORS 2019-07-18 08:26:08.000000000 +0000 @@ -0,0 +1,9 @@ +TommyDS AUTHORS +=============== + +The author of TommyDS is Andrea Mazzoleni. + +You can contact me sending an email at: + + amadvance@gmail.com + diff -Nru librtr-0.6.3/third-party/tommyds/HISTORY librtr-0.7.0/third-party/tommyds/HISTORY --- librtr-0.6.3/third-party/tommyds/HISTORY 1970-01-01 00:00:00.000000000 +0000 +++ librtr-0.7.0/third-party/tommyds/HISTORY 2019-07-18 08:26:08.000000000 +0000 @@ -0,0 +1,90 @@ +TommyDS HISTORY +=============== + +2.2 2018/02 +=========== + * Removed tommy_list_remove_head_not_empty() as not used and wrongly + implemented [Daniel Roethlisberger]. + +2.1 2016/11 +=========== + * Added a new hash function for strings: tommy_strhash_u32(). + * Added a new tree implementation. It's not intended to be fast but useful if + you need elements in order. + * Fixed some references to TOMMY_ARRAYBLKOF_SIZE, where instead + TOMMY_ARRAYBLK_SIZE was incorrectly used [Rocco]. + +2.0 2014/12 +=========== + * Fixed a Segmentation Fault bug in the trie_inplace container when inserting + duplicate elements. + * Faster array and hashlin implementation when accessing elements. + * Added new hashtable functions to iterate over all the elements. + * Added a new tommy_calloc() function used for allocating initialized memory. + If you redefined tommy_malloc(), likely you have to redefine also tommy_calloc(). + * Reached 100% code coverage in the regression test. + * Different source code organization. + * Added benchmark comparison with Binary Search Tesseract by Gregorius van + den Hoven. + +1.8 2013/12 +=========== + * Fixed build of tommy_arrayblk in C++. + * Changed the default node size of tommy_trie to fit a cache line of 64 bytes. + * Added benchmark comparison with STX BTree. + +1.7 2013/12 +=========== + * Extends tommy_hashlin_done() to work also if the hashtable is not empty. + * Removes the empty tommy_trie_done() because the real deallocation is done + by the allocator. + +1.6 2013/11 +=========== + * Added a new tommy_arrayblk and tommy_arrayblkof types to store elements + in an array minimizing memory occupation. + +1.5 2013/06 +=========== + * Fixed inline declaration to allow building with clang. + * Added a new tommy_arrayof type to store in an array elements of arbitrary + size. + +1.4 2013/03 +=========== + * Added benchmark comparison with Google BTree, and C++ map and unordered_map. + * Benchmark for Linux is now compiled with "-O3 -march=pentium4 -mtune=generic", + and the Windows one with "/Ox /GL /GS- /arch:SSE2". + +1.3 2013/02 +=========== + * Fixed a Segmentation Fault bug in the hashlin container if exact power + of 2 sizes were used. + * Removed some warnings with newer gcc. + * Minor documentation changes. + * Added benchmark comparison with the judy array implementation by Karl Malbrain. + +1.2 2012/05 +=========== + * Minor documentation changes. + * In the check application, added a speed comparison with the C qsort() + implementation. + +1.1 2012/05 +=========== + * Fixed the tommy_hashdyn_remove() function. Now it shrinks the hashtable if required. + * Minor documentation changes. + +1.0 2011/03 +=========== + * First official version of TommyDS. + * Added tommy_list_foreach functions. + +0.2 2011/03 +=========== + * Added tommy_array. A dynamic vector. + +0.1 2011/01 +=========== + * First release of Tommy. + diff -Nru librtr-0.6.3/third-party/tommyds/INSTALL librtr-0.7.0/third-party/tommyds/INSTALL --- librtr-0.6.3/third-party/tommyds/INSTALL 1970-01-01 00:00:00.000000000 +0000 +++ librtr-0.7.0/third-party/tommyds/INSTALL 2019-07-18 08:26:08.000000000 +0000 @@ -0,0 +1,8 @@ +TommyDS INSTALL +=============== + +TommyDS doesn't need any installation. + +You have only to import the required .c and .h files into your program +and use the them. + diff -Nru librtr-0.6.3/third-party/tommyds/LICENSE librtr-0.7.0/third-party/tommyds/LICENSE --- librtr-0.6.3/third-party/tommyds/LICENSE 1970-01-01 00:00:00.000000000 +0000 +++ librtr-0.7.0/third-party/tommyds/LICENSE 2019-07-18 08:26:08.000000000 +0000 @@ -0,0 +1,24 @@ +Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. + +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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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. diff -Nru librtr-0.6.3/third-party/tommyds/Makefile librtr-0.7.0/third-party/tommyds/Makefile --- librtr-0.6.3/third-party/tommyds/Makefile 1970-01-01 00:00:00.000000000 +0000 +++ librtr-0.7.0/third-party/tommyds/Makefile 2019-07-18 08:26:08.000000000 +0000 @@ -0,0 +1,219 @@ +############################################################################# +# Tommy Makefile + +# Version of TommyDS +VERSION = 2.2 + +# Build options for the check program +ifdef COVERAGE +CFLAGS = -O0 -g -fprofile-arcs -ftest-coverage +else +CFLAGS = -O3 -march=native -Wall -Wextra -Wshadow -Wuninitialized -Wpadded -Wcast-align -Wcast-qual -g +endif + +# Build options for the benchmark +# -std=gnu++0x required by Google btree +BENCHCXXFLAGS = -m32 -O3 -march=native -flto -fpermissive -std=gnu++0x -Wall -g + +# Programs +CC ?= gcc +CXX ?= g++ +OBJDUMP ?= objdump +UNAME = $(shell uname) + +# Linux +ifeq ($(UNAME),Linux) +LIB=-lrt +BENCHLIB=benchmark/lib/judy/libJudyL.a benchmark/lib/judy/libJudyMalloc.a +EXE= +O=.o +endif + +# Darwin +ifeq ($(UNAME),Darwin) +LIB= +EXE= +O=.o +endif + +# Windows +ifeq ($(UNAME),) +BENCHLIB=benchmark/lib/judy/src/judy.lib +EXE=.exe +O=.obj +endif + +#CHECK = ./tommybench -n 1000000 -d tommy-hashlin +CHECK = ./tommycheck + +DEP = \ + tommyds/tommyalloc.c \ + tommyds/tommyalloc.h \ + tommyds/tommyarray.c \ + tommyds/tommyarray.h \ + tommyds/tommyarrayof.c \ + tommyds/tommyarrayof.h \ + tommyds/tommyarrayblk.c \ + tommyds/tommyarrayblk.h \ + tommyds/tommyarrayblkof.c \ + tommyds/tommyarrayblkof.h \ + tommyds/tommy.c \ + tommyds/tommy.h \ + tommyds/tommyhash.c \ + tommyds/tommyhashdyn.c \ + tommyds/tommyhashdyn.h \ + tommyds/tommyhash.h \ + tommyds/tommyhashlin.c \ + tommyds/tommyhashlin.h \ + tommyds/tommyhashtbl.c \ + tommyds/tommyhashtbl.h \ + tommyds/tommylist.c \ + tommyds/tommylist.h \ + tommyds/tommytrie.c \ + tommyds/tommytrie.h \ + tommyds/tommytrieinp.c \ + tommyds/tommytrieinp.h \ + tommyds/tommytypes.h \ + tommyds/tommychain.h + +DEPTEST = \ + check.c \ + benchmark.cc + +all: tommycheck$(EXE) + +bench: tommybench$(EXE) + +tommy$(O): $(DEP) + $(CC) $(CFLAGS) -c tommyds/tommy.c -o tommy$(O) + -$(OBJDUMP) -S tommy$(O) > tommy.s + +tommycheck$(EXE): check.c tommy$(O) + $(CC) $(CFLAGS) check.c tommy$(O) -o tommycheck$(EXE) $(LIB) + +tommybench$(EXE): benchmark.cc $(DEP) + $(CXX) $(BENCHCXXFLAGS) benchmark.cc -o tommybench$(EXE) $(LIB) $(BENCHLIB) + +check: tommycheck$(EXE) + ./tommycheck$(EXE) + echo Check completed with success! + +lcov_reset: + lcov -d . -z + rm -f ./lcov.info + +lcov_capture: + lcov -d . --capture -o lcov.info + +lcov_html: + rm -rf ./cov + mkdir cov + genhtml -o ./cov lcov.info + +coverage: + $(MAKE) COVERAGE=1 tommycheck$(EXE) + $(MAKE) lcov_reset + ./tommycheck$(EXE) + $(MAKE) lcov_capture + $(MAKE) lcov_html + +valgrind: + valgrind \ + --tool=memcheck \ + --track-origins=yes \ + --read-var-info=yes \ + -v $(CHECK) \ + 2> valgrind.log + tail valgrind.log + +callgrind: + valgrind \ + --tool=callgrind \ + --dump-instr=yes \ + --trace-jump=yes \ + -v $(CHECK) \ + 2> callgrind.log + tail callgrind.log + +cachegrind: + valgrind \ + --tool=cachegrind \ + -v $(CHECK) \ + 2> cachegrind.log + tail cachegrind.log + +phony: + +graph: phony + cd benchmark && sh gr_all.sh + +doc: phony tommy.doxygen tommy.css $(DEP) + rm -rf doc + mkdir doc + cp -a benchmark/data/def doc/def + cp -a benchmark/data/other doc/other + cp -a benchmark/data/core_i5_650_3G2_linux doc/core_i5_650_3G2_linux + rm -f doc/*/*.lst + rm -f doc/*/*.gnu + doxygen tommy.doxygen + rm -f doc/doxygen.png + rm -f doc/tab_*.png + +web: phony tommyweb.doxygen tommy.css $(DEP) + rm -rf web + mkdir web + cp -a benchmark/data/def web/def + cp -a benchmark/data/other web/other + cp -a benchmark/data/core_i5_650_3G2_linux web/core_i5_650_3G2_linux + rm -f web/*/*.lst + rm -f web/*/*.gnu + doxygen tommyweb.doxygen + rm -f web/doxygen.png + rm -f web/tab_*.png + +clean: + rm -f *.log *.s *.lst *.o + rm -f *.ncb *.suo *.obj + rm -f *.gcno *.gcda lcov.info + rm -rf Debug Release x64 + rm -f callgrind.out.* + rm -f cachegrind.out.* + +distclean: clean + rm -f tommybench$(EXE) tommycheck$(EXE) + +maintainerclean: distclean + rm -rf doc web + +DIST=tommyds-$(VERSION) + +DISTFILES=\ + Makefile \ + README LICENSE AUTHORS INSTALL HISTORY \ + tommy.doxygen tommy.css tommy-header.html tommy-footer.html \ + benchmark.vcxproj benchmark.sln \ + benchmark.geany \ + benchmark.cc \ + check.c + +dist: + mkdir $(DIST) + mkdir $(DIST)/tommyds + cp $(DISTFILES) $(DIST) + cp $(DEP) $(DIST)/tommyds + cp $(DEPTEST) $(DIST) + cp -R doc $(DIST) + cp -R benchmark $(DIST)/benchmark + rm -f $(DIST)/benchmark/data/*/*.png + rm -rf $(DIST)/benchmark/data/test + rm -f $(DIST)/benchmark/arial.ttf + rm -f $(DIST).tar.gz + tar cfzo $(DIST).tar.gz $(DIST) + rm -f $(DIST).zip + zip -r $(DIST).zip $(DIST) + rm -r $(DIST) + +distcheck: dist + tar zxvf $(DIST).tar.gz + cd $(DIST) && make check + rm -rf $(DIST) diff -Nru librtr-0.6.3/third-party/tommyds/README librtr-0.7.0/third-party/tommyds/README --- librtr-0.6.3/third-party/tommyds/README 1970-01-01 00:00:00.000000000 +0000 +++ librtr-0.7.0/third-party/tommyds/README 2019-07-18 08:26:08.000000000 +0000 @@ -0,0 +1,31 @@ +TommyDS +======= + +TommyDS is a C library of array, hashtables and tries data structures, +designed for high performance and providing an easy to use interface. + +It's faster than all the similar libraries like rbtree, judy, goodledensehash, +khash, uthash, nedtries and others. + +The data structures provided are: + + tommy_list - A double linked list. + tommy_array - A linear array. It doesn't fragment + the heap. + tommy_arrayblk - A blocked linear array. It doesn't fragment + the heap and it minimizes the space occupation. + tommy_hashtable - A fixed size chained hashtable. + tommy_hashdyn - A dynamic chained hashtable. + tommy_hashlin - A linear chained hashtable. It doesn't have the + problem of the delay when resizing and it doesn't + fragment the heap. + tommy_trie - A trie optimized for cache utilization. + tommy_trie_inplace - A trie completely inplace. + +The documentation is available in HTML format in the doc/index.html file, +and directly in the .h files. + +The official site of TommyDS is: + + http://www.tommyds.it + diff -Nru librtr-0.6.3/third-party/tommyds/tommyalloc.c librtr-0.7.0/third-party/tommyds/tommyalloc.c --- librtr-0.6.3/third-party/tommyds/tommyalloc.c 1970-01-01 00:00:00.000000000 +0000 +++ librtr-0.7.0/third-party/tommyds/tommyalloc.c 2019-07-18 08:26:08.000000000 +0000 @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. + * + * 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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 "tommyalloc.h" + +/******************************************************************************/ +/* allocator */ + +/** + * Basic allocation segment. + * Smaller of a memory page, to allow also a little heap overread. + * The heap manager may put it in a single memory page. + */ +#define TOMMY_ALLOCATOR_BLOCK_SIZE (4096 - 64) + +void tommy_allocator_init(tommy_allocator* alloc, tommy_size_t block_size, tommy_size_t align_size) +{ + /* setup the minimal alignment */ + if (align_size < sizeof(void*)) + align_size = sizeof(void*); + + /* ensure that the block_size keeps the alignment */ + if (block_size % align_size != 0) + block_size += align_size - block_size % align_size; + + alloc->block_size = block_size; + alloc->align_size = align_size; + + alloc->count = 0; + alloc->free_block = 0; + alloc->used_segment = 0; +} + +/** + * Reset the allocator and free all. + */ +static void allocator_reset(tommy_allocator* alloc) +{ + tommy_allocator_entry* block = alloc->used_segment; + + while (block) { + tommy_allocator_entry* block_next = block->next; + tommy_free(block); + block = block_next; + } + + alloc->count = 0; + alloc->free_block = 0; + alloc->used_segment = 0; +} + +void tommy_allocator_done(tommy_allocator* alloc) +{ + allocator_reset(alloc); +} + +void* tommy_allocator_alloc(tommy_allocator* alloc) +{ + void* ptr; + + /* if no free block available */ + if (!alloc->free_block) { + tommy_uintptr_t off, mis; + tommy_size_t size; + char* data; + tommy_allocator_entry* segment; + + /* default allocation size */ + size = TOMMY_ALLOCATOR_BLOCK_SIZE; + + /* ensure that we can allocate at least one block */ + if (size < sizeof(tommy_allocator_entry) + alloc->align_size + alloc->block_size) + size = sizeof(tommy_allocator_entry) + alloc->align_size + alloc->block_size; + + data = tommy_cast(char*, tommy_malloc(size)); + segment = (tommy_allocator_entry*)data; + + /* put in the segment list */ + segment->next = alloc->used_segment; + alloc->used_segment = segment; + data += sizeof(tommy_allocator_entry); + + /* align if not aligned */ + off = (tommy_uintptr_t)data; + mis = off % alloc->align_size; + if (mis != 0) { + data += alloc->align_size - mis; + size -= alloc->align_size - mis; + } + + /* insert in free list */ + do { + tommy_allocator_entry* free_block = (tommy_allocator_entry*)data; + free_block->next = alloc->free_block; + alloc->free_block = free_block; + + data += alloc->block_size; + size -= alloc->block_size; + } while (size >= alloc->block_size); + } + + /* remove one from the free list */ + ptr = alloc->free_block; + alloc->free_block = alloc->free_block->next; + + ++alloc->count; + + return ptr; +} + +void tommy_allocator_free(tommy_allocator* alloc, void* ptr) +{ + tommy_allocator_entry* free_block = tommy_cast(tommy_allocator_entry*, ptr); + + /* put it in the free list */ + free_block->next = alloc->free_block; + alloc->free_block = free_block; + + --alloc->count; +} + +tommy_size_t tommy_allocator_memory_usage(tommy_allocator* alloc) +{ + return alloc->count * (tommy_size_t)alloc->block_size; +} + diff -Nru librtr-0.6.3/third-party/tommyds/tommyalloc.h librtr-0.7.0/third-party/tommyds/tommyalloc.h --- librtr-0.6.3/third-party/tommyds/tommyalloc.h 1970-01-01 00:00:00.000000000 +0000 +++ librtr-0.7.0/third-party/tommyds/tommyalloc.h 2019-07-18 08:26:08.000000000 +0000 @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. + * + * 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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. + */ + +/** \file + * Allocator of fixed size blocks. + */ + +#ifndef __TOMMYALLOC_H +#define __TOMMYALLOC_H + +#include "tommytypes.h" + +/******************************************************************************/ +/* allocator */ + +/** \internal + * Allocator entry. + */ +struct tommy_allocator_entry_struct { + struct tommy_allocator_entry_struct* next; /**< Pointer to the next entry. 0 for last. */ +}; +typedef struct tommy_allocator_entry_struct tommy_allocator_entry; + +/** + * Allocator of fixed size blocks. + */ +typedef struct tommy_allocator_struct { + struct tommy_allocator_entry_struct* free_block; /**< List of free blocks. */ + struct tommy_allocator_entry_struct* used_segment; /**< List of allocated segments. */ + tommy_size_t block_size; /**< Block size. */ + tommy_size_t align_size; /**< Alignment size. */ + tommy_count_t count; /**< Number of allocated elements. */ +} tommy_allocator; + +/** + * Initializes the allocator. + * \param alloc Allocator to initialize. + * \param block_size Size of the block to allocate. + * \param align_size Minimum alignment requirement. No less than sizeof(void*). + */ +void tommy_allocator_init(tommy_allocator* alloc, tommy_size_t block_size, tommy_size_t align_size); + +/** + * Deinitialize the allocator. + * It also releases all the allocated memory to the heap. + * \param alloc Allocator to deinitialize. + */ +void tommy_allocator_done(tommy_allocator* alloc); + +/** + * Allocates a block. + * \param alloc Allocator to use. + */ +void* tommy_allocator_alloc(tommy_allocator* alloc); + +/** + * Deallocates a block. + * You must use the same allocator used in the tommy_allocator_alloc() call. + * \param alloc Allocator to use. + * \param ptr Block to free. + */ +void tommy_allocator_free(tommy_allocator* alloc, void* ptr); + +/** + * Gets the size of allocated memory. + * \param alloc Allocator to use. + */ +tommy_size_t tommy_allocator_memory_usage(tommy_allocator* alloc); + +#endif + diff -Nru librtr-0.6.3/third-party/tommyds/tommyarrayblk.c librtr-0.7.0/third-party/tommyds/tommyarrayblk.c --- librtr-0.6.3/third-party/tommyds/tommyarrayblk.c 1970-01-01 00:00:00.000000000 +0000 +++ librtr-0.7.0/third-party/tommyds/tommyarrayblk.c 2019-07-18 08:26:08.000000000 +0000 @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2013, Andrea Mazzoleni. All rights reserved. + * + * 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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 "tommyarrayblk.h" + +/******************************************************************************/ +/* array */ + +void tommy_arrayblk_init(tommy_arrayblk* array) +{ + tommy_array_init(&array->block); + + array->count = 0; +} + +void tommy_arrayblk_done(tommy_arrayblk* array) +{ + tommy_count_t i; + + for (i = 0; i < tommy_array_size(&array->block); ++i) + tommy_free(tommy_array_get(&array->block, i)); + + tommy_array_done(&array->block); +} + +void tommy_arrayblk_grow(tommy_arrayblk* array, tommy_count_t count) +{ + tommy_count_t block_max; + tommy_count_t block_mac; + + if (array->count >= count) + return; + array->count = count; + + block_max = (count + TOMMY_ARRAYBLK_SIZE - 1) / TOMMY_ARRAYBLK_SIZE; + block_mac = tommy_array_size(&array->block); + + if (block_mac < block_max) { + /* grow the block array */ + tommy_array_grow(&array->block, block_max); + + /* allocate new blocks */ + while (block_mac < block_max) { + void** ptr = tommy_cast(void**, tommy_calloc(TOMMY_ARRAYBLK_SIZE, sizeof(void*))); + + /* set the new block */ + tommy_array_set(&array->block, block_mac, ptr); + + ++block_mac; + } + } +} + +tommy_size_t tommy_arrayblk_memory_usage(tommy_arrayblk* array) +{ + return tommy_array_memory_usage(&array->block) + tommy_array_size(&array->block) * TOMMY_ARRAYBLK_SIZE * sizeof(void*); +} + diff -Nru librtr-0.6.3/third-party/tommyds/tommyarrayblk.h librtr-0.7.0/third-party/tommyds/tommyarrayblk.h --- librtr-0.6.3/third-party/tommyds/tommyarrayblk.h 1970-01-01 00:00:00.000000000 +0000 +++ librtr-0.7.0/third-party/tommyds/tommyarrayblk.h 2019-07-18 08:26:08.000000000 +0000 @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2013, Andrea Mazzoleni. All rights reserved. + * + * 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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. + */ + +/** \file + * Dynamic array based on blocks of fixed size. + * + * This array is able to grow dynamically upon request, without any reallocation. + * + * The grow operation involves an allocation of a new array block, without reallocating + * the already used memory, and then not increasing the heap fragmentation, + * and minimize the space occupation. + * This also implies that the address of the stored elements never change. + * + * Allocated blocks are always of the same fixed size of 4 Ki pointers. + */ + +#ifndef __TOMMYARRAYBLK_H +#define __TOMMYARRAYBLK_H + +#include "tommytypes.h" +#include "tommyarray.h" + +#include <assert.h> /* for assert */ + +/******************************************************************************/ +/* array */ + +/** + * Elements for each block. + */ +#define TOMMY_ARRAYBLK_SIZE (4 * 1024) + +/** + * Array container type. + * \note Don't use internal fields directly, but access the container only using functions. + */ +typedef struct tommy_arrayblk_struct { + tommy_array block; /**< Array of blocks. */ + tommy_count_t count; /**< Number of initialized elements in the array. */ +} tommy_arrayblk; + +/** + * Initializes the array. + */ +void tommy_arrayblk_init(tommy_arrayblk* array); + +/** + * Deinitializes the array. + */ +void tommy_arrayblk_done(tommy_arrayblk* array); + +/** + * Grows the size up to the specified value. + * All the new elements in the array are initialized with the 0 value. + */ +void tommy_arrayblk_grow(tommy_arrayblk* array, tommy_count_t size); + +/** + * Gets a reference of the element at the specified position. + * You must be sure that space for this position is already + * allocated calling tommy_arrayblk_grow(). + */ +tommy_inline void** tommy_arrayblk_ref(tommy_arrayblk* array, tommy_count_t pos) +{ + void** ptr; + + assert(pos < array->count); + + ptr = tommy_cast(void**, tommy_array_get(&array->block, pos / TOMMY_ARRAYBLK_SIZE)); + + return &ptr[pos % TOMMY_ARRAYBLK_SIZE]; +} + +/** + * Sets the element at the specified position. + * You must be sure that space for this position is already + * allocated calling tommy_arrayblk_grow(). + */ +tommy_inline void tommy_arrayblk_set(tommy_arrayblk* array, tommy_count_t pos, void* element) +{ + *tommy_arrayblk_ref(array, pos) = element; +} + +/** + * Gets the element at the specified position. + * You must be sure that space for this position is already + * allocated calling tommy_arrayblk_grow(). + */ +tommy_inline void* tommy_arrayblk_get(tommy_arrayblk* array, tommy_count_t pos) +{ + return *tommy_arrayblk_ref(array, pos); +} + +/** + * Grows and inserts a new element at the end of the array. + */ +tommy_inline void tommy_arrayblk_insert(tommy_arrayblk* array, void* element) +{ + tommy_count_t pos = array->count; + + tommy_arrayblk_grow(array, pos + 1); + + tommy_arrayblk_set(array, pos, element); +} + +/** + * Gets the initialized size of the array. + */ +tommy_inline tommy_count_t tommy_arrayblk_size(tommy_arrayblk* array) +{ + return array->count; +} + +/** + * Gets the size of allocated memory. + */ +tommy_size_t tommy_arrayblk_memory_usage(tommy_arrayblk* array); + +#endif + diff -Nru librtr-0.6.3/third-party/tommyds/tommyarrayblkof.c librtr-0.7.0/third-party/tommyds/tommyarrayblkof.c --- librtr-0.6.3/third-party/tommyds/tommyarrayblkof.c 1970-01-01 00:00:00.000000000 +0000 +++ librtr-0.7.0/third-party/tommyds/tommyarrayblkof.c 2019-07-18 08:26:08.000000000 +0000 @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2013, Andrea Mazzoleni. All rights reserved. + * + * 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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 "tommyarrayblkof.h" + +/******************************************************************************/ +/* array */ + +void tommy_arrayblkof_init(tommy_arrayblkof* array, tommy_size_t element_size) +{ + tommy_array_init(&array->block); + + array->element_size = element_size; + array->count = 0; +} + +void tommy_arrayblkof_done(tommy_arrayblkof* array) +{ + tommy_count_t i; + + for (i = 0; i < tommy_array_size(&array->block); ++i) + tommy_free(tommy_array_get(&array->block, i)); + + tommy_array_done(&array->block); +} + +void tommy_arrayblkof_grow(tommy_arrayblkof* array, tommy_count_t count) +{ + tommy_count_t block_max; + tommy_count_t block_mac; + + if (array->count >= count) + return; + array->count = count; + + block_max = (count + TOMMY_ARRAYBLKOF_SIZE - 1) / TOMMY_ARRAYBLKOF_SIZE; + block_mac = tommy_array_size(&array->block); + + if (block_mac < block_max) { + /* grow the block array */ + tommy_array_grow(&array->block, block_max); + + /* allocate new blocks */ + while (block_mac < block_max) { + void** ptr = tommy_cast(void**, tommy_calloc(TOMMY_ARRAYBLKOF_SIZE, array->element_size)); + + /* set the new block */ + tommy_array_set(&array->block, block_mac, ptr); + + ++block_mac; + } + } +} + +tommy_size_t tommy_arrayblkof_memory_usage(tommy_arrayblkof* array) +{ + return tommy_array_memory_usage(&array->block) + tommy_array_size(&array->block) * TOMMY_ARRAYBLKOF_SIZE * array->element_size; +} + diff -Nru librtr-0.6.3/third-party/tommyds/tommyarrayblkof.h librtr-0.7.0/third-party/tommyds/tommyarrayblkof.h --- librtr-0.6.3/third-party/tommyds/tommyarrayblkof.h 1970-01-01 00:00:00.000000000 +0000 +++ librtr-0.7.0/third-party/tommyds/tommyarrayblkof.h 2019-07-18 08:26:08.000000000 +0000 @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2013, Andrea Mazzoleni. All rights reserved. + * + * 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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. + */ + +/** \file + * Dynamic array based on blocks of fixed size. + * + * This array is able to grow dynamically upon request, without any reallocation. + * + * This is very similar at ::tommy_arrayblk, but it allows to store elements of any + * size and not just pointers. + * + * Note that in this case tommy_arrayblkof_ref() returns a pointer to the element, + * that should be used for getting and setting elements in the array, + * as generic getter and setter are not available. + */ + +#ifndef __TOMMYARRAYBLKOF_H +#define __TOMMYARRAYBLKOF_H + +#include "tommytypes.h" +#include "tommyarray.h" + +#include <assert.h> /* for assert */ + +/******************************************************************************/ +/* array */ + +/** + * Elements for each block. + */ +#define TOMMY_ARRAYBLKOF_SIZE (4 * 1024) + +/** + * Array container type. + * \note Don't use internal fields directly, but access the container only using functions. + */ +typedef struct tommy_arrayblkof_struct { + tommy_array block; /**< Array of blocks. */ + tommy_size_t element_size; /**< Size of the stored element in bytes. */ + tommy_count_t count; /**< Number of initialized elements in the array. */ +} tommy_arrayblkof; + +/** + * Initializes the array. + * \param element_size Size in byte of the element to store in the array. + */ +void tommy_arrayblkof_init(tommy_arrayblkof* array, tommy_size_t element_size); + +/** + * Deinitializes the array. + */ +void tommy_arrayblkof_done(tommy_arrayblkof* array); + +/** + * Grows the size up to the specified value. + * All the new elements in the array are initialized with the 0 value. + */ +void tommy_arrayblkof_grow(tommy_arrayblkof* array, tommy_count_t size); + +/** + * Gets a reference of the element at the specified position. + * You must be sure that space for this position is already + * allocated calling tommy_arrayblkof_grow(). + */ +tommy_inline void* tommy_arrayblkof_ref(tommy_arrayblkof* array, tommy_count_t pos) +{ + unsigned char* base; + + assert(pos < array->count); + + base = tommy_cast(unsigned char*, tommy_array_get(&array->block, pos / TOMMY_ARRAYBLKOF_SIZE)); + + return base + (pos % TOMMY_ARRAYBLKOF_SIZE) * array->element_size; +} + +/** + * Gets the initialized size of the array. + */ +tommy_inline tommy_count_t tommy_arrayblkof_size(tommy_arrayblkof* array) +{ + return array->count; +} + +/** + * Gets the size of allocated memory. + */ +tommy_size_t tommy_arrayblkof_memory_usage(tommy_arrayblkof* array); + +#endif + diff -Nru librtr-0.6.3/third-party/tommyds/tommyarray.c librtr-0.7.0/third-party/tommyds/tommyarray.c --- librtr-0.6.3/third-party/tommyds/tommyarray.c 1970-01-01 00:00:00.000000000 +0000 +++ librtr-0.7.0/third-party/tommyds/tommyarray.c 2019-07-18 08:26:08.000000000 +0000 @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2011, Andrea Mazzoleni. All rights reserved. + * + * 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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 "tommyarray.h" + +/******************************************************************************/ +/* array */ + +void tommy_array_init(tommy_array* array) +{ + tommy_uint_t i; + + /* fixed initial size */ + array->bucket_bit = TOMMY_ARRAY_BIT; + array->bucket_max = 1 << array->bucket_bit; + array->bucket[0] = tommy_cast(void**, tommy_calloc(array->bucket_max, sizeof(void*))); + for (i = 1; i < TOMMY_ARRAY_BIT; ++i) + array->bucket[i] = array->bucket[0]; + + array->count = 0; +} + +void tommy_array_done(tommy_array* array) +{ + tommy_uint_t i; + + tommy_free(array->bucket[0]); + for (i = TOMMY_ARRAY_BIT; i < array->bucket_bit; ++i) { + void** segment = array->bucket[i]; + tommy_free(&segment[((tommy_ptrdiff_t)1) << i]); + } +} + +void tommy_array_grow(tommy_array* array, tommy_count_t count) +{ + if (array->count >= count) + return; + array->count = count; + + while (count > array->bucket_max) { + void** segment; + + /* allocate one more segment */ + segment = tommy_cast(void**, tommy_calloc(array->bucket_max, sizeof(void*))); + + /* store it adjusting the offset */ + /* cast to ptrdiff_t to ensure to get a negative value */ + array->bucket[array->bucket_bit] = &segment[-(tommy_ptrdiff_t)array->bucket_max]; + + ++array->bucket_bit; + array->bucket_max = 1 << array->bucket_bit; + } +} + +tommy_size_t tommy_array_memory_usage(tommy_array* array) +{ + return array->bucket_max * (tommy_size_t)sizeof(void*); +} + diff -Nru librtr-0.6.3/third-party/tommyds/tommyarray.h librtr-0.7.0/third-party/tommyds/tommyarray.h --- librtr-0.6.3/third-party/tommyds/tommyarray.h 1970-01-01 00:00:00.000000000 +0000 +++ librtr-0.7.0/third-party/tommyds/tommyarray.h 2019-07-18 08:26:08.000000000 +0000 @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2011, Andrea Mazzoleni. All rights reserved. + * + * 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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. + */ + +/** \file + * Dynamic array based on segments of exponential growing size. + * + * This array is able to grow dynamically upon request, without any reallocation. + * + * The grow operation involves an allocation of a new array segment, without reallocating + * the already used memory, and then not increasing the heap fragmentation. + * This also implies that the address of the stored elements never change. + * + * Allocated segments grow in size exponentially. + */ + +#ifndef __TOMMYARRAY_H +#define __TOMMYARRAY_H + +#include "tommytypes.h" + +#include <assert.h> /* for assert */ + +/******************************************************************************/ +/* array */ + +/** + * Initial and minimal size of the array expressed as a power of 2. + * The initial size is 2^TOMMY_ARRAY_BIT. + */ +#define TOMMY_ARRAY_BIT 6 + +/** \internal + * Max number of elements as a power of 2. + */ +#define TOMMY_ARRAY_BIT_MAX 32 + +/** + * Array container type. + * \note Don't use internal fields directly, but access the container only using functions. + */ +typedef struct tommy_array_struct { + void** bucket[TOMMY_ARRAY_BIT_MAX]; /**< Dynamic array of buckets. */ + tommy_uint_t bucket_bit; /**< Bits used in the bit mask. */ + tommy_count_t bucket_max; /**< Number of buckets. */ + tommy_count_t count; /**< Number of initialized elements in the array. */ +} tommy_array; + +/** + * Initializes the array. + */ +void tommy_array_init(tommy_array* array); + +/** + * Deinitializes the array. + */ +void tommy_array_done(tommy_array* array); + +/** + * Grows the size up to the specified value. + * All the new elements in the array are initialized with the 0 value. + */ +void tommy_array_grow(tommy_array* array, tommy_count_t size); + +/** + * Gets a reference of the element at the specified position. + * You must be sure that space for this position is already + * allocated calling tommy_array_grow(). + */ +tommy_inline void** tommy_array_ref(tommy_array* array, tommy_count_t pos) +{ + tommy_uint_t bsr; + + assert(pos < array->count); + + /* get the highest bit set, in case of all 0, return 0 */ + bsr = tommy_ilog2_u32(pos | 1); + + return &array->bucket[bsr][pos]; +} + +/** + * Sets the element at the specified position. + * You must be sure that space for this position is already + * allocated calling tommy_array_grow(). + */ +tommy_inline void tommy_array_set(tommy_array* array, tommy_count_t pos, void* element) +{ + *tommy_array_ref(array, pos) = element; +} + +/** + * Gets the element at the specified position. + * You must be sure that space for this position is already + * allocated calling tommy_array_grow(). + */ +tommy_inline void* tommy_array_get(tommy_array* array, tommy_count_t pos) +{ + return *tommy_array_ref(array, pos); +} + +/** + * Grows and inserts a new element at the end of the array. + */ +tommy_inline void tommy_array_insert(tommy_array* array, void* element) +{ + tommy_count_t pos = array->count; + + tommy_array_grow(array, pos + 1); + + tommy_array_set(array, pos, element); +} + +/** + * Gets the initialized size of the array. + */ +tommy_inline tommy_count_t tommy_array_size(tommy_array* array) +{ + return array->count; +} + +/** + * Gets the size of allocated memory. + */ +tommy_size_t tommy_array_memory_usage(tommy_array* array); + +#endif + diff -Nru librtr-0.6.3/third-party/tommyds/tommyarrayof.c librtr-0.7.0/third-party/tommyds/tommyarrayof.c --- librtr-0.6.3/third-party/tommyds/tommyarrayof.c 1970-01-01 00:00:00.000000000 +0000 +++ librtr-0.7.0/third-party/tommyds/tommyarrayof.c 2019-07-18 08:26:08.000000000 +0000 @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2013, Andrea Mazzoleni. All rights reserved. + * + * 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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 "tommyarrayof.h" + +/******************************************************************************/ +/* array */ + +void tommy_arrayof_init(tommy_arrayof* array, tommy_size_t element_size) +{ + tommy_uint_t i; + + /* fixed initial size */ + array->element_size = element_size; + array->bucket_bit = TOMMY_ARRAYOF_BIT; + array->bucket_max = 1 << array->bucket_bit; + array->bucket[0] = tommy_calloc(array->bucket_max, array->element_size); + for (i = 1; i < TOMMY_ARRAYOF_BIT; ++i) + array->bucket[i] = array->bucket[0]; + + array->count = 0; +} + +void tommy_arrayof_done(tommy_arrayof* array) +{ + tommy_uint_t i; + + tommy_free(array->bucket[0]); + for (i = TOMMY_ARRAYOF_BIT; i < array->bucket_bit; ++i) { + unsigned char* segment = tommy_cast(unsigned char*, array->bucket[i]); + tommy_free(segment + (((tommy_ptrdiff_t)1) << i) * array->element_size); + } +} + +void tommy_arrayof_grow(tommy_arrayof* array, tommy_count_t count) +{ + if (array->count >= count) + return; + array->count = count; + + while (count > array->bucket_max) { + unsigned char* segment; + + /* allocate one more segment */ + segment = tommy_cast(unsigned char*, tommy_calloc(array->bucket_max, array->element_size)); + + /* store it adjusting the offset */ + /* cast to ptrdiff_t to ensure to get a negative value */ + array->bucket[array->bucket_bit] = segment - (tommy_ptrdiff_t)array->bucket_max * array->element_size; + + ++array->bucket_bit; + array->bucket_max = 1 << array->bucket_bit; + } +} + +tommy_size_t tommy_arrayof_memory_usage(tommy_arrayof* array) +{ + return array->bucket_max * (tommy_size_t)array->element_size; +} + diff -Nru librtr-0.6.3/third-party/tommyds/tommyarrayof.h librtr-0.7.0/third-party/tommyds/tommyarrayof.h --- librtr-0.6.3/third-party/tommyds/tommyarrayof.h 1970-01-01 00:00:00.000000000 +0000 +++ librtr-0.7.0/third-party/tommyds/tommyarrayof.h 2019-07-18 08:26:08.000000000 +0000 @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2013, Andrea Mazzoleni. All rights reserved. + * + * 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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. + */ + +/** \file + * Dynamic array based on segments of exponential growing size. + * + * This array is able to grow dynamically upon request, without any reallocation. + * + * This is very similar at ::tommy_array, but it allows to store elements of any + * size and not just pointers. + * + * Note that in this case tommy_arrayof_ref() returns a pointer to the element, + * that should be used for getting and setting elements in the array, + * as generic getter and setter are not available. + */ + +#ifndef __TOMMYARRAYOF_H +#define __TOMMYARRAYOF_H + +#include "tommytypes.h" + +#include <assert.h> /* for assert */ + +/******************************************************************************/ +/* array */ + +/** + * Initial and minimal size of the array expressed as a power of 2. + * The initial size is 2^TOMMY_ARRAYOF_BIT. + */ +#define TOMMY_ARRAYOF_BIT 6 + +/** \internal + * Max number of elements as a power of 2. + */ +#define TOMMY_ARRAYOF_BIT_MAX 32 + +/** + * Array container type. + * \note Don't use internal fields directly, but access the container only using functions. + */ +typedef struct tommy_arrayof_struct { + void* bucket[TOMMY_ARRAYOF_BIT_MAX]; /**< Dynamic array of buckets. */ + tommy_size_t element_size; /**< Size of the stored element in bytes. */ + tommy_uint_t bucket_bit; /**< Bits used in the bit mask. */ + tommy_count_t bucket_max; /**< Number of buckets. */ + tommy_count_t count; /**< Number of initialized elements in the array. */ +} tommy_arrayof; + +/** + * Initializes the array. + * \param element_size Size in byte of the element to store in the array. + */ +void tommy_arrayof_init(tommy_arrayof* array, tommy_size_t element_size); + +/** + * Deinitializes the array. + */ +void tommy_arrayof_done(tommy_arrayof* array); + +/** + * Grows the size up to the specified value. + * All the new elements in the array are initialized with the 0 value. + */ +void tommy_arrayof_grow(tommy_arrayof* array, tommy_count_t size); + +/** + * Gets a reference of the element at the specified position. + * You must be sure that space for this position is already + * allocated calling tommy_arrayof_grow(). + */ +tommy_inline void* tommy_arrayof_ref(tommy_arrayof* array, tommy_count_t pos) +{ + unsigned char* ptr; + tommy_uint_t bsr; + + assert(pos < array->count); + + /* get the highest bit set, in case of all 0, return 0 */ + bsr = tommy_ilog2_u32(pos | 1); + + ptr = tommy_cast(unsigned char*, array->bucket[bsr]); + + return ptr + pos * array->element_size; +} + +/** + * Gets the initialized size of the array. + */ +tommy_inline tommy_count_t tommy_arrayof_size(tommy_arrayof* array) +{ + return array->count; +} + +/** + * Gets the size of allocated memory. + */ +tommy_size_t tommy_arrayof_memory_usage(tommy_arrayof* array); + +#endif + diff -Nru librtr-0.6.3/third-party/tommyds/tommy.c librtr-0.7.0/third-party/tommyds/tommy.c --- librtr-0.6.3/third-party/tommyds/tommy.c 1970-01-01 00:00:00.000000000 +0000 +++ librtr-0.7.0/third-party/tommyds/tommy.c 2019-07-18 08:26:08.000000000 +0000 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. + * + * 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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 "tommyhash.c" +#include "tommyalloc.c" +#include "tommyarray.c" +#include "tommyarrayof.c" +#include "tommyarrayblk.c" +#include "tommyarrayblkof.c" +#include "tommylist.c" +#include "tommytree.c" +#include "tommytrie.c" +#include "tommytrieinp.c" +#include "tommyhashtbl.c" +#include "tommyhashdyn.c" +#include "tommyhashlin.c" + diff -Nru librtr-0.6.3/third-party/tommyds/tommychain.h librtr-0.7.0/third-party/tommyds/tommychain.h --- librtr-0.6.3/third-party/tommyds/tommychain.h 1970-01-01 00:00:00.000000000 +0000 +++ librtr-0.7.0/third-party/tommyds/tommychain.h 2019-07-18 08:26:08.000000000 +0000 @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. + * + * 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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. + */ + +/** \file + * Chain of nodes. + * A chain of nodes is an abstraction used to implements complex list operations + * like sorting. + * + * Do not use this directly. Use lists instead. + */ + +#ifndef __TOMMYCHAIN_H +#define __TOMMYCHAIN_H + +#include "tommytypes.h" + +/******************************************************************************/ +/* chain */ + +/** + * Chain of nodes. + * A chain of nodes is a sequence of nodes with the following properties: + * - It contains at least one node. A chains of zero nodes cannot exist. + * - The next field of the tail is of *undefined* value. + * - The prev field of the head is of *undefined* value. + * - All the other inner prev and next fields are correctly set. + */ +typedef struct tommy_chain_struct { + tommy_node* head; /**< Pointer to the head of the chain. */ + tommy_node* tail; /**< Pointer to the tail of the chain. */ +} tommy_chain; + +/** + * Splices a chain in the middle of another chain. + */ +tommy_inline void tommy_chain_splice(tommy_node* first_before, tommy_node* first_after, tommy_node* second_head, tommy_node* second_tail) +{ + /* set the prev list */ + first_after->prev = second_tail; + second_head->prev = first_before; + + /* set the next list */ + first_before->next = second_head; + second_tail->next = first_after; +} + +/** + * Concats two chains. + */ +tommy_inline void tommy_chain_concat(tommy_node* first_tail, tommy_node* second_head) +{ + /* set the prev list */ + second_head->prev = first_tail; + + /* set the next list */ + first_tail->next = second_head; +} + +/** + * Merges two chains. + */ +tommy_inline void tommy_chain_merge(tommy_chain* first, tommy_chain* second, tommy_compare_func* cmp) +{ + tommy_node* first_i = first->head; + tommy_node* second_i = second->head; + + /* merge */ + while (1) { + if (cmp(first_i->data, second_i->data) > 0) { + tommy_node* next = second_i->next; + if (first_i == first->head) { + tommy_chain_concat(second_i, first_i); + first->head = second_i; + } else { + tommy_chain_splice(first_i->prev, first_i, second_i, second_i); + } + if (second_i == second->tail) + break; + second_i = next; + } else { + if (first_i == first->tail) { + tommy_chain_concat(first_i, second_i); + first->tail = second->tail; + break; + } + first_i = first_i->next; + } + } +} + +/** + * Merges two chains managing special degenerated cases. + * It's funtionally equivalent at tommy_chain_merge() but faster with already ordered chains. + */ +tommy_inline void tommy_chain_merge_degenerated(tommy_chain* first, tommy_chain* second, tommy_compare_func* cmp) +{ + /* identify the condition first <= second */ + if (cmp(first->tail->data, second->head->data) <= 0) { + tommy_chain_concat(first->tail, second->head); + first->tail = second->tail; + return; + } + + /* identify the condition second < first */ + /* here we must be strict on comparison to keep the sort stable */ + if (cmp(second->tail->data, first->head->data) < 0) { + tommy_chain_concat(second->tail, first->head); + first->head = second->head; + return; + } + + tommy_chain_merge(first, second, cmp); +} + +/** + * Max number of elements as a power of 2. + */ +#define TOMMY_CHAIN_BIT_MAX 32 + +/** + * Sorts a chain. + * It's a stable merge sort using power of 2 buckets, with O(N*log(N)) complexity, + * similar at the one used in the SGI STL libraries and in the Linux Kernel, + * but faster on degenerated cases like already ordered lists. + * + * SGI STL stl_list.h + * http://www.sgi.com/tech/stl/stl_list.h + * + * Linux Kernel lib/list_sort.c + * http://lxr.linux.no/#linux+v2.6.36/lib/list_sort.c + */ +tommy_inline void tommy_chain_mergesort(tommy_chain* chain, tommy_compare_func* cmp) +{ + /* + * Bit buckets of chains. + * Each bucket contains 2^i nodes or it's empty. + * The chain at address TOMMY_CHAIN_BIT_MAX is an independet variable operating as "carry". + * We keep it in the same "bit" vector to avoid reports from the valgrind tool sgcheck. + */ + tommy_chain bit[TOMMY_CHAIN_BIT_MAX + 1]; + + /** + * Value stored inside the bit bucket. + * It's used to know which bucket is empty of full. + */ + tommy_count_t counter; + tommy_node* node = chain->head; + tommy_node* tail = chain->tail; + tommy_count_t mask; + tommy_count_t i; + + counter = 0; + while (1) { + tommy_node* next; + tommy_chain* last; + + /* carry bit to add */ + last = &bit[TOMMY_CHAIN_BIT_MAX]; + bit[TOMMY_CHAIN_BIT_MAX].head = node; + bit[TOMMY_CHAIN_BIT_MAX].tail = node; + next = node->next; + + /* add the bit, propagating the carry */ + i = 0; + mask = counter; + while ((mask & 1) != 0) { + tommy_chain_merge_degenerated(&bit[i], last, cmp); + mask >>= 1; + last = &bit[i]; + ++i; + } + + /* copy the carry in the first empty bit */ + bit[i] = *last; + + /* add the carry in the counter */ + ++counter; + + if (node == tail) + break; + node = next; + } + + /* merge the buckets */ + i = tommy_ctz_u32(counter); + mask = counter >> i; + while (mask != 1) { + mask >>= 1; + if (mask & 1) + tommy_chain_merge_degenerated(&bit[i + 1], &bit[i], cmp); + else + bit[i + 1] = bit[i]; + ++i; + } + + *chain = bit[i]; +} + +#endif + diff -Nru librtr-0.6.3/third-party/tommyds/tommy.h librtr-0.7.0/third-party/tommyds/tommy.h --- librtr-0.6.3/third-party/tommyds/tommy.h 1970-01-01 00:00:00.000000000 +0000 +++ librtr-0.7.0/third-party/tommyds/tommy.h 2019-07-18 08:26:08.000000000 +0000 @@ -0,0 +1,763 @@ +/* + * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. + * + * 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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. + */ + +/** \mainpage + * \section Introduction + * Tommy is a C library of array, hashtables and tries data structures, + * designed for high performance and providing an easy to use interface. + * + * It's <b>faster</b> than all the similar libraries like + * <a href="http://www.canonware.com/rb/">rbtree</a>, + * <a href="http://judy.sourceforge.net/">judy</a>, + * <a href="http://code.google.com/p/cpp-btree/">googlebtree</a>, + * <a href="http://panthema.net/2007/stx-btree/">stxbtree</a>, + * <a href="http://attractivechaos.awardspace.com/">khash</a>, + * <a href="http://uthash.sourceforge.net/">uthash</a>, + * <a href="http://www.nedprod.com/programs/portable/nedtries/">nedtrie</a>, + * <a href="http://code.google.com/p/judyarray/">judyarray</a>, + * <a href="http://concurrencykit.org/">concurrencykit</a> and others. + * Only <a href="http://code.google.com/p/google-sparsehash/">googledensehash</a> is a real competitor for Tommy. + * + * The data structures provided are: + * + * - ::tommy_list - A double linked list. + * - ::tommy_array, ::tommy_arrayof - A linear array. + * It doesn't fragment the heap. + * - ::tommy_arrayblk, ::tommy_arrayblkof - A blocked linear array. + * It doesn't fragment the heap and it minimizes the space occupation. + * - ::tommy_hashtable - A fixed size chained hashtable. + * - ::tommy_hashdyn - A dynamic chained hashtable. + * - ::tommy_hashlin - A linear chained hashtable. + * It doesn't have the problem of the delay when resizing and + * it doesn't fragment the heap. + * - ::tommy_trie - A trie optimized for cache utilization. + * - ::tommy_trie_inplace - A trie completely inplace. + * - ::tommy_tree - A tree to keep elements in order. + * + * The most interesting are ::tommy_array, ::tommy_hashdyn, ::tommy_hashlin, ::tommy_trie and ::tommy_trie_inplace. + * + * The official site of TommyDS is <a href="http://www.tommyds.it/">http://www.tommyds.it/</a>, + * + * \section Use + * + * All the Tommy containers are used to store pointers to generic objects, associated to an + * integer value, that could be a key or a hash value. + * + * They are semantically equivalent at the C++ <a href="http://www.cplusplus.com/reference/map/multimap/">multimap\<unsigned,void*\></a> + * and <a href="http://www.cplusplus.com/reference/unordered_map/unordered_multimap/">unordered_multimap\<unsigned,void*\></a>. + * + * An object, to be inserted in a container, should contain a node of type ::tommy_node. + * Inside this node is present a pointer to the object itself in the tommy_node::data field, + * the key used to identify the object in the tommy_node::key field, and other fields used + * by the containers. + * + * This is a typical object declaration: + * \code + * struct object { + * // other fields + * tommy_node node; + * }; + * \endcode + * + * To insert an object in a container, you have to provide the address of the embedded node, + * the address of the object and the value of the key. + * \code + * int key_to_insert = 1; + * struct object* obj = malloc(sizeof(struct object)); + * ... + * tommy_trie_insert(..., &obj->node, obj, key_to_insert); + * \endcode + * + * To search an object you have to provide the key and call the search function. + * \code + * int key_to_find = 1; + * struct object* obj = tommy_trie_search(..., key_to_find); + * if (obj) { + * // found + * } + * \endcode + * + * To access all the objects with the same keys you have to iterate over the bucket + * assigned at the specified key. + * \code + * int key_to_find = 1; + * tommy_trie_node* i = tommy_trie_bucket(..., key_to_find); + * + * while (i) { + * struct object* obj = i->data; // gets the object pointer + * + * printf("%d\n", obj->value); // process the object + * + * i = i->next; // goes to the next element + * } + * \endcode + * + * To remove an object you have to provide the key and call the remove function. + * \code + * int key_to_remove = 1; + * struct object* obj = tommy_trie_remove(..., key_to_remove); + * if (obj) { + * // found + * free(obj); // frees the object allocated memory + * } + * \endcode + * + * Dealing with hashtables, instead of the key, you have to provide the hash + * value of the object, and a compare function able to differentiate objects with + * the same hash value. + * To compute the hash value, you can use the generic tommy_hash_u32() function, + * or the specialized integer hash function tommy_inthash_u32(). + * + * \section Features + * + * Tommy is fast and easy to use. + * + * Tommy is portable to all platforms and operating systems. + * + * Tommy containers support multiple elements with the same key. + * + * Tommy containers keep the original insertion order of elements with equal keys. + * + * Tommy is released with the \ref license "2-clause BSD license". + * + * See the \ref design page for more details and limitations. + * + * \section Performance + * Here you can see some timings comparing with other notable implementations. + * The <i>Hit</i> graph shows the time required for searching random objects + * with a key. + * The <i>Change</i> graph shows the time required for searching, removing and + * reinsert random objects with a different key value. + * + * Times are expressed in nanoseconds for each element, and <b>lower is better</b>. + * + * To have some reference numbers, you can check <a href="https://gist.github.com/jboner/2841832">Latency numbers every programmer should know</a>. + * + * A complete analysis is available in the \ref benchmark page. + * + * <img src="def/img_random_hit.png"/> + * + * <img src="def/img_random_change.png"/> + * + * \page benchmark Tommy Benchmarks + * + * To evaluate Tommy performances, an extensive benchmark was done, + * comparing it to the best libraries of data structures available: + * + * Specifically we test: + * - ::tommy_hashtable - Fixed size chained hashtable. + * - ::tommy_hashdyn - Dynamic chained hashtable. + * - ::tommy_hashlin - Linear chained hashtable. + * - ::tommy_trie - Trie optimized for cache usage. + * - ::tommy_trie_inplace - Trie completely inplace. + * - <a href="http://www.canonware.com/rb/">rbtree</a> - Red-black tree by Jason Evans. + * - <a href="http://www.nedprod.com/programs/portable/nedtries/">nedtrie</a> - Binary trie inplace by Niall Douglas. + * - <a href="http://attractivechaos.awardspace.com/">khash</a> - Dynamic open addressing hashtable by Attractive Chaos. + * - <a href="http://uthash.sourceforge.net/">uthash</a> - Dynamic chaining hashtable by Troy D. Hanson. + * - <a href="http://judy.sourceforge.net/">judy</a> - Burst trie (JudyL) by Doug Baskins. + * - <a href="http://code.google.com/p/judyarray/">judyarray</a> - Burst trie by Karl Malbrain. + * - <a href="http://code.google.com/p/google-sparsehash/">googledensehash</a> - Dynamic open addressing hashtable by Craig Silverstein at Google. + * - <a href="http://code.google.com/p/cpp-btree/">googlebtree</a> - Btree by Google. + * - <a href="http://panthema.net/2007/stx-btree/">stxbtree</a> - STX Btree by Timo Bingmann. + * - <a href="http://www.cplusplus.com/reference/unordered_map/unordered_map/">c++unordered_map</a> - C++ STL unordered_map<> template. + * - <a href="http://www.cplusplus.com/reference/map/map/">c++map</a> - C++ STL map<> template. + * - <a href="https://sites.google.com/site/binarysearchcube/">tesseract</a> - Binary Search Tesseract by Gregorius van den Hoven. + * - <a href="https://code.google.com/p/sparsehash/source/browse/trunk/experimental/libchash.c">googlelibchash</a> - LibCHash by Craig Silverstein at Google. + * - <a href="https://github.com/fredrikwidlund/libdynamic">libdynamic</a> - Hash set by Fredrik Widlund. + * - <a href="http://concurrencykit.org/">concurrencykit</a> - Non-blocking hash set by Samy Al Bahra. + * + * Note that <em>googlelibchash</em> and <em>concurrencykit</em> are not shown in the graphs + * because they present a lot of spikes. See the \ref notes the end. + * + * \section thebenchmark The Benchmark + * + * The benchmark consists in storing a set of N pointers to objects and + * searching them using integer keys. + * + * Compared to the case of mapping integers to integers, mapping pointers to + * objects means that the pointers are also dereferenced, to simulate the + * object access, resulting in additional cache misses. + * This gives an advantage to implementations that store information in the + * objects itself, as the additional cache misses are already implicit. + * + * The test done are: + * - <b>Insert</b> Insert all the objects starting with an empty container. + * - <b>Change</b> Find and remove one object and reinsert it with a different key, repeated for all the objects. + * - <b>Hit</b> Find with success all the objects and dereference them. + * - <b>Miss</b> Find with failure all the objects. + * - <b>Remove</b> Remove all the objects and dereference them. + * + * The <i>Change</i>, <i>Hit</i> and <i>Miss</i> tests operate always with N + * objects in the containers. + * The <i>Insert</i> test starts with an empty container, and the <i>Remove</i> + * test ends with an empty container. + * The objects are always dereferenced, as we are supposing to use them. This + * happens even in the remove case, as we are supposing to deallocate them. + * + * All the objects are preallocated in the heap, and the allocation and deallocation + * time is not included in the test. + * + * The objects contain an integer <i>value</i> field used for consistency checks, + * an unused <i>payload</i> field of 16 bytes, and any other data required by the + * data structure. + * + * The objects are identified and stored using integer and unique <i>keys</i>. + * The key domain used is <strong>dense</strong>, and it's defined by the set + * of N even numbers starting from 0x80000000 to 0x80000000+2*N. + * + * The use of even numbers allows to have missing keys inside the domain for + * the <i>Miss</i> and <i>Change</i> test. + * In such tests it's used the key domain defined by the set of N odd numbers + * starting from 0x80000000+1 to 0x80000000+2*N+1. + * Note that using additional keys at the corners of the domain would have given + * an unfair advantage to tries and trees as they implicitly keep track of the + * maximum and minimum key value inserted. + * + * The use of the 0x80000000 base, allow to test a key domain not necessarily + * starting at 0. Using a 0 base would have given an unfair advantage to some + * implementation handling it as a special case. + * + * For all the hashtables the keys are hashed using the tommy_inthash_u32() + * function that ensures an uniform distribution. This hash function is also + * reversible, meaning that no collision is going to be caused by hashing the + * keys. For tries and trees the keys are not hashed, and used directly. + * + * The tests are repeated using keys in <i>Random</i> mode and in <i>Forward</i> + * mode. In the forward mode the key values are used in order from the lowest + * to the highest. + * In the random mode the key values are used in a completely random order. + * In the <i>Change</i> test in forward mode, each object is reinserted using + * the previous key incremented by 1. In random mode each object is reinserted + * using a completely different and uncorrelated key. + * + * The forward order advantages tries and trees as they use the key directly + * and they have a cache advantage on using consecutive keys. + * The random order advantages hashtables, as the hash function already + * randomizes the key. Usually real uses case are in between, and the random + * one is the worst case. + * + * \section result Results + * + * The most significant tests depend on your data usage model, but if in doubt, + * you can look at <i>Random Hit</i> and <i>Random Change</i>. + * They represent the real world worst condition. + * + * <img src="def/img_random_hit.png"/> + * + * In the <i>Random Hit</i> graph you can see a vertical split at the 100.000 + * elements limit. Before this limit the cache of modern processor is able to + * contains most of the data, and it allows a very fast access with almost all + * data structures. + * After this limit, the number of cache misses is the dominant factor, and + * the curve depends mainly on the number of cache-miss required. + * + * rbtree and nedtrie grow as log2(N) as they have two branches on each node, + * ::tommy_trie_inplace grows as log4(N), ::tommy_trie as log8(N) and hashtables + * are almost constant and don't grow. + * For ::tommy_trie_inplace and ::tommy_trie you can change the grow curve + * configuring a different number of branches for node. + * + * <img src="def/img_random_change.png"/> + * + * The <i>Random Change</i> graph confirms the vertical split at the 100.000 + * elements limit. It also show that hashtables are almost unbeatable with a + * random access. + * + * \section random Random order + * Here you can see the whole <i>Random</i> test results in different platforms. + * + * In the <i>Random</i> test, hashtables are almost always winning, seconds are + * tries, and as last trees. + * + * The best choices are ::tommy_hashdyn, ::tommy_hashlin, and googledensehash, + * with ::tommy_hashlin having the advantage to be real-time friendly and not + * increasing the heap fragmentation. + * <table border="0"> + * <tr><td> + * <img src="core_i5_650_3G2_linux/img_random_insert.png"/> + * </td></tr><tr><td> + * <img src="core_i5_650_3G2_linux/img_random_hit.png"/> + * </td></tr><tr><td> + * <img src="core_i5_650_3G2_linux/img_random_miss.png"/> + * </td></tr><tr><td> + * <img src="core_i5_650_3G2_linux/img_random_change.png"/> + * </td></tr><tr><td> + * <img src="core_i5_650_3G2_linux/img_random_remove.png"/> + * </td></tr> + * </table> + * + * \section forward Forward order + * Here you can see the whole <i>Forward</i> test results in different platforms. + * + * In the <i>Forward</i> test, tries are the winners. Hashtables are competitive + * until the cache limit, then they lose against tries. Trees are the slowest. + * + * The best choices are ::tommy_trie and ::tommy_trie_inplace, where ::tommy_trie is + * a bit faster, and ::tommy_trie_inplace doesn't require a custom allocator. + * + * Note that also hashtables are faster in forward order than random. This may + * seem a bit surprising as the hash function randomizes the access even with + * consecutive keys. This happens because the objects are allocated in consecutive + * memory, and accessing them in order, improves the cache utilization, even if + * the hashed key is random. + * + * Note that you can make hashtables to reach tries performance tweaking the hash + * function to put near keys allocated nearby. + * This is possible if you know in advance the distribution of keys. + * For example, in the benchmark you could use something like: + * \code + * #define hash(v) tommy_inthash_u32(v & ~0xF) + (v & 0xF) + * \endcode + * and make keys that differ only by the lowest bits to have hashes with the same + * property, resulting in objects stored nearby, and improving cache utilization. + * + * <table border="0"> + * <tr><td> + * <img src="core_i5_650_3G2_linux/img_forward_insert.png"/> + * </td></tr><tr><td> + * <img src="core_i5_650_3G2_linux/img_forward_hit.png"/> + * </td></tr><tr><td> + * <img src="core_i5_650_3G2_linux/img_forward_miss.png"/> + * </td></tr><tr><td> + * <img src="core_i5_650_3G2_linux/img_forward_change.png"/> + * </td></tr><tr><td> + * <img src="core_i5_650_3G2_linux/img_forward_remove.png"/> + * </td></tr> + * </table> + * + * \section size Size + * Here you can see the memory usage of the different data structures. + * <table border="0"> + * <tr><td> + * <img src="core_i5_650_3G2_linux/img_random_size.png"/> + * </td></tr> + * </table> + * + * \section code Code + * + * The compilers used in the benchmark are: + * - <b>gcc 4.9.2</b> in Linux with options: -O3 -march=nehalem + * - <b>Visual C 2012</b> in Windows with options: /Ox /Oy- /GL /GS- /arch:SSE2 + * + * The following is pseudo code of the benchmark used. In this case it's written + * for the C++ unordered_map. + * + * \code + * #define N 10000000 // Number of elements + * #define PAYLOAD 16 // Size of the object + * + * // Basic object inserted in the colletion + * struct obj { + * unsigned value; // Key used for searching + * char payload[PAYLOAD]; + * }; + * + * // Custom hash function to avoid to use the STL one + * class custom_hash { + * public: + * unsigned operator()(unsigned key) const { return tommy_inthash_u32(key); } + * }; + * + * // Map collection from "unsigned" to "pointer to object" + * typedef std::unordered_map<unsigned, obj*, custom_hash> bag_t; + * bag_t bag; + * + * // Preallocate objects + * obj* OBJ = new obj[N]; + * + * // Keys used for inserting and searching elements + * unsigned INSERT[N]; + * unsigned SEARCH[N]; + * + * // Initialize the keys + * for(i=0;i<N;++i) { + * INSERT[i] = 0x80000000 + i * 2; + * SEARCH[i] = 0x80000000 + i * 2; + * } + * + * // If random order is required, shuffle the keys with Fisher-Yates + * // The two key orders are not correlated + * if (test_random) { + * std::random_shuffle(INSERT, INSERT + N); + * std::random_shuffle(SEARCH, SEARCH + N); + * } + * \endcode + * + * \subsection insertion Insert benchmark + * \code + * for(i=0;i<N;++i) { + * // Setup the element to insert + * unsigned key = INSERT[i]; + * obj* element = &OBJ[i]; + * element->value = key; + * + * // Insert it + * bag[key] = element; + * } + * \endcode + * + * \subsection change Change benchmark + * \code + * for(i=0;i<N;++i) { + * // Search the element + * unsigned key = SEARCH[i]; + * bag_t::iterator j = bag.find(key); + * if (j == bag.end()) + * abort(); + * + * // Remove it + * obj* element = j->second; + * bag.erase(j); + * + * // Reinsert the element with a new key + * // Use +1 in the key to ensure that the new key is unique + * key = INSERT[i] + 1; + * element->value = key; + * bag[key] = element; + * } + * \endcode + * + * \subsection hit Hit benchmark + * \code + * for(i=0;i<N;++i) { + * // Search the element + * // Use a different key order than insertion + * // Use +1 in the key because we run after the "Change" test + * unsigned key = SEARCH[i] + 1; + * bag_t::const_iterator j = bag.find(key); + * if (j == bag.end()) + * abort(); + * + * // Ensure that it's the correct element. + * // This operation is like using the object after finding it, + * // and likely involves a cache-miss operation. + * obj* element = j->second; + * if (element->value != key) + * abort(); + * } + * \endcode + * + * \subsection miss Miss benchmark + * \code + * for(i=0;i<N;++i) { + * // Search the element + * // All the keys are now shifted by +1 by the "Change" test, and we'll find nothing + * unsigned key = SEARCH[i]; + * bag_t::const_iterator j = bag.find(key); + * if (j != bag.end()) + * abort(); + * } + * \endcode + * + * \subsection remove Remove benchmark + * \code + * for(i=0;i<N;++i) { + * // Search the element + * // Use +1 in the key because we run after the "Change" test + * unsigned key = SEARCH[i] + 1; + * bag_t::iterator j = bag.find(key); + * if (j == bag.end()) + * abort(); + * + * // Remove it + * bag.erase(j); + * + * // Ensure that it's the correct element. + * obj* element = j->second; + * if (element->value != key) + * abort(); + * } + * \endcode + * + * \section others Other benchmarks + * Here some links to other performance comparison: + * + * <a href="http://attractivechaos.wordpress.com/2008/08/28/comparison-of-hash-table-libraries/">Comparison of Hash Table Libraries</a> + * + * <a href="http://incise.org/hash-table-benchmarks.html">Hash Table Benchmarks</a> + * + * \section notes Notes + * + * Here some notes about the data structure tested not part of Tommy. + * + * \subsection googlelibchash Google C libchash + * It's the C implementation located in the <i>experimental/</i> directory of the googlesparsehash archive. + * It has very bad performances in the <i>Change</i> test for some N values. + * See this <a href="other/googlelibchash_problem.png">graph</a> with a lot of spikes. + * The C++ version doesn't suffer of this problem. + * + * \subsection googledensehash Google C++ densehash + * It doesn't release memory on deletion. + * To avoid an unfair advantage in the <i>Remove</i> test, we force a periodic + * resize calling resize(0) after any deallocation. + * The resize is executed when the load factor is lower than 20%. + * + * \subsection khash khash + * It doesn't release memory on deletion. This gives an unfair advantage on the <i>Remove</i> test. + * + * \subsection nedtrie nedtrie + * I've found a crash bug when inserting keys with the 0 value. + * The <a href="https://github.com/ned14/nedtries/commit/21039696f27db4ffac70a82f89dc5d00ae74b332">fix</a> of this issue is now in the nedtries github. + * We do not use the C++ implementation as it doesn't compile with gcc 4.4.3. + * + * \subsection judy Judy + * Sometimes it has bad performances in some specific platform + * and for some specific input data size. + * This makes difficult to predict the performance, as it is usually good until + * you get one of these cases. + * See for example this <a href="other/judy_problem.png">graph</a> with a big replicable spike at 50.000 elements. + * + * \subsection ck Concurrency Kit + * It has very bad performances in the <i>Change</i> test for some N values. + * See this <a href="other/ck_problem.png">graph</a> with a lot of spikes. + * + * \page multiindex Tommy Multi Indexing + * + * Tommy provides only partial iterator support with the "foreach" functions. + * If you need real iterators you have to insert all the objects also in a ::tommy_list, + * and use the list as iterator. + * + * This technique allows to keep track of the insertion order with the list, + * and provides more search possibilities using different data structures for + * different search keys. + * + * See the next example, for a objects inserted in a ::tommy_list, and in + * two ::tommy_hashdyn using different keys. + * + * \code + * struct object { + * // data fields + * int value_0; + * int value_1; + * + * // for containers + * tommy_node list_node; // node for the list + * tommy_node hash_node_0; // node for the first hash + * tommy_node hash_node_1; // node for the second hash + * }; + * + * // search function for value_1 + * int search_1(const void* arg, const void* obj) + * { + * return *(const int*)arg != ((const struct object*)obj)->value_1; + * } + * + * tommy_hashdyn hash_0; + * tommy_hashdyn hash_1; + * tommy_list list; + * + * // initializes the hash tables and the list + * tommy_hashdyn_init(&hash_0); + * tommy_hashdyn_init(&hash_1); + * tommy_list_init(&list); + * + * ... + * + * // creates an object and inserts it + * struct object* obj = malloc(sizeof(struct object)); + * obj->value_0 = ...; + * obj->value_1 = ...; + * // inserts in the first hash table + * tommy_hashdyn_insert(&hash_0, &obj->hash_node_0, obj, tommy_inthash_u32(obj->value_0)); + * // inserts in the second hash table + * tommy_hashdyn_insert(&hash_1, &obj->hash_node_1, obj, tommy_inthash_u32(obj->value_1)); + * // inserts in the list + * tommy_list_insert_tail(&list, &obj->list_node, obj); + * + * ... + * + * // searches an object by value_1 and deletes it + * int value_to_find = ...; + * struct object* obj = tommy_hashdyn_search(&hash_1, search_1, &value_to_find, tommy_inthash_u32(value_to_find)); + * if (obj) { + * // if found removes all the references + * tommy_hashdyn_remove_existing(&hash_0, &obj->hash_node_0); + * tommy_hashdyn_remove_existing(&hash_1, &obj->hash_node_1); + * tommy_list_remove_existing(&list, &obj->list_node); + * } + * + * ... + * + * // complex iterator logic + * tommy_node* i = tommy_list_head(&list); + * while (i != 0) { + * // get the object + * struct object* obj = i->data; + * ... + * // go to the next element + * i = i->next; + * ... + * // go to the prev element + * i = i->prev; + * ... + * } + * + * ... + * + * // deallocates the objects iterating the list + * tommy_list_foreach(&list, free); + * + * // deallocates the hash tables + * tommy_hashdyn_done(&hash_0); + * tommy_hashdyn_done(&hash_1); + * \endcode + * + * \page design Tommy Design + * + * Tommy is designed to fulfill the need of generic data structures for the + * C language, providing at the same time high performance and a clean + * and easy to use interface. + * + * \section testing Testing + * + * Extensive and automated tests with the runtime checker <a href="http://valgrind.org/">valgrind</a> + * and the static analyzer <a href="http://clang-analyzer.llvm.org/">clang</a> + * are done to ensure the correctness of the library. + * + * The test has a <a href="http://www.tommyds.it/cov/tommyds/tommyds">code coverage of 100%</a>, + * measured with <a href="http://ltp.sourceforge.net/coverage/lcov.php">lcov</a>. + * + * \section Limitations + * + * Tommy is not thread safe. You have always to provide thread safety using + * locks before calling any Tommy functions. + * + * Tommy doesn't provide iterators over the implicit order defined by the data + * structures. To iterate on elements you must insert them also into a ::tommy_list, + * and use the list as iterator. See the \ref multiindex example for more details. + * Note that this is a real limitation only for ::tommy_trie, as it's the only + * data structure defining an useable order. + * + * Tommy doesn't provide an error reporting mechanism for a malloc() failure. + * You have to provide it redefining malloc() if you expect it to fail. + * + * Tommy assumes to never have more than 2^32-1 elements in a container. + * + * \section compromise Compromises + * + * Finding the right balance between efficency and easy to use required some + * comprimises. Most of them are on memory efficency, and were done to avoid to + * cripple the interface. + * + * The following is a list of such decisions. + * + * \subsection multi_key Multi key + * All the Tommy containers support the insertion of multiple elements with + * the same key, adding in each node a list of equal elements. + * + * They are the equivalent at the C++ associative containers <a href="http://www.cplusplus.com/reference/map/multimap/">multimap\<unsigned,void*\></a> + * and <a href="http://www.cplusplus.com/reference/unordered_map/unordered_multimap/">unordered_multimap\<unsigned,void*\></a> + * that allow duplicates of the same key. + * + * A more memory conservative approach is to not allow duplicated elements, + * removing the need of this list. + * + * \subsection data_pointer Data pointer + * The tommy_node::data field is present to allow search and remove functions to return + * directly a pointer to the element stored in the container. + * + * A more memory conservative approach is to require the user to compute + * the element pointer from the embedded node with a fixed displacement. + * For an example, see the Linux Kernel declaration of + * <a href="http://lxr.free-electrons.com/ident?i=container_of">container_of()</a>. + * + * \subsection insertion_order Insertion order + * The list used for collisions is double linked to allow + * insertion of elements at the end of the list to keep the + * insertion order of equal elements. + * + * A more memory conservative approach is to use a single linked list, + * inserting elements only at the start of the list, losing the + * original insertion order. + * + * \subsection zero_list Zero terminated list + * The 0 terminated format of tommy_node::next is present to provide a forward + * iterator terminating in 0. This allows the user to write a simple iteration + * loop over the list of elements in the same bucket. + * + * A more efficient approach is to use a circular list, because operating on nodes + * in a circular list doesn't requires to manage the special terminating case when + * adding or removing elements. + * + * \page license Tommy License + * Tommy is released with a <i>2-clause BSD license</i>. + * + * \code + * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. + * + * 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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. + * \endcode + */ + +/** \file + * All in one include for Tommy. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "tommytypes.h" +#include "tommyhash.h" +#include "tommyalloc.h" +#include "tommyarray.h" +#include "tommyarrayof.h" +#include "tommyarrayblk.h" +#include "tommyarrayblkof.h" +#include "tommylist.h" +#include "tommytree.h" +#include "tommytrie.h" +#include "tommytrieinp.h" +#include "tommyhashtbl.h" +#include "tommyhashdyn.h" +#include "tommyhashlin.h" + +#ifdef __cplusplus +} +#endif + diff -Nru librtr-0.6.3/third-party/tommyds/tommyhash.c librtr-0.7.0/third-party/tommyds/tommyhash.c --- librtr-0.6.3/third-party/tommyds/tommyhash.c 1970-01-01 00:00:00.000000000 +0000 +++ librtr-0.7.0/third-party/tommyds/tommyhash.c 2019-07-18 08:26:08.000000000 +0000 @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. + * + * 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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 "tommyhash.h" + +/******************************************************************************/ +/* hash */ + +tommy_inline tommy_uint32_t tommy_le_uint32_read(const void* ptr) +{ + /* allow unaligned read on Intel x86 and x86_64 platforms */ +#if defined(__i386__) || defined(_M_IX86) || defined(_X86_) || defined(__x86_64__) || defined(_M_X64) + /* defines from http://predef.sourceforge.net/ */ + return *(const tommy_uint32_t*)ptr; +#else + const unsigned char* ptr8 = tommy_cast(const unsigned char*, ptr); + return ptr8[0] + ((tommy_uint32_t)ptr8[1] << 8) + ((tommy_uint32_t)ptr8[2] << 16) + ((tommy_uint32_t)ptr8[3] << 24); +#endif +} + +#define tommy_rot(x, k) \ + (((x) << (k)) | ((x) >> (32 - (k)))) + +#define tommy_mix(a, b, c) \ + do { \ + a -= c; a ^= tommy_rot(c, 4); c += b; \ + b -= a; b ^= tommy_rot(a, 6); a += c; \ + c -= b; c ^= tommy_rot(b, 8); b += a; \ + a -= c; a ^= tommy_rot(c, 16); c += b; \ + b -= a; b ^= tommy_rot(a, 19); a += c; \ + c -= b; c ^= tommy_rot(b, 4); b += a; \ + } while (0) + +#define tommy_final(a, b, c) \ + do { \ + c ^= b; c -= tommy_rot(b, 14); \ + a ^= c; a -= tommy_rot(c, 11); \ + b ^= a; b -= tommy_rot(a, 25); \ + c ^= b; c -= tommy_rot(b, 16); \ + a ^= c; a -= tommy_rot(c, 4); \ + b ^= a; b -= tommy_rot(a, 14); \ + c ^= b; c -= tommy_rot(b, 24); \ + } while (0) + +tommy_uint32_t tommy_hash_u32(tommy_uint32_t init_val, const void* void_key, tommy_size_t key_len) +{ + const unsigned char* key = tommy_cast(const unsigned char*, void_key); + tommy_uint32_t a, b, c; + + a = b = c = 0xdeadbeef + ((tommy_uint32_t)key_len) + init_val; + + while (key_len > 12) { + a += tommy_le_uint32_read(key + 0); + b += tommy_le_uint32_read(key + 4); + c += tommy_le_uint32_read(key + 8); + + tommy_mix(a, b, c); + + key_len -= 12; + key += 12; + } + + switch (key_len) { + case 0 : + return c; /* used only when called with a zero length */ + case 12 : + c += tommy_le_uint32_read(key + 8); + b += tommy_le_uint32_read(key + 4); + a += tommy_le_uint32_read(key + 0); + break; + case 11 : c += ((tommy_uint32_t)key[10]) << 16; /* fallthrough */ + case 10 : c += ((tommy_uint32_t)key[9]) << 8; /* fallthrough */ + case 9 : c += key[8]; /* fallthrough */ + case 8 : + b += tommy_le_uint32_read(key + 4); + a += tommy_le_uint32_read(key + 0); + break; + case 7 : b += ((tommy_uint32_t)key[6]) << 16; /* fallthrough */ + case 6 : b += ((tommy_uint32_t)key[5]) << 8; /* fallthrough */ + case 5 : b += key[4]; /* fallthrough */ + case 4 : + a += tommy_le_uint32_read(key + 0); + break; + case 3 : a += ((tommy_uint32_t)key[2]) << 16; /* fallthrough */ + case 2 : a += ((tommy_uint32_t)key[1]) << 8; /* fallthrough */ + case 1 : a += key[0]; /* fallthrough */ + } + + tommy_final(a, b, c); + + return c; +} + +tommy_uint64_t tommy_hash_u64(tommy_uint64_t init_val, const void* void_key, tommy_size_t key_len) +{ + const unsigned char* key = tommy_cast(const unsigned char*, void_key); + tommy_uint32_t a, b, c; + + a = b = c = 0xdeadbeef + ((tommy_uint32_t)key_len) + (init_val & 0xffffffff); + c += init_val >> 32; + + while (key_len > 12) { + a += tommy_le_uint32_read(key + 0); + b += tommy_le_uint32_read(key + 4); + c += tommy_le_uint32_read(key + 8); + + tommy_mix(a, b, c); + + key_len -= 12; + key += 12; + } + + switch (key_len) { + case 0 : + return c + ((tommy_uint64_t)b << 32); /* used only when called with a zero length */ + case 12 : + c += tommy_le_uint32_read(key + 8); + b += tommy_le_uint32_read(key + 4); + a += tommy_le_uint32_read(key + 0); + break; + case 11 : c += ((tommy_uint32_t)key[10]) << 16; /* fallthrough */ + case 10 : c += ((tommy_uint32_t)key[9]) << 8; /* fallthrough */ + case 9 : c += key[8]; /* fallthrough */ + case 8 : + b += tommy_le_uint32_read(key + 4); + a += tommy_le_uint32_read(key + 0); + break; + case 7 : b += ((tommy_uint32_t)key[6]) << 16; /* fallthrough */ + case 6 : b += ((tommy_uint32_t)key[5]) << 8; /* fallthrough */ + case 5 : b += key[4]; /* fallthrough */ + case 4 : + a += tommy_le_uint32_read(key + 0); + break; + case 3 : a += ((tommy_uint32_t)key[2]) << 16; /* fallthrough */ + case 2 : a += ((tommy_uint32_t)key[1]) << 8; /* fallthrough */ + case 1 : a += key[0]; /* fallthrough */ + } + + tommy_final(a, b, c); + + return c + ((tommy_uint64_t)b << 32); +} + +tommy_uint32_t tommy_strhash_u32(tommy_uint64_t init_val, const void* void_key) +{ + const unsigned char* key = tommy_cast(const unsigned char*, void_key); + tommy_uint32_t a, b, c; + tommy_uint32_t m[3] = { 0xff, 0xff00, 0xff0000 }; + + a = b = c = 0xdeadbeef + init_val; + /* this is different than original lookup3 and the result won't match */ + + while (1) { + tommy_uint32_t v = tommy_le_uint32_read(key); + + if (tommy_haszero_u32(v)) { + if (v & m[0]) { + a += v & m[0]; + if (v & m[1]) { + a += v & m[1]; + if (v & m[2]) + a += v & m[2]; + } + } + + break; + } + + a += v; + + v = tommy_le_uint32_read(key + 4); + + if (tommy_haszero_u32(v)) { + if (v & m[0]) { + b += v & m[0]; + if (v & m[1]) { + b += v & m[1]; + if (v & m[2]) + b += v & m[2]; + } + } + + break; + } + + b += v; + + v = tommy_le_uint32_read(key + 8); + + if (tommy_haszero_u32(v)) { + if (v & m[0]) { + c += v & m[0]; + if (v & m[1]) { + c += v & m[1]; + if (v & m[2]) + c += v & m[2]; + } + } + + break; + } + + c += v; + + tommy_mix(a, b, c); + + key += 12; + } + + /* for lengths that are multiplers of 12 we already have called mix */ + /* this is different than the original lookup3 and the result won't match */ + + tommy_final(a, b, c); + + return c; +} + diff -Nru librtr-0.6.3/third-party/tommyds/tommyhashdyn.c librtr-0.7.0/third-party/tommyds/tommyhashdyn.c --- librtr-0.6.3/third-party/tommyds/tommyhashdyn.c 1970-01-01 00:00:00.000000000 +0000 +++ librtr-0.7.0/third-party/tommyds/tommyhashdyn.c 2019-07-18 08:26:08.000000000 +0000 @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. + * + * 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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 "tommyhashdyn.h" +#include "tommylist.h" + +/******************************************************************************/ +/* hashdyn */ + +void tommy_hashdyn_init(tommy_hashdyn* hashdyn) +{ + /* fixed initial size */ + hashdyn->bucket_bit = TOMMY_HASHDYN_BIT; + hashdyn->bucket_max = 1 << hashdyn->bucket_bit; + hashdyn->bucket_mask = hashdyn->bucket_max - 1; + hashdyn->bucket = tommy_cast(tommy_hashdyn_node**, tommy_calloc(hashdyn->bucket_max, sizeof(tommy_hashdyn_node*))); + + hashdyn->count = 0; +} + +void tommy_hashdyn_done(tommy_hashdyn* hashdyn) +{ + tommy_free(hashdyn->bucket); +} + +/** + * Resize the bucket vector. + */ +static void tommy_hashdyn_resize(tommy_hashdyn* hashdyn, tommy_count_t new_bucket_bit) +{ + tommy_count_t bucket_bit; + tommy_count_t bucket_max; + tommy_count_t new_bucket_max; + tommy_count_t new_bucket_mask; + tommy_hashdyn_node** new_bucket; + + bucket_bit = hashdyn->bucket_bit; + bucket_max = hashdyn->bucket_max; + + new_bucket_max = 1 << new_bucket_bit; + new_bucket_mask = new_bucket_max - 1; + + /* allocate the new vector using malloc() and not calloc() */ + /* because data is fully initialized in the update process */ + new_bucket = tommy_cast(tommy_hashdyn_node**, tommy_malloc(new_bucket_max * sizeof(tommy_hashdyn_node*))); + + /* reinsert all the elements */ + if (new_bucket_bit > bucket_bit) { + tommy_count_t i; + + /* grow */ + for (i = 0; i < bucket_max; ++i) { + tommy_hashdyn_node* j; + + /* setup the new two buckets */ + new_bucket[i] = 0; + new_bucket[i + bucket_max] = 0; + + /* reinsert the bucket */ + j = hashdyn->bucket[i]; + while (j) { + tommy_hashdyn_node* j_next = j->next; + tommy_count_t pos = j->key & new_bucket_mask; + if (new_bucket[pos]) + tommy_list_insert_tail_not_empty(new_bucket[pos], j); + else + tommy_list_insert_first(&new_bucket[pos], j); + j = j_next; + } + } + } else { + tommy_count_t i; + + /* shrink */ + for (i = 0; i < new_bucket_max; ++i) { + /* setup the new bucket with the lower bucket*/ + new_bucket[i] = hashdyn->bucket[i]; + + /* concat the upper bucket */ + tommy_list_concat(&new_bucket[i], &hashdyn->bucket[i + new_bucket_max]); + } + } + + tommy_free(hashdyn->bucket); + + /* setup */ + hashdyn->bucket_bit = new_bucket_bit; + hashdyn->bucket_max = new_bucket_max; + hashdyn->bucket_mask = new_bucket_mask; + hashdyn->bucket = new_bucket; +} + +/** + * Grow. + */ +tommy_inline void hashdyn_grow_step(tommy_hashdyn* hashdyn) +{ + /* grow if more than 50% full */ + if (hashdyn->count >= hashdyn->bucket_max / 2) + tommy_hashdyn_resize(hashdyn, hashdyn->bucket_bit + 1); +} + +/** + * Shrink. + */ +tommy_inline void hashdyn_shrink_step(tommy_hashdyn* hashdyn) +{ + /* shrink if less than 12.5% full */ + if (hashdyn->count <= hashdyn->bucket_max / 8 && hashdyn->bucket_bit > TOMMY_HASHDYN_BIT) + tommy_hashdyn_resize(hashdyn, hashdyn->bucket_bit - 1); +} + +void tommy_hashdyn_insert(tommy_hashdyn* hashdyn, tommy_hashdyn_node* node, void* data, tommy_hash_t hash) +{ + tommy_count_t pos = hash & hashdyn->bucket_mask; + + tommy_list_insert_tail(&hashdyn->bucket[pos], node, data); + + node->key = hash; + + ++hashdyn->count; + + hashdyn_grow_step(hashdyn); +} + +void* tommy_hashdyn_remove_existing(tommy_hashdyn* hashdyn, tommy_hashdyn_node* node) +{ + tommy_count_t pos = node->key & hashdyn->bucket_mask; + + tommy_list_remove_existing(&hashdyn->bucket[pos], node); + + --hashdyn->count; + + hashdyn_shrink_step(hashdyn); + + return node->data; +} + +void* tommy_hashdyn_remove(tommy_hashdyn* hashdyn, tommy_search_func* cmp, const void* cmp_arg, tommy_hash_t hash) +{ + tommy_count_t pos = hash & hashdyn->bucket_mask; + tommy_hashdyn_node* node = hashdyn->bucket[pos]; + + while (node) { + /* we first check if the hash matches, as in the same bucket we may have multiples hash values */ + if (node->key == hash && cmp(cmp_arg, node->data) == 0) { + tommy_list_remove_existing(&hashdyn->bucket[pos], node); + + --hashdyn->count; + + hashdyn_shrink_step(hashdyn); + + return node->data; + } + node = node->next; + } + + return 0; +} + +void tommy_hashdyn_foreach(tommy_hashdyn* hashdyn, tommy_foreach_func* func) +{ + tommy_count_t bucket_max = hashdyn->bucket_max; + tommy_hashdyn_node** bucket = hashdyn->bucket; + tommy_count_t pos; + + for (pos = 0; pos < bucket_max; ++pos) { + tommy_hashdyn_node* node = bucket[pos]; + + while (node) { + void* data = node->data; + node = node->next; + func(data); + } + } +} + +void tommy_hashdyn_foreach_arg(tommy_hashdyn* hashdyn, tommy_foreach_arg_func* func, void* arg) +{ + tommy_count_t bucket_max = hashdyn->bucket_max; + tommy_hashdyn_node** bucket = hashdyn->bucket; + tommy_count_t pos; + + for (pos = 0; pos < bucket_max; ++pos) { + tommy_hashdyn_node* node = bucket[pos]; + + while (node) { + void* data = node->data; + node = node->next; + func(arg, data); + } + } +} + +tommy_size_t tommy_hashdyn_memory_usage(tommy_hashdyn* hashdyn) +{ + return hashdyn->bucket_max * (tommy_size_t)sizeof(hashdyn->bucket[0]) + + tommy_hashdyn_count(hashdyn) * (tommy_size_t)sizeof(tommy_hashdyn_node); +} + diff -Nru librtr-0.6.3/third-party/tommyds/tommyhashdyn.h librtr-0.7.0/third-party/tommyds/tommyhashdyn.h --- librtr-0.6.3/third-party/tommyds/tommyhashdyn.h 1970-01-01 00:00:00.000000000 +0000 +++ librtr-0.7.0/third-party/tommyds/tommyhashdyn.h 2019-07-18 08:26:08.000000000 +0000 @@ -0,0 +1,296 @@ +/* + * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. + * + * 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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. + */ + +/** \file + * Dynamic chained hashtable. + * + * This hashtable resizes dynamically. It starts with the minimal size of 16 buckets, it doubles + * the size then it reaches a load factor greater than 0.5 and it halves the size with a load + * factor lower than 0.125. + * + * All the elements are reallocated in a single resize operation done inside + * tommy_hashdyn_insert() or tommy_hashdyn_remove(). + * + * Note that the resize operation takes approximatively 100 [ms] with 1 million of elements, + * and 1 [second] with 10 millions. This could be a problem in real-time applications. + * + * The resize also fragment the heap, as it involves allocating a double-sized table, copy elements, + * and deallocating the older table. Leaving a big hole in the heap. + * + * The ::tommy_hashlin hashtable fixes both problems. + * + * To initialize the hashtable you have to call tommy_hashdyn_init(). + * + * \code + * tommy_hashslin hashdyn; + * + * tommy_hashdyn_init(&hashdyn); + * \endcode + * + * To insert elements in the hashtable you have to call tommy_hashdyn_insert() for + * each element. + * In the insertion call you have to specify the address of the node, the + * address of the object, and the hash value of the key to use. + * The address of the object is used to initialize the tommy_node::data field + * of the node, and the hash to initialize the tommy_node::key field. + * + * \code + * struct object { + * int value; + * // other fields + * tommy_node node; + * }; + * + * struct object* obj = malloc(sizeof(struct object)); // creates the object + * + * obj->value = ...; // initializes the object + * + * tommy_hashdyn_insert(&hashdyn, &obj->node, obj, tommy_inthash_u32(obj->value)); // inserts the object + * \endcode + * + * To find and element in the hashtable you have to call tommy_hashtable_search() + * providing a comparison function, its argument, and the hash of the key to search. + * + * \code + * int compare(const void* arg, const void* obj) + * { + * return *(const int*)arg != ((const struct object*)obj)->value; + * } + * + * int value_to_find = 1; + * struct object* obj = tommy_hashdyn_search(&hashdyn, compare, &value_to_find, tommy_inthash_u32(value_to_find)); + * if (!obj) { + * // not found + * } else { + * // found + * } + * \endcode + * + * To iterate over all the elements in the hashtable with the same key, you have to + * use tommy_hashdyn_bucket() and follow the tommy_node::next pointer until NULL. + * You have also to check explicitely for the key, as the bucket may contains + * different keys. + * + * \code + * int value_to_find = 1; + * tommy_node* i = tommy_hashdyn_bucket(&hashdyn, tommy_inthash_u32(value_to_find)); + * while (i) { + * struct object* obj = i->data; // gets the object pointer + * + * if (obj->value == value_to_find) { + * printf("%d\n", obj->value); // process the object + * } + * + * i = i->next; // goes to the next element + * } + * \endcode + * + * To remove an element from the hashtable you have to call tommy_hashdyn_remove() + * providing a comparison function, its argument, and the hash of the key to search + * and remove. + * + * \code + * struct object* obj = tommy_hashdyn_remove(&hashdyn, compare, &value_to_remove, tommy_inthash_u32(value_to_remove)); + * if (obj) { + * free(obj); // frees the object allocated memory + * } + * \endcode + * + * To destroy the hashtable you have to remove all the elements, and deinitialize + * the hashtable calling tommy_hashdyn_done(). + * + * \code + * tommy_hashdyn_done(&hashdyn); + * \endcode + * + * If you need to iterate over all the elements in the hashtable, you can use + * tommy_hashdyn_foreach() or tommy_hashdyn_foreach_arg(). + * If you need a more precise control with a real iteration, you have to insert + * all the elements also in a ::tommy_list, and use the list to iterate. + * See the \ref multiindex example for more detail. + */ + +#ifndef __TOMMYHASHDYN_H +#define __TOMMYHASHDYN_H + +#include "tommyhash.h" + +/******************************************************************************/ +/* hashdyn */ + +/** \internal + * Initial and minimal size of the hashtable expressed as a power of 2. + * The initial size is 2^TOMMY_HASHDYN_BIT. + */ +#define TOMMY_HASHDYN_BIT 4 + +/** + * Hashtable node. + * This is the node that you have to include inside your objects. + */ +typedef tommy_node tommy_hashdyn_node; + +/** + * Hashtable container type. + * \note Don't use internal fields directly, but access the container only using functions. + */ +typedef struct tommy_hashdyn_struct { + tommy_hashdyn_node** bucket; /**< Hash buckets. One list for each hash modulus. */ + tommy_uint_t bucket_bit; /**< Bits used in the bit mask. */ + tommy_count_t bucket_max; /**< Number of buckets. */ + tommy_count_t bucket_mask; /**< Bit mask to access the buckets. */ + tommy_count_t count; /**< Number of elements. */ +} tommy_hashdyn; + +/** + * Initializes the hashtable. + */ +void tommy_hashdyn_init(tommy_hashdyn* hashdyn); + +/** + * Deinitializes the hashtable. + * + * You can call this function with elements still contained, + * but such elements are not going to be freed by this call. + */ +void tommy_hashdyn_done(tommy_hashdyn* hashdyn); + +/** + * Inserts an element in the hashtable. + */ +void tommy_hashdyn_insert(tommy_hashdyn* hashdyn, tommy_hashdyn_node* node, void* data, tommy_hash_t hash); + +/** + * Searches and removes an element from the hashtable. + * You have to provide a compare function and the hash of the element you want to remove. + * If the element is not found, 0 is returned. + * If more equal elements are present, the first one is removed. + * \param cmp Compare function called with cmp_arg as first argument and with the element to compare as a second one. + * The function should return 0 for equal elements, anything other for different elements. + * \param cmp_arg Compare argument passed as first argument of the compare function. + * \param hash Hash of the element to find and remove. + * \return The removed element, or 0 if not found. + */ +void* tommy_hashdyn_remove(tommy_hashdyn* hashdyn, tommy_search_func* cmp, const void* cmp_arg, tommy_hash_t hash); + +/** + * Gets the bucket of the specified hash. + * The bucket is guaranteed to contain ALL the elements with the specified hash, + * but it can contain also others. + * You can access elements in the bucket following the ::next pointer until 0. + * \param hash Hash of the element to find. + * \return The head of the bucket, or 0 if empty. + */ +tommy_inline tommy_hashdyn_node* tommy_hashdyn_bucket(tommy_hashdyn* hashdyn, tommy_hash_t hash) +{ + return hashdyn->bucket[hash & hashdyn->bucket_mask]; +} + +/** + * Searches an element in the hashtable. + * You have to provide a compare function and the hash of the element you want to find. + * If more equal elements are present, the first one is returned. + * \param cmp Compare function called with cmp_arg as first argument and with the element to compare as a second one. + * The function should return 0 for equal elements, anything other for different elements. + * \param cmp_arg Compare argument passed as first argument of the compare function. + * \param hash Hash of the element to find. + * \return The first element found, or 0 if none. + */ +tommy_inline void* tommy_hashdyn_search(tommy_hashdyn* hashdyn, tommy_search_func* cmp, const void* cmp_arg, tommy_hash_t hash) +{ + tommy_hashdyn_node* i = tommy_hashdyn_bucket(hashdyn, hash); + + while (i) { + /* we first check if the hash matches, as in the same bucket we may have multiples hash values */ + if (i->key == hash && cmp(cmp_arg, i->data) == 0) + return i->data; + i = i->next; + } + return 0; +} + +/** + * Removes an element from the hashtable. + * You must already have the address of the element to remove. + * \return The tommy_node::data field of the node removed. + */ +void* tommy_hashdyn_remove_existing(tommy_hashdyn* hashdyn, tommy_hashdyn_node* node); + +/** + * Calls the specified function for each element in the hashtable. + * + * You cannot add or remove elements from the inside of the callback, + * but can use it to deallocate them. + * + * \code + * tommy_hashdyn hashdyn; + * + * // initializes the hashtable + * tommy_hashdyn_init(&hashdyn); + * + * ... + * + * // creates an object + * struct object* obj = malloc(sizeof(struct object)); + * + * ... + * + * // insert it in the hashtable + * tommy_hashdyn_insert(&hashdyn, &obj->node, obj, tommy_inthash_u32(obj->value)); + * + * ... + * + * // deallocates all the objects iterating the hashtable + * tommy_hashdyn_foreach(&hashdyn, free); + * + * // deallocates the hashtable + * tommy_hashdyn_done(&hashdyn); + * \endcode + */ +void tommy_hashdyn_foreach(tommy_hashdyn* hashdyn, tommy_foreach_func* func); + +/** + * Calls the specified function with an argument for each element in the hashtable. + */ +void tommy_hashdyn_foreach_arg(tommy_hashdyn* hashdyn, tommy_foreach_arg_func* func, void* arg); + +/** + * Gets the number of elements. + */ +tommy_inline tommy_count_t tommy_hashdyn_count(tommy_hashdyn* hashdyn) +{ + return hashdyn->count; +} + +/** + * Gets the size of allocated memory. + * It includes the size of the ::tommy_hashdyn_node of the stored elements. + */ +tommy_size_t tommy_hashdyn_memory_usage(tommy_hashdyn* hashdyn); + +#endif + diff -Nru librtr-0.6.3/third-party/tommyds/tommyhash.h librtr-0.7.0/third-party/tommyds/tommyhash.h --- librtr-0.6.3/third-party/tommyds/tommyhash.h 1970-01-01 00:00:00.000000000 +0000 +++ librtr-0.7.0/third-party/tommyds/tommyhash.h 2019-07-18 08:26:08.000000000 +0000 @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. + * + * 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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. + */ + +/** \file + * Hash functions for the use with ::tommy_hashtable, ::tommy_hashdyn and ::tommy_hashlin. + */ + +#ifndef __TOMMYHASH_H +#define __TOMMYHASH_H + +#include "tommytypes.h" + +/******************************************************************************/ +/* hash */ + +/** + * Hash type used in hashtables. + */ +typedef tommy_key_t tommy_hash_t; + +/** + * Hash function with a 32 bits result. + * Implementation of the Robert Jenkins "lookup3" hash 32 bits version, + * from http://www.burtleburtle.net/bob/hash/doobs.html, function hashlittle(). + * + * This hash is designed to provide a good overall performance in all platforms, + * including 32 bits. If you target only 64 bits, you can use faster hashes, + * like SpookyHash or FarmHash. + * + * \param init_val Initialization value. + * Using a different initialization value, you can generate a completely different set of hash values. + * Use 0 if not relevant. + * \param void_key Pointer to the data to hash. + * \param key_len Size of the data to hash. + * \note + * This function is endianess independent. + * \return The hash value of 32 bits. + */ +tommy_uint32_t tommy_hash_u32(tommy_uint32_t init_val, const void* void_key, tommy_size_t key_len); + +/** + * Hash function with a 64 bits result. + * Implementation of the Robert Jenkins "lookup3" hash 64 bits versions, + * from http://www.burtleburtle.net/bob/hash/doobs.html, function hashlittle2(). + * + * This hash is designed to provide a good overall performance in all platforms, + * including 32 bits. If you target only 64 bits, you can use faster hashes, + * like SpookyHash or FarmHash. + * + * \param init_val Initialization value. + * Using a different initialization value, you can generate a completely different set of hash values. + * Use 0 if not relevant. + * \param void_key Pointer to the data to hash. + * \param key_len Size of the data to hash. + * \note + * This function is endianess independent. + * \return The hash value of 64 bits. + */ +tommy_uint64_t tommy_hash_u64(tommy_uint64_t init_val, const void* void_key, tommy_size_t key_len); + +/** + * String hash function with a 32 bits result. + * Implementation is based on the the Robert Jenkins "lookup3" hash 32 bits version, + * from http://www.burtleburtle.net/bob/hash/doobs.html, function hashlittle(). + * + * This hash is designed to handle strings with an unknown length. If you + * know the string length, the other hash functions are surely faster. + * + * \param init_val Initialization value. + * Using a different initialization value, you can generate a completely different set of hash values. + * Use 0 if not relevant. + * \param void_key Pointer to the string to hash. It has to be 0 terminated. + * \note + * This function is endianess independent. + * \return The hash value of 32 bits. + */ +tommy_uint32_t tommy_strhash_u32(tommy_uint64_t init_val, const void* void_key); + +/** + * Integer reversible hash function for 32 bits. + * Implementation of the Robert Jenkins "4-byte Integer Hashing", + * from http://burtleburtle.net/bob/hash/integer.html + */ +tommy_inline tommy_uint32_t tommy_inthash_u32(tommy_uint32_t key) +{ + key -= key << 6; + key ^= key >> 17; + key -= key << 9; + key ^= key << 4; + key -= key << 3; + key ^= key << 10; + key ^= key >> 15; + + return key; +} + +/** + * Integer reversible hash function for 64 bits. + * Implementation of the Thomas Wang "Integer Hash Function", + * from http://web.archive.org/web/20071223173210/http://www.concentric.net/~Ttwang/tech/inthash.htm + */ +tommy_inline tommy_uint64_t tommy_inthash_u64(tommy_uint64_t key) +{ + key = ~key + (key << 21); + key = key ^ (key >> 24); + key = key + (key << 3) + (key << 8); + key = key ^ (key >> 14); + key = key + (key << 2) + (key << 4); + key = key ^ (key >> 28); + key = key + (key << 31); + + return key; +} + +#endif + diff -Nru librtr-0.6.3/third-party/tommyds/tommyhashlin.c librtr-0.7.0/third-party/tommyds/tommyhashlin.c --- librtr-0.6.3/third-party/tommyds/tommyhashlin.c 1970-01-01 00:00:00.000000000 +0000 +++ librtr-0.7.0/third-party/tommyds/tommyhashlin.c 2019-07-18 08:26:08.000000000 +0000 @@ -0,0 +1,334 @@ +/* + * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. + * + * 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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 "tommyhashlin.h" +#include "tommylist.h" + +#include <assert.h> /* for assert */ + +/******************************************************************************/ +/* hashlin */ + +/** + * Reallocation states. + */ +#define TOMMY_HASHLIN_STATE_STABLE 0 +#define TOMMY_HASHLIN_STATE_GROW 1 +#define TOMMY_HASHLIN_STATE_SHRINK 2 + +/** + * Set the hashtable in stable state. + */ +tommy_inline void tommy_hashlin_stable(tommy_hashlin* hashlin) +{ + hashlin->state = TOMMY_HASHLIN_STATE_STABLE; + + /* setup low_mask/max/split to allow tommy_hashlin_bucket_ref() */ + /* and tommy_hashlin_foreach() to work regardless we are in stable state */ + hashlin->low_max = hashlin->bucket_max; + hashlin->low_mask = hashlin->bucket_mask; + hashlin->split = 0; +} + +void tommy_hashlin_init(tommy_hashlin* hashlin) +{ + tommy_uint_t i; + + /* fixed initial size */ + hashlin->bucket_bit = TOMMY_HASHLIN_BIT; + hashlin->bucket_max = 1 << hashlin->bucket_bit; + hashlin->bucket_mask = hashlin->bucket_max - 1; + hashlin->bucket[0] = tommy_cast(tommy_hashlin_node**, tommy_calloc(hashlin->bucket_max, sizeof(tommy_hashlin_node*))); + for (i = 1; i < TOMMY_HASHLIN_BIT; ++i) + hashlin->bucket[i] = hashlin->bucket[0]; + + /* stable state */ + tommy_hashlin_stable(hashlin); + + hashlin->count = 0; +} + +void tommy_hashlin_done(tommy_hashlin* hashlin) +{ + tommy_uint_t i; + + tommy_free(hashlin->bucket[0]); + for (i = TOMMY_HASHLIN_BIT; i < hashlin->bucket_bit; ++i) { + tommy_hashlin_node** segment = hashlin->bucket[i]; + tommy_free(&segment[((tommy_ptrdiff_t)1) << i]); + } +} + +/** + * Grow one step. + */ +tommy_inline void hashlin_grow_step(tommy_hashlin* hashlin) +{ + /* grow if more than 50% full */ + if (hashlin->state != TOMMY_HASHLIN_STATE_GROW + && hashlin->count > hashlin->bucket_max / 2 + ) { + /* if we are stable, setup a new grow state */ + /* otherwise continue with the already setup shrink one */ + /* but in backward direction */ + if (hashlin->state == TOMMY_HASHLIN_STATE_STABLE) { + tommy_hashlin_node** segment; + + /* set the lower size */ + hashlin->low_max = hashlin->bucket_max; + hashlin->low_mask = hashlin->bucket_mask; + + /* allocate the new vector using malloc() and not calloc() */ + /* because data is fully initialized in the split process */ + segment = tommy_cast(tommy_hashlin_node**, tommy_malloc(hashlin->low_max * sizeof(tommy_hashlin_node*))); + + /* store it adjusting the offset */ + /* cast to ptrdiff_t to ensure to get a negative value */ + hashlin->bucket[hashlin->bucket_bit] = &segment[-(tommy_ptrdiff_t)hashlin->low_max]; + + /* grow the hash size */ + ++hashlin->bucket_bit; + hashlin->bucket_max = 1 << hashlin->bucket_bit; + hashlin->bucket_mask = hashlin->bucket_max - 1; + + /* start from the beginning going forward */ + hashlin->split = 0; + } + + /* grow state */ + hashlin->state = TOMMY_HASHLIN_STATE_GROW; + } + + /* if we are growing */ + if (hashlin->state == TOMMY_HASHLIN_STATE_GROW) { + /* compute the split target required to finish the reallocation before the next resize */ + tommy_count_t split_target = 2 * hashlin->count; + + /* reallocate buckets until the split target */ + while (hashlin->split + hashlin->low_max < split_target) { + tommy_hashlin_node** split[2]; + tommy_hashlin_node* j; + tommy_count_t mask; + + /* get the low bucket */ + split[0] = tommy_hashlin_pos(hashlin, hashlin->split); + + /* get the high bucket */ + split[1] = tommy_hashlin_pos(hashlin, hashlin->split + hashlin->low_max); + + /* save the low bucket */ + j = *split[0]; + + /* reinitialize the buckets */ + *split[0] = 0; + *split[1] = 0; + + /* the bit used to identify the bucket */ + mask = hashlin->low_max; + + /* flush the bucket */ + while (j) { + tommy_hashlin_node* j_next = j->next; + tommy_count_t pos = (j->key & mask) != 0; + if (*split[pos]) + tommy_list_insert_tail_not_empty(*split[pos], j); + else + tommy_list_insert_first(split[pos], j); + j = j_next; + } + + /* go forward */ + ++hashlin->split; + + /* if we have finished, change the state */ + if (hashlin->split == hashlin->low_max) { + /* go in stable mode */ + tommy_hashlin_stable(hashlin); + break; + } + } + } +} + +/** + * Shrink one step. + */ +tommy_inline void hashlin_shrink_step(tommy_hashlin* hashlin) +{ + /* shrink if less than 12.5% full */ + if (hashlin->state != TOMMY_HASHLIN_STATE_SHRINK + && hashlin->count < hashlin->bucket_max / 8 + ) { + /* avoid to shrink the first bucket */ + if (hashlin->bucket_bit > TOMMY_HASHLIN_BIT) { + /* if we are stable, setup a new shrink state */ + /* otherwise continue with the already setup grow one */ + /* but in backward direction */ + if (hashlin->state == TOMMY_HASHLIN_STATE_STABLE) { + /* set the lower size */ + hashlin->low_max = hashlin->bucket_max / 2; + hashlin->low_mask = hashlin->bucket_mask / 2; + + /* start from the half going backward */ + hashlin->split = hashlin->low_max; + } + + /* start reallocation */ + hashlin->state = TOMMY_HASHLIN_STATE_SHRINK; + } + } + + /* if we are shrinking */ + if (hashlin->state == TOMMY_HASHLIN_STATE_SHRINK) { + /* compute the split target required to finish the reallocation before the next resize */ + tommy_count_t split_target = 8 * hashlin->count; + + /* reallocate buckets until the split target */ + while (hashlin->split + hashlin->low_max > split_target) { + tommy_hashlin_node** split[2]; + + /* go backward position */ + --hashlin->split; + + /* get the low bucket */ + split[0] = tommy_hashlin_pos(hashlin, hashlin->split); + + /* get the high bucket */ + split[1] = tommy_hashlin_pos(hashlin, hashlin->split + hashlin->low_max); + + /* concat the high bucket into the low one */ + tommy_list_concat(split[0], split[1]); + + /* if we have finished, clean up and change the state */ + if (hashlin->split == 0) { + tommy_hashlin_node** segment; + + /* shrink the hash size */ + --hashlin->bucket_bit; + hashlin->bucket_max = 1 << hashlin->bucket_bit; + hashlin->bucket_mask = hashlin->bucket_max - 1; + + /* free the last segment */ + segment = hashlin->bucket[hashlin->bucket_bit]; + tommy_free(&segment[((tommy_ptrdiff_t)1) << hashlin->bucket_bit]); + + /* go in stable mode */ + tommy_hashlin_stable(hashlin); + break; + } + } + } +} + +void tommy_hashlin_insert(tommy_hashlin* hashlin, tommy_hashlin_node* node, void* data, tommy_hash_t hash) +{ + tommy_list_insert_tail(tommy_hashlin_bucket_ref(hashlin, hash), node, data); + + node->key = hash; + + ++hashlin->count; + + hashlin_grow_step(hashlin); +} + +void* tommy_hashlin_remove_existing(tommy_hashlin* hashlin, tommy_hashlin_node* node) +{ + tommy_list_remove_existing(tommy_hashlin_bucket_ref(hashlin, node->key), node); + + --hashlin->count; + + hashlin_shrink_step(hashlin); + + return node->data; +} + +void* tommy_hashlin_remove(tommy_hashlin* hashlin, tommy_search_func* cmp, const void* cmp_arg, tommy_hash_t hash) +{ + tommy_hashlin_node** let_ptr = tommy_hashlin_bucket_ref(hashlin, hash); + tommy_hashlin_node* node = *let_ptr; + + while (node) { + /* we first check if the hash matches, as in the same bucket we may have multiples hash values */ + if (node->key == hash && cmp(cmp_arg, node->data) == 0) { + tommy_list_remove_existing(let_ptr, node); + + --hashlin->count; + + hashlin_shrink_step(hashlin); + + return node->data; + } + node = node->next; + } + + return 0; +} + +void tommy_hashlin_foreach(tommy_hashlin* hashlin, tommy_foreach_func* func) +{ + tommy_count_t bucket_max; + tommy_count_t pos; + + /* number of valid buckets */ + bucket_max = hashlin->low_max + hashlin->split; + + for (pos = 0; pos < bucket_max; ++pos) { + tommy_hashlin_node* node = *tommy_hashlin_pos(hashlin, pos); + + while (node) { + void* data = node->data; + node = node->next; + func(data); + } + } +} + +void tommy_hashlin_foreach_arg(tommy_hashlin* hashlin, tommy_foreach_arg_func* func, void* arg) +{ + tommy_count_t bucket_max; + tommy_count_t pos; + + /* number of valid buckets */ + bucket_max = hashlin->low_max + hashlin->split; + + for (pos = 0; pos < bucket_max; ++pos) { + tommy_hashlin_node* node = *tommy_hashlin_pos(hashlin, pos); + + while (node) { + void* data = node->data; + node = node->next; + func(arg, data); + } + } +} + +tommy_size_t tommy_hashlin_memory_usage(tommy_hashlin* hashlin) +{ + return hashlin->bucket_max * (tommy_size_t)sizeof(hashlin->bucket[0][0]) + + hashlin->count * (tommy_size_t)sizeof(tommy_hashlin_node); +} + diff -Nru librtr-0.6.3/third-party/tommyds/tommyhashlin.h librtr-0.7.0/third-party/tommyds/tommyhashlin.h --- librtr-0.6.3/third-party/tommyds/tommyhashlin.h 1970-01-01 00:00:00.000000000 +0000 +++ librtr-0.7.0/third-party/tommyds/tommyhashlin.h 2019-07-18 08:26:08.000000000 +0000 @@ -0,0 +1,350 @@ +/* + * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. + * + * 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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. + */ + +/** \file + * Linear chained hashtable. + * + * This hashtable resizes dynamically and progressively using a variation of the + * linear hashing algorithm described in http://en.wikipedia.org/wiki/Linear_hashing + * + * It starts with the minimal size of 16 buckets, it doubles the size then it + * reaches a load factor greater than 0.5 and it halves the size with a load + * factor lower than 0.125. + * + * The progressive resize is good for real-time and interactive applications + * as it makes insert and delete operations taking always the same time. + * + * For resizing it's used a dynamic array that supports access to not contigous + * segments. + * In this way we only allocate additional table segments on the heap, without + * freeing the previous table, and then not increasing the heap fragmentation. + * + * The resize takes place inside tommy_hashlin_insert() and tommy_hashlin_remove(). + * No resize is done in the tommy_hashlin_search() operation. + * + * To initialize the hashtable you have to call tommy_hashlin_init(). + * + * \code + * tommy_hashslin hashlin; + * + * tommy_hashlin_init(&hashlin); + * \endcode + * + * To insert elements in the hashtable you have to call tommy_hashlin_insert() for + * each element. + * In the insertion call you have to specify the address of the node, the + * address of the object, and the hash value of the key to use. + * The address of the object is used to initialize the tommy_node::data field + * of the node, and the hash to initialize the tommy_node::key field. + * + * \code + * struct object { + * int value; + * // other fields + * tommy_node node; + * }; + * + * struct object* obj = malloc(sizeof(struct object)); // creates the object + * + * obj->value = ...; // initializes the object + * + * tommy_hashlin_insert(&hashlin, &obj->node, obj, tommy_inthash_u32(obj->value)); // inserts the object + * \endcode + * + * To find and element in the hashtable you have to call tommy_hashtable_search() + * providing a comparison function, its argument, and the hash of the key to search. + * + * \code + * int compare(const void* arg, const void* obj) + * { + * return *(const int*)arg != ((const struct object*)obj)->value; + * } + * + * int value_to_find = 1; + * struct object* obj = tommy_hashlin_search(&hashlin, compare, &value_to_find, tommy_inthash_u32(value_to_find)); + * if (!obj) { + * // not found + * } else { + * // found + * } + * \endcode + * + * To iterate over all the elements in the hashtable with the same key, you have to + * use tommy_hashlin_bucket() and follow the tommy_node::next pointer until NULL. + * You have also to check explicitely for the key, as the bucket may contains + * different keys. + * + * \code + * int value_to_find = 1; + * tommy_node* i = tommy_hashlin_bucket(&hashlin, tommy_inthash_u32(value_to_find)); + * while (i) { + * struct object* obj = i->data; // gets the object pointer + * + * if (obj->value == value_to_find) { + * printf("%d\n", obj->value); // process the object + * } + * + * i = i->next; // goes to the next element + * } + * \endcode + * + * To remove an element from the hashtable you have to call tommy_hashlin_remove() + * providing a comparison function, its argument, and the hash of the key to search + * and remove. + * + * \code + * struct object* obj = tommy_hashlin_remove(&hashlin, compare, &value_to_remove, tommy_inthash_u32(value_to_remove)); + * if (obj) { + * free(obj); // frees the object allocated memory + * } + * \endcode + * + * To destroy the hashtable you have to remove all the elements, and deinitialize + * the hashtable calling tommy_hashlin_done(). + * + * \code + * tommy_hashlin_done(&hashlin); + * \endcode + * + * If you need to iterate over all the elements in the hashtable, you can use + * tommy_hashlin_foreach() or tommy_hashlin_foreach_arg(). + * If you need a more precise control with a real iteration, you have to insert + * all the elements also in a ::tommy_list, and use the list to iterate. + * See the \ref multiindex example for more detail. + */ + +#ifndef __TOMMYHASHLIN_H +#define __TOMMYHASHLIN_H + +#include "tommyhash.h" + +/******************************************************************************/ +/* hashlin */ + +/** \internal + * Initial and minimal size of the hashtable expressed as a power of 2. + * The initial size is 2^TOMMY_HASHLIN_BIT. + */ +#define TOMMY_HASHLIN_BIT 6 + +/** + * Hashtable node. + * This is the node that you have to include inside your objects. + */ +typedef tommy_node tommy_hashlin_node; + +/** \internal + * Max number of elements as a power of 2. + */ +#define TOMMY_HASHLIN_BIT_MAX 32 + +/** + * Hashtable container type. + * \note Don't use internal fields directly, but access the container only using functions. + */ +typedef struct tommy_hashlin_struct { + tommy_hashlin_node** bucket[TOMMY_HASHLIN_BIT_MAX]; /**< Dynamic array of hash buckets. One list for each hash modulus. */ + tommy_uint_t bucket_bit; /**< Bits used in the bit mask. */ + tommy_count_t bucket_max; /**< Number of buckets. */ + tommy_count_t bucket_mask; /**< Bit mask to access the buckets. */ + tommy_count_t low_max; /**< Low order max value. */ + tommy_count_t low_mask; /**< Low order mask value. */ + tommy_count_t split; /**< Split position. */ + tommy_count_t count; /**< Number of elements. */ + tommy_uint_t state; /**< Reallocation state. */ +} tommy_hashlin; + +/** + * Initializes the hashtable. + */ +void tommy_hashlin_init(tommy_hashlin* hashlin); + +/** + * Deinitializes the hashtable. + * + * You can call this function with elements still contained, + * but such elements are not going to be freed by this call. + */ +void tommy_hashlin_done(tommy_hashlin* hashlin); + +/** + * Inserts an element in the hashtable. + */ +void tommy_hashlin_insert(tommy_hashlin* hashlin, tommy_hashlin_node* node, void* data, tommy_hash_t hash); + +/** + * Searches and removes an element from the hashtable. + * You have to provide a compare function and the hash of the element you want to remove. + * If the element is not found, 0 is returned. + * If more equal elements are present, the first one is removed. + * \param cmp Compare function called with cmp_arg as first argument and with the element to compare as a second one. + * The function should return 0 for equal elements, anything other for different elements. + * \param cmp_arg Compare argument passed as first argument of the compare function. + * \param hash Hash of the element to find and remove. + * \return The removed element, or 0 if not found. + */ +void* tommy_hashlin_remove(tommy_hashlin* hashlin, tommy_search_func* cmp, const void* cmp_arg, tommy_hash_t hash); + +/** \internal + * Returns the bucket at the specified position. + */ +tommy_inline tommy_hashlin_node** tommy_hashlin_pos(tommy_hashlin* hashlin, tommy_hash_t pos) +{ + tommy_uint_t bsr; + + /* get the highest bit set, in case of all 0, return 0 */ + bsr = tommy_ilog2_u32(pos | 1); + + return &hashlin->bucket[bsr][pos]; +} + +/** \internal + * Returns a pointer to the bucket of the specified hash. + */ +tommy_inline tommy_hashlin_node** tommy_hashlin_bucket_ref(tommy_hashlin* hashlin, tommy_hash_t hash) +{ + tommy_count_t pos; + tommy_count_t high_pos; + + pos = hash & hashlin->low_mask; + high_pos = hash & hashlin->bucket_mask; + + /* if this position is already allocated in the high half */ + if (pos < hashlin->split) { + /* The following assigment is expected to be implemented */ + /* with a conditional move instruction */ + /* that results in a little better and constant performance */ + /* regardless of the split position. */ + /* This affects mostly the worst case, when the split value */ + /* is near at its half, resulting in a totally unpredictable */ + /* condition by the CPU. */ + /* In such case the use of the conditional move is generally faster. */ + + /* use also the high bit */ + pos = high_pos; + } + + return tommy_hashlin_pos(hashlin, pos); +} + +/** + * Gets the bucket of the specified hash. + * The bucket is guaranteed to contain ALL the elements with the specified hash, + * but it can contain also others. + * You can access elements in the bucket following the ::next pointer until 0. + * \param hash Hash of the element to find. + * \return The head of the bucket, or 0 if empty. + */ +tommy_inline tommy_hashlin_node* tommy_hashlin_bucket(tommy_hashlin* hashlin, tommy_hash_t hash) +{ + return *tommy_hashlin_bucket_ref(hashlin, hash); +} + +/** + * Searches an element in the hashtable. + * You have to provide a compare function and the hash of the element you want to find. + * If more equal elements are present, the first one is returned. + * \param cmp Compare function called with cmp_arg as first argument and with the element to compare as a second one. + * The function should return 0 for equal elements, anything other for different elements. + * \param cmp_arg Compare argument passed as first argument of the compare function. + * \param hash Hash of the element to find. + * \return The first element found, or 0 if none. + */ +tommy_inline void* tommy_hashlin_search(tommy_hashlin* hashlin, tommy_search_func* cmp, const void* cmp_arg, tommy_hash_t hash) +{ + tommy_hashlin_node* i = tommy_hashlin_bucket(hashlin, hash); + + while (i) { + /* we first check if the hash matches, as in the same bucket we may have multiples hash values */ + if (i->key == hash && cmp(cmp_arg, i->data) == 0) + return i->data; + i = i->next; + } + return 0; +} + +/** + * Removes an element from the hashtable. + * You must already have the address of the element to remove. + * \return The tommy_node::data field of the node removed. + */ +void* tommy_hashlin_remove_existing(tommy_hashlin* hashlin, tommy_hashlin_node* node); + +/** + * Calls the specified function for each element in the hashtable. + * + * You cannot add or remove elements from the inside of the callback, + * but can use it to deallocate them. + * + * \code + * tommy_hashlin hashlin; + * + * // initializes the hashtable + * tommy_hashlin_init(&hashlin); + * + * ... + * + * // creates an object + * struct object* obj = malloc(sizeof(struct object)); + * + * ... + * + * // insert it in the hashtable + * tommy_hashlin_insert(&hashlin, &obj->node, obj, tommy_inthash_u32(obj->value)); + * + * ... + * + * // deallocates all the objects iterating the hashtable + * tommy_hashlin_foreach(&hashlin, free); + * + * // deallocates the hashtable + * tommy_hashlin_done(&hashlin); + * \endcode + */ +void tommy_hashlin_foreach(tommy_hashlin* hashlin, tommy_foreach_func* func); + +/** + * Calls the specified function with an argument for each element in the hashtable. + */ +void tommy_hashlin_foreach_arg(tommy_hashlin* hashlin, tommy_foreach_arg_func* func, void* arg); + +/** + * Gets the number of elements. + */ +tommy_inline tommy_count_t tommy_hashlin_count(tommy_hashlin* hashlin) +{ + return hashlin->count; +} + +/** + * Gets the size of allocated memory. + * It includes the size of the ::tommy_hashlin_node of the stored elements. + */ +tommy_size_t tommy_hashlin_memory_usage(tommy_hashlin* hashlin); + +#endif + diff -Nru librtr-0.6.3/third-party/tommyds/tommyhashtbl.c librtr-0.7.0/third-party/tommyds/tommyhashtbl.c --- librtr-0.6.3/third-party/tommyds/tommyhashtbl.c 1970-01-01 00:00:00.000000000 +0000 +++ librtr-0.7.0/third-party/tommyds/tommyhashtbl.c 2019-07-18 08:26:08.000000000 +0000 @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. + * + * 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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 "tommyhashtbl.h" +#include "tommylist.h" + +#include <string.h> /* for memset */ + +/******************************************************************************/ +/* hashtable */ + +void tommy_hashtable_init(tommy_hashtable* hashtable, tommy_count_t bucket_max) +{ + if (bucket_max < 16) + bucket_max = 16; + else + bucket_max = tommy_roundup_pow2_u32(bucket_max); + + hashtable->bucket_max = bucket_max; + hashtable->bucket_mask = hashtable->bucket_max - 1; + + /* initialize the vector using malloc()+memset() instead of calloc() */ + /* to ensure that all the memory in really allocated immediately */ + /* by the OS, and not deferred at later time. */ + /* this improves performance, because we start with a fully initialized hashtable. */ + hashtable->bucket = tommy_cast(tommy_hashtable_node**, tommy_malloc(hashtable->bucket_max * sizeof(tommy_hashtable_node*))); + memset(hashtable->bucket, 0, hashtable->bucket_max * sizeof(tommy_hashtable_node*)); + + hashtable->count = 0; +} + +void tommy_hashtable_done(tommy_hashtable* hashtable) +{ + tommy_free(hashtable->bucket); +} + +void tommy_hashtable_insert(tommy_hashtable* hashtable, tommy_hashtable_node* node, void* data, tommy_hash_t hash) +{ + tommy_count_t pos = hash & hashtable->bucket_mask; + + tommy_list_insert_tail(&hashtable->bucket[pos], node, data); + + node->key = hash; + + ++hashtable->count; +} + +void* tommy_hashtable_remove_existing(tommy_hashtable* hashtable, tommy_hashtable_node* node) +{ + tommy_count_t pos = node->key & hashtable->bucket_mask; + + tommy_list_remove_existing(&hashtable->bucket[pos], node); + + --hashtable->count; + + return node->data; +} + +void* tommy_hashtable_remove(tommy_hashtable* hashtable, tommy_search_func* cmp, const void* cmp_arg, tommy_hash_t hash) +{ + tommy_count_t pos = hash & hashtable->bucket_mask; + tommy_hashtable_node* node = hashtable->bucket[pos]; + + while (node) { + /* we first check if the hash matches, as in the same bucket we may have multiples hash values */ + if (node->key == hash && cmp(cmp_arg, node->data) == 0) { + tommy_list_remove_existing(&hashtable->bucket[pos], node); + + --hashtable->count; + + return node->data; + } + node = node->next; + } + + return 0; +} + +void tommy_hashtable_foreach(tommy_hashtable* hashtable, tommy_foreach_func* func) +{ + tommy_count_t bucket_max = hashtable->bucket_max; + tommy_hashtable_node** bucket = hashtable->bucket; + tommy_count_t pos; + + for (pos = 0; pos < bucket_max; ++pos) { + tommy_hashtable_node* node = bucket[pos]; + + while (node) { + void* data = node->data; + node = node->next; + func(data); + } + } +} + +void tommy_hashtable_foreach_arg(tommy_hashtable* hashtable, tommy_foreach_arg_func* func, void* arg) +{ + tommy_count_t bucket_max = hashtable->bucket_max; + tommy_hashtable_node** bucket = hashtable->bucket; + tommy_count_t pos; + + for (pos = 0; pos < bucket_max; ++pos) { + tommy_hashtable_node* node = bucket[pos]; + + while (node) { + void* data = node->data; + node = node->next; + func(arg, data); + } + } +} + +tommy_size_t tommy_hashtable_memory_usage(tommy_hashtable* hashtable) +{ + return hashtable->bucket_max * (tommy_size_t)sizeof(hashtable->bucket[0]) + + tommy_hashtable_count(hashtable) * (tommy_size_t)sizeof(tommy_hashtable_node); +} + diff -Nru librtr-0.6.3/third-party/tommyds/tommyhashtbl.h librtr-0.7.0/third-party/tommyds/tommyhashtbl.h --- librtr-0.6.3/third-party/tommyds/tommyhashtbl.h 1970-01-01 00:00:00.000000000 +0000 +++ librtr-0.7.0/third-party/tommyds/tommyhashtbl.h 2019-07-18 08:26:08.000000000 +0000 @@ -0,0 +1,280 @@ +/* + * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. + * + * 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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. + */ + +/** \file + * Fixed size chained hashtable. + * + * This hashtable is a standard implementation of a chained hashtable with a fixed size. + * + * Note that performances starts to degenerate after reaching a load factor greater than 0.75. + * The ::tommy_hashdyn and ::tommy_hashlin hashtables fix this problem growing dynamically. + * + * To initialize the hashtable you have to call tommy_hashtable_init() specifing + * the fixed bucket size. + * + * \code + * tommy_hashslin hashtable; + * + * tommy_hashtable_init(&hashtable, 1024); + * \endcode + * + * To insert elements in the hashtable you have to call tommy_hashtable_insert() for + * each element. + * In the insertion call you have to specify the address of the node, the + * address of the object, and the hash value of the key to use. + * The address of the object is used to initialize the tommy_node::data field + * of the node, and the hash to initialize the tommy_node::key field. + * + * \code + * struct object { + * int value; + * // other fields + * tommy_node node; + * }; + * + * struct object* obj = malloc(sizeof(struct object)); // creates the object + * + * obj->value = ...; // initializes the object + * + * tommy_hashtable_insert(&hashtable, &obj->node, obj, tommy_inthash_u32(obj->value)); // inserts the object + * \endcode + * + * To find and element in the hashtable you have to call tommy_hashtable_search() + * providing a comparison function, its argument, and the hash of the key to search. + * + * \code + * int compare(const void* arg, const void* obj) + * { + * return *(const int*)arg != ((const struct object*)obj)->value; + * } + * + * int value_to_find = 1; + * struct object* obj = tommy_hashtable_search(&hashtable, compare, &value_to_find, tommy_inthash_u32(value_to_find)); + * if (!obj) { + * // not found + * } else { + * // found + * } + * \endcode + * + * To iterate over all the elements in the hashtable with the same key, you have to + * use tommy_hashtable_bucket() and follow the tommy_node::next pointer until NULL. + * You have also to check explicitely for the key, as the bucket may contains + * different keys. + * + * \code + * tommy_node* i = tommy_hashtable_bucket(&hashtable, tommy_inthash_u32(value_to_find)); + * while (i) { + * struct object* obj = i->data; // gets the object pointer + * + * if (obj->value == value_to_find) { + * printf("%d\n", obj->value); // process the object + * } + * + * i = i->next; // goes to the next element + * } + * \endcode + * + * To remove an element from the hashtable you have to call tommy_hashtable_remove() + * providing a comparison function, its argument, and the hash of the key to search + * and remove. + * + * \code + * struct object* obj = tommy_hashtable_remove(&hashtable, compare, &value_to_remove, tommy_inthash_u32(value_to_remove)); + * if (obj) { + * free(obj); // frees the object allocated memory + * } + * \endcode + * + * To destroy the hashtable you have to remove all the elements, and deinitialize + * the hashtable calling tommy_hashtable_done(). + * + * \code + * tommy_hashtable_done(&hashtable); + * \endcode + * + * If you need to iterate over all the elements in the hashtable, you can use + * tommy_hashtable_foreach() or tommy_hashtable_foreach_arg(). + * If you need a more precise control with a real iteration, you have to insert + * all the elements also in a ::tommy_list, and use the list to iterate. + * See the \ref multiindex example for more detail. + */ + +#ifndef __TOMMYHASHTBL_H +#define __TOMMYHASHTBL_H + +#include "tommyhash.h" + +/******************************************************************************/ +/* hashtable */ + +/** + * Hashtable node. + * This is the node that you have to include inside your objects. + */ +typedef tommy_node tommy_hashtable_node; + +/** + * Hashtable container type. + * \note Don't use internal fields directly, but access the container only using functions. + */ +typedef struct tommy_hashtable_struct { + tommy_hashtable_node** bucket; /**< Hash buckets. One list for each hash modulus. */ + tommy_count_t bucket_max; /**< Number of buckets. */ + tommy_count_t bucket_mask; /**< Bit mask to access the buckets. */ + tommy_count_t count; /**< Number of elements. */ +} tommy_hashtable; + +/** + * Initializes the hashtable. + * \param buckets Minimum number of buckets to allocate. The effective number used is the next power of 2. + */ +void tommy_hashtable_init(tommy_hashtable* hashtable, tommy_count_t bucket_max); + +/** + * Deinitializes the hashtable. + * + * You can call this function with elements still contained, + * but such elements are not going to be freed by this call. + */ +void tommy_hashtable_done(tommy_hashtable* hashtable); + +/** + * Inserts an element in the hashtable. + */ +void tommy_hashtable_insert(tommy_hashtable* hashtable, tommy_hashtable_node* node, void* data, tommy_hash_t hash); + +/** + * Searches and removes an element from the hashtable. + * You have to provide a compare function and the hash of the element you want to remove. + * If the element is not found, 0 is returned. + * If more equal elements are present, the first one is removed. + * \param cmp Compare function called with cmp_arg as first argument and with the element to compare as a second one. + * The function should return 0 for equal elements, anything other for different elements. + * \param cmp_arg Compare argument passed as first argument of the compare function. + * \param hash Hash of the element to find and remove. + * \return The removed element, or 0 if not found. + */ +void* tommy_hashtable_remove(tommy_hashtable* hashtable, tommy_search_func* cmp, const void* cmp_arg, tommy_hash_t hash); + +/** + * Gets the bucket of the specified hash. + * The bucket is guaranteed to contain ALL the elements with the specified hash, + * but it can contain also others. + * You can access elements in the bucket following the ::next pointer until 0. + * \param hash Hash of the element to find. + * \return The head of the bucket, or 0 if empty. + */ +tommy_inline tommy_hashtable_node* tommy_hashtable_bucket(tommy_hashtable* hashtable, tommy_hash_t hash) +{ + return hashtable->bucket[hash & hashtable->bucket_mask]; +} + +/** + * Searches an element in the hashtable. + * You have to provide a compare function and the hash of the element you want to find. + * If more equal elements are present, the first one is returned. + * \param cmp Compare function called with cmp_arg as first argument and with the element to compare as a second one. + * The function should return 0 for equal elements, anything other for different elements. + * \param cmp_arg Compare argument passed as first argument of the compare function. + * \param hash Hash of the element to find. + * \return The first element found, or 0 if none. + */ +tommy_inline void* tommy_hashtable_search(tommy_hashtable* hashtable, tommy_search_func* cmp, const void* cmp_arg, tommy_hash_t hash) +{ + tommy_hashtable_node* i = tommy_hashtable_bucket(hashtable, hash); + + while (i) { + /* we first check if the hash matches, as in the same bucket we may have multiples hash values */ + if (i->key == hash && cmp(cmp_arg, i->data) == 0) + return i->data; + i = i->next; + } + return 0; +} + +/** + * Removes an element from the hashtable. + * You must already have the address of the element to remove. + * \return The tommy_node::data field of the node removed. + */ +void* tommy_hashtable_remove_existing(tommy_hashtable* hashtable, tommy_hashtable_node* node); + +/** + * Calls the specified function for each element in the hashtable. + * + * You cannot add or remove elements from the inside of the callback, + * but can use it to deallocate them. + * + * \code + * tommy_hashtable hashtable; + * + * // initializes the hashtable + * tommy_hashtable_init(&hashtable, ...); + * + * ... + * + * // creates an object + * struct object* obj = malloc(sizeof(struct object)); + * + * ... + * + * // insert it in the hashtable + * tommy_hashdyn_insert(&hashtable, &obj->node, obj, tommy_inthash_u32(obj->value)); + * + * ... + * + * // deallocates all the objects iterating the hashtable + * tommy_hashtable_foreach(&hashtable, free); + * + * // deallocates the hashtable + * tommy_hashdyn_done(&hashtable); + * \endcode + */ +void tommy_hashtable_foreach(tommy_hashtable* hashtable, tommy_foreach_func* func); + +/** + * Calls the specified function with an argument for each element in the hashtable. + */ +void tommy_hashtable_foreach_arg(tommy_hashtable* hashtable, tommy_foreach_arg_func* func, void* arg); + +/** + * Gets the number of elements. + */ +tommy_inline tommy_count_t tommy_hashtable_count(tommy_hashtable* hashtable) +{ + return hashtable->count; +} + +/** + * Gets the size of allocated memory. + * It includes the size of the ::tommy_hashtable_node of the stored elements. + */ +tommy_size_t tommy_hashtable_memory_usage(tommy_hashtable* hashtable); + +#endif + diff -Nru librtr-0.6.3/third-party/tommyds/tommylist.c librtr-0.7.0/third-party/tommyds/tommylist.c --- librtr-0.6.3/third-party/tommyds/tommylist.c 1970-01-01 00:00:00.000000000 +0000 +++ librtr-0.7.0/third-party/tommyds/tommylist.c 2019-07-18 08:26:08.000000000 +0000 @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. + * + * 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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 "tommylist.h" +#include "tommychain.h" + +/** \internal + * Setup a list. + */ +tommy_inline void tommy_list_set(tommy_list* list, tommy_node* head, tommy_node* tail) +{ + head->prev = tail; + tail->next = 0; + *list = head; +} + +void tommy_list_sort(tommy_list* list, tommy_compare_func* cmp) +{ + tommy_chain chain; + tommy_node* head; + + if (tommy_list_empty(list)) + return; + + head = tommy_list_head(list); + + /* create a chain from the list */ + chain.head = head; + chain.tail = head->prev; + + tommy_chain_mergesort(&chain, cmp); + + /* restore the list */ + tommy_list_set(list, chain.head, chain.tail); +} + diff -Nru librtr-0.6.3/third-party/tommyds/tommylist.h librtr-0.7.0/third-party/tommyds/tommylist.h --- librtr-0.6.3/third-party/tommyds/tommylist.h 1970-01-01 00:00:00.000000000 +0000 +++ librtr-0.7.0/third-party/tommyds/tommylist.h 2019-07-18 08:26:08.000000000 +0000 @@ -0,0 +1,381 @@ +/* + * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. + * + * 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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. + */ + +/** \file + * Double linked list for collisions into hashtables. + * + * This list is a double linked list mainly targetted for handling collisions + * into an hashtables, but useable also as a generic list. + * + * The main feature of this list is to require only one pointer to represent the + * list, compared to a classic implementation requiring a head an a tail pointers. + * This reduces the memory usage in hashtables. + * + * Another feature is to support the insertion at the end of the list. This allow to store + * collisions in a stable order. Where for stable order we mean that equal elements keep + * their insertion order. + * + * To initialize the list, you have to call tommy_list_init(), or to simply assign + * to it NULL, as an empty list is represented by the NULL value. + * + * \code + * tommy_list list; + * + * tommy_list_init(&list); // initializes the list + * \endcode + * + * To insert elements in the list you have to call tommy_list_insert_tail() + * or tommy_list_insert_head() for each element. + * In the insertion call you have to specify the address of the node and the + * address of the object. + * The address of the object is used to initialize the tommy_node::data field + * of the node. + * + * \code + * struct object { + * int value; + * // other fields + * tommy_node node; + * }; + * + * struct object* obj = malloc(sizeof(struct object)); // creates the object + * + * obj->value = ...; // initializes the object + * + * tommy_list_insert_tail(&list, &obj->node, obj); // inserts the object + * \endcode + * + * To iterate over all the elements in the list you have to call + * tommy_list_head() to get the head of the list and follow the + * tommy_node::next pointer until NULL. + * + * \code + * tommy_node* i = tommy_list_head(&list); + * while (i) { + * struct object* obj = i->data; // gets the object pointer + * + * printf("%d\n", obj->value); // process the object + * + * i = i->next; // go to the next element + * } + * \endcode + * + * To destroy the list you have to remove all the elements, + * as the list is completely inplace and it doesn't allocate memory. + * This can be done with the tommy_list_foreach() function. + * + * \code + * // deallocates all the objects iterating the list + * tommy_list_foreach(&list, free); + * \endcode + */ + +#ifndef __TOMMYLIST_H +#define __TOMMYLIST_H + +#include "tommytypes.h" + +/******************************************************************************/ +/* list */ + +/** + * Double linked list type. + */ +typedef tommy_node* tommy_list; + +/** + * Initializes the list. + * The list is completely inplace, so it doesn't need to be deinitialized. + */ +tommy_inline void tommy_list_init(tommy_list* list) +{ + *list = 0; +} + +/** + * Gets the head of the list. + * \return The head node. For empty lists 0 is returned. + */ +tommy_inline tommy_node* tommy_list_head(tommy_list* list) +{ + return *list; +} + +/** + * Gets the tail of the list. + * \return The tail node. For empty lists 0 is returned. + */ +tommy_inline tommy_node* tommy_list_tail(tommy_list* list) +{ + tommy_node* head = tommy_list_head(list); + + if (!head) + return 0; + + return head->prev; +} + +/** \internal + * Creates a new list with a single element. + * \param list The list to initialize. + * \param node The node to insert. + */ +tommy_inline void tommy_list_insert_first(tommy_list* list, tommy_node* node) +{ + /* one element "circular" prev list */ + node->prev = node; + + /* one element "0 terminated" next list */ + node->next = 0; + + *list = node; +} + +/** \internal + * Inserts an element at the head of a not empty list. + * The element is inserted at the head of the list. The list cannot be empty. + * \param list The list. The list cannot be empty. + * \param node The node to insert. + */ +tommy_inline void tommy_list_insert_head_not_empty(tommy_list* list, tommy_node* node) +{ + tommy_node* head = tommy_list_head(list); + + /* insert in the "circular" prev list */ + node->prev = head->prev; + head->prev = node; + + /* insert in the "0 terminated" next list */ + node->next = head; + + *list = node; +} + +/** \internal + * Inserts an element at the tail of a not empty list. + * The element is inserted at the tail of the list. The list cannot be empty. + * \param head The node at the list head. It cannot be 0. + * \param node The node to insert. + */ +tommy_inline void tommy_list_insert_tail_not_empty(tommy_node* head, tommy_node* node) +{ + /* insert in the "circular" prev list */ + node->prev = head->prev; + head->prev = node; + + /* insert in the "0 terminated" next list */ + node->next = 0; + node->prev->next = node; +} + +/** + * Inserts an element at the head of a list. + * \param node The node to insert. + * \param data The object containing the node. It's used to set the tommy_node::data field of the node. + */ +tommy_inline void tommy_list_insert_head(tommy_list* list, tommy_node* node, void* data) +{ + tommy_node* head = tommy_list_head(list); + + if (head) + tommy_list_insert_head_not_empty(list, node); + else + tommy_list_insert_first(list, node); + + node->data = data; +} + +/** + * Inserts an element at the tail of a list. + * \param node The node to insert. + * \param data The object containing the node. It's used to set the tommy_node::data field of the node. + */ +tommy_inline void tommy_list_insert_tail(tommy_list* list, tommy_node* node, void* data) +{ + tommy_node* head = tommy_list_head(list); + + if (head) + tommy_list_insert_tail_not_empty(head, node); + else + tommy_list_insert_first(list, node); + + node->data = data; +} + +/** + * Removes an element from the list. + * You must already have the address of the element to remove. + * \note The node content is left unchanged, including the tommy_node::next + * and tommy_node::prev fields that still contain pointers at the list. + * \param node The node to remove. The node must be in the list. + * \return The tommy_node::data field of the node removed. + */ +tommy_inline void* tommy_list_remove_existing(tommy_list* list, tommy_node* node) +{ + tommy_node* head = tommy_list_head(list); + + /* remove from the "circular" prev list */ + if (node->next) + node->next->prev = node->prev; + else + head->prev = node->prev; /* the last */ + + /* remove from the "0 terminated" next list */ + if (head == node) + *list = node->next; /* the new head, in case 0 */ + else + node->prev->next = node->next; + + return node->data; +} + +/** + * Concats two lists. + * The second list is concatenated at the first list. + * \param first The first list. + * \param second The second list. After this call the list content is undefined, + * and you should not use it anymore. + */ +tommy_inline void tommy_list_concat(tommy_list* first, tommy_list* second) +{ + tommy_node* first_head; + tommy_node* first_tail; + tommy_node* second_head; + + /* if the second is empty, nothing to do */ + second_head = tommy_list_head(second); + if (second_head == 0) + return; + + /* if the first is empty, copy the second */ + first_head = tommy_list_head(first); + if (first_head == 0) { + *first = *second; + return; + } + + /* tail of the first list */ + first_tail = first_head->prev; + + /* set the "circular" prev list */ + first_head->prev = second_head->prev; + second_head->prev = first_tail; + + /* set the "0 terminated" next list */ + first_tail->next = second_head; +} + +/** + * Sorts a list. + * It's a stable merge sort with O(N*log(N)) worst complexity. + * It's faster on degenerated cases like partially ordered lists. + * \param cmp Compare function called with two elements. + * The function should return <0 if the first element is less than the second, ==0 if equal, and >0 if greather. + */ +void tommy_list_sort(tommy_list* list, tommy_compare_func* cmp); + +/** + * Checks if empty. + * \return If the list is empty. + */ +tommy_inline tommy_bool_t tommy_list_empty(tommy_list* list) +{ + return tommy_list_head(list) == 0; +} + +/** + * Gets the number of elements. + * \note This operation is O(n). + */ +tommy_inline tommy_count_t tommy_list_count(tommy_list* list) +{ + tommy_count_t count = 0; + tommy_node* i = tommy_list_head(list); + + while (i) { + ++count; + i = i->next; + } + + return count; +} + +/** + * Calls the specified function for each element in the list. + * + * You cannot add or remove elements from the inside of the callback, + * but can use it to deallocate them. + * + * \code + * tommy_list list; + * + * // initializes the list + * tommy_list_init(&list); + * + * ... + * + * // creates an object + * struct object* obj = malloc(sizeof(struct object)); + * + * ... + * + * // insert it in the list + * tommy_list_insert_tail(&list, &obj->node, obj); + * + * ... + * + * // deallocates all the objects iterating the list + * tommy_list_foreach(&list, free); + * \endcode + */ +tommy_inline void tommy_list_foreach(tommy_list* list, tommy_foreach_func* func) +{ + tommy_node* node = tommy_list_head(list); + + while (node) { + void* data = node->data; + node = node->next; + func(data); + } +} + +/** + * Calls the specified function with an argument for each element in the list. + */ +tommy_inline void tommy_list_foreach_arg(tommy_list* list, tommy_foreach_arg_func* func, void* arg) +{ + tommy_node* node = tommy_list_head(list); + + while (node) { + void* data = node->data; + node = node->next; + func(arg, data); + } +} + +#endif + diff -Nru librtr-0.6.3/third-party/tommyds/tommytree.c librtr-0.7.0/third-party/tommyds/tommytree.c --- librtr-0.6.3/third-party/tommyds/tommytree.c 1970-01-01 00:00:00.000000000 +0000 +++ librtr-0.7.0/third-party/tommyds/tommytree.c 2019-07-18 08:26:08.000000000 +0000 @@ -0,0 +1,292 @@ +/* + * Copyright (c) 2015, Andrea Mazzoleni. All rights reserved. + * + * 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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 "tommytree.h" + +#include <assert.h> /* for assert */ + +/******************************************************************************/ +/* tree */ + +void tommy_tree_init(tommy_tree* tree, tommy_compare_func* cmp) +{ + tree->root = 0; + tree->count = 0; + tree->cmp = cmp; +} + +static int tommy_tree_delta(tommy_tree_node* root) +{ + int left_height = root->prev ? root->prev->key : 0; + int right_height = root->next ? root->next->key : 0; + + return left_height - right_height; +} + +/* AVL tree operations */ +static tommy_tree_node* tommy_tree_balance(tommy_tree_node*); + +static tommy_tree_node* tommy_tree_rotate_left(tommy_tree_node* root) +{ + tommy_tree_node* next = root->next; + + root->next = next->prev; + + next->prev = tommy_tree_balance(root); + + return tommy_tree_balance(next); +} + +static tommy_tree_node* tommy_tree_rotate_right(tommy_tree_node* root) +{ + tommy_tree_node* prev = root->prev; + + root->prev = prev->next; + + prev->next = tommy_tree_balance(root); + + return tommy_tree_balance(prev); +} + +static tommy_tree_node* tommy_tree_move_right(tommy_tree_node* root, tommy_tree_node* node) +{ + if (!root) + return node; + + root->next = tommy_tree_move_right(root->next, node); + + return tommy_tree_balance(root); +} + +static tommy_tree_node* tommy_tree_balance(tommy_tree_node* root) +{ + int delta = tommy_tree_delta(root); + + if (delta < -1) { + if (tommy_tree_delta(root->next) > 0) + root->next = tommy_tree_rotate_right(root->next); + return tommy_tree_rotate_left(root); + } + + if (delta > 1) { + if (tommy_tree_delta(root->prev) < 0) + root->prev = tommy_tree_rotate_left(root->prev); + return tommy_tree_rotate_right(root); + } + + /* recompute key */ + root->key = 0; + + if (root->prev && root->prev->key > root->key) + root->key = root->prev->key; + + if (root->next && root->next->key > root->key) + root->key = root->next->key; + + /* count itself */ + root->key += 1; + + return root; +} + +static tommy_tree_node* tommy_tree_insert_node(tommy_compare_func* cmp, tommy_tree_node* root, tommy_tree_node** let) +{ + int c; + + if (!root) + return *let; + + c = cmp((*let)->data, root->data); + + if (c < 0) { + root->prev = tommy_tree_insert_node(cmp, root->prev, let); + return tommy_tree_balance(root); + } + + if (c > 0) { + root->next = tommy_tree_insert_node(cmp, root->next, let); + return tommy_tree_balance(root); + } + + /* already present, set the return pointer */ + *let = root; + + return root; +} + +void* tommy_tree_insert(tommy_tree* tree, tommy_tree_node* node, void* data) +{ + tommy_tree_node* insert = node; + + insert->data = data; + insert->prev = 0; + insert->next = 0; + insert->key = 0; + + tree->root = tommy_tree_insert_node(tree->cmp, tree->root, &insert); + + if (insert == node) + ++tree->count; + + return insert->data; +} + +static tommy_tree_node* tommy_tree_remove_node(tommy_compare_func* cmp, tommy_tree_node* root, void* data, tommy_tree_node** let) +{ + int c; + + if (!root) + return 0; + + c = cmp(data, root->data); + + if (c < 0) { + root->prev = tommy_tree_remove_node(cmp, root->prev, data, let); + return tommy_tree_balance(root); + } + + if (c > 0) { + root->next = tommy_tree_remove_node(cmp, root->next, data, let); + return tommy_tree_balance(root); + } + + /* found */ + *let = root; + + return tommy_tree_move_right(root->prev, root->next); +} + +void* tommy_tree_remove(tommy_tree* tree, void* data) +{ + tommy_tree_node* node = 0; + + tree->root = tommy_tree_remove_node(tree->cmp, tree->root, data, &node); + + if (!node) + return 0; + + --tree->count; + + return node->data; +} + +static tommy_tree_node* tommy_tree_search_node(tommy_compare_func* cmp, tommy_tree_node* root, void* data) +{ + int c; + + if (!root) + return 0; + + c = cmp(data, root->data); + + if (c < 0) + return tommy_tree_search_node(cmp, root->prev, data); + + if (c > 0) + return tommy_tree_search_node(cmp, root->next, data); + + return root; +} + +void* tommy_tree_search(tommy_tree* tree, void* data) +{ + tommy_tree_node* node = tommy_tree_search_node(tree->cmp, tree->root, data); + + if (!node) + return 0; + + return node->data; +} + +void* tommy_tree_search_compare(tommy_tree* tree, tommy_compare_func* cmp, void* cmp_arg) +{ + tommy_tree_node* node = tommy_tree_search_node(cmp, tree->root, cmp_arg); + + if (!node) + return 0; + + return node->data; +} + +void* tommy_tree_remove_existing(tommy_tree* tree, tommy_tree_node* node) +{ + void* data = tommy_tree_remove(tree, node->data); + + assert(data != 0); + + return data; +} + +static void tommy_tree_foreach_node(tommy_tree_node* root, tommy_foreach_func* func) +{ + tommy_tree_node* next; + + if (!root) + return; + + tommy_tree_foreach_node(root->prev, func); + + /* make a copy in case func is free() */ + next = root->next; + + func(root->data); + + tommy_tree_foreach_node(next, func); +} + +void tommy_tree_foreach(tommy_tree* tree, tommy_foreach_func* func) +{ + tommy_tree_foreach_node(tree->root, func); +} + +static void tommy_tree_foreach_arg_node(tommy_tree_node* root, tommy_foreach_arg_func* func, void* arg) +{ + tommy_tree_node* next; + + if (!root) + return; + + tommy_tree_foreach_arg_node(root->prev, func, arg); + + /* make a copy in case func is free() */ + next = root->next; + + func(arg, root->data); + + tommy_tree_foreach_arg_node(next, func, arg); +} + +void tommy_tree_foreach_arg(tommy_tree* tree, tommy_foreach_arg_func* func, void* arg) +{ + tommy_tree_foreach_arg_node(tree->root, func, arg); +} + +tommy_size_t tommy_tree_memory_usage(tommy_tree* tree) +{ + return tommy_tree_count(tree) * sizeof(tommy_tree_node); +} + diff -Nru librtr-0.6.3/third-party/tommyds/tommytree.h librtr-0.7.0/third-party/tommyds/tommytree.h --- librtr-0.6.3/third-party/tommyds/tommytree.h 1970-01-01 00:00:00.000000000 +0000 +++ librtr-0.7.0/third-party/tommyds/tommytree.h 2019-07-18 08:26:08.000000000 +0000 @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2015, Andrea Mazzoleni. All rights reserved. + * + * 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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. + */ + +/** \file + * AVL tree. + * + * This tree is a standard AVL tree implementation that stores elements in the + * order defined by the comparison function. + * + * As difference than other tommy containers, duplicate elements cannot be inserted. + * + * To initialize a tree you have to call tommy_tree_init() specifing a comparison + * function that will define the order in the tree. + * + * \code + * tommy_tree tree; + * + * tommy_tree_init(&tree, cmp); + * \endcode + * + * To insert elements in the tree you have to call tommy_tree_insert() for + * each element. + * In the insertion call you have to specify the address of the node and the + * address of the object. + * The address of the object is used to initialize the tommy_node::data field + * of the node. + * + * \code + * struct object { + * int value; + * // other fields + * tommy_tree_node node; + * }; + * + * struct object* obj = malloc(sizeof(struct object)); // creates the object + * + * obj->value = ...; // initializes the object + * + * tommy_tree_insert(&tree, &obj->node, obj); // inserts the object + * \endcode + * + * To find and element in the tree you have to call tommy_tree_search() providing + * the key to search. + * + * \code + * struct object value_to_find = { 1 }; + * struct object* obj = tommy_tree_search(&tree, &value_to_find); + * if (!obj) { + * // not found + * } else { + * // found + * } + * \endcode + * + * To remove an element from the tree you have to call tommy_tree_remove() + * providing the key to search and remove. + * + * \code + * struct object value_to_remove = { 1 }; + * struct object* obj = tommy_tree_remove(&tree, &value_to_remove); + * if (obj) { + * free(obj); // frees the object allocated memory + * } + * \endcode + * + * To destroy the tree you have to remove or destroy all the contained elements. + * The tree itself doesn't have or need a deallocation function. + * + * If you need to iterate over all the elements in the tree, you can use + * tommy_tree_foreach() or tommy_tree_foreach_arg(). + * If you need a more precise control with a real iteration, you have to insert + * all the elements also in a ::tommy_list, and use the list to iterate. + * See the \ref multiindex example for more detail. + */ + +#ifndef __TOMMYTREE_H +#define __TOMMYTREE_H + +#include "tommytypes.h" + +/******************************************************************************/ +/* tree */ + +/** + * Tree node. + * This is the node that you have to include inside your objects. + */ +typedef tommy_node tommy_tree_node; + +/** + * Tree container type. + * \note Don't use internal fields directly, but access the container only using functions. + */ +typedef struct tommy_tree_struct { + tommy_tree_node* root; /**< Root node. */ + tommy_count_t count; /**< Number of elements. */ + tommy_compare_func* cmp; /**< Comparison function. */ +} tommy_tree; + +/** + * Initializes the tree. + * \param cmp The comparison function that defines the orderin the tree. + */ +void tommy_tree_init(tommy_tree* tree, tommy_compare_func* cmp); + +/** + * Inserts an element in the tree. + * If the element is already present, it's not inserted again. + * Check the return value to identify if the element was already present or not. + * You have to provide the pointer of the node embedded into the object and + * the pointer to the object. + * \param node Pointer to the node embedded into the object to insert. + * \param data Pointer to the object to insert. + * \return The element in the tree. Either the already existing one, or the one just inserted. + */ +void* tommy_tree_insert(tommy_tree* tree, tommy_tree_node* node, void* data); + +/** + * Searches and removes an element. + * If the element is not found, 0 is returned. + * \param data Element used for comparison. + * \return The removed element, or 0 if not found. + */ +void* tommy_tree_remove(tommy_tree* tree, void* data); + +/** + * Searches an element in the tree. + * If the element is not found, 0 is returned. + * \param data Element used for comparison. + * \return The first element found, or 0 if none. + */ +void* tommy_tree_search(tommy_tree* tree, void* data); + +/** + * Searches an element in the tree with a specific comparison function. + * + * Like tommy_tree_search() but you can specify a different comparison function. + * Note that this function must define a suborder of the original one. + * + * The ::data argument will be the first argument of the comparison function, + * and it can be of a different type of the objects in the tree. + */ +void* tommy_tree_search_compare(tommy_tree* tree, tommy_compare_func* cmp, void* cmp_arg); + +/** + * Removes an element from the tree. + * You must already have the address of the element to remove. + * \return The tommy_node::data field of the node removed. + */ +void* tommy_tree_remove_existing(tommy_tree* tree, tommy_tree_node* node); + +/** + * Calls the specified function for each element in the tree. + * + * The elements are processed in order. + * + * You cannot add or remove elements from the inside of the callback, + * but can use it to deallocate them. + * + * \code + * tommy_tree tree; + * + * // initializes the tree + * tommy_tree_init(&tree, cmp); + * + * ... + * + * // creates an object + * struct object* obj = malloc(sizeof(struct object)); + * + * ... + * + * // insert it in the tree + * tommy_tree_insert(&tree, &obj->node, obj); + * + * ... + * + * // deallocates all the objects iterating the tree + * tommy_tree_foreach(&tree, free); + * \endcode + */ +void tommy_tree_foreach(tommy_tree* tree, tommy_foreach_func* func); + +/** + * Calls the specified function with an argument for each element in the tree. + */ +void tommy_tree_foreach_arg(tommy_tree* tree, tommy_foreach_arg_func* func, void* arg); + +/** + * Gets the number of elements. + */ +tommy_inline tommy_count_t tommy_tree_count(tommy_tree* tree) +{ + return tree->count; +} + +/** + * Gets the size of allocated memory. + * It includes the size of the ::tommy_tree_node of the stored elements. + */ +tommy_size_t tommy_tree_memory_usage(tommy_tree* tree); + +#endif + diff -Nru librtr-0.6.3/third-party/tommyds/tommytrie.c librtr-0.7.0/third-party/tommyds/tommytrie.c --- librtr-0.6.3/third-party/tommyds/tommytrie.c 1970-01-01 00:00:00.000000000 +0000 +++ librtr-0.7.0/third-party/tommyds/tommytrie.c 2019-07-18 08:26:08.000000000 +0000 @@ -0,0 +1,323 @@ +/* + * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. + * + * 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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 "tommytrie.h" +#include "tommylist.h" + +#include <assert.h> /* for assert */ + +/******************************************************************************/ +/* trie */ + +/** + * Mask for the inner branches. + */ +#define TOMMY_TRIE_TREE_MASK (TOMMY_TRIE_TREE_MAX - 1) + +/** + * Shift for the first level of branches. + */ +#define TOMMY_TRIE_BUCKET_SHIFT (TOMMY_KEY_BIT - TOMMY_TRIE_BUCKET_BIT) + +/** + * Max number of levels. + */ +#define TOMMY_TRIE_LEVEL_MAX ((TOMMY_KEY_BIT - TOMMY_TRIE_BUCKET_BIT) / TOMMY_TRIE_TREE_BIT) + +/** + * Hashtrie tree. + * A tree contains TOMMY_TRIE_TREE_MAX ordered pointers to <null/node/tree>. + * + * Each tree level uses exactly TOMMY_TRIE_TREE_BIT bits from the key. + */ +struct tommy_trie_tree_struct { + tommy_trie_node* map[TOMMY_TRIE_TREE_MAX]; +}; +typedef struct tommy_trie_tree_struct tommy_trie_tree; + +/** + * Kinds of an trie node. + */ +#define TOMMY_TRIE_TYPE_NODE 0 /**< The node is of type ::tommy_trie_node. */ +#define TOMMY_TRIE_TYPE_TREE 1 /**< The node is of type ::tommy_trie_tree. */ + +/** + * Get and set pointer of trie nodes. + * + * The pointer type is stored in the lower bit. + */ +#define trie_get_type(ptr) (((tommy_uintptr_t)(ptr)) & 1) +#define trie_get_tree(ptr) ((tommy_trie_tree*)(((tommy_uintptr_t)(ptr)) - TOMMY_TRIE_TYPE_TREE)) +#define trie_set_tree(ptr) (void*)(((tommy_uintptr_t)(ptr)) + TOMMY_TRIE_TYPE_TREE) + +void tommy_trie_init(tommy_trie* trie, tommy_allocator* alloc) +{ + tommy_uint_t i; + + for (i = 0; i < TOMMY_TRIE_BUCKET_MAX; ++i) + trie->bucket[i] = 0; + + trie->count = 0; + trie->node_count = 0; + + trie->alloc = alloc; +} + +static void trie_bucket_insert(tommy_trie* trie, tommy_uint_t shift, tommy_trie_node** let_ptr, tommy_trie_node* insert, tommy_key_t key) +{ + tommy_trie_tree* tree; + tommy_trie_node* node; + void* ptr; + tommy_uint_t i; + tommy_uint_t j; + +recurse: + ptr = *let_ptr; + + /* if null, just insert the node */ + if (!ptr) { + /* setup the node as a list */ + tommy_list_insert_first(let_ptr, insert); + return; + } + + if (trie_get_type(ptr) == TOMMY_TRIE_TYPE_TREE) { + /* repeat the process one level down */ + let_ptr = &trie_get_tree(ptr)->map[(key >> shift) & TOMMY_TRIE_TREE_MASK]; + shift -= TOMMY_TRIE_TREE_BIT; + goto recurse; + } + + node = tommy_cast(tommy_trie_node*, ptr); + + /* if it's the same key, insert in the list */ + if (node->key == key) { + tommy_list_insert_tail_not_empty(node, insert); + return; + } + +expand: + /* convert to a tree */ + tree = tommy_cast(tommy_trie_tree*, tommy_allocator_alloc(trie->alloc)); + ++trie->node_count; + *let_ptr = tommy_cast(tommy_trie_node*, trie_set_tree(tree)); + + /* initialize it */ + for (i = 0; i < TOMMY_TRIE_TREE_MAX; ++i) + tree->map[i] = 0; + + /* get the position of the two elements */ + i = (node->key >> shift) & TOMMY_TRIE_TREE_MASK; + j = (key >> shift) & TOMMY_TRIE_TREE_MASK; + + /* if they don't collide */ + if (i != j) { + /* insert the already existing element */ + tree->map[i] = node; + + /* insert the new node */ + tommy_list_insert_first(&tree->map[j], insert); + return; + } + + /* expand one more level */ + let_ptr = &tree->map[i]; + shift -= TOMMY_TRIE_TREE_BIT; + goto expand; +} + +void tommy_trie_insert(tommy_trie* trie, tommy_trie_node* node, void* data, tommy_key_t key) +{ + tommy_trie_node** let_ptr; + + node->data = data; + node->key = key; + + let_ptr = &trie->bucket[key >> TOMMY_TRIE_BUCKET_SHIFT]; + + trie_bucket_insert(trie, TOMMY_TRIE_BUCKET_SHIFT, let_ptr, node, key); + + ++trie->count; +} + +static tommy_trie_node* trie_bucket_remove_existing(tommy_trie* trie, tommy_uint_t shift, tommy_trie_node** let_ptr, tommy_trie_node* remove, tommy_key_t key) +{ + tommy_trie_node* node; + tommy_trie_tree* tree; + void* ptr; + tommy_trie_node** let_back[TOMMY_TRIE_LEVEL_MAX + 1]; + tommy_uint_t level; + tommy_uint_t i; + tommy_uint_t count; + tommy_uint_t last; + + level = 0; +recurse: + ptr = *let_ptr; + + if (!ptr) + return 0; + + if (trie_get_type(ptr) == TOMMY_TRIE_TYPE_TREE) { + tree = trie_get_tree(ptr); + + /* save the path */ + let_back[level++] = let_ptr; + + /* go down one level */ + let_ptr = &tree->map[(key >> shift) & TOMMY_TRIE_TREE_MASK]; + shift -= TOMMY_TRIE_TREE_BIT; + + goto recurse; + } + + node = tommy_cast(tommy_trie_node*, ptr); + + /* if the node to remove is not specified */ + if (!remove) { + /* remove the first */ + remove = node; + + /* check if it's really the element to remove */ + if (remove->key != key) + return 0; + } + + tommy_list_remove_existing(let_ptr, remove); + + /* if the list is not empty, try to reduce */ + if (*let_ptr || !level) + return remove; + +reduce: + /* go one level up */ + let_ptr = let_back[--level]; + + tree = trie_get_tree(*let_ptr); + + /* check if there is only one child node */ + count = 0; + last = 0; + for (i = 0; i < TOMMY_TRIE_TREE_MAX; ++i) { + if (tree->map[i]) { + /* if we have a sub tree, we cannot reduce */ + if (trie_get_type(tree->map[i]) != TOMMY_TRIE_TYPE_NODE) + return remove; + /* if more than one node, we cannot reduce */ + if (++count > 1) + return remove; + last = i; + } + } + + /* here count is never 0, as we cannot have a tree with only one sub node */ + assert(count == 1); + + *let_ptr = tree->map[last]; + + tommy_allocator_free(trie->alloc, tree); + --trie->node_count; + + /* repeat until more level */ + if (level) + goto reduce; + + return remove; +} + +void* tommy_trie_remove(tommy_trie* trie, tommy_key_t key) +{ + tommy_trie_node* ret; + tommy_trie_node** let_ptr; + + let_ptr = &trie->bucket[key >> TOMMY_TRIE_BUCKET_SHIFT]; + + ret = trie_bucket_remove_existing(trie, TOMMY_TRIE_BUCKET_SHIFT, let_ptr, 0, key); + + if (!ret) + return 0; + + --trie->count; + + return ret->data; +} + +void* tommy_trie_remove_existing(tommy_trie* trie, tommy_trie_node* node) +{ + tommy_trie_node* ret; + tommy_key_t key = node->key; + tommy_trie_node** let_ptr; + + let_ptr = &trie->bucket[key >> TOMMY_TRIE_BUCKET_SHIFT]; + + ret = trie_bucket_remove_existing(trie, TOMMY_TRIE_BUCKET_SHIFT, let_ptr, node, key); + + /* the element removed must match the one passed */ + assert(ret == node); + + --trie->count; + + return ret->data; +} + +tommy_trie_node* tommy_trie_bucket(tommy_trie* trie, tommy_key_t key) +{ + tommy_trie_node* node; + void* ptr; + tommy_uint_t type; + tommy_uint_t shift; + + ptr = trie->bucket[key >> TOMMY_TRIE_BUCKET_SHIFT]; + + shift = TOMMY_TRIE_BUCKET_SHIFT; + +recurse: + if (!ptr) + return 0; + + type = trie_get_type(ptr); + + switch (type) { + case TOMMY_TRIE_TYPE_NODE : + node = tommy_cast(tommy_trie_node*, ptr); + if (node->key != key) + return 0; + return node; + default : + case TOMMY_TRIE_TYPE_TREE : + ptr = trie_get_tree(ptr)->map[(key >> shift) & TOMMY_TRIE_TREE_MASK]; + shift -= TOMMY_TRIE_TREE_BIT; + goto recurse; + } +} + +tommy_size_t tommy_trie_memory_usage(tommy_trie* trie) +{ + return tommy_trie_count(trie) * (tommy_size_t)sizeof(tommy_trie_node) + + trie->node_count * (tommy_size_t)TOMMY_TRIE_BLOCK_SIZE; +} + diff -Nru librtr-0.6.3/third-party/tommyds/tommytrie.h librtr-0.7.0/third-party/tommyds/tommytrie.h --- librtr-0.6.3/third-party/tommyds/tommytrie.h 1970-01-01 00:00:00.000000000 +0000 +++ librtr-0.7.0/third-party/tommyds/tommytrie.h 2019-07-18 08:26:08.000000000 +0000 @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. + * + * 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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. + */ + +/** \file + * Trie optimized for cache utilization. + * + * This trie is a standard implementation that stores elements in the order defined + * by the key. + * + * It needs an external allocator for the inner nodes in the trie. + * + * You can control the number of branches of each node using the ::TOMMY_TRIE_TREE_MAX + * define. More branches imply more speed, but a bigger memory occupation. + * + * Compared to ::tommy_trie_inplace you have to provide a ::tommy_allocator allocator. + * Note that the C malloc() is too slow to futfill this role. + * + * To initialize the trie you have to call tommy_allocator_init() to initialize + * the allocator, and tommy_trie_init() for the trie. + * + * \code + * tommy_allocator alloc; + * tommy_trie trie; + * + * tommy_allocator_init(&alloc, TOMMY_TRIE_BLOCK_SIZE, TOMMY_TRIE_BLOCK_SIZE); + * + * tommy_trie_init(&trie, &alloc); + * \endcode + * + * To insert elements in the trie you have to call tommy_trie_insert() for + * each element. + * In the insertion call you have to specify the address of the node, the + * address of the object, and the key value to use. + * The address of the object is used to initialize the tommy_node::data field + * of the node, and the key to initialize the tommy_node::key field. + * + * \code + * struct object { + * int value; + * // other fields + * tommy_node node; + * }; + * + * struct object* obj = malloc(sizeof(struct object)); // creates the object + * + * obj->value = ...; // initializes the object + * + * tommy_trie_insert(&trie, &obj->node, obj, obj->value); // inserts the object + * \endcode + * + * To find and element in the trie you have to call tommy_trie_search() providing + * the key to search. + * + * \code + * int value_to_find = 1; + * struct object* obj = tommy_trie_search(&trie, value_to_find); + * if (!obj) { + * // not found + * } else { + * // found + * } + * \endcode + * + * To iterate over all the elements in the trie with the same key, you have to + * use tommy_trie_bucket() and follow the tommy_node::next pointer until NULL. + * + * \code + * int value_to_find = 1; + * tommy_node* i = tommy_trie_bucket(&trie, value_to_find); + * while (i) { + * struct object* obj = i->data; // gets the object pointer + * + * printf("%d\n", obj->value); // process the object + * + * i = i->next; // goes to the next element + * } + * \endcode + * + * To remove an element from the trie you have to call tommy_trie_remove() + * providing the key to search and remove. + * + * \code + * struct object* obj = tommy_trie_remove(&trie, value_to_remove); + * if (obj) { + * free(obj); // frees the object allocated memory + * } + * \endcode + * + * To destroy the trie you have to remove all the elements, and deinitialize + * the allocator using tommy_allocator_done(). + * + * \code + * tommy_allocator_done(&alloc); + * \endcode + * + * Note that you cannot iterate over all the elements in the trie using the + * trie itself. You have to insert all the elements also in a ::tommy_list, + * and use the list to iterate. See the \ref multiindex example for more detail. + */ + +#ifndef __TOMMYTRIE_H +#define __TOMMYTRIE_H + +#include "tommytypes.h" +#include "tommyalloc.h" + +/******************************************************************************/ +/* trie */ + +/** + * Number of branches on each inner node. It must be a power of 2. + * Suggested values are 8, 16 and 32. + * Any inner node, excluding leafs, contains a pointer to each branch. + * + * The default size is choosen to exactly fit a typical cache line of 64 bytes. + */ +#define TOMMY_TRIE_TREE_MAX (64 / sizeof(void*)) + +/** + * Trie block size. + * You must use this value to initialize the allocator. + */ +#define TOMMY_TRIE_BLOCK_SIZE (TOMMY_TRIE_TREE_MAX * sizeof(void*)) + +/** \internal + * Number of bits for each branch. + */ +#define TOMMY_TRIE_TREE_BIT TOMMY_ILOG2(TOMMY_TRIE_TREE_MAX) + +/** \internal + * Number of bits of the first level. + */ +#define TOMMY_TRIE_BUCKET_BIT ((TOMMY_KEY_BIT % TOMMY_TRIE_TREE_BIT) + TOMMY_TRIE_TREE_BIT) + +/** \internal + * Number of branches of the first level. + * It's like a inner branch, but bigger to get any remainder bits. + */ +#define TOMMY_TRIE_BUCKET_MAX (1 << TOMMY_TRIE_BUCKET_BIT) + +/** + * Trie node. + * This is the node that you have to include inside your objects. + */ +typedef tommy_node tommy_trie_node; + +/** + * Trie container type. + * \note Don't use internal fields directly, but access the container only using functions. + */ +typedef struct tommy_trie_struct { + tommy_trie_node* bucket[TOMMY_TRIE_BUCKET_MAX]; /**< First tree level. */ + tommy_count_t count; /**< Number of elements. */ + tommy_count_t node_count; /**< Number of nodes. */ + tommy_allocator* alloc; /**< Allocator for internal nodes. */ +} tommy_trie; + +/** + * Initializes the trie. + * You have to provide an allocator initialized with *both* the size and align with TOMMY_TRIE_BLOCK_SIZE. + * You can share this allocator with other tries. + * + * The tries is completely allocated through the allocator, and it doesn't need to be deinitialized. + * \param alloc Allocator initialized with *both* the size and align with TOMMY_TRIE_BLOCK_SIZE. + */ +void tommy_trie_init(tommy_trie* trie, tommy_allocator* alloc); + +/** + * Inserts an element in the trie. + * You have to provide the pointer of the node embedded into the object, + * the pointer to the object and the key to use. + * \param node Pointer to the node embedded into the object to insert. + * \param data Pointer to the object to insert. + * \param key Key to use to insert the object. + */ +void tommy_trie_insert(tommy_trie* trie, tommy_trie_node* node, void* data, tommy_key_t key); + +/** + * Searches and removes the first element with the specified key. + * If the element is not found, 0 is returned. + * If more equal elements are present, the first one is removed. + * This operation is faster than calling tommy_trie_bucket() and tommy_trie_remove_existing() separately. + * \param key Key of the element to find and remove. + * \return The removed element, or 0 if not found. + */ +void* tommy_trie_remove(tommy_trie* trie, tommy_key_t key); + +/** + * Gets the bucket of the specified key. + * The bucket is guaranteed to contain ALL and ONLY the elements with the specified key. + * You can access elements in the bucket following the ::next pointer until 0. + * \param key Key of the element to find. + * \return The head of the bucket, or 0 if empty. + */ +tommy_trie_node* tommy_trie_bucket(tommy_trie* trie, tommy_key_t key); + +/** + * Searches an element in the trie. + * You have to provide the key of the element you want to find. + * If more elements with the same key are present, the first one is returned. + * \param key Key of the element to find. + * \return The first element found, or 0 if none. + */ +tommy_inline void* tommy_trie_search(tommy_trie* trie, tommy_key_t key) +{ + tommy_trie_node* i = tommy_trie_bucket(trie, key); + + if (!i) + return 0; + + return i->data; +} + +/** + * Removes an element from the trie. + * You must already have the address of the element to remove. + * \return The tommy_node::data field of the node removed. + */ +void* tommy_trie_remove_existing(tommy_trie* trie, tommy_trie_node* node); + +/** + * Gets the number of elements. + */ +tommy_inline tommy_count_t tommy_trie_count(tommy_trie* trie) +{ + return trie->count; +} + +/** + * Gets the size of allocated memory. + * It includes the size of the ::tommy_trie_node of the stored elements. + */ +tommy_size_t tommy_trie_memory_usage(tommy_trie* trie); + +#endif + diff -Nru librtr-0.6.3/third-party/tommyds/tommytrieinp.c librtr-0.7.0/third-party/tommyds/tommytrieinp.c --- librtr-0.6.3/third-party/tommyds/tommytrieinp.c 1970-01-01 00:00:00.000000000 +0000 +++ librtr-0.7.0/third-party/tommyds/tommytrieinp.c 2019-07-18 08:26:08.000000000 +0000 @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. + * + * 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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 "tommytrieinp.h" + +#include <assert.h> /* for assert */ + +/******************************************************************************/ +/* trie_inplace */ + +/** + * Mask for the inner branches. + */ +#define TOMMY_TRIE_INPLACE_TREE_MASK (TOMMY_TRIE_INPLACE_TREE_MAX - 1) + +/** + * Shift for the first level of branches. + */ +#define TOMMY_TRIE_INPLACE_BUCKET_SHIFT (TOMMY_KEY_BIT - TOMMY_TRIE_INPLACE_BUCKET_BIT) + +/** + * Create a new list with a single element. + */ +tommy_inline tommy_trie_inplace_node* tommy_trie_inplace_list_insert_first(tommy_trie_inplace_node* node) +{ + /* one element "circular" prev list */ + node->prev = node; + + /* one element "0 terminated" next list */ + node->next = 0; + + return node; +} + +/** + * Add an element to an existing list. + * \note The element is inserted at the end of the list. + */ +tommy_inline void tommy_trie_inplace_list_insert_tail_not_empty(tommy_trie_inplace_node* head, tommy_trie_inplace_node* node) +{ + /* insert in the list in the last position */ + + /* insert in the "circular" prev list */ + node->prev = head->prev; + head->prev = node; + + /* insert in the "0 terminated" next list */ + node->next = 0; + node->prev->next = node; +} + +/** + * Remove an element from the list. + */ +tommy_inline void tommy_trie_inplace_list_remove(tommy_trie_inplace_node** let_ptr, tommy_trie_inplace_node* node) +{ + tommy_trie_inplace_node* head = *let_ptr; + + /* remove from the "circular" prev list */ + if (node->next) + node->next->prev = node->prev; + else + head->prev = node->prev; /* the last */ + + /* remove from the "0 terminated" next list */ + if (head == node) + *let_ptr = node->next; /* the new first */ + else + node->prev->next = node->next; +} + +void tommy_trie_inplace_init(tommy_trie_inplace* trie_inplace) +{ + tommy_uint_t i; + + for (i = 0; i < TOMMY_TRIE_INPLACE_BUCKET_MAX; ++i) + trie_inplace->bucket[i] = 0; + + trie_inplace->count = 0; +} + +static void trie_inplace_bucket_insert(tommy_uint_t shift, tommy_trie_inplace_node** let_ptr, tommy_trie_inplace_node* insert, tommy_key_t key) +{ + tommy_trie_inplace_node* node; + + node = *let_ptr; + while (node && node->key != key) { + let_ptr = &node->map[(key >> shift) & TOMMY_TRIE_INPLACE_TREE_MASK]; + node = *let_ptr; + shift -= TOMMY_TRIE_INPLACE_TREE_BIT; + } + + /* if null, just insert the node */ + if (!node) { + /* setup the node as a list */ + *let_ptr = tommy_trie_inplace_list_insert_first(insert); + } else { + /* if it's the same key, insert in the list */ + tommy_trie_inplace_list_insert_tail_not_empty(node, insert); + } +} + +void tommy_trie_inplace_insert(tommy_trie_inplace* trie_inplace, tommy_trie_inplace_node* node, void* data, tommy_key_t key) +{ + tommy_trie_inplace_node** let_ptr; + tommy_uint_t i; + + node->data = data; + node->key = key; + /* clear the child pointers */ + for (i = 0; i < TOMMY_TRIE_INPLACE_TREE_MAX; ++i) + node->map[i] = 0; + + let_ptr = &trie_inplace->bucket[key >> TOMMY_TRIE_INPLACE_BUCKET_SHIFT]; + + trie_inplace_bucket_insert(TOMMY_TRIE_INPLACE_BUCKET_SHIFT, let_ptr, node, key); + + ++trie_inplace->count; +} + +static tommy_trie_inplace_node* trie_inplace_bucket_remove(tommy_uint_t shift, tommy_trie_inplace_node** let_ptr, tommy_trie_inplace_node* remove, tommy_key_t key) +{ + tommy_trie_inplace_node* node; + int i; + tommy_trie_inplace_node** leaf_let_ptr; + tommy_trie_inplace_node* leaf; + + node = *let_ptr; + while (node && node->key != key) { + let_ptr = &node->map[(key >> shift) & TOMMY_TRIE_INPLACE_TREE_MASK]; + node = *let_ptr; + shift -= TOMMY_TRIE_INPLACE_TREE_BIT; + } + + if (!node) + return 0; + + /* if the node to remove is not specified */ + if (!remove) + remove = node; /* remove the first */ + + tommy_trie_inplace_list_remove(let_ptr, remove); + + /* if not change in the node, nothing more to do */ + if (*let_ptr == node) + return remove; + + /* if we have a substitute */ + if (*let_ptr != 0) { + /* copy the child pointers to the new one */ + node = *let_ptr; + for (i = 0; i < TOMMY_TRIE_INPLACE_TREE_MAX; ++i) + node->map[i] = remove->map[i]; + + return remove; + } + + /* find a leaf */ + leaf_let_ptr = 0; + leaf = remove; + + /* search backward, statistically we have more zeros than ones */ + i = TOMMY_TRIE_INPLACE_TREE_MAX - 1; + while (i >= 0) { + if (leaf->map[i]) { + leaf_let_ptr = &leaf->map[i]; + leaf = *leaf_let_ptr; + i = TOMMY_TRIE_INPLACE_TREE_MAX - 1; + continue; + } + --i; + } + + /* if it's itself a leaf */ + if (!leaf_let_ptr) + return remove; + + /* remove the leaf */ + *leaf_let_ptr = 0; + + /* copy the child pointers */ + for (i = 0; i < TOMMY_TRIE_INPLACE_TREE_MAX; ++i) + leaf->map[i] = remove->map[i]; + + /* put it in place */ + *let_ptr = leaf; + + return remove; +} + +void* tommy_trie_inplace_remove(tommy_trie_inplace* trie_inplace, tommy_key_t key) +{ + tommy_trie_inplace_node* ret; + tommy_trie_inplace_node** let_ptr; + + let_ptr = &trie_inplace->bucket[key >> TOMMY_TRIE_INPLACE_BUCKET_SHIFT]; + + ret = trie_inplace_bucket_remove(TOMMY_TRIE_INPLACE_BUCKET_SHIFT, let_ptr, 0, key); + + if (!ret) + return 0; + + --trie_inplace->count; + + return ret->data; +} + +void* tommy_trie_inplace_remove_existing(tommy_trie_inplace* trie_inplace, tommy_trie_inplace_node* node) +{ + tommy_trie_inplace_node* ret; + tommy_key_t key = node->key; + tommy_trie_inplace_node** let_ptr; + + let_ptr = &trie_inplace->bucket[key >> TOMMY_TRIE_INPLACE_BUCKET_SHIFT]; + + ret = trie_inplace_bucket_remove(TOMMY_TRIE_INPLACE_BUCKET_SHIFT, let_ptr, node, key); + + /* the element removed must match the one passed */ + assert(ret == node); + + --trie_inplace->count; + + return ret->data; +} + +tommy_trie_inplace_node* tommy_trie_inplace_bucket(tommy_trie_inplace* trie_inplace, tommy_key_t key) +{ + tommy_trie_inplace_node* node; + tommy_uint_t shift; + + node = trie_inplace->bucket[key >> TOMMY_TRIE_INPLACE_BUCKET_SHIFT]; + shift = TOMMY_TRIE_INPLACE_BUCKET_SHIFT; + + while (node && node->key != key) { + node = node->map[(key >> shift) & TOMMY_TRIE_INPLACE_TREE_MASK]; + shift -= TOMMY_TRIE_INPLACE_TREE_BIT; + } + + return node; +} + +tommy_size_t tommy_trie_inplace_memory_usage(tommy_trie_inplace* trie_inplace) +{ + return tommy_trie_inplace_count(trie_inplace) * (tommy_size_t)sizeof(tommy_trie_inplace_node); +} + diff -Nru librtr-0.6.3/third-party/tommyds/tommytrieinp.h librtr-0.7.0/third-party/tommyds/tommytrieinp.h --- librtr-0.6.3/third-party/tommyds/tommytrieinp.h 1970-01-01 00:00:00.000000000 +0000 +++ librtr-0.7.0/third-party/tommyds/tommytrieinp.h 2019-07-18 08:26:08.000000000 +0000 @@ -0,0 +1,239 @@ +/* + * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. + * + * 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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. + */ + +/** \file + * Inplace trie. + * + * This trie is a inplace implementation not needing any external allocation. + * + * Elements are not stored in order, like ::tommy_trie, because some elements + * should be used to represent the inner nodes in the trie. + * + * You can control the number of branches of each node using the ::TOMMY_TRIE_INPLACE_TREE_MAX define. + * More branches imply more speed, but a bigger memory occupation. + * + * Compared to ::tommy_trie you should use a lower number of branches to limit the unused memory + * occupation of the leaf nodes. This imply a lower speed, but without the need of an external allocator. + * + * To initialize the trie you have to call tommy_trie_inplace_init(). + * + * \code + * tommy_trie_inplace trie_inplace; + * + * tommy_trie_inplace_init(&trie_inplace); + * \endcode + * + * To insert elements in the trie you have to call tommy_trie_inplace_insert() for + * each element. + * In the insertion call you have to specify the address of the node, the + * address of the object, and the key value to use. + * The address of the object is used to initialize the tommy_node::data field + * of the node, and the key to initialize the tommy_node::key field. + * + * \code + * struct object { + * int value; + * // other fields + * tommy_node node; + * }; + * + * struct object* obj = malloc(sizeof(struct object)); // creates the object + * + * obj->value = ...; // initializes the object + * + * tommy_trie_inplace_insert(&trie_inplace, &obj->node, obj, obj->value); // inserts the object + * \endcode + * + * To find and element in the trie you have to call tommy_trie_inplace_search() providing + * the key to search. + * + * \code + * int value_to_find = 1; + * struct object* obj = tommy_trie_inplace_search(&trie_inplace, value_to_find); + * if (!obj) { + * // not found + * } else { + * // found + * } + * \endcode + * + * To iterate over all the elements in the trie with the same key, you have to + * use tommy_trie_inplace_bucket() and follow the tommy_node::next pointer until NULL. + * + * \code + * int value_to_find = 1; + * tommy_node* i = tommy_trie_inplace_bucket(&trie_inplace, value_to_find); + * while (i) { + * struct object* obj = i->data; // gets the object pointer + * + * printf("%d\n", obj->value); // process the object + * + * i = i->next; // goes to the next element + * } + * \endcode + * + * To remove an element from the trie you have to call tommy_trie_inplace_remove() + * providing the key to search and remove. + * + * \code + * struct object* obj = tommy_trie_inplace_remove(&trie_inplace, value_to_remove); + * if (obj) { + * free(obj); // frees the object allocated memory + * } + * \endcode + * + * To destroy the trie you have only to remove all the elements, as the trie is + * completely inplace and it doesn't allocate memory. + * + * Note that you cannot iterate over all the elements in the trie using the + * trie itself. You have to insert all the elements also in a ::tommy_list, + * and use the list to iterate. See the \ref multiindex example for more detail. + */ + +#ifndef __TOMMYTRIEINP_H +#define __TOMMYTRIEINP_H + +#include "tommytypes.h" + +/******************************************************************************/ +/* trie_inplace */ + +/** + * Number of branches on each node. It must be a power of 2. + * Suggested values are 2, 4 and 8. + * Any node, including leafs, contains a pointer to each branch. + */ +#define TOMMY_TRIE_INPLACE_TREE_MAX 4 + +/** \internal + * Number of bits for each branch. + */ +#define TOMMY_TRIE_INPLACE_TREE_BIT TOMMY_ILOG2(TOMMY_TRIE_INPLACE_TREE_MAX) + +/** \internal + * Number of bits of the first level. + */ +#define TOMMY_TRIE_INPLACE_BUCKET_BIT ((TOMMY_KEY_BIT % TOMMY_TRIE_INPLACE_TREE_BIT) + 3 * TOMMY_TRIE_INPLACE_TREE_BIT) + +/** \internal + * Number of branches of the first level. + * It's like a inner branch, but bigger to get any remainder bits. + */ +#define TOMMY_TRIE_INPLACE_BUCKET_MAX (1 << TOMMY_TRIE_INPLACE_BUCKET_BIT) + +/** + * Trie node. + * This is the node that you have to include inside your objects. + */ +typedef struct tommy_trie_inplace_node_struct { + struct tommy_trie_inplace_node_struct* next; /**< Next element. 0 if it's the last. */ + struct tommy_trie_inplace_node_struct* prev; /**< Circular previous element. */ + void* data; /**< Pointer to the data. */ + tommy_key_t key; /**< Used to store the key or the hash. */ + struct tommy_trie_inplace_node_struct* map[TOMMY_TRIE_INPLACE_TREE_MAX]; /** Branches of the node. */ +} tommy_trie_inplace_node; + +/** + * Trie container type. + * \note Don't use internal fields directly, but access the container only using functions. + */ +typedef struct tommy_trie_inplace_struct { + tommy_trie_inplace_node* bucket[TOMMY_TRIE_INPLACE_BUCKET_MAX]; /**< First tree level. */ + tommy_count_t count; /**< Number of elements. */ +} tommy_trie_inplace; + +/** + * Initializes the trie. + * + * The tries is completely inplace, and it doesn't need to be deinitialized. + */ +void tommy_trie_inplace_init(tommy_trie_inplace* trie_inplace); + +/** + * Inserts an element in the trie. + */ +void tommy_trie_inplace_insert(tommy_trie_inplace* trie_inplace, tommy_trie_inplace_node* node, void* data, tommy_key_t key); + +/** + * Searches and removes the first element with the specified key. + * If the element is not found, 0 is returned. + * If more equal elements are present, the first one is removed. + * This operation is faster than calling tommy_trie_inplace_bucket() and tommy_trie_inplace_remove_existing() separately. + * \param key Key of the element to find and remove. + * \return The removed element, or 0 if not found. + */ +void* tommy_trie_inplace_remove(tommy_trie_inplace* trie_inplace, tommy_key_t key); + +/** + * Gets the bucket of the specified key. + * The bucket is guaranteed to contain ALL and ONLY the elements with the specified key. + * You can access elements in the bucket following the ::next pointer until 0. + * \param key Key of the element to find. + * \return The head of the bucket, or 0 if empty. + */ +tommy_trie_inplace_node* tommy_trie_inplace_bucket(tommy_trie_inplace* trie_inplace, tommy_key_t key); + +/** + * Searches an element in the trie. + * You have to provide the key of the element you want to find. + * If more elements with the same key are present, the first one is returned. + * \param key Key of the element to find. + * \return The first element found, or 0 if none. + */ +tommy_inline void* tommy_trie_inplace_search(tommy_trie_inplace* trie_inplace, tommy_key_t key) +{ + tommy_trie_inplace_node* i = tommy_trie_inplace_bucket(trie_inplace, key); + + if (!i) + return 0; + + return i->data; +} + +/** + * Removes an element from the trie. + * You must already have the address of the element to remove. + * \return The tommy_node::data field of the node removed. + */ +void* tommy_trie_inplace_remove_existing(tommy_trie_inplace* trie_inplace, tommy_trie_inplace_node* node); + +/** + * Gets the number of elements. + */ +tommy_inline tommy_count_t tommy_trie_inplace_count(tommy_trie_inplace* trie_inplace) +{ + return trie_inplace->count; +} + +/** + * Gets the size of allocated memory. + * It includes the size of the ::tommy_inplace_node of the stored elements. + */ +tommy_size_t tommy_trie_inplace_memory_usage(tommy_trie_inplace* trie_inplace); + +#endif + diff -Nru librtr-0.6.3/third-party/tommyds/tommytypes.h librtr-0.7.0/third-party/tommyds/tommytypes.h --- librtr-0.6.3/third-party/tommyds/tommytypes.h 1970-01-01 00:00:00.000000000 +0000 +++ librtr-0.7.0/third-party/tommyds/tommytypes.h 2019-07-18 08:26:08.000000000 +0000 @@ -0,0 +1,424 @@ +/* + * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. + * + * 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER 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. + */ + +/** \file + * Generic types. + */ + +#ifndef __TOMMYTYPES_H +#define __TOMMYTYPES_H + +/******************************************************************************/ +/* types */ + +#include <stddef.h> + +#if defined(_MSC_VER) +typedef unsigned tommy_uint32_t; /**< Generic uint32_t type. */ +typedef unsigned _int64 tommy_uint64_t; /**< Generic uint64_t type. */ +typedef size_t tommy_uintptr_t; /**< Generic uintptr_t type. */ +#else +#include <stdint.h> +typedef uint32_t tommy_uint32_t; /**< Generic uint32_t type. */ +typedef uint64_t tommy_uint64_t; /**< Generic uint64_t type. */ +typedef uintptr_t tommy_uintptr_t; /**< Generic uintptr_t type. */ +#endif +typedef size_t tommy_size_t; /**< Generic size_t type. */ +typedef ptrdiff_t tommy_ptrdiff_t; /**< Generic ptrdiff_t type. */ +typedef int tommy_bool_t; /**< Generic boolean type. */ + +/** + * Generic unsigned integer type. + * + * It has no specific size, as is used to store only small values. + * To make the code more efficient, a full 32 bit integer is used. + */ +typedef tommy_uint32_t tommy_uint_t; + +/** + * Generic unsigned integer for counting objects. + * + * TommyDS doesn't support more than 2^32-1 objects. + */ +typedef tommy_uint32_t tommy_count_t; + +/** \internal + * Type cast required for the C++ compilation. + * When compiling in C++ we cannot convert a void* pointer to another pointer. + * In such case we need an explicit cast. + */ +#ifdef __cplusplus +#define tommy_cast(type, value) static_cast<type>(value) +#else +#define tommy_cast(type, value) (value) +#endif + +/******************************************************************************/ +/* heap */ + +/* by default uses malloc/calloc/realloc/free */ + +/** + * Generic malloc(), calloc(), realloc() and free() functions. + * Redefine them to what you need. By default they map to the C malloc(), calloc(), realloc() and free(). + */ +#if !defined(tommy_malloc) || !defined(tommy_calloc) || !defined(tommy_realloc) || !defined(tommy_free) +#include "rtrlib/lib/alloc_utils_private.h" +#endif +#if !defined(tommy_malloc) +#define tommy_malloc lrtr_malloc +#endif +#if !defined(tommy_calloc) +#define tommy_calloc lrtr_calloc +#endif +#if !defined(tommy_realloc) +#define tommy_realloc lrtr_realloc +#endif +#if !defined(tommy_free) +#define tommy_free lrtr_free +#endif + +/******************************************************************************/ +/* modificators */ + +/** \internal + * Definition of the inline keyword if available. + */ +#if !defined(tommy_inline) +#if defined(_MSC_VER) || defined(__GNUC__) +#define tommy_inline static __inline +#else +#define tommy_inline static +#endif +#endif + +/** \internal + * Definition of the restrict keyword if available. + */ +#if !defined(tommy_restrict) +#if __STDC_VERSION__ >= 199901L +#define tommy_restrict restrict +#elif defined(_MSC_VER) || defined(__GNUC__) +#define tommy_restrict __restrict +#else +#define tommy_restrict +#endif +#endif + +/** \internal + * Hints the compiler that a condition is likely true. + */ +#if !defined(tommy_likely) +#if defined(__GNUC__) +#define tommy_likely(x) __builtin_expect(!!(x), 1) +#else +#define tommy_likely(x) (x) +#endif +#endif + +/** \internal + * Hints the compiler that a condition is likely false. + */ +#if !defined(tommy_unlikely) +#if defined(__GNUC__) +#define tommy_unlikely(x) __builtin_expect(!!(x), 0) +#else +#define tommy_unlikely(x) (x) +#endif +#endif + +/******************************************************************************/ +/* key */ + +/** + * Key type used in indexed data structures to store the key or the hash value. + */ +typedef tommy_uint32_t tommy_key_t; + +/** + * Bits into the ::tommy_key_t type. + */ +#define TOMMY_KEY_BIT (sizeof(tommy_key_t) * 8) + +/******************************************************************************/ +/* node */ + +/** + * Data structure node. + * This node type is shared between all the data structures and used to store some + * info directly into the objects you want to store. + * + * A typical declaration is: + * \code + * struct object { + * tommy_node node; + * // other fields + * }; + * \endcode + */ +typedef struct tommy_node_struct { + /** + * Next node. + * The tail node has it at 0, like a 0 terminated list. + */ + struct tommy_node_struct* next; + + /** + * Previous node. + * The head node points to the tail node, like a circular list. + */ + struct tommy_node_struct* prev; + + /** + * Pointer to the object containing the node. + * This field is initialized when inserting nodes into a data structure. + */ + void* data; + + /** + * Key used to store the node. + * With hashtables this field is used to store the hash value. + * With lists this field is not used. + */ + tommy_key_t key; +} tommy_node; + +/******************************************************************************/ +/* compare */ + +/** + * Compare function for elements. + * \param obj_a Pointer to the first object to compare. + * \param obj_b Pointer to the second object to compare. + * \return <0 if the first element is less than the second, ==0 equal, >0 if greather. + * + * This function is like the C strcmp(). + * + * \code + * struct object { + * tommy_node node; + * int value; + * }; + * + * int compare(const void* obj_a, const void* obj_b) + * { + * if (((const struct object*)obj_a)->value < ((const struct object*)obj_b)->value) + * return -1; + * if (((const struct object*)obj_a)->value > ((const struct object*)obj_b)->value) + * return 1; + * return 0; + * } + * + * tommy_list_sort(&list, compare); + * \endcode + * + */ +typedef int tommy_compare_func(const void* obj_a, const void* obj_b); + +/** + * Search function for elements. + * \param arg Pointer to the value to search as passed at the search function. + * \param obj Pointer to the object to compare to. + * \return ==0 if the value matches the element. !=0 if different. + * + * The first argument is a pointer to the value to search exactly + * as it's passed at the search function called. + * The second argument is a pointer to the object inside the hashtable to compare. + * + * The return value has to be 0 if the values are equal. != 0 if they are different. + * + * \code + * struct object { + * tommy_node node; + * int value; + * }; + * + * int compare(const void* arg, const void* obj) + * { + * const int* value_to_find = arg; + * const struct object* object_to_compare = obj; + * + * return *value_to_find != object_to_compare->value; + * } + * + * int value_to_find = 1; + * struct object* obj = tommy_hashtable_search(&hashtable, compare, &value_to_find, tommy_inthash_u32(value_to_find)); + * if (!obj) { + * // not found + * } else { + * // found + * } + * \endcode + * + */ +typedef int tommy_search_func(const void* arg, const void* obj); + +/** + * Foreach function. + * \param obj Pointer to the object to iterate. + * + * A typical example is to use free() to deallocate all the objects in a list. + * \code + * tommy_list_foreach(&list, (tommy_foreach_func*)free); + * \endcode + */ +typedef void tommy_foreach_func(void* obj); + +/** + * Foreach function with an argument. + * \param arg Pointer to a generic argument. + * \param obj Pointer to the object to iterate. + */ +typedef void tommy_foreach_arg_func(void* arg, void* obj); + +/******************************************************************************/ +/* bit hacks */ + +#if defined(_MSC_VER) && !defined(__cplusplus) +#include <intrin.h> +#pragma intrinsic(_BitScanReverse) +#pragma intrinsic(_BitScanForward) +#endif + +/** \internal + * Integer log2 for constants. + * You can use it only for exact power of 2 up to 256. + */ +#define TOMMY_ILOG2(value) ((value) == 256 ? 8 : (value) == 128 ? 7 : (value) == 64 ? 6 : (value) == 32 ? 5 : (value) == 16 ? 4 : (value) == 8 ? 3 : (value) == 4 ? 2 : (value) == 2 ? 1 : 0) + +/** + * Bit scan reverse or integer log2. + * Return the bit index of the most significant 1 bit. + * + * If no bit is set, the result is undefined. + * To force a return 0 in this case, you can use tommy_ilog2_u32(value | 1). + * + * Other interesting ways for bitscan are at: + * + * Bit Twiddling Hacks + * http://graphics.stanford.edu/~seander/bithacks.html + * + * Chess Programming BitScan + * http://chessprogramming.wikispaces.com/BitScan + * + * \param value Value to scan. 0 is not allowed. + * \return The index of the most significant bit set. + */ +tommy_inline tommy_uint_t tommy_ilog2_u32(tommy_uint32_t value) +{ +#if defined(_MSC_VER) + unsigned long count; + _BitScanReverse(&count, value); + return count; +#elif defined(__GNUC__) + /* + * GCC implements __builtin_clz(x) as "__builtin_clz(x) = bsr(x) ^ 31" + * + * Where "x ^ 31 = 31 - x", but gcc does not optimize "31 - __builtin_clz(x)" to bsr(x), + * but generates 31 - (bsr(x) xor 31). + * + * So we write "__builtin_clz(x) ^ 31" instead of "31 - __builtin_clz(x)", + * to allow the double xor to be optimized out. + */ + return __builtin_clz(value) ^ 31; +#else + /* Find the log base 2 of an N-bit integer in O(lg(N)) operations with multiply and lookup */ + /* from http://graphics.stanford.edu/~seander/bithacks.html */ + static unsigned char TOMMY_DE_BRUIJN_INDEX_ILOG2[32] = { + 0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30, + 8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31 + }; + + value |= value >> 1; + value |= value >> 2; + value |= value >> 4; + value |= value >> 8; + value |= value >> 16; + + return TOMMY_DE_BRUIJN_INDEX_ILOG2[(tommy_uint32_t)(value * 0x07C4ACDDU) >> 27]; +#endif +} + +/** + * Bit scan forward or trailing zero count. + * Return the bit index of the least significant 1 bit. + * + * If no bit is set, the result is undefined. + * \param value Value to scan. 0 is not allowed. + * \return The index of the least significant bit set. + */ +tommy_inline tommy_uint_t tommy_ctz_u32(tommy_uint32_t value) +{ +#if defined(_MSC_VER) + unsigned long count; + _BitScanForward(&count, value); + return count; +#elif defined(__GNUC__) + return __builtin_ctz(value); +#else + /* Count the consecutive zero bits (trailing) on the right with multiply and lookup */ + /* from http://graphics.stanford.edu/~seander/bithacks.html */ + static const unsigned char TOMMY_DE_BRUIJN_INDEX_CTZ[32] = { + 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, + 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9 + }; + + return TOMMY_DE_BRUIJN_INDEX_CTZ[(tommy_uint32_t)(((value & - value) * 0x077CB531U)) >> 27]; +#endif +} + +/** + * Rounds up to the next power of 2. + * For the value 0, the result is undefined. + * \return The smallest power of 2 not less than the specified value. + */ +tommy_inline tommy_uint32_t tommy_roundup_pow2_u32(tommy_uint32_t value) +{ + /* Round up to the next highest power of 2 */ + /* from http://graphics.stanford.edu/~seander/bithacks.html */ + + --value; + value |= value >> 1; + value |= value >> 2; + value |= value >> 4; + value |= value >> 8; + value |= value >> 16; + ++value; + + return value; +} + +/** + * Check if the specified word has a byte at 0. + * \return 0 or 1. + */ +tommy_inline int tommy_haszero_u32(tommy_uint32_t value) +{ + return ((value - 0x01010101) & ~value & 0x80808080) != 0; +} +#endif + diff -Nru librtr-0.6.3/tools/CMakeLists.txt librtr-0.7.0/tools/CMakeLists.txt --- librtr-0.6.3/tools/CMakeLists.txt 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/tools/CMakeLists.txt 2019-07-18 08:26:08.000000000 +0000 @@ -1,4 +1,6 @@ -add_executable(rtrclient rtrclient.c) +add_executable(rtrclient rtrclient.c ${mustach} ${tommyds}) +set_source_files_properties("${mustach}" PROPERTIES COMPILE_FLAGS "-DNO_EXTENSION_FOR_MUSTACH -DNO_OPEN_MEMSTREAM") +set_source_files_properties("${tommyds}" PROPERTIES COMPILE_FLAGS "-Dtommy_malloc=malloc -Dtommy_calloc=calloc -Dtommy_realloc=realloc -Dtommy_free=free -include stdlib.h") target_link_libraries(rtrclient rtrlib) install(TARGETS rtrclient DESTINATION bin) install(FILES "rtrclient.1" DESTINATION "${CMAKE_INSTALL_MANDIR}/man1") @@ -8,3 +10,28 @@ install(TARGETS rpki-rov DESTINATION bin) install(FILES "rpki-rov.1" DESTINATION "${CMAKE_INSTALL_MANDIR}/man1") + +set(rtrclient_pfx_templates default csv csvwithheader json) + +# Generate escaped string sequence for every template file +# This runs at configure time, changes are not picked up automatically +foreach(template_name IN LISTS rtrclient_pfx_templates) + file(READ "templates/${template_name}" template_data HEX) + string(LENGTH "${template_data}" template_length) + math(EXPR template_length "${template_length} - 1") + + set(TEMPLATES "${TEMPLATES}{ .name = \"${template_name}\", .template = \"") + + foreach(iter RANGE 0 ${template_length} 2) + string(SUBSTRING ${template_data} ${iter} 2 line) + set(TEMPLATES "${TEMPLATES}\\x${line}") + endforeach() + set(TEMPLATES "${TEMPLATES} \"},\n") + +endforeach() + +string(STRIP ${TEMPLATES} TEMPLATES) + +CONFIGURE_FILE(templates.h.cmake templates.h) + +include_directories(${CMAKE_CURRENT_BINARY_DIR}) diff -Nru librtr-0.6.3/tools/rpki-rov.c librtr-0.7.0/tools/rpki-rov.c --- librtr-0.6.3/tools/rpki-rov.c 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/tools/rpki-rov.c 2019-07-18 08:26:08.000000000 +0000 @@ -11,6 +11,8 @@ #include <stdio.h> #include <string.h> #include <unistd.h> +#include <errno.h> +#include <arpa/inet.h> #include "rtrlib/rtrlib.h" const int connection_timeout = 20; @@ -25,7 +27,7 @@ connection_status = status; } -int connection_error(enum rtr_mgr_status status) +static int connection_error(enum rtr_mgr_status status) { if (status == RTR_MGR_ERROR) { /* @@ -43,6 +45,18 @@ return 0; } +static int str_to_int(const char *str, int *value) +{ + errno = 0; + int tmp = strtol(str, NULL, 10); + + if (errno != 0) + return 1; + + *value = tmp; + return 0; +} + int main(int argc, char *argv[]) { /* check arguments, need hostname/IP and port of cache-server */ @@ -97,9 +111,6 @@ } } - char ip[128]; - int mask; - int asn; int counter; /* loop for input */ while (1) { @@ -141,17 +152,42 @@ char *input_tok = NULL; input_tok = strtok(input, delims); - strcpy(ip, input_tok); + struct lrtr_ip_addr pref; + char ip[INET6_ADDRSTRLEN]; + + if (strlen(input_tok) > sizeof(ip) - 1) { + fprintf(stderr, "Error: Invalid ip addr\n"); + continue; + } + + memset(ip, 0, sizeof(ip)); + strncpy(ip, input_tok, sizeof(ip) - 1); + + if (lrtr_ip_str_to_addr(ip, &pref) != 0) { + fprintf(stderr, "Error: Invalid ip addr\n"); + continue; + } + input_tok = strtok(NULL, delims); - mask = atoi(input_tok); - input_tok = strtok(NULL, delims); asn = atoi(input_tok); + int mask; + + if (str_to_int(input_tok, &mask)) { + fprintf(stderr, "Error: Invalid mask\n"); + continue; + } + + input_tok = strtok(NULL, delims); + int asn; + + if (str_to_int(input_tok, &asn)) { + fprintf(stderr, "Error: Invalid asn\n"); + continue; + } - struct lrtr_ip_addr pref; enum pfxv_state result; struct pfx_record *reason = NULL; unsigned int reason_len = 0; - lrtr_ip_str_to_addr(ip, &pref); /* do validation */ pfx_table_validate_r(groups[0].sockets[0]->pfx_table, &reason, &reason_len, asn, &pref, mask, &result); diff -Nru librtr-0.6.3/tools/rtrclient.1 librtr-0.7.0/tools/rtrclient.1 --- librtr-0.6.3/tools/rtrclient.1 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/tools/rtrclient.1 2019-07-18 08:26:08.000000000 +0000 @@ -11,16 +11,16 @@ rtrclient \- rtr rpki client .SH SYNOPSIS .B rtrclient +[\fB\-kph\fR] +.I SOCKETS\fR... +.SH SOCKETS .B tcp -[\fB\-k\fR] -[\fB\-p\fR] +[\fB\-kpb \fIbindaddr\fR] .IR HOST .IR PORT .br -.B rtrclient .B ssh -[\fB\-k\fR] -[\fB\-p\fR] +[\fB\-kpb \fIbindaddr\fR] .IR HOST .IR PORT .IR USERNAME @@ -28,7 +28,24 @@ [\fIHOST_KEY\fR] .SH DESCRIPTION \fBrtrclient\fR connects to an RPKI/RTR cache server and prints prefix, origin AS, and router key updates. +\fBrtrclient\fR can use plain tcp or ssh transport to connect to an RPKI/RTR cache server. +The amount is not limited and different transport types can be mixed arbitrarily. +.LP +For \fBtcp\fR you must specify the \fIHOST\fR and \fIPORT\fR. +.LP +For \fBssh\fR you must specify the \fIHOST\fR, \fIPORT\fR, \fIUSERNAME\fR and a file containing the \fIPRIVATE_KEY\fR. +You may specify a file containing a list of \fIHOST_KEY\fRs, in the well known +.B SSH_KNOWN_HOSTS +file format. See \fIsshd(8)\fR for details. .SH OPTIONS +\fB-b \fIbindaddr\fR +.RS 4 +Set explicit bind address +.RE +.B -h +.RS 4 +Print help message +.RE \fB-k\fR .RS 4 Print information about router key updates @@ -36,7 +53,30 @@ \fB-p\fR .RS 4 Print information about prefix and origin AS updates +\fB-s\fR +.RS 4 +Print information about connection status updates +.RE +\fB-e\fR +.RS 4 +Export ROAs after completing synchronisation and exit .RE +\fB-t\fR +.RS 4 +Select template for pfx export. May be a build in template (see \fB-l\fR) or a file path to a custom template (see \fBTEMPLATES\fR) +.RE +\fB-l\fR +.RS 4 +Print available templates and exit. Prints specified templated, when used with -t. +.RE +\fB-o\fR +.RS 4 +Output file for export +.SH TEMPLATES +Templates can be used to export ROA information in a custom format. They are written in the \fBmustache\fR(\fIhttps://mustache.github.io/\fR) templating language. + +A template should contain a section called \fBroas\fR which may contain the variables \fBprefix\fR, \fBlength\fR, \fBmaxlen\fR and \fBorigin\fR. +The content of this section is expanded for every entry in the ROA table. The special variable \fBlast\fR is true for the last entry of the prefix table. See the json template for a usage example. .SH EXAMPLES Print prefix and origin AS updates from a tcp based server .PP @@ -53,3 +93,9 @@ rtrclient ssh -k -p rpki.example.com 22 rtr-ssh ~/.ssh/id_rsa ~/.ssh/known_hosts .RE .fi +.PP +Use multiple rtr server, print prefix updates for some +.PP +.nf +.RS +rtrclient tcp -p rpki.example.com 323 tcp rpki2.example.com 323 ssh -p rpki.example.com 22 rtr-ssh ~/.ssh/id_rsa diff -Nru librtr-0.6.3/tools/rtrclient.c librtr-0.7.0/tools/rtrclient.c --- librtr-0.6.3/tools/rtrclient.c 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/tools/rtrclient.c 2019-07-18 08:26:08.000000000 +0000 @@ -7,36 +7,394 @@ * Website: http://rtrlib.realmv6.org/ */ -#include <stdlib.h> -#include <pthread.h> #include <arpa/inet.h> +#include <ctype.h> +#include <errno.h> +#include <netdb.h> +#include <pthread.h> +#include <stdarg.h> #include <stdio.h> +#include <stdlib.h> #include <string.h> #include <sys/socket.h> +#include <sys/stat.h> +#include <sys/types.h> #include <unistd.h> + #include "rtrlib/rtrlib.h" +#include <third-party/mustach/mustach.h> +#include <third-party/tommyds/tommyarray.h> +#include <third-party/tommyds/tommyhashlin.h> + +#include "templates.h" + +__attribute__((format (printf, 1, 2), noreturn)) +static void print_error_exit(const char *fmt, ...); + +static bool is_readable_file(const char *str); + +enum socket_type { + SOCKET_TYPE_TCP, +#ifdef RTRLIB_HAVE_LIBSSH + SOCKET_TYPE_SSH, +#endif +}; + +struct socket_config { + struct rtr_socket socket; + struct tr_socket tr_socket; + enum socket_type type; + bool print_pfx_updates; + bool print_spki_updates; + char *bindaddr; + char *host; + char *port; +#ifdef RTRLIB_HAVE_LIBSSH + char *ssh_username; + char *ssh_private_key; + char *ssh_host_key; +#endif +}; + +/* activate update callback */ +bool activate_pfx_update_cb = false; +bool activate_spki_update_cb = false; + +/* print all updates regardless of socket configuration */ +bool print_all_pfx_updates = false; +bool print_all_spki_updates = false; + +bool print_status_updates = false; + +bool export_pfx = false; +char *export_file_path = NULL; +const char *template_name = NULL; + +struct socket_config **socket_config = NULL; +size_t socket_count = 0; + +pthread_mutex_t stdout_mutex = PTHREAD_MUTEX_INITIALIZER; + +/** + * @brief Check if argument is NULL and exit if it is + */ +static void *check_alloc_ret(void *ptr) +{ + if (ptr) + return ptr; + + fprintf(stderr, "Memory allocation error\n"); + exit(-1); +} + +/** + * @brief Return value checking malloc wrapper. Exits program on error + */ +static inline void *checked_malloc(size_t size) +{ + return check_alloc_ret(malloc(size)); +} + +/** + * @brief Return value checking realloc wrapper. Exits program on error. + */ +static inline void *checked_realloc(void *ptr, size_t size) +{ + return check_alloc_ret(realloc(ptr, size)); +} + +static struct socket_config *extend_socket_config() +{ + socket_config = checked_realloc( + socket_config, + sizeof(struct socket_config *) * (socket_count + 1)); + struct socket_config *config = socket_config[socket_count] = + checked_malloc(sizeof(struct socket_config)); + + memset(config, 0, sizeof(*config)); + ++socket_count; + + return config; +} + +static const char *get_template(const char *name) +{ + const struct pfx_output_template *current = templates; + + while (current->name) { + if (strcmp(name, current->name) == 0) + return current->template; + + ++current; + } + + if (is_readable_file(name)) { + FILE *template_file = fopen(name, "r"); + + if (fseek(template_file, 0, SEEK_END) == -1) + goto read_error; + + long file_size = ftell(template_file); + + if (file_size == -1) + goto read_error; + + rewind(template_file); + + char *template = checked_malloc(file_size); + + size_t bytes_read = fread(template, 1, file_size, template_file); + + if (bytes_read != (unsigned long)file_size) + goto read_error; + + fclose(template_file); + return template; + +read_error: + fclose(template_file); + print_error_exit("Could not read template"); + } + + print_error_exit("Template \"%s\" not found", name); +} + +static void print_templates(void) +{ + const struct pfx_output_template *current = templates; + + while (current->name) { + if (template_name && strcmp(current->name, template_name) == 0) + printf("%s", current->template); + else if (!template_name) + printf("%s\n", current->name); + + ++current; + } +} + +static bool is_numeric(const char *str) +{ + for (size_t i = 0; i < strlen(str); ++i) { + if (!isdigit(str[i])) + return false; + } + return true; +} + +static bool is_valid_port_number(const char *str) +{ + if (!is_numeric(str)) + return false; + + int port = atoi(str); + + if (port < 1 || port > 65535) + return false; + + return true; +} + +static bool is_resolveable_host(const char *str) +{ + struct addrinfo hints; + struct addrinfo *result = NULL; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = 0; + hints.ai_protocol = 0; + + int ret = getaddrinfo(str, NULL, &hints, &result); + + freeaddrinfo(result); + + if (ret != 0) + return false; + return true; +} + +static bool is_file(const char *str) +{ + struct stat stat_buf; + + if (stat(str, &stat_buf) == -1) + return false; + + return S_ISREG(stat_buf.st_mode); +} + +static bool is_readable_file(const char *str) +{ + if (access(str, R_OK) != 0) + return false; + + return is_file(str); +} + +struct exporter_state { + bool roa_section; + tommy_count_t current_roa; + tommy_array *roas; +}; + +struct pfx_export_cb_arg { + tommy_array *array; + tommy_hashlin *hashtable; +}; + +static void pfx_export_cb(const struct pfx_record *pfx_record, void *data); + +static tommy_uint32_t hash_pfx_record(const struct pfx_record *record) { + struct pfx_record_packed { + uint8_t ip[16]; + uint32_t asn; + uint8_t min_len; + uint8_t max_len; + } __attribute__((packed)); + + struct pfx_record_packed packed_record; + + memset(&packed_record, 0, sizeof(packed_record)); + + if (record->prefix.ver == LRTR_IPV4) + memcpy(&packed_record.ip, &record->prefix.u.addr4, sizeof(struct lrtr_ipv4_addr)); + else + memcpy(&packed_record.ip, &record->prefix.u.addr6, sizeof(struct lrtr_ipv6_addr)); + + packed_record.asn = record->asn; + packed_record.min_len = record->min_len; + packed_record.max_len = record->max_len; + + return tommy_hash_u32(0, &packed_record, sizeof(packed_record)); +} + +static int pfx_record_cmp(const void *data1, const void *data2) +{ + const struct pfx_record *arg1 = data1; + const struct pfx_record *arg2 = data2; + + if ((arg1->asn == arg2->asn) && (arg1->max_len == arg2->max_len) && (arg1->min_len == arg2->min_len) && + lrtr_ip_addr_equal(arg1->prefix, arg2->prefix)) + return 0; + + return 1; +} + +static int template_enter(void *closure, const char *name) +{ + struct exporter_state *state = closure; + + if (strncmp("roas", name, strlen(name)) == 0) { + state->roa_section = true; + state->current_roa = 0; + + return 1; + + } else if (state->current_roa && strcmp("last", name) == 0 && + state->current_roa == tommy_array_size(state->roas) - 1) { + return 1; + } + + return 0; +} + +static int template_leave(void *closure) +{ + struct exporter_state *state = closure; + + state->roa_section = false; + return 0; +} + +static int template_next(void *closure) +{ + struct exporter_state *state = closure; + + if (++state->current_roa >= tommy_array_size(state->roas)) + return 0; + + return 1; +} + +static int template_put(void *closure, const char *name, __attribute__((unused)) int escape, FILE *file) +{ + struct exporter_state *state = closure; + + size_t namelen = strlen(name); + + if (!state->roa_section || namelen == 0) + return MUSTACH_ERROR_SYSTEM; + + struct pfx_record *pfx_record = tommy_array_get(state->roas, state->current_roa); + + if (strncmp("prefix", name, namelen) == 0) { + char prefix_str[INET6_ADDRSTRLEN]; + + lrtr_ip_addr_to_str(&pfx_record->prefix, prefix_str, sizeof(prefix_str)); + fputs(prefix_str, file); + + } else if (strncmp("length", name, namelen) == 0) { + fprintf(file, "%d", pfx_record->min_len); + + } else if (strncmp("maxlen", name, namelen) == 0) { + fprintf(file, "%d", pfx_record->max_len); + + } else if (strncmp("origin", name, namelen) == 0) { + fprintf(file, "%d", pfx_record->asn); + } + + return MUSTACH_OK; +} + +struct mustach_itf template_itf = { .start = NULL, + .put = template_put, + .enter = template_enter, + .next = template_next, + .leave = template_leave }; + static void print_usage(char **argv) { printf("Usage:\n"); - printf(" %s tcp [options] <host> <port>\n", argv[0]); + printf(" %s [-hpkels] [-o file] [-t template] <socket>...\n", argv[0]); + printf("\nSocket:\n"); + printf(" tcp [-hpkb bindaddr] <host> <port>\n"); #ifdef RTRLIB_HAVE_LIBSSH - printf(" %s ssh [options] <host> <port>", argv[0]); - printf(" <username> <private_key> [<host_key>]\n"); + printf(" ssh [-hpkb bindaddr] <host> <port> <username> <private_key> [<host_key>]\n"); #endif printf("\nOptions:\n"); + printf("-b bindaddr Hostnamne or IP address to connect from\n\n"); + printf("-k Print information about SPKI updates.\n"); printf("-p Print information about PFX updates.\n"); + printf("-s Print information about connection status updates.\n\n"); + + printf("-e export pfx table and exit\n"); + printf("-o output file for export\n"); + printf("-t template used for export\n"); + printf("-l list available templates\n\n"); + + printf("-h Print this help message.\n"); printf("\nExamples:\n"); - printf(" %s tcp rpki-validator.realmv6.org 8282\n", argv[0]); - printf(" %s tcp -k -p rpki-validator.realmv6.org 8282\n", argv[0]); + printf(" %s tcp rpki-validator.realmv6.org 8283\n", argv[0]); + printf(" %s tcp -k -p rpki-validator.realmv6.org 8283\n", argv[0]); + printf(" %s tcp -k rpki-validator.realmv6.org 8283 tcp -s example.com 323\n", argv[0]); + printf(" %s -kp tcp rpki-validator.realmv6.org 8283 tcp example.com 323\n", argv[0]); #ifdef RTRLIB_HAVE_LIBSSH printf(" %s ssh rpki-validator.realmv6.org 22 rtr-ssh", argv[0]); printf(" ~/.ssh/id_rsa ~/.ssh/known_hosts\n"); - printf(" %s ssh -k -p rpki-validator.realmv6.org 22 rtr-ssh", argv[0]); - printf(" ~/.ssh/id_rsa ~/.ssh/known_hosts\n"); + printf(" %s ssh -k -p rpki-validator.realmv6.org 22 rtr-ssh ~/.ssh/id_rsa ~/.ssh/known_hosts\n", argv[0]); + printf(" %s ssh -k -p rpki-validator.realmv6.org 22 rtr-ssh ~/.ssh/id_rsa ~/.ssh/known_hosts", argv[0]); + printf(" ssh -k -p example.com 22 rtr-ssh ~/.ssh/id_rsa_example\n"); + printf(" %s ssh -k -p rpki-validator.realmv6.org 22 rtr-ssh ~/.ssh/id_rsa ~/.ssh/known_hosts", argv[0]); + printf(" tcp -k -p example.com 323\n"); #endif + + printf(" %s -e tcp rpki-validator.realmv6.org 8283\n", argv[0]); + printf(" %s -e -t csv -o roa.csv tcp rpki-validator.realmv6.org 8283\n", argv[0]); } static void status_fp(const struct rtr_mgr_group *group __attribute__((unused)), @@ -44,9 +402,13 @@ const struct rtr_socket *rtr_sock, void *data __attribute__((unused))) { - printf("RTR-Socket changed connection status to: %s, Mgr Status: %s\n", - rtr_state_to_str(rtr_sock->state), - rtr_mgr_status_to_str(mgr_status)); + if (print_status_updates) { + pthread_mutex_lock(&stdout_mutex); + printf("RTR-Socket changed connection status to: %s, Mgr Status: %s\n", + rtr_state_to_str(rtr_sock->state), + rtr_mgr_status_to_str(mgr_status)); + pthread_mutex_unlock(&stdout_mutex); + } } static void update_cb(struct pfx_table *p __attribute__((unused)), @@ -55,19 +417,41 @@ { char ip[INET6_ADDRSTRLEN]; + const struct socket_config *config = + (const struct socket_config *)rec.socket; + + if (!print_all_pfx_updates && !config->print_pfx_updates) + return; + + pthread_mutex_lock(&stdout_mutex); if (added) printf("+ "); else printf("- "); lrtr_ip_addr_to_str(&rec.prefix, ip, sizeof(ip)); - printf("%-40s %3u - %3u %10u\n", - ip, rec.min_len, rec.max_len, rec.asn); + if (socket_count > 1) + printf("%s:%s %-40s %3u - %3u %10u\n", + config->host, config->port, ip, + rec.min_len, rec.max_len, rec.asn); + else + printf("%-40s %3u - %3u %10u\n", + ip, rec.min_len, rec.max_len, rec.asn); + + pthread_mutex_unlock(&stdout_mutex); } static void update_spki(struct spki_table *s __attribute__((unused)), const struct spki_record record, const bool added) { + const struct socket_config *config = + (const struct socket_config *)record.socket; + + if (!print_all_spki_updates && !config->print_spki_updates) + return; + + pthread_mutex_lock(&stdout_mutex); + char c; if (added) @@ -76,6 +460,7 @@ c = '-'; printf("%c ", c); + printf("HOST: %s:%s\n", config->host, config->port); printf("ASN: %u\n ", record.asn); int i; @@ -101,134 +486,471 @@ printf(":"); } printf("\n"); + + pthread_mutex_unlock(&stdout_mutex); } -int main(int argc, char **argv) +static void parse_global_opts(int argc, char **argv) { - enum mode_t { TCP, SSH } mode = TCP; - char *host = NULL; - char *port = NULL; + int opt; - spki_update_fp spki_update_fp = NULL; - pfx_update_fp pfx_update_fp = NULL; + bool print_template = false; - #ifdef RTRLIB_HAVE_LIBSSH - char *user = NULL; - char *privkey = NULL; - char *hostkey = NULL; - #endif + while ((opt = getopt(argc, argv, "+kphelo:t:s")) != -1) { + switch (opt) { + case 'k': + activate_spki_update_cb = true; + print_all_spki_updates = true; + break; - if (argc == 1) { - print_usage(argv); - return EXIT_FAILURE; - } + case 'p': + activate_pfx_update_cb = true; + print_all_pfx_updates = true; + break; - if (strncasecmp(argv[1], "tcp", strlen(argv[1])) == 0) { - mode = TCP; - } else if (strncasecmp(argv[1], "ssh", strlen(argv[1])) == 0) { - mode = SSH; - } else { - print_usage(argv); - return EXIT_FAILURE; - } + case 'e': + export_pfx = true; + break; - int args_it = 2; - /* Optional args */ - for (args_it = 2; args_it < argc; args_it++) { - if (argv[args_it][0] == '-') { - if (argv[args_it][1] == 'k') { - spki_update_fp = update_spki; - } else if (argv[args_it][1] == 'p') { - pfx_update_fp = update_cb; - } else { - print_usage(argv); - return EXIT_FAILURE; - } - } else { + case 'o': + if (export_file_path) + print_error_exit("output file can not be specified more than once"); + + export_file_path = optarg; + break; + + case 't': + template_name = optarg; + break; + + case 'l': + print_template = true; + break; + + case 's': + print_status_updates = true; break; + + default: + print_usage(argv); + exit(EXIT_FAILURE); } } - if (mode == TCP) { - if ((argc - args_it) < 2) { + if (print_template) { + print_templates(); + exit(EXIT_SUCCESS); + } + + if ((export_file_path || template_name) && !export_pfx) + print_error_exit("Specifying -o or -t without -e does not make sense"); +} + +static void parse_socket_opts( + int argc, + char **argv, + struct socket_config *config) +{ + int opt; + + while ((opt = getopt(argc, argv, "+kphb:")) != -1) { + switch (opt) { + case 'k': + activate_spki_update_cb = true; + config->print_spki_updates = true; + break; + + case 'p': + activate_pfx_update_cb = true; + config->print_pfx_updates = true; + break; + + case 'b': + config->bindaddr = optarg; + break; + + default: print_usage(argv); - return EXIT_FAILURE; + exit(EXIT_FAILURE); } - host = argv[args_it++]; - port = argv[args_it++]; } - #ifdef RTRLIB_HAVE_LIBSSH - else if (mode == SSH) { - if ((argc - args_it) < 4) { - print_usage(argv); - return EXIT_FAILURE; +} + +static void print_error_exit(const char *fmt, ...) +{ + va_list argptr; + + va_start(argptr, fmt); + + fprintf(stderr, "error: "); + + vfprintf(stderr, fmt, argptr); + + fprintf(stderr, "\n"); + + va_end(argptr); + + exit(EXIT_FAILURE); +} + +static int parse_cli(int argc, char **argv) { + enum cli_parse_state { + CLI_PARSE_STATE_BEGIN, + CLI_PARSE_STATE_GLOBAL_OPTS, + CLI_PARSE_STATE_SOCKET_BEGIN, + CLI_PARSE_STATE_SOCKET_TCP_OPTIONS, + CLI_PARSE_STATE_SOCKET_TCP_HOST, + CLI_PARSE_STATE_SOCKET_TCP_PORT, +#ifdef RTRLIB_HAVE_LIBSSH + CLI_PARSE_STATE_SOCKET_SSH_OPTIONS, + CLI_PARSE_STATE_SOCKET_SSH_HOST, + CLI_PARSE_STATE_SOCKET_SSH_PORT, + CLI_PARSE_STATE_SOCKET_SSH_USERNAME, + CLI_PARSE_STATE_SOCKET_SSH_PRIVATE_KEY, + CLI_PARSE_STATE_SOCKET_SSH_HOST_KEY, +#endif + CLI_PARSE_STATE_END, + }; + + enum cli_parse_state state = CLI_PARSE_STATE_BEGIN; + + struct socket_config *current_config; + + while (true) { + switch (state) { + case CLI_PARSE_STATE_BEGIN: + optind = 1; + state = CLI_PARSE_STATE_GLOBAL_OPTS; + break; + + case CLI_PARSE_STATE_GLOBAL_OPTS: + parse_global_opts(argc, argv); + + state = CLI_PARSE_STATE_SOCKET_BEGIN; + break; + + case CLI_PARSE_STATE_SOCKET_BEGIN: + current_config = extend_socket_config(); + + if (strncasecmp(argv[optind], + "tcp", strlen(argv[optind])) == 0) { + state = CLI_PARSE_STATE_SOCKET_TCP_OPTIONS; + current_config->type = SOCKET_TYPE_TCP; + + /* Check if enough arguments are available + * to configure this socket. + * This does not account for options. + */ + if ((argc - optind) < 2) + print_error_exit("Not enough arguments for tcp socket"); + + } else if (strncasecmp(argv[optind], "ssh", + strlen(argv[optind])) == 0) { +#ifdef RTRLIB_HAVE_LIBSSH + state = CLI_PARSE_STATE_SOCKET_SSH_OPTIONS; + current_config->type = SOCKET_TYPE_SSH; + + /* Check if enough arguments are available + * to configure this socket. + * This does not account for options. + */ + if ((argc - optind) < 4) + print_error_exit("Not enough arguments for ssh socket"); +#else + print_error_exit("ssh support disabled at compile time\n"); +#endif + + } else { + print_error_exit( + "\"%s\" is not a valid socket type\n", + argv[optind]); + break; + } + + ++optind; + break; + + case CLI_PARSE_STATE_SOCKET_TCP_OPTIONS: + parse_socket_opts(argc, argv, current_config); + + /* Check again if enough arguments are available, + * accounting for options + */ + if ((argc - optind) < 2) + print_error_exit( + "Not enough arguments for tcp socket"); + + state = CLI_PARSE_STATE_SOCKET_TCP_HOST; + break; + + case CLI_PARSE_STATE_SOCKET_TCP_HOST: + if (!is_resolveable_host(argv[optind])) + print_error_exit( + "cannot resolve \"%s\"\n", + argv[optind]); + + current_config->host = argv[optind++]; + + state = CLI_PARSE_STATE_SOCKET_TCP_PORT; + break; + + case CLI_PARSE_STATE_SOCKET_TCP_PORT: + if (!is_valid_port_number(argv[optind])) + print_error_exit( + "\"%s\" is not a valid port number\n", + argv[optind]); + + current_config->port = argv[optind++]; + + state = CLI_PARSE_STATE_SOCKET_BEGIN; + break; + +#ifdef RTRLIB_HAVE_LIBSSH + case CLI_PARSE_STATE_SOCKET_SSH_OPTIONS: + parse_socket_opts(argc, argv, current_config); + + /* Check again if enough arguments are available, + * accounting for options + */ + if ((argc - optind) < 4) + print_error_exit( + "Not enough arguments for ssh socket"); + + state = CLI_PARSE_STATE_SOCKET_SSH_HOST; + break; + + case CLI_PARSE_STATE_SOCKET_SSH_HOST: + if (!is_resolveable_host(argv[optind])) + print_error_exit( + "cannot resolve \"%s\"\n", + argv[optind]); + + current_config->host = argv[optind++]; + + state = CLI_PARSE_STATE_SOCKET_SSH_PORT; + break; + + case CLI_PARSE_STATE_SOCKET_SSH_PORT: + if (!is_valid_port_number(argv[optind])) + print_error_exit( + "\"%s\" is not a valid port number\n", + argv[optind]); + + current_config->port = argv[optind++]; + + state = CLI_PARSE_STATE_SOCKET_SSH_USERNAME; + break; + + case CLI_PARSE_STATE_SOCKET_SSH_USERNAME: + current_config->ssh_username = argv[optind++]; + + state = CLI_PARSE_STATE_SOCKET_SSH_PRIVATE_KEY; + break; + + case CLI_PARSE_STATE_SOCKET_SSH_PRIVATE_KEY: + if (!is_readable_file(argv[optind])) + print_error_exit( + "\"%s\" is not a readable file\n", + argv[optind]); + + current_config->ssh_private_key = argv[optind++]; + + state = CLI_PARSE_STATE_SOCKET_SSH_HOST_KEY; + break; + + case CLI_PARSE_STATE_SOCKET_SSH_HOST_KEY: + if (strncasecmp(argv[optind], "tcp", + strlen(argv[optind])) == 0 || + strncasecmp(argv[optind], "ssh", + strlen(argv[optind])) == 0) { + state = CLI_PARSE_STATE_SOCKET_BEGIN; + break; + } + + if (!is_readable_file(argv[optind])) + print_error_exit( + "\"%s\" is not a readable file\n", + argv[optind]); + + current_config->ssh_host_key = argv[optind++]; + + state = CLI_PARSE_STATE_SOCKET_BEGIN; + break; +#endif + + case CLI_PARSE_STATE_END: + return 0; } - host = argv[args_it++]; - port = argv[args_it++]; - user = argv[args_it++]; - privkey = argv[args_it++]; - if ((argc - args_it) == 1) - hostkey = argv[args_it++]; - else - hostkey = NULL; + + if (optind >= argc) + state = CLI_PARSE_STATE_END; } - #endif +} - struct tr_socket tr_sock; - struct tr_tcp_config tcp_config; +static void init_sockets(void) +{ + for (size_t i = 0; i < socket_count; ++i) { + struct socket_config *config = socket_config[i]; + struct tr_tcp_config tcp_config; +#ifdef RTRLIB_HAVE_LIBSSH + struct tr_ssh_config ssh_config; +#endif + + switch (config->type) { + case SOCKET_TYPE_TCP: + tcp_config.host = config->host; + tcp_config.port = config->port; + tcp_config.bindaddr = config->bindaddr; - #ifdef RTRLIB_HAVE_LIBSSH - struct tr_ssh_config ssh_config; - #endif + tr_tcp_init(&tcp_config, &config->tr_socket); + config->socket.tr_socket = &config->tr_socket; + break; + +#ifdef RTRLIB_HAVE_LIBSSH + case SOCKET_TYPE_SSH: + ssh_config.host = config->host; + ssh_config.port = atoi(config->port); + ssh_config.bindaddr = config->bindaddr; + ssh_config.username = config->ssh_username; + ssh_config.client_privkey_path = + config->ssh_private_key; + ssh_config.server_hostkey_path = config->ssh_host_key; - if (mode == TCP) { - tcp_config = (struct tr_tcp_config) { host, port, 0 }; - tr_tcp_init(&tcp_config, &tr_sock); + tr_ssh_init(&ssh_config, &config->tr_socket); + config->socket.tr_socket = &config->tr_socket; + break; +#endif + } } - #ifdef RTRLIB_HAVE_LIBSSH - else { - unsigned int iport = atoi(port); +} - ssh_config = (struct tr_ssh_config) { - host, - iport, - NULL, - user, - hostkey, - privkey, - }; - tr_ssh_init(&ssh_config, &tr_sock); +int main(int argc, char **argv) +{ + parse_cli(argc, argv); + + if (socket_count == 0) { + print_usage(argv); + exit(EXIT_FAILURE); } - #endif - struct rtr_socket rtr; + init_sockets(); + struct rtr_mgr_config *conf; struct rtr_mgr_group groups[1]; - rtr.tr_socket = &tr_sock; - groups[0].sockets_len = 1; - groups[0].sockets = malloc(1 * sizeof(rtr)); - groups[0].sockets[0] = &rtr; + groups[0].sockets_len = socket_count; + groups[0].sockets = (struct rtr_socket **)socket_config; groups[0].preference = 1; + spki_update_fp spki_update_fp = + activate_spki_update_cb ? update_spki : NULL; + pfx_update_fp pfx_update_fp = activate_pfx_update_cb ? update_cb : NULL; + int ret = rtr_mgr_init(&conf, groups, 1, 30, 600, 600, pfx_update_fp, spki_update_fp, status_fp, NULL); if (ret == RTR_ERROR) - printf("Error in rtr_mgr_init!\n"); + fprintf(stderr, "Error in rtr_mgr_init!\n"); else if (ret == RTR_INVALID_PARAM) - printf("Invalid params passed to rtr_mgr_init\n"); + fprintf(stderr, "Invalid params passed to rtr_mgr_init\n"); if (!conf) return EXIT_FAILURE; - rtr_mgr_start(conf); - printf("%-40s %3s %3s %3s\n", - "Prefix", "Prefix Length", "", "ASN"); - pause(); + if (!export_pfx && activate_pfx_update_cb && socket_count > 1) + printf("%-40s %-40s %3s %3s %3s\n", + "host", "Prefix", "Prefix Length", "", "ASN"); + else if (!export_pfx && activate_pfx_update_cb) + printf("%-40s %3s %3s %3s\n", + "Prefix", "Prefix Length", "", "ASN"); + + if (export_pfx) { + const char *template; + + if (template_name) + template = get_template(template_name); + else + template = templates->template; + FILE *export_file = stdout; + + if (export_file_path) { + export_file = fopen(export_file_path, "w"); + if (!export_file) { + char *errormsg = strerror(errno); + + print_error_exit("\"%s\" could not be opened for writing: %s", optarg, errormsg); + } + } + + rtr_mgr_start(conf); + + while (!rtr_mgr_conf_in_sync(conf)) + sleep(1); + + printf("Sync done\n"); + tommy_array prefixes; + tommy_hashlin prefix_hash; + struct pfx_export_cb_arg arg = { + .array = &prefixes, + .hashtable = &prefix_hash, + }; + + tommy_array_init(&prefixes); + tommy_hashlin_init(&prefix_hash); + + pfx_table_for_each_ipv4_record(conf->pfx_table, pfx_export_cb, &arg); + pfx_table_for_each_ipv6_record(conf->pfx_table, pfx_export_cb, &arg); + + struct exporter_state state = { + .roa_section = false, + .current_roa = 0, + .roas = &prefixes, + }; + + fmustach(template, &template_itf, &state, export_file); + + tommy_hashlin_foreach(&prefix_hash, free); + tommy_hashlin_done(&prefix_hash); + tommy_array_done(&prefixes); + + if (export_file != stdout && fclose(export_file) == EOF) { + char *errormsg = strerror(errno); + + print_error_exit("\"Error during write into output file: %s\"", errormsg); + } + + } else { + rtr_mgr_start(conf); + pause(); + } + rtr_mgr_stop(conf); rtr_mgr_free(conf); free(groups[0].sockets); return EXIT_SUCCESS; } + +struct pfx_record_entry { + struct pfx_record record; + tommy_node hash_node; +}; + +static void pfx_export_cb(const struct pfx_record *pfx_record, void *data) +{ + struct pfx_export_cb_arg *arg = data; + tommy_array *roa_array = arg->array; + tommy_hashlin *roa_hashtable = arg->hashtable; + + tommy_hash_t record_hash = hash_pfx_record(pfx_record); + + if (tommy_hashlin_search(roa_hashtable, &pfx_record_cmp, pfx_record, record_hash) != 0) + return; + + struct pfx_record_entry *pfx_record_entry = malloc(sizeof(struct pfx_record_entry)); + + memcpy(&pfx_record_entry->record, pfx_record, sizeof(struct pfx_record)); + + tommy_hashlin_insert(roa_hashtable, &pfx_record_entry->hash_node, pfx_record_entry, record_hash); + tommy_array_insert(roa_array, pfx_record_entry); +} diff -Nru librtr-0.6.3/tools/templates/csv librtr-0.7.0/tools/templates/csv --- librtr-0.6.3/tools/templates/csv 1970-01-01 00:00:00.000000000 +0000 +++ librtr-0.7.0/tools/templates/csv 2019-07-18 08:26:08.000000000 +0000 @@ -0,0 +1,3 @@ +{{#roas}} +{{prefix}}, {{length}}, {{maxlen}}, {{origin}} +{{/roas}} diff -Nru librtr-0.6.3/tools/templates/csvwithheader librtr-0.7.0/tools/templates/csvwithheader --- librtr-0.6.3/tools/templates/csvwithheader 1970-01-01 00:00:00.000000000 +0000 +++ librtr-0.7.0/tools/templates/csvwithheader 2019-07-18 08:26:08.000000000 +0000 @@ -0,0 +1,4 @@ +prefix, minlen, maxlen, asn +{{#roas}} +{{prefix}}, {{length}}, {{maxlen}}, {{origin}} +{{/roas}} diff -Nru librtr-0.6.3/tools/templates/default librtr-0.7.0/tools/templates/default --- librtr-0.6.3/tools/templates/default 1970-01-01 00:00:00.000000000 +0000 +++ librtr-0.7.0/tools/templates/default 2019-07-18 08:26:08.000000000 +0000 @@ -0,0 +1,3 @@ +{{#roas}} +{{prefix}}/{{length}}-{{maxlen}} AS {{origin}} +{{/roas}} diff -Nru librtr-0.6.3/tools/templates/json librtr-0.7.0/tools/templates/json --- librtr-0.6.3/tools/templates/json 1970-01-01 00:00:00.000000000 +0000 +++ librtr-0.7.0/tools/templates/json 2019-07-18 08:26:08.000000000 +0000 @@ -0,0 +1,10 @@ +[ +{{#roas}} + { + "prefix": "{{prefix}}", + "length": "{{length}}", + "maxlen": "{{maxlen}}", + "origin": "{{origin}}" + }{{^last}},{{/last}} +{{/roas}} +] diff -Nru librtr-0.6.3/tools/templates.h.cmake librtr-0.7.0/tools/templates.h.cmake --- librtr-0.6.3/tools/templates.h.cmake 1970-01-01 00:00:00.000000000 +0000 +++ librtr-0.7.0/tools/templates.h.cmake 2019-07-18 08:26:08.000000000 +0000 @@ -0,0 +1,20 @@ +/* + * This file is part of RTRlib. + * + * This file is subject to the terms and conditions of the MIT license. + * See the file LICENSE in the top level directory for more details. + * + * Website: http://rtrlib.realmv6.org/ + */ + + +struct pfx_output_template { + const char *name; + const char *template; +}; + + +const struct pfx_output_template templates[] = { +${TEMPLATES} +{NULL, NULL} +}; diff -Nru librtr-0.6.3/.travis.yml librtr-0.7.0/.travis.yml --- librtr-0.6.3/.travis.yml 2018-12-09 12:13:50.000000000 +0000 +++ librtr-0.7.0/.travis.yml 2019-07-18 08:26:08.000000000 +0000 @@ -1,21 +1,7 @@ -sudo: required -dist: trusty +dist: xenial language: c -before_install: - - sudo apt-get -qq update - - sudo apt-get -y install cppcheck doxygen libssh-dev - -install: - - wget https://cmocka.org/files/1.1/cmocka-1.1.0.tar.xz - - tar xvf cmocka-1.1.0.tar.xz && pushd cmocka-1.1.0 - - mkdir build && pushd build - - cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Debug - - make - - sudo make install - - popd && popd - env: global: # The next declaration is the encrypted COVERITY_SCAN_TOKEN, created @@ -32,6 +18,14 @@ build_command: "make" branch_pattern: coverity_scan + apt: + packages: + - cppcheck + - doxygen + - libssh-dev + - libcmocka-dev + - ctags + script: - if [ "${COVERITY_SCAN_BRANCH}" != 1 ]; then scripts/travis.sh; fi