├── .gitignore ├── .travis.yml ├── CMakeLists.txt ├── LICENSE ├── NOTICE ├── README.md ├── acf.dsc ├── acf.pc.in ├── acf.spec ├── an_proj.conf ├── cmake ├── CheckTest.cmake ├── FindCheck.cmake ├── FindGcov.cmake ├── FindLcov.cmake ├── Findcodecov.cmake └── llvm-cov-wrapper ├── common ├── an_array.c ├── an_array.h ├── an_average.h ├── an_buf.c ├── an_buf.h ├── an_buf_http.c ├── an_buf_http.h ├── an_buf_if.h ├── an_buf_libevent.c ├── an_buf_libevent.h ├── an_buf_plain.c ├── an_buf_plain.h ├── an_cc.h ├── an_dict.h ├── an_dict_common.h ├── an_hook.c ├── an_hook.h ├── an_hrlock.c ├── an_hrlock.h ├── an_hrw.h ├── an_interpolation_table.c ├── an_interpolation_table.h ├── an_interval.c ├── an_interval.h ├── an_malloc.c ├── an_malloc.h ├── an_map.h ├── an_md.c ├── an_md.h ├── an_poison.h ├── an_qsort.inc ├── an_rand.c ├── an_rand.h ├── an_ring.h ├── an_sampling.c ├── an_sampling.h ├── an_server.c ├── an_server.h ├── an_smr.c ├── an_smr.h ├── an_sparse_bitmap.c ├── an_sparse_bitmap.h ├── an_sstm.c ├── an_sstm.h ├── an_streaming_quantile.c ├── an_streaming_quantile.h ├── an_string.c ├── an_string.h ├── an_swlock.h ├── an_syslog.c ├── an_syslog.h ├── an_table_hash.h ├── an_thread.c ├── an_thread.h ├── an_time.h ├── an_zorder.c ├── an_zorder.h ├── btree.c ├── btree.h ├── check │ ├── check_an_array.c │ ├── check_an_average.c │ ├── check_an_hrw.c │ ├── check_an_interpolation_table.c │ ├── check_an_interval.c │ ├── check_an_sampling.c │ ├── check_an_smr.c │ ├── check_an_sparse_bitmap.c │ ├── check_an_streaming_quantile.c │ ├── check_an_thread.c │ ├── check_an_zorder.c │ ├── check_bsearch_bounds.c │ ├── check_bump.c │ ├── check_int_set.c │ └── check_log_linear_bin.c ├── int_set.c ├── int_set.h ├── log_linear_bin.h ├── memory │ ├── bump.c │ ├── bump.h │ ├── freelist.c │ ├── freelist.h │ ├── map.c │ ├── map.h │ ├── pool.c │ ├── pool.h │ ├── reserve.c │ └── reserve.h ├── rtbr │ ├── rtbr.c │ ├── rtbr.h │ ├── rtbr.md │ ├── rtbr_impl.c │ ├── rtbr_impl.h │ ├── rtbr_poll.c │ ├── rtbr_record.c │ └── rtbr_tid.c ├── tsearch.inc ├── util.c ├── util.h └── x86_64 │ ├── cpuid.c │ ├── cpuid.h │ └── x86_64.c ├── debian.changelog ├── debian.compat ├── debian.control ├── debian.copyright ├── debian.rules ├── example ├── Makefile └── main.c ├── include ├── an_allocator.h ├── an_array.h ├── an_bitmap.h ├── an_cc.h ├── an_charset.h ├── an_itoa.h ├── an_syslog.h └── an_util.h ├── src ├── an_allocator.c ├── an_array.c ├── an_charset.c ├── an_itoa.c ├── an_syslog.c └── an_util.c ├── test ├── CMakeLists.txt ├── check_an_allocator.c ├── check_an_array.c ├── check_an_array_cpp.cc ├── check_an_charset.c ├── check_an_itoa.c ├── check_an_util.c └── check_bitmap.c └── third_party └── http-parser ├── AUTHORS ├── LICENSE-MIT ├── Makefile ├── README.md ├── bench.c ├── contrib ├── parsertrace.c └── url_parser.c ├── http_parser.c ├── http_parser.gyp ├── http_parser.h └── test.c /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | core 3 | core.* 4 | vgcore.* 5 | ctags 6 | *~ 7 | *# 8 | *.swp 9 | *.swo 10 | *.swn 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | script: cmake 3 | 4 | dist: trusty 5 | sudo: required 6 | group: edge 7 | 8 | env: 9 | global: 10 | # The next declaration is the encrypted COVERITY_SCAN_TOKEN, created 11 | # via the "travis encrypt" command using the project repo's public key 12 | - secure: "Zn1EgSdB3nsVXp/c6OTWrD7o/Sofc/jhbBHOePWrG94qhzuU06kQlaONDdOFm//WXWuhvbLxCXZvBPTnVoYk72F1DUQREnaZ5+Wvoi/Xju1of49ynbX28EZrK8RjYHy1yD8DXJAYEsgD+bDoSWGYFJ8bUqgJq1QFBUKkGBum7OOikidQ4R2VBAGZUSL/1q7McgiOOrhdqS7HoOs5xUzd+Y2Kcr02SIdaw3ty6m82+5HCcrIZbSlGYAWC7DD/VKn6xlIF4Ap/STPMoO2xTNrNLzG4b4LgRvolvHvXFRJz3r49D7oaU9AufCpXNLzuC7IpjvGO1ORBSHZni4VfHuPCt1tJvjAmEc7iWYcumnI/wIAGaSLX+/e+bWkfi5j2zX97kKYO8ly5imwrcO4oLuThe1fVhseyvQiufDLltTyjAlYosR0InksWYuBv/YKREKGyjG9kGwl5qUPj07w2X3E9Eur6OCmjjQiX3cYXWP2ffZx+0SQHSw15JPs4RuaiIKdNiKh0kBMG+nrSUuYAgBqxYcUDVfuQJz3AZXRIO1/uVtBYfpROS0TgnN8NewbEhasICMsAgZDwyWbUDcGxjTA/U4rAIv36hhWTXisCtnzsttuU3N2zdutdc/xNlZvAaa6N1bUz8IF+jhMSF4NZAXvt0mdoWholqrl/+MnmGQyZyEs=" 13 | 14 | script: 15 | - mkdir build 16 | - cd build 17 | - cmake .. ${CMAKE_OPTIONS} 18 | - make 19 | - make test 20 | 21 | matrix: 22 | include: 23 | 24 | # Coveralls 25 | - os: linux 26 | compiler: gcc 27 | addons: 28 | apt: 29 | sources: ['ubuntu-toolchain-r-test'] 30 | packages: ['check', 'g++-4.9'] 31 | before_script: 32 | - pip install --user cpp-coveralls 33 | after_success: 34 | - coveralls --root .. --build-root . --include src --gcov 'gcov-4.9' --gcov-options '\-lp' 35 | env: 36 | - COMPILER=g++-4.9 37 | - CMAKE_OPTIONS=-DENABLE_COVERAGE=ON 38 | 39 | # Coverity 40 | - os: linux 41 | compiler: gcc 42 | before_install: echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca-certificates.crt 43 | addons: 44 | apt: 45 | sources: ['ubuntu-toolchain-r-test'] 46 | packages: ['check', 'g++-4.9'] 47 | coverity_scan: 48 | project: 49 | name: "appnexus/acf" 50 | description: "Build submitted via Travis CI" 51 | notification_email: asweeney86@gmail.com 52 | build_command_prepend: "mkdir coverity_build ; cd coverity_build ; cmake .. ; cd .." 53 | build_command: "make -C coverity_build" 54 | branch_pattern: coverity_scan 55 | env: 56 | - COMPILER=g++-4.9 57 | - TESTING=coverity 58 | 59 | # Linux / GCC 60 | - os: linux 61 | compiler: gcc 62 | env: COMPILER=gcc-4.9 63 | addons: 64 | apt: 65 | sources: ['ubuntu-toolchain-r-test'] 66 | packages: ['check', 'gcc-4.9'] 67 | 68 | - os: linux 69 | compiler: gcc 70 | env: COMPILER=gcc-5 71 | addons: 72 | apt: 73 | sources: ['ubuntu-toolchain-r-test'] 74 | packages: ['check', 'gcc-5'] 75 | 76 | - os: linux 77 | compiler: gcc 78 | env: COMPILER=gcc-6 79 | addons: 80 | apt: 81 | sources: ['ubuntu-toolchain-r-test'] 82 | packages: ['check', 'gcc-6'] 83 | 84 | - os: linux 85 | compiler: gcc 86 | env: COMPILER=gcc-7 87 | addons: 88 | apt: 89 | sources: ['ubuntu-toolchain-r-test'] 90 | packages: ['check', 'gcc-7'] 91 | 92 | # Linux / Clang 93 | - os: linux 94 | compiler: clang 95 | env: COMPILER=clang-3.5 96 | addons: 97 | apt: 98 | sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.5'] 99 | packages: ['check', 'g++-6', 'clang-3.5'] 100 | 101 | - os: linux 102 | compiler: clang 103 | env: COMPILER=clang-3.6 104 | addons: 105 | apt: 106 | sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.6'] 107 | packages: ['check', 'g++-6', 'clang-3.6'] 108 | 109 | - os: linux 110 | compiler: clang 111 | env: COMPILER=clang-3.7 112 | addons: 113 | apt: 114 | sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.7'] 115 | packages: ['check', 'g++-6', 'clang-3.7'] 116 | 117 | - os: linux 118 | compiler: clang 119 | env: COMPILER=clang-3.8 120 | addons: 121 | apt: 122 | sources: ['ubuntu-toolchain-r-test'] 123 | packages: ['check', 'g++-6', 'clang-3.8'] 124 | 125 | - os: linux 126 | compiler: clang 127 | env: COMPILER=clang-3.9 128 | addons: 129 | apt: 130 | sources: ['ubuntu-toolchain-r-test'] 131 | packages: ['check', 'g++-6', 'clang-3.9'] 132 | 133 | - os: linux 134 | compiler: clang 135 | env: COMPILER=clang-4.0 136 | addons: 137 | apt: 138 | sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-trusty-4.0'] 139 | packages: ['check', 'g++-6', 'clang-4.0'] 140 | 141 | - os: linux 142 | compiler: clang 143 | env: COMPILER=clang-5.0 144 | addons: 145 | apt: 146 | sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-trusty-5.0'] 147 | packages: ['check', 'g++-6', 'clang-5.0'] 148 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.0) 2 | project(acf C CXX ASM) 3 | 4 | option(DISABLE_TESTS "If tests should be compiled or not" OFF) 5 | 6 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release") 7 | if (NOT CMAKE_BUILD_TYPE) 8 | set(CMAKE_BUILD_TYPE "Debug" CACHE STRING "default to debug build type" FORCE) 9 | endif() 10 | 11 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin) 12 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib) 13 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib) 14 | 15 | option(SANITIZE "Enable Clang sanitizers" OFF) 16 | if(SANITIZE AND NOT CMAKE_C_COMPILER_ID MATCHES "Clang") 17 | message(WARNING "SANITIZE is only supported for Clang ... disabling") 18 | set(SANITIZE OFF) 19 | endif() 20 | 21 | if(SANITIZE) 22 | message(STATUS "Enabling Clang sanitizers") 23 | set(ASAN_BUILD_FLAGS " -fno-sanitize-recover -fno-omit-frame-pointer -fno-optimize-sibling-calls -fsanitize=address ") 24 | SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${ASAN_BUILD_FLAGS}") 25 | SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${ASAN_BUILD_FLAGS}") 26 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address -fsanitize=undefined ") 27 | endif() 28 | 29 | set(COMMON_FLAGS "${COMMON_FLAGS} -DMARCH_x86_64") 30 | set(COMMON_FLAGS "${COMMON_FLAGS} -D_GNU_SOURCE") 31 | set(COMMON_FLAGS "${COMMON_FLAGS} -Wall") 32 | set(COMMON_FLAGS "${COMMON_FLAGS} -Werror") 33 | set(COMMON_FLAGS "${COMMON_FLAGS} -Wextra") 34 | set(COMMON_FLAGS "${COMMON_FLAGS} -Wformat=2") 35 | set(COMMON_FLAGS "${COMMON_FLAGS} -Wno-unused-parameter") 36 | set(COMMON_FLAGS "${COMMON_FLAGS} -Wno-unused-function") 37 | set(COMMON_FLAGS "${COMMON_FLAGS} -Wno-format-nonliteral") 38 | set(COMMON_FLAGS "${COMMON_FLAGS} -fms-extensions") 39 | set(COMMON_FLAGS "${COMMON_FLAGS} -fno-omit-frame-pointer") 40 | set(COMMON_FLAGS "${COMMON_FLAGS} -fno-strict-aliasing") 41 | set(COMMON_FLAGS "${COMMON_FLAGS} -fvisibility=hidden") 42 | set(COMMON_FLAGS "${COMMON_FLAGS} -fPIC") 43 | set(COMMON_FLAGS "${COMMON_FLAGS} -g3") 44 | set(COMMON_FLAGS "${COMMON_FLAGS} -pipe") 45 | set(COMMON_FLAGS "${COMMON_FLAGS} -gdwarf-2") 46 | 47 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COMMON_FLAGS}") 48 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99") 49 | 50 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COMMON_FLAGS}") 51 | set(CMAKE_C_FLAGS_RELWITHDEBINFO "-g2 -O2") 52 | set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-g2 -O2") 53 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake/") 54 | set(CMAKE_MACOSX_RPATH 1) 55 | 56 | set(SOURCE_DIR ${PROJECT_SOURCE_DIR}/src) 57 | 58 | find_package(Threads REQUIRED) 59 | 60 | include_directories(${SOURCE_DIR}) 61 | include_directories(${PROJECT_SOURCE_DIR}/include) 62 | include_directories(${PROJECT_BINARY_DIR}) 63 | 64 | list(APPEND SRC 65 | ${SOURCE_DIR}/an_allocator.c 66 | ${SOURCE_DIR}/an_array.c 67 | ${SOURCE_DIR}/an_charset.c 68 | ${SOURCE_DIR}/an_itoa.c 69 | ${SOURCE_DIR}/an_syslog.c 70 | ${SOURCE_DIR}/an_util.c 71 | ) 72 | 73 | set(LIBS 74 | ) 75 | 76 | include(GenerateExportHeader) 77 | add_library(acf SHARED ${SRC}) 78 | generate_export_header(acf) 79 | target_link_libraries(acf ${LIBS}) 80 | 81 | set(VERSION_MAJOR 0) 82 | set(VERSION_MINOR 1) 83 | set(VERSION_PATCH 1) 84 | set(VERSION_STRING ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) 85 | 86 | set_target_properties(acf PROPERTIES VERSION ${VERSION_STRING} SOVERSION ${VERSION_MAJOR}) 87 | 88 | get_property(LIB64 GLOBAL PROPERTY FIND_LIBRARY_USE_LIB64_PATHS) 89 | if ("${LIB64}" STREQUAL "TRUE") 90 | set(LIBSUFFIX 64) 91 | else() 92 | set(LIBSUFFIX "") 93 | endif() 94 | 95 | set(INSTALL_LIB_DIR lib${LIBSUFFIX} CACHE PATH "Installation directory for libraries") 96 | mark_as_advanced(INSTALL_LIB_DIR) 97 | 98 | install(TARGETS acf EXPORT acfTargets 99 | LIBRARY DESTINATION ${INSTALL_LIB_DIR} 100 | ARCHIVE DESTINATION ${INSTALL_LIB_DIR} 101 | RUNTIME DESTINATION bin 102 | INCLUDES DESTINATION include 103 | ) 104 | 105 | file(GLOB INSTALL_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h) 106 | list(APPEND INSTALL_HEADERS ${PROJECT_BINARY_DIR}/acf_export.h) 107 | 108 | install(FILES ${INSTALL_HEADERS} DESTINATION include/acf COMPONENT headers) 109 | 110 | CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/acf.pc.in 111 | ${PROJECT_BINARY_DIR}/acf.pc) 112 | 113 | install(FILES ${PROJECT_BINARY_DIR}/acf.pc 114 | DESTINATION "${CMAKE_INSTALL_PREFIX}/${INSTALL_LIB_DIR}/pkgconfig") 115 | 116 | if (NOT ${DISABLE_TESTS}) 117 | enable_testing() 118 | find_package(codecov) 119 | add_coverage(acf) 120 | add_subdirectory(test) 121 | coverage_evaluate() 122 | add_custom_target(coverage 123 | make && make test && make gcov && make lcov 124 | ) 125 | endif() 126 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | AppNexus Common Framework (ACF) 2 | Copyright 2017 AppNexus, Inc. 3 | -------------------------------------------------------------------------------- /acf.dsc: -------------------------------------------------------------------------------- 1 | Format: 1.0 2 | Source: acf 3 | Binary: acf 4 | Architecture: any 5 | Version: 0.1 6 | Maintainer: Mobai Zhang 7 | Standards-Version: 3.7.2 8 | Build-Depends: debhelper (>= 9.0.0), 9 | g++-4.8, 10 | pkg-config, 11 | check, 12 | cmake (>= 2.8.0) 13 | -------------------------------------------------------------------------------- /acf.pc.in: -------------------------------------------------------------------------------- 1 | prefix=${CMAKE_INSTALL_PREFIX} 2 | includedir=${CMAKE_INSTALL_PREFIX}/include 3 | libdir=${CMAKE_INSTALL_PREFIX}/${INSTALL_LIB_DIR} 4 | 5 | Name: acf 6 | Description: AppNexus Common Framework Library 7 | Version: ${CMAKE_ACF_VERSION} 8 | Libs: -L${CMAKE_INSTALL_PREFIX}/${INSTALL_LIB_DIR} -lacf 9 | Cflags: -I${CMAKE_INSTALL_PREFIX}/include 10 | -------------------------------------------------------------------------------- /acf.spec: -------------------------------------------------------------------------------- 1 | Summary: acf library 2 | Name: acf 3 | Version: 0 4 | Release: 0 5 | License: AppNexus, Inc. 6 | Group: Applications/Internet 7 | Source: libacf-%{version}.tar.gz 8 | BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot 9 | Vendor: AppNexus, Inc. 10 | Packager: Mobai Zhang 11 | 12 | BuildRequires: cmake >= 2.8.0 13 | 14 | %if 0%{?rhel} < 6 15 | BuildRequires: gcc44, gcc44-c++ 16 | %else 17 | BuildRequires: gcc, gcc-c++ 18 | %endif 19 | 20 | %description 21 | This is shared C code for the real-time platform at AppNexus. 22 | 23 | %prep 24 | 25 | %setup -q 26 | rm -rf %{buildroot} 27 | 28 | %build 29 | 30 | %if 0%{?rhel} < 6 31 | %define _CC gcc44 32 | %define _CXX g++44 33 | %else 34 | %define _CC gcc 35 | %define _CXX g++ 36 | %endif 37 | 38 | mkdir build 39 | cd build 40 | CC=%{_CC} CXX=%{_CXX} cmake -DDISABLE_TESTS=ON -DCMAKE_BUILD_TYPE=RELWITHDEBINFO -DCMAKE_INSTALL_PREFIX:PATH=/usr -DCMAKE_ACF_VERSION=%{version} .. 41 | make 42 | cd ..; 43 | 44 | %install 45 | cd build && make install DESTDIR=%{buildroot} 46 | 47 | %files 48 | %defattr(-,root,root) 49 | /usr/include/acf/*.h 50 | /usr/lib64/libacf.* 51 | /usr/lib64/pkgconfig/acf.pc 52 | -------------------------------------------------------------------------------- /an_proj.conf: -------------------------------------------------------------------------------- 1 | [build] 2 | build_project=AdNexus:AdServer 3 | -------------------------------------------------------------------------------- /cmake/CheckTest.cmake: -------------------------------------------------------------------------------- 1 | file(MAKE_DIRECTORY "${PROJECT_BINARY_DIR}/check") 2 | 3 | macro(CHECK_TEST test_name) 4 | set(options OPTIONAL) 5 | set(oneValueArgs NAME) 6 | set(multiValueArgs LIBS SRC DEPS) 7 | cmake_parse_arguments(CHECK_TEST "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) 8 | 9 | add_executable(${test_name} ${CHECK_TEST_SRC}) 10 | target_link_libraries(${test_name} ${CHECK_LIBRARIES} ${CHECK_TEST_LIBS}) 11 | target_include_directories(${test_name} PUBLIC ${CHECK_INCLUDE_DIR}) 12 | add_test(NAME ${test_name} COMMAND $ WORKING_DIRECTORY ${PROJECT_BINARY_DIR}) 13 | if (DEFINED CHECK_TEST_DEPS) 14 | add_dependencies(${test_name} ${CHECK_TEST_DEPS}) 15 | endif() 16 | endmacro() 17 | -------------------------------------------------------------------------------- /cmake/FindCheck.cmake: -------------------------------------------------------------------------------- 1 | # - Try to find the CHECK libraries 2 | # Once done this will define 3 | # 4 | # CHECK_FOUND - system has check 5 | # CHECK_INCLUDE_DIR - the check include directory 6 | # CHECK_LIBRARIES - check library 7 | # 8 | # This configuration file for finding libcheck is originally from 9 | # the opensync project. The originally was downloaded from here: 10 | # opensync.org/browser/branches/3rd-party-cmake-modules/modules/FindCheck.cmake 11 | # 12 | # Copyright (c) 2007 Daniel Gollub 13 | # Copyright (c) 2007 Bjoern Ricks 14 | # 15 | # Redistribution and use is allowed according to the terms of the New 16 | # BSD license. 17 | # For details see the accompanying COPYING-CMAKE-SCRIPTS file. 18 | 19 | 20 | INCLUDE( FindPkgConfig ) 21 | 22 | # Take care about check.pc settings 23 | PKG_SEARCH_MODULE( CHECK check ) 24 | 25 | # Look for CHECK include dir and libraries 26 | IF( NOT CHECK_FOUND ) 27 | IF ( CHECK_INSTALL_DIR ) 28 | MESSAGE ( STATUS "Using override CHECK_INSTALL_DIR to find check" ) 29 | SET ( CHECK_INCLUDE_DIR "${CHECK_INSTALL_DIR}/include" ) 30 | SET ( CHECK_INCLUDE_DIRS "${CHECK_INCLUDE_DIR}" ) 31 | FIND_LIBRARY( CHECK_LIBRARY NAMES check PATHS "${CHECK_INSTALL_DIR}/lib" ) 32 | FIND_LIBRARY( COMPAT_LIBRARY NAMES compat PATHS "${CHECK_INSTALL_DIR}/lib" ) 33 | SET ( CHECK_LIBRARIES "${CHECK_LIBRARY}" "${COMPAT_LIBRARY}" ) 34 | ELSE ( CHECK_INSTALL_DIR ) 35 | FIND_PATH( CHECK_INCLUDE_DIR check.h ) 36 | FIND_LIBRARY( CHECK_LIBRARIES NAMES check ) 37 | ENDIF ( CHECK_INSTALL_DIR ) 38 | 39 | IF ( CHECK_INCLUDE_DIR AND CHECK_LIBRARIES ) 40 | SET( CHECK_FOUND 1 ) 41 | IF ( NOT Check_FIND_QUIETLY ) 42 | MESSAGE ( STATUS "Found CHECK: ${CHECK_LIBRARIES}" ) 43 | ENDIF ( NOT Check_FIND_QUIETLY ) 44 | ELSE ( CHECK_INCLUDE_DIR AND CHECK_LIBRARIES ) 45 | IF ( Check_FIND_REQUIRED ) 46 | MESSAGE( FATAL_ERROR "Could NOT find CHECK" ) 47 | ELSE ( Check_FIND_REQUIRED ) 48 | IF ( NOT Check_FIND_QUIETLY ) 49 | MESSAGE( STATUS "Could NOT find CHECK" ) 50 | ENDIF ( NOT Check_FIND_QUIETLY ) 51 | ENDIF ( Check_FIND_REQUIRED ) 52 | ENDIF ( CHECK_INCLUDE_DIR AND CHECK_LIBRARIES ) 53 | ENDIF( NOT CHECK_FOUND ) 54 | 55 | # Hide advanced variables from CMake GUIs 56 | MARK_AS_ADVANCED( CHECK_INCLUDE_DIR CHECK_LIBRARIES ) 57 | 58 | -------------------------------------------------------------------------------- /cmake/llvm-cov-wrapper: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # This file is part of CMake-codecov. 4 | # 5 | # Copyright (c) 6 | # 2015-2017 RWTH Aachen University, Federal Republic of Germany 7 | # 8 | # See the LICENSE file in the package base directory for details 9 | # 10 | # Written by Alexander Haase, alexander.haase@rwth-aachen.de 11 | # 12 | 13 | if [ -z "$LLVM_COV_BIN" ] 14 | then 15 | echo "LLVM_COV_BIN not set!" >& 2 16 | exit 1 17 | fi 18 | 19 | 20 | # Get LLVM version to find out. 21 | LLVM_VERSION=$($LLVM_COV_BIN -version | grep -i "LLVM version" \ 22 | | sed "s/^\([A-Za-z ]*\)\([0-9]\).\([0-9]\).*$/\2.\3/g") 23 | 24 | if [ "$1" = "-v" ] 25 | then 26 | echo "llvm-cov-wrapper $LLVM_VERSION" 27 | exit 0 28 | fi 29 | 30 | 31 | if [ -n "$LLVM_VERSION" ] 32 | then 33 | MAJOR=$(echo $LLVM_VERSION | cut -d'.' -f1) 34 | MINOR=$(echo $LLVM_VERSION | cut -d'.' -f2) 35 | 36 | if [ $MAJOR -eq 3 ] && [ $MINOR -le 4 ] 37 | then 38 | if [ -f "$1" ] 39 | then 40 | filename=$(basename "$1") 41 | extension="${filename##*.}" 42 | 43 | case "$extension" in 44 | "gcno") exec $LLVM_COV_BIN --gcno="$1" ;; 45 | "gcda") exec $LLVM_COV_BIN --gcda="$1" ;; 46 | esac 47 | fi 48 | fi 49 | 50 | if [ $MAJOR -eq 3 ] && [ $MINOR -le 5 ] 51 | then 52 | exec $LLVM_COV_BIN $@ 53 | fi 54 | fi 55 | 56 | exec $LLVM_COV_BIN gcov $@ 57 | -------------------------------------------------------------------------------- /common/an_average.h: -------------------------------------------------------------------------------- 1 | #ifndef AN_AVERAGE_H 2 | #define AN_AVERAGE_H 3 | 4 | #include 5 | #include 6 | 7 | #include "an_cc.h" 8 | #include "util.h" 9 | 10 | #define AN_AVERAGE_DENOM_BITS 8ULL 11 | 12 | /** 13 | * Geometrically decaying average, in fixed precision rationals. 14 | * 15 | * values is AN_AVERAGE_DENOM_BITS for the denominator, and the rest 16 | * of an uint64_t for the numerator. 17 | * 18 | * The counter may lose updates, but the numerator and denominator will 19 | * always be consistent. 20 | */ 21 | struct an_average { 22 | uint64_t values; 23 | }; 24 | 25 | /** 26 | * @brief read the current numerator and denominator for the running average. 27 | * @param average the an_average to read 28 | * @param num overwritten with the numerator 29 | * @param denom overwritten with the denominator 30 | */ 31 | static AN_CC_UNUSED void 32 | an_average_read(const struct an_average *average, uint64_t *num, uint64_t *denom) 33 | { 34 | uint64_t data, mask; 35 | 36 | data = ck_pr_load_64(&average->values); 37 | mask = (1ULL << AN_AVERAGE_DENOM_BITS) - 1; 38 | 39 | *num = data >> AN_AVERAGE_DENOM_BITS; 40 | *denom = data & mask; 41 | return; 42 | } 43 | 44 | /** 45 | * @brief insert a new observation in the running average 46 | * @param average the an_average to update 47 | * @param value the new observation 48 | */ 49 | static AN_CC_UNUSED void 50 | an_average_insert(struct an_average *average, uint64_t value) 51 | { 52 | uint64_t denom, limit, num, output; 53 | uint64_t mask = (1ULL << AN_AVERAGE_DENOM_BITS) - 1; 54 | 55 | an_average_read(average, &num, &denom); 56 | 57 | if (AN_CC_UNLIKELY(denom >= mask)) { 58 | /* 59 | * Round to closest after multiplication by 3/4. 60 | * 61 | * 3/4 is arbitrary, and can be parameterised if 62 | * necessary. The rationale is that 1/2 is very 63 | * uneven, and ratios closer to 1 increase the risk of 64 | * consecutive decays within the same internal 65 | * auction, which makes an_average_increment less 66 | * accurate. 67 | */ 68 | num = (3 * num + 2) / 4; 69 | denom = (3 * denom + 2) / 4; 70 | } 71 | 72 | limit = (UINT64_MAX >> AN_AVERAGE_DENOM_BITS) - num; 73 | if (AN_CC_UNLIKELY(value > limit)) { 74 | output = UINT64_MAX / 2; 75 | output &= ~mask; 76 | output |= (denom / 2) + 1; 77 | } else { 78 | num += value; 79 | denom++; 80 | output = (num << AN_AVERAGE_DENOM_BITS) | denom; 81 | } 82 | 83 | ck_pr_store_64(&average->values, output); 84 | return; 85 | } 86 | 87 | /** 88 | * @brief increment a pre-existing observation in the running average 89 | * @param average the an_average to update 90 | * @param value the increment to add to a prior observation (denom is unaffected) 91 | */ 92 | static AN_CC_UNUSED void 93 | an_average_increment(struct an_average *average, uint64_t value) 94 | { 95 | uint64_t denom, limit, num, output; 96 | uint64_t mask = (1ULL << AN_AVERAGE_DENOM_BITS) - 1; 97 | 98 | an_average_read(average, &num, &denom); 99 | limit = (UINT64_MAX >> AN_AVERAGE_DENOM_BITS) - num; 100 | if (AN_CC_UNLIKELY(value > limit)) { 101 | output = UINT64_MAX / 2; 102 | output &= ~mask; 103 | output |= ((denom + 1) / 2); 104 | } else { 105 | num += value; 106 | output = (num << AN_AVERAGE_DENOM_BITS) | denom; 107 | } 108 | 109 | ck_pr_store_64(&average->values, output); 110 | return; 111 | } 112 | 113 | /** 114 | * @brief initialize the an_average struct 115 | * @param average the an_average struct 116 | */ 117 | static AN_CC_UNUSED void 118 | an_average_init(struct an_average *average) 119 | { 120 | 121 | memset(average, 0, sizeof(struct an_average)); 122 | } 123 | 124 | #endif /* !defined(AN_AVERAGE_H) */ 125 | -------------------------------------------------------------------------------- /common/an_buf.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include "common/an_array.h" 7 | #include "common/an_buf_if.h" 8 | #include "common/an_malloc.h" 9 | 10 | /* Buffer flags */ 11 | #define AN_BUF_OWNED 1 12 | #define AN_BUF_FROZEN 2 13 | 14 | #define BUF_OP(BUF, OP, ...) (BUF->buf.bif->OP(buf, ##__VA_ARGS__)) 15 | 16 | struct an_buf_cleanup { 17 | an_buf_cleanup_t *cb; 18 | void *arg; 19 | }; 20 | AN_ARRAY(an_buf_cleanup, cleanup_list); 21 | 22 | struct an_buf { 23 | const struct an_buf_if *bif; 24 | AN_ARRAY_INSTANCE(cleanup_list) cleanups; 25 | int flags; 26 | char data[] CK_CC_ALIGN(16); 27 | }; 28 | 29 | struct an_rbuf { 30 | struct an_buf buf; 31 | }; 32 | 33 | struct an_wbuf { 34 | struct an_buf buf; 35 | }; 36 | 37 | static AN_MALLOC_DEFINE(an_buf_token, 38 | .string = "an_buf", 39 | .mode = AN_MEMORY_MODE_VARIABLE); 40 | 41 | /* Get access to the backend-specific data. */ 42 | void * 43 | an_buf_private(an_buf_ptr_t buf) 44 | { 45 | 46 | return buf.rbuf->buf.data; 47 | } 48 | 49 | static void 50 | an_buf_init(struct an_buf *buf, const struct an_buf_if *bif) 51 | { 52 | 53 | buf->flags = 0; 54 | buf->bif = bif; 55 | AN_ARRAY_INIT(cleanup_list, &buf->cleanups, 0); 56 | } 57 | 58 | struct an_rbuf * 59 | an_rbuf_create(const struct an_buf_if *bif, size_t size) 60 | { 61 | struct an_rbuf *buf; 62 | 63 | buf = an_calloc_region(an_buf_token, 1, sizeof(struct an_rbuf) + size); 64 | an_buf_init(&buf->buf, bif); 65 | return buf; 66 | } 67 | 68 | struct an_wbuf * 69 | an_wbuf_create(const struct an_buf_if *bif, size_t size) 70 | { 71 | struct an_wbuf *buf; 72 | 73 | buf = an_calloc_region(an_buf_token, 1, sizeof(struct an_wbuf) + size); 74 | an_buf_init(&buf->buf, bif); 75 | return buf; 76 | } 77 | 78 | /* 79 | * Consumer API. 80 | */ 81 | 82 | void 83 | an_buf_own(an_buf_ptr_t buf) 84 | { 85 | 86 | buf.rbuf->buf.flags |= AN_BUF_OWNED; 87 | } 88 | 89 | void 90 | an_buf_disown(an_buf_ptr_t buf) 91 | { 92 | 93 | buf.rbuf->buf.flags &= ~AN_BUF_OWNED; 94 | } 95 | 96 | bool 97 | an_buf_owned(an_buf_const_ptr_t buf) 98 | { 99 | 100 | return buf.rbuf->buf.flags & AN_BUF_OWNED; 101 | } 102 | 103 | void 104 | an_buf_add_cleanup(an_buf_ptr_t buf, an_buf_cleanup_t *cb, void *arg) 105 | { 106 | struct an_buf_cleanup c = { .cb = cb, .arg = arg }; 107 | 108 | AN_ARRAY_PUSH(cleanup_list, &buf.rbuf->buf.cleanups, &c); 109 | } 110 | 111 | void 112 | an_buf_destroy(an_buf_ptr_t buf) 113 | { 114 | AN_ARRAY_INSTANCE(cleanup_list) *cleanups; 115 | struct an_buf_cleanup *c; 116 | 117 | cleanups = &buf.rbuf->buf.cleanups; 118 | 119 | while ((c = AN_ARRAY_POP(cleanup_list, cleanups, NULL)) != NULL) { 120 | c->cb(buf, c->arg); 121 | } 122 | if (buf.rbuf->buf.bif->destroy != NULL) { 123 | BUF_OP(buf.rbuf, destroy); 124 | } 125 | AN_ARRAY_DEINIT(cleanup_list, cleanups); 126 | an_free(an_buf_token, buf.rbuf); 127 | } 128 | 129 | size_t 130 | an_buf_length(an_buf_const_ptr_t buf) 131 | { 132 | 133 | return BUF_OP(buf.const_rbuf, length); 134 | } 135 | 136 | /* API for write buffers */ 137 | void 138 | an_buf_add(struct an_wbuf *buf, const void *data, size_t len) 139 | { 140 | 141 | assert((buf->buf.flags & AN_BUF_FROZEN) == 0); 142 | BUF_OP(buf, add, data, len); 143 | } 144 | 145 | void 146 | an_buf_add_printf(struct an_wbuf *buf, const char *fmt, ...) 147 | { 148 | va_list ap; 149 | 150 | assert((buf->buf.flags & AN_BUF_FROZEN) == 0); 151 | va_start(ap, fmt); 152 | BUF_OP(buf, add_printf, fmt, ap); 153 | va_end(ap); 154 | } 155 | 156 | void 157 | an_buf_reset(struct an_wbuf *buf) 158 | { 159 | 160 | assert((buf->buf.flags & AN_BUF_FROZEN) == 0); 161 | BUF_OP(buf, reset); 162 | } 163 | 164 | void 165 | an_buf_freeze(struct an_wbuf *buf) 166 | { 167 | 168 | buf->buf.flags |= AN_BUF_FROZEN; 169 | } 170 | 171 | void 172 | an_buf_thaw(struct an_wbuf *buf) 173 | { 174 | 175 | buf->buf.flags &= ~AN_BUF_FROZEN; 176 | } 177 | 178 | /* API for read buffers */ 179 | const void * 180 | an_buf_linearize(struct an_rbuf *buf) 181 | { 182 | 183 | return BUF_OP(buf, linearize); 184 | } 185 | -------------------------------------------------------------------------------- /common/an_buf.h: -------------------------------------------------------------------------------- 1 | #ifndef AN_BUF_H_ 2 | #define AN_BUF_H_ 3 | 4 | #include 5 | #include 6 | 7 | /* 8 | * An abstract buffer API. 9 | * 10 | * The backend specific headers (an_buf_.h) contain the 11 | * functions required to create an an_[rw]buf object. Consumers should 12 | * include the backend specific header they need instead of this one. 13 | */ 14 | 15 | struct an_rbuf; 16 | struct an_wbuf; 17 | 18 | /* 19 | * We use transparent unions so that any function that can act on both 20 | * read and write buffers can be called with a pointer to either type 21 | * of buffer without needing to cast. 22 | * 23 | * You can think of a function having an an_buf_ptr_t parameter as a 24 | * function that accepts either a struct an_rbuf * or a struct an_wbuf *. 25 | * Similarly, an an_buf_const_ptr_t parameter is equivalent to a function 26 | * accepting either a const struct an_rbuf * or a const struct an_wbuf *. 27 | */ 28 | typedef union { 29 | struct an_rbuf *rbuf; 30 | struct an_wbuf *wbuf; 31 | } an_buf_ptr_t __attribute__((__transparent_union__)); 32 | 33 | typedef union { 34 | struct an_rbuf *rbuf; 35 | struct an_wbuf *wbuf; 36 | const struct an_rbuf *const_rbuf; 37 | const struct an_wbuf *const_wbuf; 38 | } an_buf_const_ptr_t __attribute__((__transparent_union__)); 39 | 40 | typedef void (an_buf_cleanup_t)(an_buf_ptr_t, void *); 41 | 42 | 43 | /* API for both read and write buffers. */ 44 | 45 | /* 46 | * Returns true if the underlying buffer is "owned" or false. 47 | * 48 | * When the underlying buffer is owned, it is destroyed when the parent 49 | * an_buf object is destroyed using an_buf_destroy(). If it isn't owned, 50 | * it will persist and remain usable after an_buf_destroy() is called. 51 | */ 52 | bool an_buf_owned(an_buf_const_ptr_t); 53 | /* Mark the underlying buffer as owned. */ 54 | void an_buf_own(an_buf_ptr_t); 55 | /* Mark the underlying buffer as *not* owned. */ 56 | void an_buf_disown(an_buf_ptr_t); 57 | /* Destroy an an_buf object (and the underlying buffer if owned). */ 58 | void an_buf_destroy(an_buf_ptr_t); 59 | /* Return the total length of the buffer. */ 60 | size_t an_buf_length(an_buf_const_ptr_t); 61 | /* Add a cleanup function to be called at destruction time. */ 62 | void an_buf_add_cleanup(an_buf_ptr_t, an_buf_cleanup_t *, void *); 63 | 64 | 65 | /* API for write buffers. */ 66 | 67 | /* Append data to a buffer. */ 68 | void an_buf_add(struct an_wbuf *, const void *, size_t); 69 | /* Append a printf() formatted string to the buffer. */ 70 | void an_buf_add_printf(struct an_wbuf *, const char *, ...) 71 | __attribute__((format(printf, 2, 3))); 72 | /* Empty the buffer. */ 73 | void an_buf_reset(struct an_wbuf *); 74 | /* 75 | * Mark this buffer as frozen. Any subsequent writes to this buffer 76 | * will cause an asertion failure. Mostly useful for debugging. 77 | */ 78 | void an_buf_freeze(struct an_wbuf *); 79 | /* Allow writes on this buffer again. */ 80 | void an_buf_thaw(struct an_wbuf *); 81 | 82 | 83 | /* API for read buffers. */ 84 | 85 | /* 86 | * Linearize the entire buffer chain in memory. 87 | * 88 | * This is a destructive operation: if the buffer hasn't been modified 89 | * since the last call, subsequent calls will be mostly free. The zone 90 | * of memory we return a pointer to is owned by the an_buf object. 91 | */ 92 | const void *an_buf_linearize(struct an_rbuf *); 93 | 94 | #endif /* AN_BUF_H_ */ 95 | -------------------------------------------------------------------------------- /common/an_buf_http.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "common/an_server.h" 8 | #include "common/an_buf_if.h" 9 | 10 | struct an_buf_http_state { 11 | struct an_buffer *hbuf; 12 | }; 13 | 14 | #define an_buf_http_get(buf) \ 15 | (((struct an_buf_http_state *)an_buf_private(buf))->hbuf) 16 | 17 | static size_t 18 | an_buf_http_length(an_buf_const_ptr_t buf) 19 | { 20 | struct an_buffer *hbuf; 21 | 22 | hbuf = an_buf_http_get(buf.wbuf); 23 | return hbuf->size; 24 | } 25 | 26 | static void 27 | an_buf_http_add(struct an_wbuf *buf, const void *data, size_t len) 28 | { 29 | struct an_buffer *hbuf; 30 | 31 | hbuf = an_buf_http_get(buf); 32 | 33 | assert(hbuf->size - hbuf->in >= len); 34 | memcpy(hbuf->data + hbuf->in, data, len); 35 | hbuf->in += len; 36 | } 37 | 38 | static void __attribute__((format(printf, 2, 0))) 39 | an_buf_http_add_printf(struct an_wbuf *buf, const char *fmt, va_list ap) 40 | { 41 | struct an_buffer *hbuf; 42 | size_t left; 43 | int len; 44 | 45 | hbuf = an_buf_http_get(buf); 46 | left = hbuf->size - hbuf->in; 47 | len = vsnprintf(hbuf->data + hbuf->in, left, fmt, ap); 48 | if ((unsigned)len >= left) { 49 | /* Output was truncated. */ 50 | hbuf->in += left; 51 | } else { 52 | hbuf->in += len; 53 | } 54 | } 55 | 56 | static const struct an_buf_if an_buf_http_if = { 57 | .length = an_buf_http_length, 58 | .add = an_buf_http_add, 59 | .add_printf = an_buf_http_add_printf 60 | }; 61 | 62 | struct an_wbuf * 63 | an_buf_http_wrap(struct an_buffer *hbuf) 64 | { 65 | struct an_buf_http_state *s; 66 | struct an_wbuf *buf; 67 | 68 | buf = an_wbuf_create(&an_buf_http_if, sizeof(struct an_buf_http_state)); 69 | s = an_buf_private(buf); 70 | s->hbuf = hbuf; 71 | return buf; 72 | } 73 | -------------------------------------------------------------------------------- /common/an_buf_http.h: -------------------------------------------------------------------------------- 1 | #ifndef AN_BUF_HTTP_H_ 2 | #define AN_BUF_HTTP_H_ 3 | 4 | #include "common/an_buf.h" 5 | 6 | struct an_buffer; 7 | 8 | struct an_wbuf *an_buf_http_wrap(struct an_buffer *); 9 | 10 | #endif /* AN_BUF_HTTP_H_ */ 11 | -------------------------------------------------------------------------------- /common/an_buf_if.h: -------------------------------------------------------------------------------- 1 | #ifndef AN_BUF_IF_H_ 2 | #define AN_BUF_IF_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "common/an_buf.h" 9 | 10 | struct an_buf_if { 11 | void (*destroy)(an_buf_ptr_t); 12 | size_t (*length)(an_buf_const_ptr_t); 13 | const void *(*linearize)(struct an_rbuf *); 14 | void (*add)(struct an_wbuf *, const void *, size_t); 15 | void (*add_printf)(struct an_wbuf *, const char *, va_list); 16 | void (*reset)(struct an_wbuf *); 17 | }; 18 | 19 | struct an_rbuf *an_rbuf_create(const struct an_buf_if *, size_t); 20 | struct an_wbuf *an_wbuf_create(const struct an_buf_if *, size_t); 21 | void *an_buf_private(an_buf_ptr_t); 22 | 23 | #endif /* AN_BUF_IF_H_ */ 24 | -------------------------------------------------------------------------------- /common/an_buf_libevent.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include "common/an_buf_if.h" 8 | 9 | /* A reasonably large initial buffer size */ 10 | #define AN_BUF_LIBEVENT_INITIAL_SIZE (2 * getpagesize() - CK_MD_CACHELINE) 11 | 12 | struct an_buf_libevent_state { 13 | struct evbuffer *evbuf; 14 | }; 15 | 16 | #define an_buf_libevent_get(buf) \ 17 | (((struct an_buf_libevent_state *)an_buf_private(buf))->evbuf) 18 | 19 | static void 20 | an_buf_libevent_destroy(an_buf_ptr_t buf) 21 | { 22 | struct evbuffer *evbuf; 23 | 24 | if (!an_buf_owned(buf.rbuf)) { 25 | return; 26 | } 27 | evbuf = an_buf_libevent_get(buf); 28 | evbuffer_free(evbuf); 29 | } 30 | 31 | static size_t 32 | an_buf_libevent_length(an_buf_const_ptr_t buf) 33 | { 34 | struct evbuffer *evbuf; 35 | 36 | evbuf = an_buf_libevent_get(buf.rbuf); 37 | return evbuffer_get_length(evbuf); 38 | } 39 | 40 | static void 41 | an_buf_libevent_add(struct an_wbuf *buf, const void *data, size_t len) 42 | { 43 | struct evbuffer *evbuf; 44 | 45 | evbuf = an_buf_libevent_get(buf); 46 | evbuffer_add(evbuf, data, len); 47 | } 48 | 49 | static void __attribute__((format(printf, 2, 0))) 50 | an_buf_libevent_add_printf(struct an_wbuf *buf, const char *fmt, va_list ap) 51 | { 52 | struct evbuffer *evbuf; 53 | 54 | evbuf = an_buf_libevent_get(buf); 55 | evbuffer_add_vprintf(evbuf, fmt, ap); 56 | } 57 | 58 | static void 59 | an_buf_libevent_reset(struct an_wbuf *buf) 60 | { 61 | struct evbuffer *evbuf; 62 | 63 | evbuf = an_buf_libevent_get(buf); 64 | evbuffer_drain(evbuf, evbuffer_get_length(evbuf)); 65 | } 66 | 67 | static const void * 68 | an_buf_libevent_linearize(struct an_rbuf *buf) 69 | { 70 | struct evbuffer *evbuf; 71 | 72 | evbuf = an_buf_libevent_get(buf); 73 | return evbuffer_pullup(evbuf, -1); 74 | } 75 | 76 | static const struct an_buf_if an_buf_libevent_if = { 77 | .destroy = an_buf_libevent_destroy, 78 | .length = an_buf_libevent_length, 79 | .linearize = an_buf_libevent_linearize, 80 | .add = an_buf_libevent_add, 81 | .add_printf = an_buf_libevent_add_printf, 82 | .reset = an_buf_libevent_reset 83 | }; 84 | 85 | struct an_rbuf * 86 | an_buf_libevent_wrap(struct evbuffer *evbuf) 87 | { 88 | struct an_buf_libevent_state *s; 89 | struct an_rbuf *buf; 90 | 91 | buf = an_rbuf_create(&an_buf_libevent_if, 92 | sizeof(struct an_buf_libevent_state)); 93 | s = an_buf_private(buf); 94 | s->evbuf = evbuf; 95 | return buf; 96 | } 97 | 98 | struct an_wbuf * 99 | an_buf_libevent_create(void) 100 | { 101 | struct an_buf_libevent_state *s; 102 | struct an_wbuf *buf; 103 | struct evbuffer *evbuf; 104 | 105 | evbuf = evbuffer_new(); 106 | evbuffer_set_strategy(evbuf, EVBUFFER_STRATEGY_CHAIN, 107 | AN_BUF_LIBEVENT_INITIAL_SIZE); 108 | 109 | buf = an_wbuf_create(&an_buf_libevent_if, 110 | sizeof(struct an_buf_libevent_state)); 111 | s = an_buf_private(buf); 112 | s->evbuf = evbuf; 113 | an_buf_own(buf); 114 | return buf; 115 | } 116 | -------------------------------------------------------------------------------- /common/an_buf_libevent.h: -------------------------------------------------------------------------------- 1 | #ifndef AN_BUF_LIBEVENT_H_ 2 | #define AN_BUF_LIBEVENT_H_ 3 | 4 | #include "common/an_buf.h" 5 | 6 | struct evbuffer; 7 | 8 | struct an_rbuf *an_buf_libevent_wrap(struct evbuffer *); 9 | struct an_wbuf *an_buf_libevent_create(void); 10 | 11 | #endif /* AN_BUF_LIBEVENT_H_ */ 12 | -------------------------------------------------------------------------------- /common/an_buf_plain.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "common/an_buf_if.h" 4 | 5 | struct an_buf_plain_state { 6 | const void *data; 7 | size_t len; 8 | }; 9 | 10 | static size_t 11 | an_buf_plain_length(an_buf_const_ptr_t buf) 12 | { 13 | struct an_buf_plain_state *s; 14 | 15 | s = an_buf_private(buf.rbuf); 16 | return s->len; 17 | } 18 | 19 | static const void * 20 | an_buf_plain_linearize(struct an_rbuf *buf) 21 | { 22 | struct an_buf_plain_state *s; 23 | 24 | s = an_buf_private(buf); 25 | return s->data; 26 | } 27 | 28 | static const struct an_buf_if an_buf_plain_if = { 29 | .length = an_buf_plain_length, 30 | .linearize = an_buf_plain_linearize 31 | }; 32 | 33 | struct an_rbuf * 34 | an_buf_plain_wrap(const void *data, size_t len) 35 | { 36 | struct an_buf_plain_state *s; 37 | struct an_rbuf *buf; 38 | 39 | buf = an_rbuf_create(&an_buf_plain_if, 40 | sizeof(struct an_buf_plain_state)); 41 | s = an_buf_private(buf); 42 | s->data = data; 43 | s->len = len; 44 | return buf; 45 | } 46 | -------------------------------------------------------------------------------- /common/an_buf_plain.h: -------------------------------------------------------------------------------- 1 | #ifndef AN_BUF_PLAIN_H_ 2 | #define AN_BUF_PLAIN_H_ 3 | 4 | #include "common/an_buf.h" 5 | 6 | struct an_rbuf *an_buf_plain_wrap(const void *, size_t); 7 | 8 | #endif /* AN_BUF_PLAIN_H_ */ 9 | -------------------------------------------------------------------------------- /common/an_cc.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMON_AN_CC_H 2 | #define COMMON_AN_CC_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | /* 9 | * This makes it look like we're passing three arguments to 10 | * ck_pr_store_ptr, but the first "two" (the second line of the 11 | * #defined val) is actually a single argument, which is a comma 12 | * expression (similar to "for (i = 0, j = 0;..."). Both clauses of 13 | * the comma expression will be "evaluated", but the trick here is 14 | * that the first clause will be compiled to a constant and then 15 | * optimized away. 16 | * 17 | * *(DST) = (VAL) does exactly what it says on the tin: it assigns VAL 18 | * to *DST. The compiler knows what DST's type is, and what it points 19 | * to (if it is indeed a pointer). This means we get type checking on 20 | * the assignment, but we don't want the assignment to actually be 21 | * _done_ at runtime, so we add a sizeof, which will compile to a 22 | * constant (an assignment is also a value, with a size), and the 23 | * (void) cast declares that we don't want the result of the 24 | * expression, because otherwise the left-hand clause of the comma 25 | * expression is declared as having no effect (which it 26 | * doesn't--that's the beauty of it). 27 | */ 28 | #define an_pr_store_ptr(DST, VAL) \ 29 | ck_pr_store_ptr( \ 30 | ((void)(sizeof(*(DST) = (VAL))), (DST)), \ 31 | (VAL)) 32 | 33 | /* 34 | * Cast the result of the dereference to the type of SRC--the type of 35 | * which we expect to get back. 36 | * Not strictly C99 but all reasonable compilers should really support this. 37 | */ 38 | #define an_pr_load_ptr(SRC) ((__typeof__(*(SRC)))ck_pr_load_ptr((SRC))) 39 | 40 | _Static_assert(sizeof(float) == sizeof(uint32_t), 41 | "sizeof float not equal to sizeof uint32_t"); 42 | 43 | static inline float 44 | an_pr_load_float(const float *value) 45 | { 46 | union { 47 | float dst; 48 | uint32_t dummy; 49 | } temp; 50 | 51 | /* coverity[incompatible_cast : FALSE] */ 52 | temp.dummy = ck_pr_load_32((const uint32_t *)value); 53 | 54 | return temp.dst; 55 | } 56 | 57 | static inline void 58 | an_pr_store_float(float *target, float value) 59 | { 60 | union { 61 | float dummy; 62 | uint32_t dst; 63 | } temp; 64 | 65 | temp.dummy = value; 66 | ck_pr_store_32((uint32_t *)target, temp.dst); 67 | 68 | return; 69 | } 70 | 71 | #endif /* COMMON_AN_CC_H */ 72 | -------------------------------------------------------------------------------- /common/an_hrlock.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "common/an_hrlock.h" 6 | #include "common/an_md.h" 7 | #include "common/util.h" 8 | 9 | void 10 | an_hrlock_init(an_hrlock_t *lock) 11 | { 12 | unsigned int hash; 13 | 14 | hash = an_rand(); 15 | ck_pr_store_uint(&lock->hash, hash); 16 | ck_pr_barrier(); 17 | return; 18 | } 19 | 20 | void 21 | an_hrlock_write_lock_impl(an_hrlock_t *lock, an_hrlock_table_t *table) 22 | { 23 | struct an_hrlock_record *record; 24 | unsigned int h = ck_pr_load_uint(&lock->hash) % AN_HRLOCK_COUNT; 25 | unsigned int id = current->id; 26 | uint8_t old_depth = 0; 27 | 28 | for (size_t i = 0; i < AN_THREAD_LIMIT; i++) { 29 | record = &table->records[i * AN_HRLOCK_COUNT + h]; 30 | old_depth = ck_pr_load_8(&record->write_depth); 31 | ck_pr_store_8(&record->write_depth, old_depth + 1); 32 | } 33 | 34 | if (CK_CC_UNLIKELY(old_depth > 0)) { 35 | an_thread_push_poison(an_hrlock_write_unlock_impl); 36 | return; 37 | } 38 | 39 | ck_pr_fence_store_load(); 40 | for (size_t i = 0; i < AN_THREAD_LIMIT; i++) { 41 | record = &table->records[i * AN_HRLOCK_COUNT + h]; 42 | 43 | if (i == id) { /* Skip self. */ 44 | continue; 45 | } 46 | 47 | while (ck_pr_load_8(&record->read_depth) != 0) { 48 | ck_pr_stall(); 49 | } 50 | } 51 | 52 | an_thread_push_poison(an_hrlock_write_unlock_impl); 53 | return; 54 | } 55 | 56 | void 57 | an_hrlock_write_unlock_impl(an_hrlock_t *lock, an_hrlock_table_t *table) 58 | { 59 | struct an_hrlock_record *record; 60 | unsigned int h = ck_pr_load_uint(&lock->hash) % AN_HRLOCK_COUNT; 61 | uint8_t old_depth; 62 | 63 | ck_pr_fence_release(); 64 | an_thread_pop_poison(an_hrlock_write_unlock_impl); 65 | for (size_t i = 0; i < AN_THREAD_LIMIT; i++) { 66 | record = &table->records[i * AN_HRLOCK_COUNT + h]; 67 | old_depth = ck_pr_load_8(&record->write_depth); 68 | ck_pr_store_8(&record->write_depth, old_depth - 1); 69 | } 70 | 71 | return; 72 | } 73 | 74 | bool 75 | an_hrlock_read_lock_slow(struct an_hrlock_record *record, uint64_t timeout_us) 76 | { 77 | uint64_t begin = 0, ticks = 0; 78 | 79 | ck_pr_store_8(&record->read_depth, 0); 80 | if (timeout_us == 0) { 81 | return false; 82 | } 83 | 84 | if (timeout_us != UINT64_MAX) { 85 | ticks = an_md_us_to_rdtsc(timeout_us); 86 | if (ticks == 0) { 87 | ticks = 1; 88 | } 89 | 90 | begin = an_md_rdtsc(); 91 | } 92 | 93 | for (;;) { 94 | for (;;) { 95 | if (ticks != 0) { 96 | uint64_t delta; 97 | 98 | delta = an_md_rdtsc() - begin; 99 | if (delta > ticks) { 100 | return false; 101 | } 102 | } 103 | 104 | for (size_t i = 0; i < 128; i++) { 105 | if (ck_pr_load_8(&record->write_depth) == 0) { 106 | goto try; 107 | } 108 | 109 | ck_pr_stall(); 110 | } 111 | } 112 | 113 | try: 114 | ck_pr_store_8(&record->read_depth, 1); 115 | ck_pr_fence_store_load(); 116 | if (ck_pr_load_8(&record->write_depth) == 0) { 117 | break; 118 | } 119 | 120 | ck_pr_store_8(&record->read_depth, 0); 121 | } 122 | 123 | return true; 124 | } 125 | 126 | static size_t 127 | an_hrlock_read_maybe_lock_all(an_hrlock_table_t *table, uint64_t timeout_us) 128 | { 129 | 130 | /* XXX: HACK HACK HACK. Clean this up if we need it for a good reason. */ 131 | for (size_t i = 0; i < AN_HRLOCK_COUNT; i++) { 132 | struct an_hrlock lock = { .hash = i }; 133 | 134 | if (an_hrlock_read_lock_timeout(&lock, table, timeout_us) == false) { 135 | return i; 136 | } 137 | 138 | an_thread_pop(an_hrlock_read_unlock_all, table); 139 | } 140 | 141 | return AN_HRLOCK_COUNT; 142 | } 143 | 144 | void 145 | an_hrlock_read_lock_all(an_hrlock_table_t *table) 146 | { 147 | 148 | (void)an_hrlock_read_maybe_lock_all(table, UINT64_MAX); 149 | return; 150 | } 151 | 152 | void 153 | an_hrlock_read_unlock_all(an_hrlock_table_t *table) 154 | { 155 | unsigned int id = current->id; 156 | 157 | for (size_t i = 0; i < AN_HRLOCK_COUNT; i++) { 158 | ck_pr_store_8(&table->records[id * AN_HRLOCK_COUNT + i].read_depth, 0); 159 | } 160 | 161 | ck_pr_fence_release(); 162 | return; 163 | } 164 | 165 | bool 166 | an_hrlock_read_trylock_all(an_hrlock_table_t *table) 167 | { 168 | size_t locked_count; 169 | unsigned int id = current->id; 170 | 171 | locked_count = an_hrlock_read_maybe_lock_all(table, 0); 172 | 173 | if (locked_count == AN_HRLOCK_COUNT) { 174 | return true; 175 | } 176 | 177 | /* 178 | * We were not able to lock all of the members, we need to free the ones 179 | * that we previously locked 180 | */ 181 | for (size_t i = 0; i < locked_count; i++) { 182 | ck_pr_store_8(&table->records[id * AN_HRLOCK_COUNT + i].read_depth, 0); 183 | } 184 | 185 | return false; 186 | } 187 | -------------------------------------------------------------------------------- /common/an_hrw.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMON_AN_HRW 2 | #define COMMON_AN_HRW 3 | 4 | #include "common/an_dict.h" 5 | 6 | /* 7 | * This file implements Highest Random Weight hashing or Rendezvous hashing. 8 | * Right now, it only supports use cases that want the single highest 9 | * resource (instead of N highest), since it is trivial to keep track of 10 | * the maximum element while looping a list, but annoying to track N. 11 | * 12 | * Invoke AN_HRW with the same NAME as the dict. Tell it the 13 | * TYPE, and the KEY_FIELD to use as an identifier for each entry. 14 | * Note that KEY_FIELD can not be a pointer or contain pointers. 15 | * 16 | * AN_HRW should only need to be invoked in a source file, 17 | * even if the AN_DICT_INLINE call is in a header. If both are going 18 | * into a source file, use AN_HRW_DICT to declare both at once. 19 | */ 20 | #define AN_HRW_SEED 0x1122334455667788 21 | 22 | /* As per above, you should only use this in the source files. */ 23 | #define AN_HRW(NAME, TYPE, KEY_FIELD) \ 24 | \ 25 | AN_CC_UNUSED static TYPE * \ 26 | an_hrw_single_##NAME(struct an_dict_##NAME *dict, \ 27 | const void *key, size_t key_len) \ 28 | { \ 29 | TYPE *current_max = NULL; \ 30 | uint64_t max_murmur_score = 0; \ 31 | uint64_t key_hash = MurmurHash64A(key, key_len, \ 32 | AN_HRW_SEED); \ 33 | \ 34 | AN_DICT_FOREACH(NAME, dict, cursor) { \ 35 | uint64_t murmur_score = 0; \ 36 | size_t entry_len = sizeof(cursor->KEY_FIELD); \ 37 | \ 38 | murmur_score = \ 39 | MurmurHash64A(&cursor->KEY_FIELD, \ 40 | entry_len, key_hash); \ 41 | \ 42 | if (murmur_score < max_murmur_score) { \ 43 | continue; \ 44 | } \ 45 | \ 46 | max_murmur_score = murmur_score; \ 47 | current_max = cursor; \ 48 | } \ 49 | \ 50 | return current_max; \ 51 | } 52 | 53 | /* As per above, you should only use this in the source files. */ 54 | #define AN_HRW_DICT(NAME, TYPE, DICT_KEY_FIELD, \ 55 | HRW_KEY_FIELD) \ 56 | AN_DICT_INLINE(NAME, TYPE, DICT_KEY_FIELD); \ 57 | AN_HRW(NAME, TYPE, HRW_KEY_FIELD) 58 | 59 | #endif /* COMMON_AN_HRW */ 60 | -------------------------------------------------------------------------------- /common/an_interpolation_table.c: -------------------------------------------------------------------------------- 1 | #include "common/an_interpolation_table.h" 2 | #include "common/an_malloc.h" 3 | #include "common/an_syslog.h" 4 | #include "common/util.h" 5 | 6 | static AN_MALLOC_DEFINE(an_interpolation_table_buckets_token, 7 | .string = "an_interpolation_table.buckets", 8 | .mode = AN_MEMORY_MODE_VARIABLE); 9 | 10 | void 11 | an_interpolation_table_init_internal(struct an_interpolation_table *at, const void *sorted, size_t n_elem, size_t n_buckets, size_t size, uint64_t (*key_fn)(const void *)) 12 | { 13 | #define VALUE_AT_INTERNAL(PTR, SIZE, INDEX, KEY_FN) (KEY_FN((void *)((char *)(PTR) + ((INDEX) * SIZE)))) 14 | #define SORTED_VALUE_AT(INDEX) VALUE_AT_INTERNAL(sorted, size, (INDEX), key_fn) 15 | size_t bucket_size, cur_index, actual_n_buckets; 16 | uint64_t delta, last, first, value_at_end; 17 | /* __uint128_t only to avoid explicit casts when taking high 64 bits */ 18 | __uint128_t multiplier; 19 | 20 | memset(at, 0, sizeof(struct an_interpolation_table)); 21 | 22 | if (n_elem > INT32_MAX) { 23 | an_syslog(LOG_ERR, "an_interpolation_table_init: Number of elements in sorted array too high %lu\n", n_elem); 24 | assert(n_elem <= INT32_MAX); 25 | return; 26 | } 27 | 28 | /* store [0, nelem) in index_range but reserve LSB for storing state about whether there is only one value in the range */ 29 | at->index_range[1] = n_elem << 1; 30 | 31 | if (sorted == NULL || n_elem == 0) { 32 | at->delta = UINT64_MAX; 33 | return; 34 | } 35 | 36 | first = SORTED_VALUE_AT(0); 37 | last = SORTED_VALUE_AT(n_elem - 1); 38 | 39 | delta = last - first; 40 | cur_index = 0; 41 | 42 | at->min = first; 43 | at->delta = delta; 44 | 45 | if (n_buckets <= 1 || delta <= 1) { 46 | /* Use index range as our one bucket */ 47 | if (delta == 0) { 48 | /* there is only a single value in the entire sorted array */ 49 | at->index_range[0] = 1; 50 | } 51 | return; 52 | } 53 | 54 | /* coerce n_buckets to be small enough that we will always have bucket_size >= 1 */ 55 | n_buckets = min(n_buckets, delta); 56 | 57 | /* 58 | * Computing ceiling(delta/n_buckets) so that we will never use more buckets than asked for 59 | * (so bucket size has to always round up) 60 | * compute ceiling(x/y) as 1 + ((x- 1)/y) to avoid overflow 61 | */ 62 | bucket_size = 1 + ((delta - 1) / n_buckets); 63 | 64 | /* 65 | * Approximate division of n/d by computing 66 | * multiplier = ceiling(2^k / d) 67 | * so n/d = (multiplier * n) / 2^k 68 | * k = 64 here 69 | */ 70 | multiplier = 1; 71 | multiplier <<= 64; 72 | multiplier = (multiplier + bucket_size - 1)/bucket_size; 73 | 74 | /* check for overflow in the high bits of the multiplier and if so do the best that we can w/ 64 bits */ 75 | at->multiplier = min(multiplier, UINT64_MAX); 76 | 77 | /* n_buckets is the high index */ 78 | actual_n_buckets = an_interpolation_table_get_lower_index(at, at->delta) + 1; 79 | 80 | /* verify that actual number of buckets is close enough */ 81 | if (actual_n_buckets < n_buckets / 2 || actual_n_buckets > 2 * n_buckets) { 82 | an_syslog(LOG_DEBUG, "an_interpolation_table failed to correctly allocate buckets: asked for %lu actually allocated %lu nelem: %lu min: %lu max %lu\n", 83 | n_buckets, actual_n_buckets, n_elem, first, last); 84 | } 85 | 86 | at->buckets = an_calloc_region(an_interpolation_table_buckets_token, actual_n_buckets + 1, sizeof(uint32_t)); 87 | 88 | at->buckets[actual_n_buckets] = n_elem << 1; 89 | 90 | for (size_t i = 0; i < n_elem; i++) { 91 | uint64_t cur_value = SORTED_VALUE_AT(i); 92 | uint64_t index = an_interpolation_table_get_lower_index(at, cur_value - at->min); 93 | /* Set empty buckets to have empty range [i, i) */ 94 | while (cur_index < index) { 95 | /* Use the LSB to denote whether the bucket has only one value */ 96 | at->buckets[++cur_index] = i << 1; 97 | 98 | /* check if bucket is empty [i, i) and if so set the lsb */ 99 | if ((at->buckets[cur_index] >> 1) == (at->buckets[cur_index - 1] >> 1)) { 100 | at->buckets[cur_index - 1] |= 1; 101 | } else { 102 | /* get endpoint values for all buckets [low, high) --> [buckets[i - 1], buckets[i]) */ 103 | uint64_t value_at_low_index = SORTED_VALUE_AT(at->buckets[cur_index - 1] >> 1); 104 | uint64_t value_at_high_index = SORTED_VALUE_AT((at->buckets[cur_index] >> 1) - 1); 105 | 106 | /* Check if bucket has only one value in entire range and if so set the lsb */ 107 | if (value_at_low_index == value_at_high_index) { 108 | at->buckets[cur_index - 1] |= 1; 109 | } 110 | } 111 | } 112 | 113 | } 114 | 115 | /* check the last bucket to see if its empty or has a single value */ 116 | value_at_end = SORTED_VALUE_AT(at->buckets[actual_n_buckets - 1] >> 1); 117 | 118 | if (value_at_end == last) { 119 | at->buckets[actual_n_buckets - 1] |= 1; 120 | } 121 | 122 | #undef SORTED_VALUE_AT 123 | #undef VALUE_AT_INTERNAL 124 | return; 125 | } 126 | 127 | void 128 | an_interpolation_table_deinit(struct an_interpolation_table *at) 129 | { 130 | 131 | an_free(an_interpolation_table_buckets_token, at->buckets); 132 | memset(at, 0, sizeof(struct an_interpolation_table)); 133 | 134 | return; 135 | } 136 | -------------------------------------------------------------------------------- /common/an_interpolation_table.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMON_AN_INTERPOLATION_TABLE_H 2 | #define COMMON_AN_INTERPOLATION_TABLE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | /** 10 | * Interpolation table structure used to make searching on sorted arrays of arbitrary structs faster. Requires that the sorted arrays have <= INT32_MAX elements. 11 | */ 12 | struct an_interpolation_table { 13 | uint64_t multiplier; /** this is used to avoid dividing to interpolate approximate n/d by (n * (2^k/d) )/ 2^k. The multiplier is (2^k/d) */ 14 | uint64_t min; /** min key value in sorted array */ 15 | uint64_t delta; /** precomputed max - min */ 16 | uint32_t *buckets; /** array of buckets (indices in sorted array) */ 17 | uint32_t index_range[2];/** holds [0,n_elem] used for small object optimization */ 18 | }; 19 | 20 | /** 21 | * @brief Initialize and allocate buckets for an interpolation table 22 | * @param at Pointer to already allocated struct an_interpolation_table 23 | * @param sorted Pointer to he array of sorted elements 24 | * @param n_elem the number of elements in sorted array (must be less than UINT32_MAX) 25 | * @param n_buckets the number of buckets in your interpolation table 26 | * @param size size in bytes of each element 27 | * @param key_function Pointer to function that maps each element to a uin64_t key 28 | * 29 | * Since the interpolation table is only valid for a static sorted array, we save the min, max 30 | * number of elements and stride to avoid doing unnecessary computation during lookups. 31 | * 32 | * at->delta is set to UINT64_MAX if sorted is null or nelem is zero 33 | * at->buckets is set to NULL and index_range is used instead if n_buckets <= 1 or delta <= 1 34 | */ 35 | void an_interpolation_table_init_internal(struct an_interpolation_table *at, 36 | const void *sorted, size_t n_elem, size_t n_buckets, size_t size, uint64_t (*key_fn)(const void *)); 37 | 38 | #define an_interpolation_table_init(TABLE, SORTED, NELEM, NBUCKETS, KEY_FN) \ 39 | an_interpolation_table_init_internal((TABLE), \ 40 | (SORTED), (NELEM), (NBUCKETS), sizeof((SORTED)[0]), \ 41 | AN_CC_CAST_CONST_CB(uint64_t, (KEY_FN), (SORTED)[0])) \ 42 | 43 | /** 44 | * @brief maps the offset of a key where offset = (key - min) being searched for to an index 45 | * such that sorted[buckets[index]] <= searched_key 46 | * Assumes that the key is min <= key <= max 47 | */ 48 | static inline uint64_t 49 | an_interpolation_table_get_lower_index(const struct an_interpolation_table *at, uint64_t offset) 50 | { 51 | 52 | return ((__uint128_t)at->multiplier * offset) >> 64; 53 | } 54 | 55 | /** 56 | * @brief Returns the bucket (high and low inclusive indices) the search key may be in inside the sorted array. Also returns true if the bucket contains 57 | * 1 value or no values, and false otherwise 58 | * @param search the key being searched for 59 | * 60 | * Returns the bucket in the interpolation table that the search key potentially lies in. 61 | * The bucket is a pair of indices low and high such that sorted[buckets[low]] <= search < sorted[buckets[high]]. 62 | * Another way to look at it is that the key exists in indices in range [low, high). Note that 63 | * this method does not definitely say whether or not the search key is in the sorted array unless the search 64 | * key is either less than the minimum value or greater than the maximum value. 65 | */ 66 | static inline bool 67 | an_interpolation_table_get_indices(const struct an_interpolation_table *at, uint64_t search, uint32_t *ret_low_index, uint32_t *ret_high_index) 68 | { 69 | const uint32_t *buckets; 70 | uint64_t offset = search - at->min; 71 | 72 | buckets = (at->buckets == NULL) ? at->index_range : at->buckets; 73 | 74 | /* take advantage of unsignedness to compute whether search lies in the range [min, max] with one comparison */ 75 | if (offset <= at->delta) { 76 | size_t index = an_interpolation_table_get_lower_index(at, offset); 77 | uint32_t low = buckets[index]; 78 | uint32_t high = buckets[index + 1]; 79 | bool ret = (low & 1) != 0; 80 | 81 | /* clear the low bits of both the upper and lower index */ 82 | low >>= 1; 83 | high >>= 1; 84 | 85 | *ret_low_index = low; 86 | *ret_high_index = high; 87 | 88 | return ret; 89 | } 90 | 91 | if (search < at->min) { 92 | *ret_low_index = *ret_high_index = 0; 93 | return true; 94 | } 95 | 96 | /* It can only be greater than the max */ 97 | *ret_low_index = *ret_high_index = at->index_range[1] >> 1; 98 | 99 | return true; 100 | } 101 | 102 | /** 103 | * @brief Deallocates the interpolation table 104 | */ 105 | void an_interpolation_table_deinit(struct an_interpolation_table *at); 106 | 107 | #endif /* !COMMON_AN_INTERPOLATION_TABLE_H */ 108 | -------------------------------------------------------------------------------- /common/an_md.h: -------------------------------------------------------------------------------- 1 | #ifndef _AN_MD_H 2 | #define _AN_MD_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #ifndef __x86_64__ 9 | #error "Unsupported platform." 10 | #endif 11 | 12 | typedef uint64_t an_md_rdtsc_t(void); 13 | an_md_rdtsc_t *an_md_probe_rdtsc(const char **, bool fast); 14 | /* Return the invariant tick frequency in Hz, 0 on failure. */ 15 | unsigned long long an_md_scale_invariant_rdtsc(void); 16 | 17 | void an_md_probe(void); 18 | void an_md_handler_http_enable(struct evhttp *); 19 | 20 | /* 21 | * Public functions. 22 | */ 23 | 24 | extern an_md_rdtsc_t *an_md_rdtsc; 25 | /* Like an_md_rdtsc, but sloppy (e.g., RDTSC instead of RDTSCP). */ 26 | extern an_md_rdtsc_t *an_md_rdtsc_fast; 27 | 28 | /** 29 | * Returns number of microseconds from ticks. 30 | */ 31 | unsigned long long an_md_us_to_rdtsc(unsigned long long); 32 | 33 | /** 34 | * Returns number of microseconds in a tick interval. 35 | * Provides nanosecond granularity. 36 | */ 37 | double an_md_rdtsc_scale(uint64_t); 38 | 39 | /** 40 | * Returns number of microseconds in a tick interval 41 | * in integer format. The number of microseconds is 42 | * rounded up to the nearest microsecond. 43 | * 44 | * For example, 45 | * 0.50 microseconds -> 1 microsecond 46 | * 0.49 microseconds -> 0 microseconds 47 | */ 48 | unsigned long long an_md_rdtsc_to_us(uint64_t); 49 | 50 | #endif /* _AN_MD_H */ 51 | -------------------------------------------------------------------------------- /common/an_poison.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Centralised location to poison identifiers away. Included in util.h. 3 | */ 4 | #ifndef AN_POISON_H 5 | #define AN_POISON_H 6 | 7 | #if (defined(IS_BIDDER) || defined(IS_IMPBUS)) && !defined(IS_REPACKD) 8 | #pragma GCC poison itoa ftoa ftop litoa strerror 9 | /* Bad and thread-unsafe PRNG. */ 10 | #pragma GCC poison rand random drand48 lrand48 mrand48 11 | #pragma GCC poison erand48 nrand48 jrand48 12 | /* Bad PRNG. */ 13 | #pragma GCC poison rand_r drand48_r lrand48_r mrand48_r 14 | #pragma GCC poison erand48_r nrand48_r jrand48_r 15 | /* Seed for said bad PRNG */ 16 | #pragma GCC poison srand srandom srand48 seed48 lcong48 17 | #pragma GCC poison srand48_r seed48_r lcong48_r 18 | 19 | #endif 20 | 21 | #endif /* AN_POISON_H */ 22 | -------------------------------------------------------------------------------- /common/an_rand.c: -------------------------------------------------------------------------------- 1 | #include "common/an_rand.h" 2 | #include "common/an_thread.h" 3 | 4 | /* Two arbitrary non-zero values. */ 5 | static uint64_t xorshift_state[2] = { 6 | 0x123456789ABCDEFULL, 7 | /* ASCII-encoded "appnexus" */ 8 | 0x737578656E707061ULL 9 | }; 10 | 11 | void 12 | an_xorshift_plus_seed(uint64_t state[static 2], uint64_t seed) 13 | { 14 | 15 | /* MurmurHash 3 finalizer (as suggested by Vigna) */ 16 | for (size_t i = 0; i < 2; i++) { 17 | seed ^= (seed >> 33); 18 | seed *= 0xFF51AFD7ED558CCDULL; 19 | seed ^= (seed >> 33); 20 | seed *= 0xC4CEB9FE1A85EC53ULL; 21 | seed ^= (seed >> 33); 22 | 23 | if (seed == 0) { 24 | /* Avalanching can't get us out of 0. Arbitrary value. */ 25 | seed = 0x123456789ABCDEFULL; 26 | } 27 | 28 | state[i] = seed; 29 | } 30 | 31 | return; 32 | } 33 | 34 | void 35 | an_srand(uint64_t seed) 36 | { 37 | 38 | an_xorshift_plus_seed(xorshift_state, seed); 39 | } 40 | 41 | uint64_t 42 | an_rand64(void) 43 | { 44 | 45 | return an_xorshift_plus(AN_CC_UNLIKELY(current == NULL) ? 46 | xorshift_state : 47 | current->xorshift_state); 48 | } 49 | -------------------------------------------------------------------------------- /common/an_ring.h: -------------------------------------------------------------------------------- 1 | #ifndef _AN_RING_H 2 | #define _AN_RING_H 3 | 4 | #include 5 | 6 | /* 7 | * Prepare to enqueue. 8 | * 9 | * The data to enqueue will be stored at the returned pointer 10 | * for up to `size' bytes. Caller should modify this space as 11 | * they see fit before calling commit(). If commit is not called, 12 | * the enqueue is implicitly aborted. 13 | */ 14 | inline static void * 15 | an_ring_enqueue_spsc_prepare(struct ck_ring *ring, void *restrict buffer, 16 | unsigned int size) 17 | { 18 | unsigned int consumer, producer, delta; 19 | unsigned int mask = ring->mask; 20 | 21 | consumer = ck_pr_load_uint(&ring->c_head); 22 | producer = ring->p_tail; 23 | delta = producer + 1; 24 | 25 | if ((delta & mask) == (consumer & mask)) { 26 | return NULL; 27 | } 28 | 29 | buffer = (char *)buffer + size * (producer & mask); 30 | return buffer; 31 | } 32 | 33 | /* 34 | * Commit the last prepared enqueue. 35 | */ 36 | inline static void 37 | an_ring_enqueue_spsc_commit(struct ck_ring *ring) 38 | { 39 | unsigned int producer, delta; 40 | 41 | producer = ring->p_tail; 42 | delta = producer + 1; 43 | 44 | /* 45 | * Make sure to update slot value before indicating 46 | * that the slot is available for consumption. 47 | */ 48 | ck_pr_fence_store(); 49 | ck_pr_store_uint(&ring->p_tail, delta); 50 | } 51 | 52 | /* 53 | * Prepare to dequeue an object. 54 | * 55 | * Return a pointer to the beginning of the object. 56 | */ 57 | inline static void * 58 | an_ring_dequeue_spsc_prepare(struct ck_ring *ring, void *restrict buffer, 59 | unsigned int size) 60 | { 61 | unsigned int consumer, producer; 62 | unsigned int mask = ring->mask; 63 | 64 | consumer = ring->c_head; 65 | producer = ck_pr_load_uint(&ring->p_tail); 66 | 67 | if (consumer == producer) { 68 | return NULL; 69 | } 70 | 71 | /* 72 | * Make sure to serialize with respect to our snapshot 73 | * of the producer counter. 74 | */ 75 | ck_pr_fence_load(); 76 | 77 | buffer = (char *)buffer + size * (consumer & mask); 78 | return buffer; 79 | } 80 | 81 | /* 82 | * Commit the last prepared dequeue. 83 | */ 84 | inline static void 85 | an_ring_dequeue_spsc_commit(struct ck_ring *ring) 86 | { 87 | unsigned int consumer; 88 | 89 | consumer = ring->c_head; 90 | 91 | /* 92 | * Make sure copy is completed with respect to consumer 93 | * update. 94 | */ 95 | ck_pr_fence_store(); 96 | ck_pr_store_uint(&ring->c_head, consumer + 1); 97 | } 98 | 99 | #endif /* _AN_RING_H */ 100 | -------------------------------------------------------------------------------- /common/an_sampling.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "common/an_rand.h" 7 | #include "common/an_sampling.h" 8 | 9 | void 10 | an_sampling_fixed_window_init(struct an_sampling_fixed_window *inst, size_t population_size, 11 | size_t sample_size) 12 | { 13 | 14 | memset(inst, 0, sizeof(struct an_sampling_fixed_window)); 15 | inst->population_size = population_size; 16 | inst->sample_size = sample_size; 17 | an_sampling_fixed_window_reset(inst); 18 | } 19 | 20 | void 21 | an_sampling_fixed_window_deinit(struct an_sampling_fixed_window *inst) 22 | { 23 | 24 | an_sampling_fixed_window_reset(inst); 25 | } 26 | 27 | void 28 | an_sampling_fixed_window_reset(struct an_sampling_fixed_window *inst) 29 | { 30 | 31 | inst->current_index = 0; 32 | inst->selected_count = 0; 33 | } 34 | 35 | bool 36 | an_sampling_fixed_window_is_exhausted(struct an_sampling_fixed_window *inst) 37 | { 38 | 39 | return (inst->current_index >= inst->population_size); 40 | } 41 | 42 | bool 43 | an_sampling_fixed_window_next_is_selected(struct an_sampling_fixed_window *inst) 44 | { 45 | bool is_selected = false; 46 | 47 | if (an_sampling_fixed_window_is_exhausted(inst)) { 48 | an_sampling_fixed_window_reset(inst); 49 | } 50 | 51 | if (inst->selected_count < inst->sample_size) { 52 | is_selected = (double)(inst->population_size - inst->current_index) * an_drandom() < (inst->sample_size - 53 | inst->selected_count); 54 | } 55 | 56 | if (is_selected) { 57 | inst->selected_count++; 58 | } 59 | inst->current_index++; 60 | 61 | return is_selected; 62 | } 63 | -------------------------------------------------------------------------------- /common/an_sampling.h: -------------------------------------------------------------------------------- 1 | #ifndef AN_SAMPLING_H 2 | #define AN_SAMPLING_H 3 | 4 | #include 5 | #include 6 | 7 | /** 8 | * an_sampling_fixed_window: fixed-window algoritm: samples n elements from population of size N so that 9 | * each of the population elements have the equal chance to be selected. 10 | * Gives instant decision (selected/rejected) for the next population element. 11 | * 12 | * Usage: 13 | * 1) Call an_sampling_fixed_window_init to initialize instance object once 14 | * 2) Call an_sampling_fixed_window_next_is_selected() every time the item from the population arrives. 15 | * The return value would indicate if item has been sampled. 16 | * After the population (N) has all been enumerated, the next call to an_sampling_fixed_window_next_is_selected 17 | * will reset the state to account for the new population/new sample is started. 18 | * 3) Cleanup instance with an_sampling_fixed_window_deinit call 19 | * 20 | * Implementation detail: implements Selection-Rejection algorithm, Fan et al (1962) 21 | * N - population size 22 | * n - sample size 23 | * m - number of items already selected on previous steps (0 initially) 24 | * t - record index, t: [0,N-1]; 25 | * t+1 record is selected with probability (n-m)/(N-t), if m items has already been selected 26 | * When t=0 there is no physical record, but we make decision for the first physical record (t+1) 27 | */ 28 | 29 | /** Algorithm core state */ 30 | struct an_sampling_fixed_window { 31 | size_t population_size; /**< Size of the population */ 32 | size_t sample_size; /**< Size of the sample to be chosen from the population */ 33 | size_t current_index; /**< Internal - current index within the population, 0-based */ 34 | size_t selected_count; /**< Internal - number of items selected in sample so far */ 35 | }; 36 | 37 | /** 38 | * @brief Initializes algorithm state 39 | * @param inst State instance 40 | * @param population_size Size of the population 41 | * @param sample_size Size of the sample to be selected from the population 42 | */ 43 | void an_sampling_fixed_window_init(struct an_sampling_fixed_window *inst, size_t population_size, 44 | size_t sample_size); 45 | 46 | /** 47 | * @brief Uninitializes algorithm state 48 | * @param inst State instance 49 | */ 50 | void an_sampling_fixed_window_deinit(struct an_sampling_fixed_window *inst); 51 | 52 | /** 53 | * @brief Resets algorithm state 54 | * @param inst State instance 55 | */ 56 | void an_sampling_fixed_window_reset(struct an_sampling_fixed_window *inst); 57 | 58 | /** 59 | * @brief Checks if current population is exhausted 60 | * @param inst State instance 61 | * @return True if the population has been exhausted, false otherwise 62 | */ 63 | bool an_sampling_fixed_window_is_exhausted(struct an_sampling_fixed_window *inst); 64 | 65 | /** 66 | * @brief Calculates if current element from the population is sampled 67 | * Automatically resets state when current population is exhausted 68 | * @param inst State instance 69 | */ 70 | bool 71 | an_sampling_fixed_window_next_is_selected(struct an_sampling_fixed_window *inst); 72 | 73 | #endif /* AN_SAMPLING_H */ 74 | -------------------------------------------------------------------------------- /common/an_server.h: -------------------------------------------------------------------------------- 1 | #ifndef _COMMON_AN_SERVER_H 2 | #define _COMMON_AN_SERVER_H 3 | 4 | #include 5 | #include 6 | 7 | #include "common/an_array.h" 8 | #include "third_party/http-parser/http_parser.h" 9 | 10 | #define AN_IO_CONNECTIONS_MAX (1U << 28) 11 | #define AN_IO_THREADS_MAX (1U << 8) 12 | 13 | /* Server configuration */ 14 | struct an_server_config_listener { 15 | char *host; 16 | in_port_t port; 17 | }; 18 | AN_ARRAY(an_server_config_listener, an_server_config_listener); 19 | 20 | struct an_server_config { 21 | uint32_t max_active_connections; 22 | uint32_t max_total_connections; 23 | size_t max_response_size; 24 | unsigned int num_threads; 25 | int request_timeout_ms; 26 | AN_ARRAY_INSTANCE(an_server_config_listener) listeners; 27 | }; 28 | 29 | /* 30 | * Internal HTTP buffers. Do not use directly; wrap it 31 | * in an an_wbuf using an_buf_http_wrap() instead. 32 | */ 33 | struct an_buffer { 34 | char *data; 35 | size_t size; 36 | size_t in; /* Offset for reads (from socket to buffer) */ 37 | size_t out; /* Offset for writes (from buffer to socket) */ 38 | bool external_allocation; /* Allocated using plain malloc() */ 39 | }; 40 | 41 | struct tcp_info; 42 | 43 | typedef struct { 44 | uint64_t id; 45 | } an_request_id_t; 46 | 47 | struct an_http_request { 48 | an_request_id_t id; 49 | const char *buffer; /* The full HTTP request buffer */ 50 | struct http_parser_url url; 51 | uint32_t total_len; 52 | uint32_t uri_offset; 53 | uint32_t uri_len; 54 | uint32_t body_offset; 55 | uint32_t body_len; 56 | }; 57 | 58 | struct an_io_server; 59 | 60 | /** 61 | * Create a server supporting at most @a max_total_connections concurrent 62 | * connections, with at most @a max_active_connections active ones, and 63 | * allocating at most @a max_bytes for input and output buffers, and using 64 | * @a num_io_threads I/O threads. An active connection is defined as a 65 | * connection we are currently reading bytes from, or a connection we 66 | * already read a request from and that is still waiting for our response. 67 | * 68 | * All limits are per-I/O thread rather than global limits. 69 | */ 70 | struct an_io_server *an_io_server_create(struct an_server_config *); 71 | 72 | /* 73 | * Instruct the server to listen on the specified address and port. 74 | * If the host parameter is NULL, listen on all available addresses. 75 | * 76 | * This needs to be called before an_io_server_start(). 77 | */ 78 | int an_io_server_listen(struct an_io_server *, const char *, in_port_t); 79 | 80 | /* Spawn the I/O threads and start processing requests. */ 81 | void an_io_server_start(struct an_io_server *); 82 | 83 | /* 84 | * Check whether the server is ready. It is always ready after 85 | * an_io_server_start() returns, this function is only useful for 86 | * code that does not know whether that has happened yet. 87 | */ 88 | bool an_io_server_ready(const struct an_io_server *); 89 | 90 | /* 91 | * Stop accepting new requests for graceful shutdown. This function 92 | * needs to be called repeatedly until it returns true, signaling us 93 | * that every in-flight request has been processed. 94 | */ 95 | bool an_io_server_quiesce(struct an_io_server *); 96 | 97 | /* 98 | * Destroy the server. This can only be called once the server has 99 | * been successfully quiesced, that is after an_io_server_quiesce() 100 | * has returned true. If that pre-condition hasn't been respected, 101 | * this function will fail, returning -1 and setting errno to EAGAIN. 102 | * Otherwise, it will return 0 and release all resources associated 103 | * with this an_server instance. 104 | */ 105 | int an_io_server_destroy(struct an_io_server *); 106 | 107 | /* API for worker threads. */ 108 | int an_io_server_notify_fd(struct an_io_server *); 109 | void an_io_server_read(struct an_io_server *, struct an_http_request *); 110 | int an_io_server_tryread(struct an_io_server *, struct an_http_request *); 111 | 112 | /* Low-level API for handlers, use an_http instead. */ 113 | struct an_buffer *an_io_get_outbuf(struct an_io_server *, 114 | an_request_id_t, size_t); 115 | void an_io_server_write(struct an_io_server *, an_request_id_t, 116 | struct an_buffer *); 117 | 118 | /* API for handlers. */ 119 | int an_io_set_deadline(an_request_id_t, unsigned int); 120 | int an_io_get_tcp_info(an_request_id_t, struct tcp_info *); 121 | 122 | #endif /* _COMMON_AN_SERVER_H */ 123 | -------------------------------------------------------------------------------- /common/an_smr.h: -------------------------------------------------------------------------------- 1 | #ifndef _COMMON_AN_SMR_H 2 | #define _COMMON_AN_SMR_H 3 | #include 4 | #include 5 | 6 | #include "autoconf_globals.h" 7 | #include "common/an_hook.h" 8 | #include "common/an_malloc.h" 9 | 10 | /* 11 | * Pause memory reclamation for this thread. 12 | * 13 | * When an_smr_poll() is called by this thread, it will no-op and return false. 14 | */ 15 | void an_smr_pause(void); 16 | 17 | /* 18 | * Resume memory reclamation for this thread. 19 | * 20 | * an_smr_poll() will no longer be forced to no-op when called by this thread. 21 | */ 22 | void an_smr_resume(void); 23 | 24 | /** 25 | * Get the current pause depth for this thread. 26 | */ 27 | size_t an_smr_get_pause_depth(void); 28 | 29 | #if defined(DISABLE_SMR) 30 | typedef struct an_smr_record { } an_smr_record_t; 31 | typedef struct an_smr_section { } an_smr_section_t; 32 | typedef struct an_smr { } an_smr_t; 33 | 34 | #define an_smr_begin(z) \ 35 | do { \ 36 | (void)(z); \ 37 | } while (0) 38 | #define an_smr_end(z) \ 39 | do { \ 40 | (void)(z); \ 41 | } while (0) 42 | #define an_smr_unregister(y) \ 43 | do { \ 44 | (void)(y); \ 45 | } while (0) 46 | #define an_smr_register(y) \ 47 | do { \ 48 | (void)(y); \ 49 | } while (0) 50 | 51 | #elif defined(USE_EPOCH) 52 | #include 53 | 54 | typedef ck_epoch_record_t an_smr_record_t; 55 | typedef ck_epoch_section_t an_smr_section_t; 56 | typedef ck_epoch_t an_smr_t; 57 | 58 | void an_smr_begin(an_smr_section_t *section); 59 | void an_smr_end(an_smr_section_t *section); 60 | 61 | #define an_smr_register(y) ck_epoch_register(&global_smr, y) 62 | #define an_smr_unregister(y) ck_epoch_unregister(&global_smr, y) 63 | 64 | #elif defined(USE_RTBR) 65 | #include "common/rtbr/rtbr.h" 66 | 67 | typedef const struct an_rtbr_record *an_smr_record_t; 68 | typedef struct an_rtbr_section an_smr_section_t; 69 | typedef struct { } an_smr_t; 70 | 71 | void an_smr_begin(an_smr_section_t *section); 72 | void an_smr_end(an_smr_section_t *section); 73 | 74 | #define an_smr_register(y) \ 75 | do { \ 76 | const struct an_rtbr_record **dst = (y); \ 77 | \ 78 | if (*dst == NULL) { \ 79 | *dst = an_rtbr_self(); \ 80 | } \ 81 | } while (0) 82 | #define an_smr_unregister(y) do { (void)(y); } while (0) 83 | #endif /* defined(USE_SMR) */ 84 | 85 | /** 86 | * @brief determines if smr record has any active read sections 87 | * @param smr smr record to check 88 | * 89 | * @return true if there is at least one active smr read section 90 | */ 91 | bool an_smr_is_active(const an_smr_record_t *smr); 92 | 93 | bool an_smr_entry_pending_destruction(void *obj); 94 | 95 | /* really declared in an_thread.h, but circular deps. */ 96 | extern __thread unsigned int an_thread_current_id; 97 | 98 | #if defined(__COVERITY__) 99 | #define an_smr_call(OBJECT, FUNCTION) \ 100 | do { \ 101 | (FUNCTION)((OBJECT)); \ 102 | } while (0); 103 | #else 104 | #define an_smr_call(OBJECT, FUNCTION) \ 105 | ({ \ 106 | void *AN_SMR_CALL_obj = (OBJECT); \ 107 | void (*AN_SMR_CALL_func)(void *) = \ 108 | AN_CC_CAST_CB(void, (FUNCTION), (OBJECT)); \ 109 | \ 110 | do { \ 111 | AN_HOOK(an_smr_call, disable) { \ 112 | if (an_thread_current_id == 0) { \ 113 | break; \ 114 | } \ 115 | } \ 116 | \ 117 | an_smr_call_inner(AN_SMR_CALL_obj, AN_SMR_CALL_func); \ 118 | } while (0); \ 119 | }) 120 | #endif 121 | 122 | void an_smr_call_inner(void* obj, void(*func)(void *)); 123 | void an_smr_free(an_malloc_token_t token, void *obj); 124 | 125 | void an_smr_init(); 126 | bool an_smr_poll(); 127 | void an_smr_synchronize(void); 128 | 129 | unsigned int an_smr_n_pending(an_smr_record_t *record); 130 | 131 | struct an_thread; /* Forward declaration for function below */ 132 | void an_smr_handler_http(struct evhttp_request *request, struct an_thread *cursor); 133 | #endif /* _COMMON_AN_SMR_H */ 134 | -------------------------------------------------------------------------------- /common/an_streaming_quantile.c: -------------------------------------------------------------------------------- 1 | #include "common/an_malloc.h" 2 | #include "common/an_rand.h" 3 | #include "common/an_streaming_quantile.h" 4 | #include "common/an_thread.h" 5 | 6 | static AN_MALLOC_DEFINE(streaming_qnt_token, 7 | .string = "streaming_qnt", 8 | .mode = AN_MEMORY_MODE_FIXED, 9 | .size = sizeof(struct an_streaming_qnt)); 10 | 11 | struct an_streaming_qnt * 12 | an_streaming_qnt_estimate_create(double quantile, uint64_t initial_value, double adjustment_value) 13 | { 14 | struct an_streaming_qnt *new_qnt; 15 | 16 | new_qnt = an_calloc_object(streaming_qnt_token); 17 | new_qnt->quantile = quantile; 18 | new_qnt->estimate = initial_value; 19 | new_qnt->adjustment_value = adjustment_value; 20 | 21 | return new_qnt; 22 | }; 23 | 24 | void 25 | an_streaming_qnt_update_mpmc(struct an_streaming_qnt *streaming_qnt, uint64_t sample) 26 | { 27 | bool is_below_quantile = an_random_indicator(streaming_qnt->quantile); 28 | uint64_t curr_estimate = ck_pr_load_64(&streaming_qnt->estimate); 29 | 30 | if (sample < curr_estimate) { 31 | if (is_below_quantile == false) { 32 | /* only update if curr_estimate hasn't changed since before first if check. 33 | * if it has changed, don't update. we're ok with the occasional missed update, 34 | * rather than looping until it's safe to update */ 35 | ck_pr_cas_64(&streaming_qnt->estimate, curr_estimate, curr_estimate - streaming_qnt->adjustment_value); 36 | } 37 | } else if (sample > curr_estimate) { 38 | if (is_below_quantile == true) { 39 | ck_pr_cas_64(&streaming_qnt->estimate, curr_estimate, curr_estimate + streaming_qnt->adjustment_value); 40 | } 41 | } 42 | } 43 | 44 | void 45 | an_streaming_qnt_update_spmc(struct an_streaming_qnt *streaming_qnt, uint64_t sample) 46 | { 47 | uint64_t curr_estimate = streaming_qnt->estimate; 48 | bool is_below_quantile = an_random_indicator(streaming_qnt->quantile); 49 | 50 | if (sample < curr_estimate) { 51 | if (is_below_quantile == false) { 52 | streaming_qnt->estimate -= streaming_qnt->adjustment_value; 53 | } 54 | } else { 55 | if (is_below_quantile == true) { 56 | streaming_qnt->estimate += streaming_qnt->adjustment_value; 57 | } 58 | } 59 | } 60 | 61 | uint64_t 62 | an_streaming_qnt_observe(const struct an_streaming_qnt *streaming_qnt) 63 | { 64 | 65 | return ck_pr_load_64(&streaming_qnt->estimate); 66 | } 67 | 68 | void 69 | an_streaming_qnt_destroy(struct an_streaming_qnt *streaming_qnt) 70 | { 71 | 72 | an_free(streaming_qnt_token, streaming_qnt); 73 | } 74 | -------------------------------------------------------------------------------- /common/an_streaming_quantile.h: -------------------------------------------------------------------------------- 1 | #ifndef AN_STREAMING_QUANTILE_H 2 | #define AN_STREAMING_QUANTILE_H 3 | /** 4 | * Implements "Frugal Streaming for Estimating Quantiles" --Ma, et al 5 | * Useful for getting quantile estimates on a streaming input source. 6 | * To use, either initialize or create an an_streaming_qnt, and sample new values with 7 | * an_streaming_qnt_update_mpmc or an_streaming_qnt_update_spmc, depending on use case. 8 | * To obtain an estimate of the specified quantile, use an_streaming_qnt_observe. 9 | * 10 | * The reasoning behind this frugal implementation is that when estimating any x quantile, 11 | * if the current estimate is at the stream's true x quantile, we expect to see items larger 12 | * than the current estimate with probability 1 - x. 13 | */ 14 | 15 | #include 16 | 17 | struct an_streaming_qnt { 18 | double quantile; /** quantile to keep track of. value should be between 0 and 1 */ 19 | uint64_t estimate; /** estimate for quantile */ 20 | double adjustment_value; /** value by which current quantile estimate will be adjusted (+/-) at a time */ 21 | }; 22 | 23 | /** 24 | * @brief static initializer for an_streaming_qnt struct. 25 | * Example usage: struct an_streaming_qnt qnt = AN_STREAMING_QUANTILE_INITIALIZER(0.99, 0, 1); 26 | * @param QUANTILE quantile to keep track of 27 | * @param INITIAL_VALUE initial estimate 28 | * @param ADJUSTMENT_VALUE value by which current quantile estimate will be adjusted (+/-) at a time 29 | */ 30 | #define AN_STREAMING_QUANTILE_INITIALIZER(QUANTILE, INITIAL_VALUE, ADJUSTMENT_VALUE) \ 31 | { .quantile = (QUANTILE), .estimate = (INITIAL_VALUE), .adjustment_value = (ADJUSTMENT_VALUE) } 32 | 33 | /** 34 | * @brief Create an_streaming_qnt object to monitor quantile estimates 35 | * @param quant quantile to monitor (e.g. 0.75 for 75th quantile) 36 | * @param initial_value initial estimate 37 | * @param adjustment_value value by which current quantile estimate will be adjusted (+/-) at a time 38 | * @return pointer to created an_streaming_qnt object 39 | */ 40 | struct an_streaming_qnt *an_streaming_qnt_estimate_create(double quantile, uint64_t initial_value, double adjustment_value); 41 | 42 | /** 43 | * @brief free @streaming_qnt 44 | */ 45 | void an_streaming_qnt_destroy(struct an_streaming_qnt *streaming_qnt); 46 | 47 | /** 48 | * @brief insert new value for an_streaming_qnt obj. _mpmc indicates atomic updates 49 | * @param streaming_qnt object to sample for 50 | * @param sample new value 51 | */ 52 | void an_streaming_qnt_update_mpmc(struct an_streaming_qnt *streaming_qnt, uint64_t sample); 53 | 54 | /** 55 | * @brief insert new value for an_streaming_qnt obj. _spmc indicates non-atomic updates 56 | * @param streaming_qnt object to sample for 57 | * @param sample new value 58 | */ 59 | void an_streaming_qnt_update_spmc(struct an_streaming_qnt *streaming_qnt, uint64_t sample); 60 | 61 | /** 62 | * @brief get quantile estimate for @streaming_qnt 63 | */ 64 | uint64_t an_streaming_qnt_observe(const struct an_streaming_qnt *streaming_qnt); 65 | 66 | #endif /* AN_STREAMING_QUANTILE_H */ 67 | -------------------------------------------------------------------------------- /common/an_string.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "common/an_malloc.h" 6 | #include "common/an_sstm.h" 7 | #include "common/an_string.h" 8 | #include "common/an_thread.h" 9 | #include "common/json_parser.h" 10 | 11 | /* string type support */ 12 | static AN_MALLOC_DEFINE(an_string_token, 13 | .string = "an_string", 14 | .mode = AN_MEMORY_MODE_VARIABLE, 15 | .use_pool_allocation = true); 16 | 17 | char * 18 | an_string_malloc(size_t size) 19 | { 20 | 21 | return an_malloc_region(an_string_token, size); 22 | } 23 | 24 | void 25 | an_string_free(char *string) 26 | { 27 | 28 | an_free(an_string_token, string); 29 | return; 30 | } 31 | 32 | void 33 | an_string_defer(char *string) 34 | { 35 | 36 | if (string == NULL) { 37 | return; 38 | } 39 | 40 | an_thread_defer(string, 41 | AN_CC_CAST_CB(void, an_string_free, string)); 42 | return; 43 | } 44 | 45 | void 46 | an_string_sstm_call(char *string) 47 | { 48 | 49 | if (string == NULL) { 50 | return; 51 | } 52 | 53 | an_sstm_call(an_string_free, string); 54 | return; 55 | } 56 | 57 | char * 58 | an_string_dup(const char *string) 59 | { 60 | char *copy; 61 | size_t len; 62 | 63 | if (string == NULL) 64 | return NULL; 65 | 66 | len = strlen(string) + 1; 67 | copy = an_string_malloc(len); 68 | memcpy(copy, string, len); 69 | 70 | return copy; 71 | } 72 | 73 | char * 74 | an_string_strndup(const char *string, size_t n) 75 | { 76 | size_t len; 77 | char *copy; 78 | 79 | if (string == NULL) 80 | return NULL; 81 | 82 | len = strnlen(string, n); 83 | copy = an_string_malloc(len + 1); 84 | memcpy(copy, string, len); 85 | copy[len] = '\0'; 86 | return copy; 87 | } 88 | 89 | char * 90 | an_string_realloc(char *src, size_t old_size, size_t new_size) 91 | { 92 | 93 | return an_realloc_region(an_string_token, src, old_size, new_size); 94 | } 95 | 96 | int __attribute__((format(printf, 2, 0))) 97 | an_string_vasprintf(char **strp, const char *fmt, va_list ap) 98 | { 99 | va_list aq; 100 | va_copy(aq, ap); 101 | int size = vsnprintf(NULL, 0, fmt, ap); 102 | if (size < 0) { 103 | goto failure; 104 | } 105 | 106 | *strp = an_string_malloc(size + 1); 107 | size = vsprintf(*strp, fmt, aq); 108 | va_end(aq); 109 | 110 | return size; 111 | 112 | failure: 113 | va_end(aq); 114 | return -1; 115 | } 116 | 117 | int 118 | an_string_asprintf(char **strp, const char *fmt, ...) 119 | { 120 | int ret; 121 | va_list ap; 122 | 123 | va_start(ap, fmt); 124 | ret = an_string_vasprintf(strp, fmt, ap); 125 | va_end(ap); 126 | 127 | return ret; 128 | } 129 | 130 | static inline char * 131 | _an_string_jsonp_dup(an_json_parser_t *parser, size_t (*fn)(an_json_parser_t *, char *, int)) 132 | { 133 | char *ret; 134 | size_t len; 135 | 136 | if (parser->type != AN_JSON_STRING || parser->val_len == 0) { 137 | return NULL; 138 | } 139 | 140 | len = parser->val_len + 1; 141 | ret = an_string_malloc(len); 142 | len = fn(parser, ret, len); 143 | if (len == 0) { 144 | an_string_free(ret); 145 | return NULL; 146 | } 147 | 148 | return ret; 149 | } 150 | 151 | char * 152 | an_string_jsonp_dup(an_json_parser_t *parser) 153 | { 154 | 155 | return _an_string_jsonp_dup(parser, an_json_get_string); 156 | } 157 | 158 | char * 159 | an_string_jsonp_escaped_dup(an_json_parser_t *parser) 160 | { 161 | 162 | return _an_string_jsonp_dup(parser, an_json_get_escaped_string); 163 | } 164 | 165 | char * 166 | an_string_dup_urldecode(const char *s, size_t len) 167 | { 168 | char *decode; 169 | 170 | if (s == NULL) { 171 | return NULL; 172 | } 173 | 174 | if (len == 0) { 175 | len = strlen(s); 176 | } 177 | 178 | decode = an_string_malloc(modp_burl_decode_len(len)); 179 | modp_burl_decode(decode, s, len); 180 | 181 | return decode; 182 | } 183 | 184 | char * 185 | an_string_dup_tolower(const char *s, size_t len) 186 | { 187 | char *lower; 188 | 189 | if (s == NULL) { 190 | return NULL; 191 | } 192 | 193 | if (len == 0) { 194 | len = strlen(s); 195 | } 196 | 197 | lower = an_string_malloc(len + 1); 198 | modp_tolower_copy(lower, s, len); 199 | lower[len] = '\0'; 200 | 201 | return lower; 202 | } 203 | -------------------------------------------------------------------------------- /common/an_string.h: -------------------------------------------------------------------------------- 1 | #ifndef AN_STRING_H 2 | #define AN_STRING_H 3 | 4 | #include 5 | #include 6 | 7 | #if defined(__GNUC__) || defined(__clang__) 8 | #define MALLOC_ATTRIBUTE __attribute__((malloc, warn_unused_result)) 9 | #else 10 | #define MALLOC_ATTRIBUTE 11 | #endif 12 | 13 | MALLOC_ATTRIBUTE char *an_string_malloc(size_t); 14 | void an_string_free(char *); 15 | void an_string_defer(char *); 16 | void an_string_sstm_call(char *); 17 | MALLOC_ATTRIBUTE char *an_string_dup(const char *); 18 | MALLOC_ATTRIBUTE char *an_string_strndup(const char *, size_t); 19 | MALLOC_ATTRIBUTE char *an_string_realloc(char *src, size_t old_size, size_t new_size); 20 | int an_string_vasprintf(char **, const char *, va_list) __attribute__((format(printf, 2, 0))); 21 | int an_string_asprintf(char **, const char *, ...) __attribute__((format(printf, 2, 3))); 22 | 23 | struct an_json_parser; 24 | MALLOC_ATTRIBUTE char *an_string_jsonp_dup(struct an_json_parser *); 25 | MALLOC_ATTRIBUTE char *an_string_jsonp_escaped_dup(struct an_json_parser *); 26 | MALLOC_ATTRIBUTE char *an_string_dup_urldecode(const char *s, size_t len); 27 | MALLOC_ATTRIBUTE char *an_string_dup_tolower(const char *s, size_t len); 28 | 29 | #undef MALLOC_ATTRIBUTE 30 | 31 | #endif /* _AN_STRING_H */ 32 | -------------------------------------------------------------------------------- /common/an_syslog.h: -------------------------------------------------------------------------------- 1 | #ifndef _AN_SYSLOG_H 2 | #define _AN_SYSLOG_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "common/an_cc.h" 10 | 11 | AN_EXTERN_C_BEGIN 12 | 13 | /* 14 | * Initialize the syslog subsystem. 15 | */ 16 | void an_syslog_init(void); 17 | 18 | /* 19 | * Clean up the syslog subsystem. 20 | */ 21 | void an_syslog_deinit(void); 22 | 23 | /* 24 | * Initialize the consumer thread. Must be called after an_syslog_init(). 25 | * 26 | * @param num_producers The maximum number of producers that will register 27 | * @return 0 on success, < 0 on error 28 | */ 29 | int an_syslog_init_consumer(unsigned int num_producers); 30 | 31 | /* 32 | * Register this thread as a producer of syslog work. 33 | */ 34 | void an_syslog_register_producer(void); 35 | 36 | /* Open a log in syslog. See openlog(3). */ 37 | void an_syslog_openlog(const char *ident, int option, int facility); 38 | 39 | /* Close all logs opened by syslog. Optional. See closelog(3). */ 40 | void an_syslog_closelog(void); 41 | 42 | /* Write to syslog. See syslog(3). */ 43 | void an_syslog(int priority, const char *format, ...) __attribute__((format(printf, 2, 3))); 44 | 45 | /* Write to syslog. See syslog(3). */ 46 | void an_vsyslog(int priority, const char *format, va_list ap); 47 | 48 | #define an_syslog_backtrace(priority, format, ...) \ 49 | an_syslog_backtrace_internal(priority, format "\n", ##__VA_ARGS__) 50 | 51 | /* Write a backtrace to syslog. See syslog(3) and backtrace(3). */ 52 | void an_syslog_backtrace_internal(int priority, const char *format, ...) __attribute__((format(printf, 2, 3))); 53 | 54 | AN_EXTERN_C_END 55 | 56 | #endif /* _AN_SYSLOG_H */ 57 | -------------------------------------------------------------------------------- /common/an_time.h: -------------------------------------------------------------------------------- 1 | #ifndef _AN_TIME_H 2 | #define _AN_TIME_H 3 | 4 | #include "an_thread.h" 5 | #include "an_md.h" 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #define SECONDS_PER_DAY 86400 12 | #define SECONDS_PER_HOUR 3600 13 | #define MICROSECONDS_PER_SECOND 1000000ULL 14 | #define HOURS_PER_DAY 24 15 | 16 | #if defined(CLOCK_MONOTONIC_RAW) 17 | #define AN_TIME_MONOTONIC_CLOCK CLOCK_MONOTONIC_RAW 18 | #else 19 | #define AN_TIME_MONOTONIC_CLOCK CLOCK_MONOTONIC 20 | #endif 21 | 22 | static inline uint64_t 23 | an_time_tvtous(const struct timeval *tv) 24 | { 25 | 26 | return (tv->tv_sec * MICROSECONDS_PER_SECOND) + (uint64_t)tv->tv_usec; 27 | } 28 | 29 | static inline void 30 | an_time_ustotv(uint64_t micros, struct timeval *tv) 31 | { 32 | tv->tv_sec = micros / MICROSECONDS_PER_SECOND; 33 | tv->tv_usec = micros % MICROSECONDS_PER_SECOND; 34 | } 35 | 36 | static inline int 37 | an_gettimeofday(struct timeval *tv, bool cache) 38 | { 39 | 40 | if (current == NULL) { 41 | return gettimeofday(tv, NULL); 42 | } 43 | 44 | if (cache == false) 45 | event_base_update_cache_time(current->event_base); 46 | 47 | return event_base_gettimeofday_cached(current->event_base, tv); 48 | } 49 | 50 | static inline uint64_t 51 | an_time_now_us(bool cache) 52 | { 53 | struct timeval tv; 54 | 55 | if (an_gettimeofday(&tv, cache) == -1) 56 | return 0; 57 | 58 | return an_time_tvtous(&tv); 59 | } 60 | 61 | static inline time_t 62 | an_time(bool cache) 63 | { 64 | struct timeval tv; 65 | 66 | if (an_gettimeofday(&tv, cache) == -1) 67 | return 0; 68 | 69 | return tv.tv_sec; 70 | } 71 | 72 | static inline uint64_t 73 | an_rdtsc(void) 74 | { 75 | 76 | return an_md_rdtsc(); 77 | } 78 | 79 | static inline int64_t 80 | an_time_tstons(struct timespec *ts) 81 | { 82 | 83 | return (ts->tv_sec * 1000000000UL) + ts->tv_nsec; 84 | } 85 | 86 | static inline int64_t 87 | an_time_monotonic_ns() 88 | { 89 | struct timespec ts; 90 | 91 | clock_gettime(AN_TIME_MONOTONIC_CLOCK, &ts); 92 | 93 | return an_time_tstons(&ts); 94 | } 95 | 96 | #endif /* _AN_TIME_H */ 97 | -------------------------------------------------------------------------------- /common/an_zorder.c: -------------------------------------------------------------------------------- 1 | #include "common/an_zorder.h" 2 | 3 | /* 4 | * See https://graphics.stanford.edu/~seander/bithacks.html#InterleaveBMN for more info 5 | */ 6 | uint32_t 7 | an_zorder(uint16_t x, uint16_t y) 8 | { 9 | uint64_t key = x; 10 | 11 | key |= ((uint64_t)y << 32); 12 | 13 | key = (key | (key << 8)) & 0x00FF00FF00FF00FFULL; 14 | key = (key | (key << 4)) & 0x0F0F0F0F0F0F0F0FULL; 15 | key = (key | (key << 2)) & 0x3333333333333333ULL; 16 | key = (key | (key << 1)) & 0x5555555555555555ULL; 17 | 18 | return (uint32_t)(key | key >> 31); 19 | } 20 | -------------------------------------------------------------------------------- /common/an_zorder.h: -------------------------------------------------------------------------------- 1 | #ifndef AN_ZORDER_H 2 | #define AN_ZORDER_H 3 | 4 | #include 5 | 6 | /** 7 | * @brief computes the z-order index of 2 16 bit integers 8 | * 9 | * Bits of x will be in the zero-indexed even bit positions and bits 10 | * of y in the odd positions. 11 | */ 12 | uint32_t an_zorder(uint16_t x, uint16_t y); 13 | 14 | #endif /* AN_ZORDER_H */ 15 | -------------------------------------------------------------------------------- /common/check/check_an_array.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "common/an_array.h" 8 | #include "common/an_malloc.h" 9 | #include "common/common_types.h" 10 | 11 | struct node { 12 | ssize_t value; 13 | }; 14 | 15 | AN_ARRAY(node, test); 16 | AN_ARRAY_PRIMITIVE(int, test2); 17 | AN_ARRAY_DEFINE_COPY_INT_SET(test2) 18 | 19 | START_TEST(stack_generic) 20 | { 21 | AN_ARRAY_INSTANCE(test) array; 22 | struct node entry[1024]; 23 | ssize_t i; 24 | struct node *cursor; 25 | 26 | AN_ARRAY_INIT(test, &array, 1); 27 | for (i = 0; i < 1024; i++) { 28 | entry[i].value = i; 29 | struct node *p = AN_ARRAY_PUSH(test, &array, &entry[i]); 30 | fail_if(p != &array.values[i]); 31 | } 32 | 33 | i = 0; 34 | AN_ARRAY_FOREACH(&array, cursor) { 35 | fail_if((uintptr_t)cursor->value != (uintptr_t)i++); 36 | } 37 | 38 | i = 0; 39 | AN_ARRAY_FOREACH(&array, cursor) { 40 | fail_if((uintptr_t)cursor->value != (uintptr_t)i++); 41 | } 42 | 43 | for (i = 1024; i > 0; i--) { 44 | unsigned int n_entries; 45 | struct node *n; 46 | 47 | n = AN_ARRAY_POP(test, &array, &n_entries); 48 | fail_if(n_entries != i); 49 | fail_if((uintptr_t)n->value != (uintptr_t)i - 1); 50 | } 51 | 52 | AN_ARRAY_PUSH(test, &array, &entry[0]); 53 | fail_if(array.capacity != 1024); 54 | 55 | struct node testnode; 56 | size_t size = sizeof(struct node); 57 | memset(&testnode, 0, size); 58 | 59 | for (i = 0; i < 10; i++) { 60 | struct node *p = AN_ARRAY_PUSH(test, &array, NULL); 61 | fail_if(memcmp(p, &testnode, size) != 0); 62 | } 63 | } 64 | END_TEST 65 | 66 | START_TEST(stack) 67 | { 68 | an_array_t array; 69 | ssize_t i; 70 | 71 | an_array_init(&array, 1); 72 | for (i = 0; i < 1024; i++) { 73 | an_array_push(&array, (void *)i); 74 | } 75 | 76 | for (i = 1024; i > 0; i--) { 77 | unsigned int n_entries; 78 | void *value = NULL; 79 | 80 | value = an_array_pop(&array, &n_entries); 81 | fail_if(n_entries != i); 82 | fail_if((uintptr_t)value != (uintptr_t)i - 1); 83 | } 84 | 85 | an_array_push(&array, NULL); 86 | fail_if(array.capacity != 1024); 87 | } 88 | END_TEST 89 | START_TEST(find_element) 90 | { 91 | an_array_t array; 92 | ssize_t i; 93 | 94 | an_array_init(&array, 1); 95 | for (i = 0; i < 1024; i++) { 96 | an_array_push(&array, (void *)i); 97 | } 98 | for (i = 0; i < 1024; i++) { 99 | fail_if(!an_array_member(&array, (void*)i), "Could not find a valid member"); 100 | } 101 | fail_if(an_array_member(&array, (void*)-1), "Found a member that does not belong"); 102 | an_array_pop(&array,NULL); // remove one to test the odd element count 103 | fail_if(an_array_length(&array) != 1023,"Pop failed ?"); 104 | for (i = 0; i < 1023; i++) { 105 | fail_if(!an_array_member(&array, (void*)i), "Could not find a valid member"); 106 | } 107 | fail_if(an_array_member(&array, (void*)-1), "Found a member that does not belong"); 108 | } 109 | END_TEST 110 | 111 | START_TEST(remove_element) 112 | { 113 | an_array_t array; 114 | ssize_t i; 115 | 116 | an_array_init(&array, 1); 117 | for (i = 0; i < 1024; i++) 118 | an_array_push(&array, (void *)i); 119 | 120 | for (i = 0; i < 1024; i++) { 121 | fail_if(!an_array_remove(&array, (void*) i) , "Could not find a valid member to remove"); 122 | fail_if(an_array_member(&array, (void*)i), "Found a deleted member"); 123 | fail_if(an_array_length(&array) != 1023-i,"Remove member failed"); 124 | } 125 | } 126 | END_TEST 127 | 128 | START_TEST(resize) 129 | { 130 | an_array_t array; 131 | AN_ARRAY_INSTANCE(test) array2; 132 | 133 | an_array_init(&array, 16); 134 | AN_ARRAY_INIT(test, &array2, 16); 135 | 136 | fail_if(an_array_length(&array) != 0); 137 | an_array_resize(&array, 2); 138 | fail_if(an_array_length(&array) != 0); 139 | 140 | fail_if(AN_ARRAY_LENGTH(test, &array2) != 0); 141 | AN_ARRAY_RESIZE(test, &array2, 2); 142 | fail_if(AN_ARRAY_LENGTH(test, &array2) != 0); 143 | } 144 | END_TEST 145 | 146 | START_TEST(int_set) 147 | { 148 | AN_ARRAY_INSTANCE(test2) array; 149 | AN_ARRAY_INIT(test2, &array, 1); 150 | 151 | int_set_t *set = new_int_set(NULL, sizeof(int), 16); 152 | 153 | for (int i = 16; i > 0; i--) { 154 | add_int_to_set(set, i); 155 | } 156 | 157 | AN_ARRAY_COPY_INT_SET(test2, &array, set); 158 | 159 | for (int i = 0; i < 16; i++) { 160 | int *x = AN_ARRAY_VALUE(test2, &array, i); 161 | fail_if(*x != int_set_index(set, i)); 162 | } 163 | } 164 | END_TEST 165 | 166 | 167 | int 168 | main(int argc, char *argv[]) 169 | { 170 | SRunner *sr; 171 | Suite *suite = suite_create("common/an_array"); 172 | TCase *tc = tcase_create("test_an_array"); 173 | 174 | an_malloc_init(); 175 | an_md_probe(); 176 | common_type_register(); 177 | 178 | tcase_add_test(tc, stack); 179 | tcase_add_test(tc, stack_generic); 180 | tcase_add_test(tc, find_element); 181 | tcase_add_test(tc, remove_element); 182 | tcase_add_test(tc, resize); 183 | tcase_add_test(tc, int_set); 184 | 185 | suite_add_tcase(suite, tc); 186 | 187 | sr = srunner_create(suite); 188 | srunner_set_xml(sr, "check/check_an_array.xml"); 189 | srunner_set_fork_status(sr, CK_FORK); 190 | srunner_run_all(sr, CK_NORMAL); 191 | 192 | return srunner_ntests_failed(sr); 193 | } 194 | -------------------------------------------------------------------------------- /common/check/check_an_average.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "common/an_average.h" 7 | #include "common/util.h" 8 | 9 | START_TEST(test_small) { 10 | struct an_average avg; 11 | double sum = 0, count = 0; 12 | 13 | an_average_init(&avg); 14 | for (size_t i = 0; i < 1UL << 10; i++) { 15 | double approx, delta, exact; 16 | uint64_t num, denom; 17 | size_t x; 18 | 19 | x = an_random_below(100); 20 | an_average_insert(&avg, x); 21 | sum += x; 22 | count++; 23 | 24 | an_average_read(&avg, &num, &denom); 25 | approx = 1.0 * num / denom; 26 | exact = sum / count; 27 | 28 | if (denom > 200) { 29 | delta = fabs(approx - exact) / (fabs(exact) + 1); 30 | fail_if(delta > 1e-1); 31 | } 32 | } 33 | } 34 | END_TEST 35 | 36 | START_TEST(test_large) { 37 | struct an_average avg; 38 | double sum = 0, count = 0; 39 | uint64_t offset = 1ULL << 61; 40 | 41 | an_average_init(&avg); 42 | for (size_t i = 0; i < 1UL << 10; i++) { 43 | double approx, delta, exact; 44 | uint64_t num, denom; 45 | size_t x; 46 | 47 | x = an_random_below(100); 48 | an_average_insert(&avg, offset + x); 49 | sum += x; 50 | count++; 51 | 52 | an_average_read(&avg, &num, &denom); 53 | approx = 1.0 * num / denom; 54 | exact = offset + (sum / count); 55 | 56 | if (denom > 200) { 57 | delta = fabs(approx - exact) / (fabs(exact) + 1); 58 | fail_if(delta > 1e-1); 59 | } 60 | } 61 | } 62 | END_TEST 63 | 64 | START_TEST(test_inc) { 65 | struct an_average avg; 66 | double sum = 0, count = 0; 67 | 68 | an_average_init(&avg); 69 | for (size_t i = 0; i < 1UL << 10; i++) { 70 | double approx, delta, exact; 71 | uint64_t num, denom; 72 | size_t x; 73 | 74 | x = an_random_below(100); 75 | an_average_insert(&avg, x); 76 | sum += x; 77 | count++; 78 | 79 | x = an_random_below(10000); 80 | an_average_increment(&avg, x); 81 | sum += x; 82 | 83 | an_average_read(&avg, &num, &denom); 84 | approx = 1.0 * num / denom; 85 | exact = sum / count; 86 | 87 | if (denom > 200) { 88 | delta = fabs(approx - exact) / (fabs(exact) + 1); 89 | fail_if(delta > 1e-1); 90 | } 91 | } 92 | } 93 | END_TEST 94 | 95 | int 96 | main(int argc, char **argv) 97 | { 98 | int num_failed; 99 | Suite *suite = suite_create("common/check_an_average"); 100 | 101 | TCase *tc = tcase_create("test_an_average"); 102 | tcase_add_test(tc, test_small); 103 | tcase_add_test(tc, test_large); 104 | tcase_add_test(tc, test_inc); 105 | suite_add_tcase(suite, tc); 106 | 107 | SRunner *sr = srunner_create(suite); 108 | srunner_set_xml(sr, "check/check_an_average.xml"); 109 | srunner_set_fork_status(sr, CK_FORK); 110 | srunner_run_all(sr, CK_NORMAL); 111 | 112 | num_failed = srunner_ntests_failed(sr); 113 | srunner_free(sr); 114 | 115 | return num_failed; 116 | } 117 | -------------------------------------------------------------------------------- /common/check/check_an_sampling.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "common/server_config.h" 7 | #include "common/an_sampling.h" 8 | 9 | 10 | #define ASSERT_EQ(observed, expected, prop, ctx, line_no) \ 11 | ck_assert_msg(observed.prop == expected.prop, "Expected " #prop " size to be %zu, got %zu. Ctx: \"%s (line %d)\"", expected.prop, observed.prop, ctx, line_no) 12 | 13 | static void 14 | check_fw_state(const struct an_sampling_fixed_window observed, const struct an_sampling_fixed_window expected, const char *ctx, int line_no) 15 | { 16 | ASSERT_EQ(observed, expected, population_size, ctx, line_no); 17 | ASSERT_EQ(observed, expected, sample_size, ctx, line_no); 18 | ASSERT_EQ(observed, expected, current_index, ctx, line_no); 19 | ASSERT_EQ(observed, expected, selected_count, ctx, line_no); 20 | } 21 | 22 | START_TEST(test_fw) 23 | { 24 | const size_t population_size = 1000; 25 | const size_t sample_size = 10; 26 | 27 | printf("test_sr: testing fixed window algorithm\n"); 28 | 29 | struct an_sampling_fixed_window inst; 30 | an_sampling_fixed_window_init(&inst, population_size, sample_size); 31 | check_fw_state(inst, (struct an_sampling_fixed_window) {.population_size = population_size, .sample_size = sample_size, .current_index = 0, .selected_count = 0}, "init", __LINE__); 32 | ck_assert_msg(an_sampling_fixed_window_is_exhausted(&inst) == false, "Population is not expected to be exhausted (%d)", __LINE__); 33 | 34 | size_t selected = 0; 35 | for (size_t i=0; i 0; i--) { 100 | an_rand(); 101 | } 102 | an_malloc_init(); 103 | an_thread_set_epoch_malloc(current, true); 104 | 105 | Suite *suite = suite_create("common/check_an_sampling"); 106 | 107 | TCase *tc = tcase_create("test_an_sampling"); 108 | tcase_set_timeout(tc, 0.0); 109 | tcase_add_test(tc, test_fw); 110 | suite_add_tcase(suite, tc); 111 | 112 | SRunner *sr = srunner_create(suite); 113 | srunner_set_xml(sr, "check/check_an_sampling.xml"); 114 | 115 | srunner_run_all(sr, CK_NORMAL); 116 | 117 | num_failed = srunner_ntests_failed(sr); 118 | srunner_free(sr); 119 | 120 | return num_failed; 121 | } 122 | -------------------------------------------------------------------------------- /common/check/check_an_streaming_quantile.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "common/an_array.h" 9 | #include "common/an_streaming_quantile.h" 10 | #include "common/an_malloc.h" 11 | #include "common/an_thread.h" 12 | #include "common/common_types.h" 13 | 14 | START_TEST(same_val) 15 | { 16 | struct an_streaming_qnt *qnt; 17 | 18 | qnt = an_streaming_qnt_estimate_create(0.99, 0, 1); 19 | for (size_t i = 0; i < 5000; i++) { 20 | an_streaming_qnt_update_mpmc(qnt, 100); 21 | } 22 | 23 | uint64_t result = an_streaming_qnt_observe(qnt); 24 | fail_if(result != 100); 25 | 26 | an_streaming_qnt_destroy(qnt); 27 | } 28 | END_TEST 29 | 30 | START_TEST(same_val_static) 31 | { 32 | struct an_streaming_qnt qnt = AN_STREAMING_QUANTILE_INITIALIZER(0.99, 0, 1); 33 | 34 | for (size_t i = 0; i < 5000; i++) { 35 | an_streaming_qnt_update_mpmc(&qnt, 100); 36 | } 37 | 38 | uint64_t result = an_streaming_qnt_observe(&qnt); 39 | fail_if(result != 100); 40 | } 41 | END_TEST 42 | 43 | START_TEST(correct_esimate) 44 | { 45 | int rand_arr[100] = { 0 }; 46 | for (size_t i = 0; i < 100; i++) { 47 | rand_arr[i] = i + 1; 48 | } 49 | 50 | size_t error_count = 0; 51 | 52 | for (int i = 0; i < 100; i++) { 53 | struct an_streaming_qnt qnt = AN_STREAMING_QUANTILE_INITIALIZER(0.75, 75, 1); 54 | for (size_t j = 0; j < 1000; j++) { 55 | an_random_shuffle(rand_arr, 100); 56 | for (size_t k = 0; k < 100; k++) { 57 | an_streaming_qnt_update_mpmc(&qnt, rand_arr[k]); 58 | } 59 | } 60 | 61 | int result = an_streaming_qnt_observe(&qnt); 62 | int accepted = 75; 63 | double percent_error = (result - accepted) / (double) (accepted) * 100.0; 64 | if (fabs(percent_error) > 10.0) { 65 | error_count++; 66 | } 67 | } 68 | fail_if(error_count > 10); 69 | } 70 | END_TEST 71 | 72 | START_TEST(zero_estimate) 73 | { 74 | int rand_arr[100] = { 0 }; 75 | for (size_t i = 0; i < 100; i++) { 76 | rand_arr[i] = i + 1; 77 | } 78 | 79 | size_t error_count = 0; 80 | 81 | for (int i = 0; i < 100; i++) { 82 | struct an_streaming_qnt qnt = AN_STREAMING_QUANTILE_INITIALIZER(0.75, 0, 1); 83 | for (size_t j = 0; j < 1000; j++) { 84 | an_random_shuffle(rand_arr, 100); 85 | for (size_t k = 0; k < 100; k++) { 86 | an_streaming_qnt_update_mpmc(&qnt, rand_arr[k]); 87 | } 88 | } 89 | 90 | int result = an_streaming_qnt_observe(&qnt); 91 | int accepted = 75; 92 | double percent_error = (result - accepted) / (double) (accepted) * 100.0; 93 | if (fabs(percent_error) > 10.0) { 94 | error_count++; 95 | } 96 | } 97 | fail_if(error_count > 10); 98 | } 99 | END_TEST 100 | 101 | int 102 | main(int argc, char *argv[]) 103 | { 104 | SRunner *sr; 105 | Suite *suite = suite_create("common/an_streaming_quantile"); 106 | TCase *tc = tcase_create("test_an_streaming_quantile"); 107 | 108 | an_malloc_init(); 109 | an_md_probe(); 110 | an_thread_init(); 111 | common_type_register(); 112 | 113 | tcase_add_test(tc, same_val_static); 114 | tcase_add_test(tc, same_val); 115 | tcase_add_test(tc, correct_esimate); 116 | tcase_add_test(tc, zero_estimate); 117 | 118 | suite_add_tcase(suite, tc); 119 | 120 | sr = srunner_create(suite); 121 | srunner_set_xml(sr, "check/an_streaming_quantile.xml"); 122 | srunner_set_fork_status(sr, CK_FORK); 123 | srunner_run_all(sr, CK_NORMAL); 124 | 125 | return srunner_ntests_failed(sr); 126 | } 127 | -------------------------------------------------------------------------------- /common/check/check_an_thread.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "common/an_malloc.h" 11 | #include "common/an_thread.h" 12 | #include "common/common_types.h" 13 | #include "common/server_config.h" 14 | #include "common/uuid_manager.h" 15 | 16 | void 17 | tls_key_dtor(void *data) 18 | { 19 | int *i = data; 20 | printf("tls_key_dtor called for %d!\n", *i); 21 | free(i); 22 | } 23 | 24 | void * 25 | thread_func(void *n) 26 | { 27 | 28 | for(int i=0; i < 50; i++) { 29 | printf("creating key %d\n", i); 30 | an_thread_key_t key; 31 | fail_if(an_thread_key_create(&key, n) != 0); 32 | int *j = malloc(sizeof(int)); 33 | *j = i; 34 | an_thread_setspecific(key, j); 35 | void *x = an_thread_getspecific(key); 36 | 37 | fail_if(x == NULL); 38 | } 39 | return NULL; 40 | } 41 | 42 | START_TEST(test_tls) { 43 | printf("test tls destruction order\n"); 44 | 45 | pthread_t t1; 46 | pthread_create(&t1, NULL, thread_func, tls_key_dtor); 47 | 48 | pthread_join(t1, NULL); 49 | } 50 | END_TEST 51 | 52 | int main(int argc, char **argv) { 53 | 54 | server_config_init("check_an_thread", argc, argv); 55 | 56 | Suite *suite = suite_create("common/an_thread"); 57 | 58 | TCase *tc = tcase_create("test_an_thread"); 59 | tcase_add_test(tc, test_tls); 60 | 61 | suite_add_tcase(suite, tc); 62 | 63 | SRunner *sr = srunner_create(suite); 64 | srunner_set_xml(sr, "check/check_an_thread.xml"); 65 | srunner_set_fork_status(sr, CK_FORK); 66 | srunner_run_all(sr, CK_NORMAL); 67 | int num_failed = srunner_ntests_failed(sr); 68 | srunner_free(sr); 69 | return num_failed; 70 | } 71 | -------------------------------------------------------------------------------- /common/check/check_an_zorder.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "common/an_zorder.h" 7 | 8 | #define LOW 2048U 9 | #define HIGH (65536U - 2048U) 10 | #define PRINT_MODULO 256U 11 | 12 | static uint32_t 13 | simple_zorder(uint16_t x, uint16_t y) 14 | { 15 | uint32_t result = 0; 16 | 17 | for (size_t i = 0; i < 16; i++) { 18 | uint32_t x_bit = (x >> i) & 1; 19 | uint32_t y_bit = (y >> i) & 1; 20 | 21 | result |= x_bit << (i * 2); 22 | result |= y_bit << (i * 2 + 1); 23 | } 24 | 25 | return result; 26 | } 27 | 28 | static void 29 | assert_zorder_valid(uint16_t x, uint16_t y) 30 | { 31 | uint32_t an_result, simple_result; 32 | 33 | an_result = an_zorder(x, y); 34 | simple_result = simple_zorder(x, y); 35 | 36 | ck_assert_msg(an_result == simple_result, "Assertion 'an_zorder(x, y)==simple_zorder(x, y)'" 37 | " failed for x = 0x%x and y = 0x%x: an_zorder(x, y)==0x%x, simple_zorder(x, y)==0x%x", 38 | x, y, an_result, simple_result); 39 | 40 | return; 41 | } 42 | 43 | static void 44 | print_range(uint32_t x_low, uint32_t x_high, uint32_t y_low, uint32_t y_high) 45 | { 46 | 47 | printf("Testing zorder(x, y) for x in [0x%x, 0x%x) and y in [0x%x, 0x%x)\n", 48 | x_low, x_high, y_low, y_high); 49 | return; 50 | } 51 | 52 | START_TEST(zorder_low) 53 | { 54 | for (uint32_t x = 0; x < LOW; x++) { 55 | if (x % PRINT_MODULO == 0) { 56 | print_range(x, x + PRINT_MODULO, 0, LOW); 57 | } 58 | 59 | for (uint32_t y = 0; y < LOW; y++) { 60 | assert_zorder_valid(x, y); 61 | } 62 | } 63 | } END_TEST 64 | 65 | START_TEST(zorder_high) 66 | { 67 | for (uint32_t x = HIGH; x <= UINT16_MAX; x++) { 68 | if (x % PRINT_MODULO == 0) { 69 | print_range(x, x + PRINT_MODULO, HIGH, UINT16_MAX + 1); 70 | } 71 | 72 | for (uint32_t y = HIGH; y <= UINT16_MAX; y++) { 73 | assert_zorder_valid(x, y); 74 | } 75 | } 76 | } END_TEST 77 | 78 | START_TEST(zorder_powers_of_two) 79 | { 80 | uint32_t from = (1 << _i) - 64; 81 | uint32_t to = (1 << _i) + 64; 82 | 83 | print_range(from, to, from, to); 84 | 85 | for (uint32_t x = from; x < to; x++) { 86 | for (uint32_t y = from; y < to; y++) { 87 | assert_zorder_valid(x, y); 88 | } 89 | } 90 | } END_TEST 91 | 92 | int 93 | main(int argc, char *argv[]) 94 | { 95 | SRunner *sr; 96 | Suite *suite = suite_create("common/an_zorder"); 97 | TCase *tc = tcase_create("test_an_zorder"); 98 | 99 | tcase_add_test(tc, zorder_low); 100 | tcase_add_test(tc, zorder_high); 101 | tcase_add_loop_test(tc, zorder_powers_of_two, 11, 16); 102 | suite_add_tcase(suite, tc); 103 | 104 | sr = srunner_create(suite); 105 | srunner_set_xml(sr, "check/check_an_zorder.xml"); 106 | srunner_set_fork_status(sr, CK_NOFORK); 107 | srunner_run_all(sr, CK_NORMAL); 108 | 109 | return srunner_ntests_failed(sr); 110 | } 111 | -------------------------------------------------------------------------------- /common/check/check_bump.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "common/memory/bump.h" 7 | 8 | START_TEST(smoke_private) 9 | { 10 | struct an_bump_private *private; 11 | uintptr_t last = 0; 12 | size_t allocated = 0; 13 | size_t capacity = 1UL << 23; 14 | 15 | private = an_bump_private_create(capacity, NULL); 16 | { 17 | void *first = an_bump_alloc(private, 0, 0); 18 | capacity -= 1 + (uintptr_t)first - (uintptr_t)private; 19 | } 20 | 21 | while (allocated < capacity) { 22 | size_t request; 23 | void *alloc; 24 | 25 | request = 1 + (1024.0 * random() / RAND_MAX); 26 | if (request > capacity - allocated) { 27 | request = capacity - allocated; 28 | } 29 | 30 | alloc = an_bump_alloc(private, request, 0); 31 | fail_if(alloc == NULL); 32 | fail_if((uintptr_t)alloc < last); 33 | last = (uintptr_t)alloc + request; 34 | allocated += request; 35 | } 36 | } END_TEST 37 | 38 | START_TEST(smoke_shared) 39 | { 40 | struct an_bump_shared *shared; 41 | uintptr_t last = 0; 42 | size_t allocated = 0; 43 | size_t capacity = 1UL << 22; 44 | 45 | shared = an_bump_shared_create(capacity, NULL); 46 | { 47 | void *first = an_bump_alloc(shared, 0, 0); 48 | capacity -= 1 + (uintptr_t)first - (uintptr_t)shared; 49 | } 50 | 51 | while (allocated < capacity) { 52 | size_t request; 53 | void *alloc; 54 | 55 | request = 1 + (1024.0 * random() / RAND_MAX); 56 | if (request > capacity - allocated) { 57 | request = capacity - allocated; 58 | } 59 | 60 | alloc = an_bump_alloc(shared, request, 0); 61 | fail_if(alloc == NULL); 62 | fail_if((uintptr_t)alloc < last); 63 | last = (uintptr_t)alloc + request; 64 | allocated += request; 65 | } 66 | } END_TEST 67 | 68 | int 69 | main(int argc, char *argv[]) 70 | { 71 | SRunner *sr; 72 | Suite *suite = suite_create("common/bump"); 73 | TCase *tc = tcase_create("test_bump"); 74 | 75 | tcase_add_test(tc, smoke_private); 76 | tcase_add_test(tc, smoke_shared); 77 | 78 | suite_add_tcase(suite, tc); 79 | 80 | sr = srunner_create(suite); 81 | srunner_set_xml(sr, "check/check_bump.xml"); 82 | srunner_set_fork_status(sr, CK_NOFORK); 83 | srunner_run_all(sr, CK_NORMAL); 84 | 85 | return srunner_ntests_failed(sr); 86 | } 87 | -------------------------------------------------------------------------------- /common/check/check_log_linear_bin.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "common/log_linear_bin.h" 5 | 6 | /* Check doesn't like having millions of test cases... */ 7 | #if 1 8 | #define must(X) assert((X)) 9 | #else 10 | #define must(X) fail_if(!(X)) 11 | #endif 12 | 13 | #define N_TEST_LB 10UL 14 | 15 | START_TEST(round_up) { 16 | unsigned int linear = _i; 17 | 18 | for (unsigned subbin = 0; subbin <= linear; subbin++) { 19 | size_t last_bin, last_bin_size, last_rounded; 20 | 21 | last_bin = log_linear_bin_of(0, &last_rounded, &last_bin_size, 22 | linear, subbin); 23 | 24 | must(last_bin == 0); 25 | must(last_bin_size == 1); 26 | must(last_rounded == 0); 27 | 28 | for (size_t i = 0; i < 1UL << N_TEST_LB; i++) { 29 | size_t bin, bin_size, rounded; 30 | 31 | bin = log_linear_bin_of(i, &rounded, &bin_size, linear, subbin); 32 | must(bin >= last_bin); 33 | must(bin_size > 0); 34 | must(i <= rounded); 35 | must(i + bin_size > rounded); 36 | 37 | if (bin == last_bin) { 38 | must(last_rounded == rounded); 39 | must(last_bin_size == bin_size); 40 | } else { 41 | must(rounded > last_rounded); 42 | must(bin_size >= last_bin_size); 43 | must(last_rounded + 1 == i); 44 | 45 | last_bin = bin; 46 | last_rounded = rounded; 47 | last_bin_size = bin_size; 48 | } 49 | } 50 | 51 | fail_if(last_bin > 1 + (N_TEST_LB << subbin)); 52 | } 53 | } 54 | END_TEST 55 | 56 | START_TEST(round_down) { 57 | unsigned int linear = _i; 58 | 59 | for (unsigned subbin = 0; subbin <= linear; subbin++) { 60 | size_t last_bin, last_bin_size, last_rounded; 61 | 62 | last_bin = log_linear_bin_down_of(0, &last_rounded, &last_bin_size, 63 | linear, subbin); 64 | 65 | must(last_bin == 0); 66 | must(last_bin_size > 0); 67 | must(last_rounded == 0); 68 | 69 | for (size_t i = 0; i < 1UL << N_TEST_LB; i++) { 70 | size_t bin, bin_size, rounded; 71 | 72 | bin = log_linear_bin_down_of(i, &rounded, &bin_size, linear, subbin); 73 | 74 | must(bin >= last_bin); 75 | must(i >= rounded); 76 | must(rounded + bin_size > i); 77 | if (bin == last_bin) { 78 | must(last_rounded == rounded); 79 | must(last_bin_size == bin_size); 80 | } else { 81 | must(rounded > last_rounded); 82 | must(bin_size >= last_bin_size); 83 | must(last_rounded + last_bin_size == i); 84 | 85 | last_bin = bin; 86 | last_rounded = rounded; 87 | last_bin_size = bin_size; 88 | } 89 | } 90 | 91 | fail_if(last_bin > (N_TEST_LB << subbin)); 92 | } 93 | } 94 | END_TEST 95 | 96 | int 97 | main(int argc, char *argv[]) 98 | { 99 | SRunner *sr; 100 | Suite *suite = suite_create("common/log_linear_bin"); 101 | TCase *tc = tcase_create("test_log_linear_bin"); 102 | 103 | tcase_add_loop_test(tc, round_up, 0, 32); 104 | tcase_add_loop_test(tc, round_down, 0, 32); 105 | (void)round_up; 106 | 107 | suite_add_tcase(suite, tc); 108 | sr = srunner_create(suite); 109 | 110 | srunner_set_xml(sr, "check/check_log_linear_bin.xml"); 111 | srunner_set_fork_status(sr, CK_NOFORK); 112 | srunner_run_all(sr, CK_NORMAL); 113 | 114 | int num_failed = srunner_ntests_failed(sr); 115 | srunner_free(sr); 116 | return num_failed; 117 | } 118 | -------------------------------------------------------------------------------- /common/memory/freelist.h: -------------------------------------------------------------------------------- 1 | #ifndef MEMORY_FREELIST_H 2 | #define MEMORY_FREELIST_H 3 | /* 4 | * Statically allocated fixed-size free list of RTBRed pointers. 5 | * 6 | * A pointer enters the free list on the limbo FIFO, with a timestamp 7 | * equal to ~now. When RTBR tells us that nothing refers to that 8 | * pointer (all read-side sections started after the pointer's 9 | * timestamp), the pointer leaves the FIFO and enters the stack of 10 | * entries that can actually be reused. 11 | * 12 | * A pointer leaves the free list when a caller pops off the free 13 | * list. If that is successful, the caller also receives a free list 14 | * entry. It should use that entry when pushing the item back on the 15 | * free list: entries are allocated statically to prevent runaway 16 | * resource allocation. 17 | * 18 | * N.B., freelists are global singletons even for thread-local 19 | * resources. The idea is that the unit of freelist allocation should 20 | * be coarse enough to perform thread-caching; once we recycle a 21 | * coarse allocation, we should push it back to the global pool to 22 | * keep fragmentation in check. 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | struct an_freelist_entry { 32 | struct ck_fifo_mpmc_entry fifo_entry; 33 | struct ck_stack_entry stack_entry; 34 | void *value; 35 | uint64_t deletion_timestamp; 36 | } CK_CC_CACHELINE; 37 | 38 | struct an_freelist { 39 | struct ck_stack stack CK_CC_ALIGN(16); 40 | const uint64_t n_elem; 41 | uint64_t used_elem; 42 | struct ck_fifo_mpmc fifo; 43 | struct an_freelist_entry *const entries; 44 | }; 45 | 46 | #define AN_FREELIST(LINKAGE, NAME, N_ELEM) \ 47 | static struct an_freelist_entry NAME##_freelist_entries[(N_ELEM) + 1]; \ 48 | LINKAGE struct an_freelist NAME = { \ 49 | .stack = CK_STACK_INITIALIZER, \ 50 | .n_elem = (N_ELEM) + 1, \ 51 | .used_elem = 1, \ 52 | .fifo = { \ 53 | .head = { \ 54 | .pointer = &NAME##_freelist_entries[0].fifo_entry \ 55 | }, \ 56 | .tail = { \ 57 | .pointer = &NAME##_freelist_entries[0].fifo_entry \ 58 | } \ 59 | }, \ 60 | .entries = &NAME##_freelist_entries[0] \ 61 | }; 62 | 63 | /** 64 | * @brief allocate a new entry in the free list. 65 | * @return an entry on success, or NULL if the free list has reached capacity. 66 | */ 67 | struct an_freelist_entry * 68 | an_freelist_register(struct an_freelist *); 69 | 70 | /** 71 | * @brief attempt to pop an element off the free list 72 | * @param OUT_entry the free list entry associated with the return value 73 | * on success, NULL otherwise. 74 | * @return the element on success, NULL on failure. 75 | */ 76 | void *an_freelist_pop(struct an_freelist *, struct an_freelist_entry **OUT_entry); 77 | 78 | /** 79 | * @brief schedule @a value for re-use once all current read-side sections have terminated. 80 | */ 81 | void an_freelist_shelve(struct an_freelist *, struct an_freelist_entry *entry, void *value); 82 | 83 | /** 84 | * @brief Mark @a value as immediately ready for re-use. 85 | */ 86 | void an_freelist_push(struct an_freelist *, struct an_freelist_entry *entry, void *value); 87 | #endif /* !MEMORY_FREELIST_H */ 88 | -------------------------------------------------------------------------------- /common/memory/map.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "common/memory/map.h" 5 | #include "common/memory/reserve.h" 6 | 7 | #ifndef MADV_DODUMP 8 | #define MADV_DODUMP 17 9 | #endif 10 | 11 | size_t 12 | an_memory_map(void *address, size_t at_least, size_t at_most) 13 | { 14 | size_t mask; 15 | size_t rounded_size; 16 | void *ret; 17 | 18 | if (an_memory_reserve_reserved((uintptr_t)address) == false) { 19 | return 0; 20 | } 21 | 22 | mask = ck_pr_load_64(&an_memory_reserve_page_size) - 1; 23 | rounded_size = (at_least + mask) & ~mask; 24 | if (rounded_size < at_least || rounded_size > at_most) { 25 | return 0; 26 | } 27 | 28 | ret = mmap(address, rounded_size, PROT_READ | PROT_WRITE, 29 | MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, 30 | -1, 0); 31 | 32 | if (ret == MAP_FAILED) { 33 | return 0; 34 | } 35 | 36 | (void)madvise(ret, rounded_size, MADV_DODUMP); 37 | assert(ret == address); 38 | if (rounded_size < at_most) { 39 | return rounded_size; 40 | } 41 | 42 | return at_most; 43 | } 44 | -------------------------------------------------------------------------------- /common/memory/map.h: -------------------------------------------------------------------------------- 1 | #ifndef MEMORY_MAP_H 2 | #define MEMORY_MAP_H 3 | #include 4 | 5 | /** 6 | * @brief map at least @a at_least bytes at @a address, and at most @a at_most. 7 | * @return 0 if the mapping failed, number of bytes mapped on success. 8 | * 9 | * Assumes the address is already reserved via map_reserve. 10 | * TODO: NUMA and hugepage hints. 11 | */ 12 | size_t an_memory_map(void *address, size_t at_least, size_t at_most); 13 | #endif /* !MEMORY_MAP_H */ 14 | -------------------------------------------------------------------------------- /common/memory/pool.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "common/an_cc.h" 7 | #include "common/memory/bump.h" 8 | #include "common/memory/freelist.h" 9 | #include "common/memory/pool.h" 10 | #include "common/util.h" 11 | 12 | static inline bool 13 | refresh(void *snapshot, const void *source) 14 | { 15 | void *actual[2]; 16 | bool delta; 17 | 18 | ck_pr_load_ptr_2(source, actual); 19 | delta = (((void **)snapshot)[0] != actual[0] || 20 | ((void **)snapshot)[1] != actual[1]); 21 | memcpy(snapshot, &actual, sizeof(actual)); 22 | return delta; 23 | } 24 | 25 | static struct an_bump_shared * 26 | an_pool_shared_alloc_bump(struct an_pool_shared *shared, 27 | struct an_freelist_entry **OUT_entry) 28 | { 29 | struct an_bump_policy policy = { 30 | .premap = true 31 | }; 32 | struct an_bump_shared *ret; 33 | 34 | if (OUT_entry != NULL) { 35 | 36 | *OUT_entry = an_freelist_register(shared->freelist); 37 | 38 | if (*OUT_entry == NULL) { 39 | return NULL; 40 | } 41 | } 42 | 43 | ret = an_bump_shared_create(shared->bump_size, &policy); 44 | return ret; 45 | } 46 | 47 | static bool 48 | an_pool_shared_swap(struct an_pool_shared *shared, 49 | struct an_bump_shared **snapshot) 50 | { 51 | struct an_bump_shared *actual[2]; 52 | struct an_freelist_entry *entry; 53 | struct an_bump_shared *next; 54 | struct an_bump_shared *old; 55 | bool r; 56 | 57 | if (refresh(snapshot, &shared->bumps)) { 58 | return true; 59 | } 60 | 61 | memcpy(&actual, snapshot, sizeof(actual)); 62 | 63 | next = an_freelist_pop(shared->freelist, &entry); 64 | if (next != NULL) { 65 | an_bump_shared_reset(next); 66 | } else { 67 | entry = NULL; 68 | 69 | /* Only get an entry if we have something to recycle. */ 70 | next = an_pool_shared_alloc_bump(shared, 71 | (actual[1] != NULL) ? &entry : NULL); 72 | } 73 | 74 | if (next == NULL) { 75 | return refresh(snapshot, &shared->bumps); 76 | } 77 | 78 | old = actual[1]; 79 | actual[1] = actual[0]; 80 | actual[0] = next; 81 | if (ck_pr_cas_ptr_2_value(&shared->bumps, snapshot, actual, snapshot) == false) { 82 | if (entry == NULL) { 83 | entry = an_freelist_register(shared->freelist); 84 | } 85 | if (entry != NULL) { 86 | /* 87 | * the entry will be leaked but it's better than crashing. 88 | * if we size buffers correctly this won't get hit 89 | */ 90 | an_freelist_push(shared->freelist, entry, next); 91 | } 92 | return true; 93 | } 94 | 95 | memcpy(snapshot, &actual, sizeof(actual)); 96 | if (old != NULL) { 97 | r = an_bump_shared_quiesce(old); 98 | assert(r && "Race condition despite CMPXCHG16B?"); 99 | an_freelist_shelve(shared->freelist, entry, old); 100 | } 101 | return true; 102 | } 103 | 104 | void * 105 | an_pool_shared_alloc_slow(struct an_pool_shared *pool, 106 | size_t size, bool zero, size_t alignment) 107 | { 108 | struct an_bump_shared *snapshot[2] = { 0 }; 109 | void *ret = NULL; 110 | 111 | if (size > pool->bump_size / 2 || 112 | size + alignment >= pool->bump_size / 2) { 113 | return NULL; 114 | } 115 | 116 | do { 117 | for (size_t i = 0; i < ARRAY_SIZE(pool->bumps); i++) { 118 | ret = an_bump_alloc(pool->bumps[i], size, alignment); 119 | if (ret != NULL) { 120 | goto out; 121 | } 122 | } 123 | } while (an_pool_shared_swap(pool, snapshot)); 124 | 125 | out: 126 | if (zero && ret != NULL) { 127 | memset(ret, 0, size); 128 | } 129 | 130 | return ret; 131 | } 132 | 133 | static void 134 | an_pool_private_swap(struct an_pool_private *private) 135 | { 136 | struct an_bump_policy policy = { 137 | .premap = true 138 | }; 139 | struct an_bump_private *bump; 140 | struct an_freelist_entry *entry = NULL; 141 | 142 | if (private->bump != NULL) { 143 | assert(private->entry != NULL); 144 | an_freelist_shelve(private->freelist, 145 | private->entry, private->bump); 146 | } 147 | 148 | private->bump = NULL; 149 | private->entry = NULL; 150 | bump = an_freelist_pop(private->freelist, &entry); 151 | if (bump == NULL) { 152 | entry = an_freelist_register(private->freelist); 153 | if (entry == NULL) { 154 | return; 155 | } 156 | 157 | bump = an_bump_private_create(private->bump_size, &policy); 158 | if (bump == NULL) { 159 | return; 160 | } 161 | } 162 | 163 | an_bump_private_reset(bump); 164 | private->bump = bump; 165 | private->entry = entry; 166 | return; 167 | } 168 | 169 | void * 170 | an_pool_private_alloc_slow(struct an_pool_private *pool, 171 | size_t size, bool zero, size_t alignment) 172 | { 173 | void *ret; 174 | 175 | if (size > pool->bump_size / 2 || 176 | size + alignment >= pool->bump_size / 2) { 177 | return NULL; 178 | } 179 | 180 | ret = an_bump_alloc(pool->bump, size, alignment); 181 | if (AN_CC_LIKELY(ret == NULL)) { 182 | an_pool_private_swap(pool); 183 | ret = an_bump_alloc(pool->bump, size, alignment); 184 | } 185 | 186 | if (zero && ret != NULL) { 187 | memset(ret, 0, size); 188 | } 189 | 190 | return ret; 191 | } 192 | -------------------------------------------------------------------------------- /common/memory/pool.h: -------------------------------------------------------------------------------- 1 | #ifndef MEMORY_POOL_H 2 | #define MEMORY_POOL_H 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "common/an_cc.h" 9 | #include "common/memory/bump.h" 10 | #include "common/memory/freelist.h" 11 | 12 | struct an_pool_shared { 13 | struct an_bump_shared *bumps[2]; 14 | struct an_freelist *const freelist; 15 | const uint64_t bump_size; 16 | } CK_CC_ALIGN(16); 17 | 18 | struct an_pool_private { 19 | struct an_bump_private *bump; 20 | struct an_freelist_entry *entry; 21 | struct an_freelist *const freelist; 22 | const uint64_t bump_size; 23 | } CK_CC_ALIGN(16); 24 | 25 | #define AN_POOL_SHARED(LINKAGE, NAME, BUMP_SIZE, ALLOCATION_LIMIT) \ 26 | AN_FREELIST(static, NAME##_freelist, 2 + (ALLOCATION_LIMIT / BUMP_SIZE)); \ 27 | LINKAGE struct an_pool_shared NAME = { \ 28 | .freelist = &NAME##_freelist, \ 29 | .bump_size = (BUMP_SIZE) \ 30 | }; 31 | 32 | #define AN_POOL_PRIVATE(LINKAGE, NAME, BUMP_SIZE, ALLOCATION_LIMIT) \ 33 | AN_FREELIST(static, NAME##_freelist, 2 + (ALLOCATION_LIMIT / BUMP_SIZE)); \ 34 | LINKAGE __thread struct an_pool_private NAME = { \ 35 | .freelist = &NAME##_freelist, \ 36 | .bump_size = (BUMP_SIZE) \ 37 | }; 38 | 39 | #define an_pool_alloc(POOL, SIZE, ZERO, ALIGN) \ 40 | (__builtin_choose_expr( \ 41 | __builtin_types_compatible_p(__typeof__(POOL), struct an_pool_private *), \ 42 | an_pool_private_alloc, an_pool_shared_alloc)((POOL), (SIZE), (ZERO), (ALIGN))) 43 | 44 | void *an_pool_shared_alloc_slow(struct an_pool_shared *pool, size_t size, bool zero, size_t align); 45 | 46 | static AN_CC_UNUSED void * 47 | an_pool_shared_alloc(struct an_pool_shared *pool, size_t size, bool zero, size_t align) 48 | { 49 | void *ret; 50 | 51 | if (an_pr_load_ptr(&pool->bumps[1]) != NULL) { 52 | ret = an_bump_alloc(pool->bumps[1], size, align); 53 | if (ret != NULL) { 54 | goto out; 55 | } 56 | } 57 | 58 | ret = an_bump_alloc(pool->bumps[0], size, align); 59 | if (AN_CC_LIKELY(ret != NULL)) { 60 | goto out; 61 | } 62 | 63 | return an_pool_shared_alloc_slow(pool, size, zero, align); 64 | 65 | out: 66 | if (zero) { 67 | memset(ret, 0, size); 68 | } 69 | 70 | return ret; 71 | } 72 | 73 | void *an_pool_private_alloc_slow(struct an_pool_private *pool, size_t size, bool zero, size_t align); 74 | 75 | static inline void * 76 | an_pool_private_alloc(struct an_pool_private *pool, size_t size, bool zero, size_t align) 77 | { 78 | void *ret; 79 | 80 | ret = an_bump_alloc(pool->bump, size, align); 81 | if (AN_CC_LIKELY(ret != NULL)) { 82 | if (zero) { 83 | memset(ret, 0, size); 84 | } 85 | 86 | return ret; 87 | } 88 | 89 | return an_pool_private_alloc_slow(pool, size, zero, align); 90 | } 91 | #endif /* !MEMORY_POOL_H */ 92 | -------------------------------------------------------------------------------- /common/memory/reserve.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "common/memory/reserve.h" 8 | 9 | #ifndef MADV_DONTDUMP 10 | #define MADV_DONTDUMP 16 11 | #endif 12 | 13 | #define MEMORY_RESERVE_VMA_SIZE (1ULL << 40) 14 | #define MEMORY_RESERVE_VMA_ALIGNMENT (1ULL << 30) 15 | 16 | extern uint64_t an_memory_reserve_vma_base; 17 | extern uint64_t an_memory_reserve_vma_size; 18 | 19 | static ck_spinlock_t lock; 20 | uint64_t an_memory_reserve_vma_base = 0; 21 | uint64_t an_memory_reserve_vma_size = 0; 22 | size_t an_memory_reserve_page_size = 0; 23 | static uint64_t alloc_pointer = 0; 24 | 25 | static void 26 | map(size_t size, size_t alignment) 27 | { 28 | size_t mask = alignment - 1; 29 | size_t round_size = (size + mask) & ~mask; 30 | size_t bumped_size = round_size + alignment; 31 | size_t alloc_size = (bumped_size + mask) & ~mask; 32 | uintptr_t mapped_end; 33 | uintptr_t ret_end; 34 | void *mapped; 35 | void *ret; 36 | 37 | assert(round_size >= size); 38 | assert(bumped_size >= round_size); 39 | assert(alloc_size >= bumped_size); 40 | 41 | mapped = mmap(NULL, round_size, 42 | PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, 43 | -1, 0); 44 | if (mapped != MAP_FAILED && ((uintptr_t)mapped & mask) != 0) { 45 | ret = mapped; 46 | goto success; 47 | } 48 | 49 | if (mapped != MAP_FAILED) { 50 | int r; 51 | 52 | r = munmap(mapped, round_size); 53 | assert(r == 0); 54 | } 55 | 56 | mapped = mmap(NULL, alloc_size, 57 | PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, 58 | -1, 0); 59 | assert(mapped != MAP_FAILED); 60 | mapped_end = (uintptr_t)mapped + alloc_size; 61 | 62 | ret = (void *)(((uintptr_t)mapped + mask) & ~mask); 63 | assert((uintptr_t)ret >= (uintptr_t)mapped); 64 | 65 | if ((uintptr_t)ret != (uintptr_t)mapped) { 66 | size_t extra = (uintptr_t)ret - (uintptr_t)mapped; 67 | int r; 68 | 69 | r = munmap(mapped, extra); 70 | assert(r == 0); 71 | } 72 | 73 | ret_end = (uintptr_t)ret + round_size; 74 | assert(ret_end <= mapped_end); 75 | if (ret_end != mapped_end) { 76 | size_t extra = mapped_end - ret_end; 77 | int r; 78 | 79 | r = munmap((void *)ret_end, extra); 80 | assert(r == 0); 81 | } 82 | 83 | success: 84 | (void)madvise(ret, round_size, MADV_DONTDUMP); 85 | ck_pr_store_64(&an_memory_reserve_vma_base, (uint64_t)ret); 86 | ck_pr_store_64(&an_memory_reserve_vma_size, round_size); 87 | return; 88 | } 89 | 90 | void 91 | an_memory_reserve_init(size_t vma_size) 92 | { 93 | size_t alignment = MEMORY_RESERVE_VMA_ALIGNMENT; 94 | size_t page_size; 95 | 96 | if (vma_size == 0) { 97 | vma_size = MEMORY_RESERVE_VMA_SIZE; 98 | } 99 | 100 | { 101 | long ret; 102 | 103 | ret = sysconf(_SC_PAGE_SIZE); 104 | assert(ret > 0); 105 | page_size = (size_t)ret; 106 | ck_pr_store_64(&an_memory_reserve_page_size, page_size); 107 | } 108 | 109 | if ((alignment & (alignment - 1)) != 0 || 110 | alignment < page_size) { 111 | alignment = page_size; 112 | } 113 | 114 | if (ck_pr_load_64(&an_memory_reserve_vma_size) != 0) { 115 | return; 116 | } 117 | 118 | ck_spinlock_lock(&lock); 119 | if (ck_pr_load_64(&an_memory_reserve_vma_size) != 0) { 120 | goto out; 121 | } 122 | 123 | map(vma_size, MEMORY_RESERVE_VMA_ALIGNMENT); 124 | alloc_pointer = an_memory_reserve_vma_base; 125 | 126 | out: 127 | ck_spinlock_unlock(&lock); 128 | return; 129 | } 130 | 131 | void * 132 | an_memory_reserve(size_t size, size_t alignment) 133 | { 134 | uintptr_t current; 135 | uintptr_t next; 136 | uintptr_t ret; 137 | uint64_t page_size; 138 | uint64_t vma_base; 139 | uint64_t vma_size; 140 | size_t mask; 141 | 142 | if (ck_pr_load_64(&an_memory_reserve_vma_size) == 0) { 143 | an_memory_reserve_init(MEMORY_RESERVE_VMA_SIZE); 144 | } 145 | 146 | page_size = ck_pr_load_64(&an_memory_reserve_page_size); 147 | vma_size = ck_pr_load_64(&an_memory_reserve_vma_size); 148 | vma_base = ck_pr_load_64(&an_memory_reserve_vma_base); 149 | 150 | /* 151 | * convert power of two x to x - 1, and get a valid mask for 152 | * non-powers-of two (corresponds to the largest power of two 153 | * that divides the alignment value). 154 | */ 155 | mask = page_size - 1; 156 | if (alignment > page_size) { 157 | mask |= (alignment ^ (alignment - 1)) >> 1; 158 | } 159 | 160 | current = ck_pr_load_64(&alloc_pointer); 161 | while (1) { 162 | ret = (current + mask) & ~mask; 163 | if (ret < current) { 164 | return NULL; 165 | } 166 | 167 | next = ret + size; 168 | if (next < ret || (next - vma_base) > vma_size) { 169 | return NULL; 170 | } 171 | 172 | if (ck_pr_cas_64_value(&alloc_pointer, current, next, 173 | ¤t)) { 174 | return (void *)ret; 175 | } 176 | } 177 | 178 | return NULL; 179 | } 180 | -------------------------------------------------------------------------------- /common/memory/reserve.h: -------------------------------------------------------------------------------- 1 | #ifndef MEMORY_RESERVE_H 2 | #define MEMORY_RESERVE_H 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | extern uint64_t an_memory_reserve_page_size; 9 | 10 | /** 11 | * @brief Initialise the memory_reserve subsystem to grab a VMA 12 | * @a vma_size contiguous bytes. First call wins, later calls are 13 | * no-ops. 14 | * 15 | * Asserts out on failure. 16 | */ 17 | void an_memory_reserve_init(size_t vma_size); 18 | 19 | /** 20 | * @brief Attempt to reserve a chunk of address space in our VMA of 21 | * @a size bytes with @a alignment (e.g., 32) byte alignment. 22 | * @return the lowest address in the chunk on success, NULL on failure. 23 | * 24 | * Calls init with a default VMA size if necessary. 25 | */ 26 | void *an_memory_reserve(size_t size, size_t alignment); 27 | 28 | /** 29 | * @brief Determine if @a address is allocated *strictly* in the VMA. 30 | * 31 | * @return True iff the address is strictly contained in the VMA. 32 | */ 33 | static bool an_memory_reserve_reserved(uintptr_t address); 34 | 35 | static inline bool 36 | an_memory_reserve_reserved(uintptr_t address) 37 | { 38 | /* Base is written first, read last */ 39 | extern uint64_t an_memory_reserve_vma_base; 40 | extern uint64_t an_memory_reserve_vma_size; 41 | 42 | uint64_t size = ck_pr_load_64(&an_memory_reserve_vma_size); 43 | uintptr_t base = ck_pr_load_64(&an_memory_reserve_vma_base); 44 | 45 | return (address - base) < size; 46 | } 47 | #endif /*! MEMORY_RESERVE_H */ 48 | -------------------------------------------------------------------------------- /common/rtbr/rtbr.h: -------------------------------------------------------------------------------- 1 | #ifndef RTBR_H 2 | #define RTBR_H 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | struct an_rtbr_entry { 9 | void (*function)(void *); 10 | void *argument; 11 | uint64_t timestamp; 12 | STAILQ_ENTRY(an_rtbr_entry) next; 13 | }; 14 | 15 | struct an_rtbr_timestamp { 16 | uint64_t timestamp; 17 | }; 18 | 19 | struct an_rtbr_section { 20 | struct an_rtbr_timestamp timestamp; 21 | void *cookie; 22 | TAILQ_ENTRY(an_rtbr_section) next; 23 | char info[32]; 24 | }; 25 | 26 | struct an_rtbr_record; 27 | 28 | struct an_rtbr_iterator { 29 | size_t slice; 30 | size_t index; 31 | }; 32 | 33 | #define AN_RTBR_ITERATOR_INITIALIZER { 0 } 34 | 35 | /** 36 | * A full RTBR sequence is: 37 | * 1. an_rtbr_prepare for section; 38 | * 2. allocate section; 39 | * 3. an_rtbr_begin section 40 | * ... 41 | * 4. an_rtbr_end section. 42 | */ 43 | struct an_rtbr_timestamp an_rtbr_prepare(void); 44 | void an_rtbr_begin(struct an_rtbr_section *, struct an_rtbr_timestamp, const char *info); 45 | void an_rtbr_end(struct an_rtbr_section *); 46 | 47 | #define AN_RTBR_SCOPE(NAME, INFO) \ 48 | struct an_rtbr_section NAME \ 49 | __attribute__((__cleanup__(an_rtbr_end))) = { \ 50 | .cookie = NULL \ 51 | }; \ 52 | an_rtbr_begin(&NAME, an_rtbr_prepare(), (INFO)) 53 | 54 | void an_rtbr_call(struct an_rtbr_entry *, void (*fn)(void *), void *); 55 | 56 | uint64_t an_rtbr_active(const struct an_rtbr_record *); 57 | const char *an_rtbr_info(const struct an_rtbr_record *); 58 | uint64_t an_rtbr_epoch(void); 59 | uint64_t an_rtbr_local_epoch(const struct an_rtbr_record *); 60 | 61 | bool an_rtbr_poll(bool hard); 62 | void an_rtbr_synchronize(void); 63 | 64 | /* Force forward progress up to now - latency_ms. */ 65 | void an_rtbr_force_progress(uint64_t latency_ms); 66 | 67 | uint64_t an_rtbr_record_count(void); 68 | const struct an_rtbr_record *an_rtbr_self(void); 69 | const struct an_rtbr_record *an_rtbr_record_by_id(size_t id); 70 | uint64_t an_rtbr_id(void); 71 | uint64_t an_rtbr_record_id(const struct an_rtbr_record *); 72 | bool an_rtbr_record_oldest_section(struct an_rtbr_section *, const struct an_rtbr_record *); 73 | 74 | const struct an_rtbr_record *an_rtbr_iterate(struct an_rtbr_iterator *); 75 | #endif /* !RTBR_H */ 76 | -------------------------------------------------------------------------------- /common/rtbr/rtbr.md: -------------------------------------------------------------------------------- 1 | Real-Time-Based Reclamation: not your forebears' TBR 2 | ==================================================== 3 | 4 | RTBR takes the EPC (epoch proxy collection), mixes it with the 5 | time-ordered pool trick of pool party, and squirts a bit of ParSec in 6 | there. With constant forward progress and minimal OS cooperation, 7 | we're getting rid of the store/load barrier in epoch SMR. We also 8 | centralise all but one of our transaction managers (we still have to 9 | deal with SSTM, but that's just more work). 10 | 11 | The basic theory goes as follows. Assume all threads are always 12 | making forward progress (always have an active section). Each thread 13 | can keep track of the oldest active section it owns, and we can scan 14 | the list of all threads to find the oldest active section in the 15 | system. Anything that was logically deleted before that section was 16 | created is OK to delete physically. In practice, time is hard and 17 | even RDTSC has some clock *offset* because the BIOS doesn't boot 18 | everything simultanenously; I've observed offset < 200K cycles for 4 19 | sockets, and the current config assumes it can be as bad as 1M cycles. 20 | 21 | We also use the ParSec trick to get some easy progress while marking 22 | sleeping sections without incurring RDTSC: we find the current time 23 | when we open a new section, but, when we notice that all sections are 24 | closed, only advance the timestamp to make it even (inactive), without 25 | bothering to compute a real timestamp. Reclamation can still note 26 | that no task created before the inactive timestamp is still running on 27 | that thread. When no thread is idle for too long, we don't even need 28 | to get the OS involved. 29 | 30 | The hard part is handling threads that are idle for a long time. We 31 | could assume that idle threads will still check in from time to time 32 | and update their "oldest section" timestamp, but that doesn't seem 33 | ideal. We instead rely on the operating system to let us detect 34 | sleeping tasks and context switches. 35 | 36 | Sleeping tasks are straightforward: 37 | 38 | 1. Time is now T0; 39 | 2. Task is sleeping at time T1 > T0; 40 | 3. Task has no visible active section at time T2 > T1. 41 | 42 | We can assume that all active sections in that tasks were either 43 | terminated before T2, or created after T0. In either case, it is now 44 | safe to physically delete resources that were logically released 45 | before T0. We can assume that because sleeping implies a context 46 | switch, which implies a memory barrier. Even if the next instruction 47 | the task will execute creates a new section with a timestamp before 48 | T0, the section itself will execute after T0. 49 | 50 | For running tasks, we must detect context switches that happen between 51 | a timestamp in the past and now. The sequence is: 52 | 53 | 1. Time is T0; 54 | 2. Observe that the task has used N1 units of CPU time at T1 > T0; 55 | 3. Task has no visible active section at T2 > T1; 56 | 3. Observe that the task has used N3 > N1 units of CPU time at T3 > T2; 57 | 4. Task has no visible active section at T4 > T3. 58 | 59 | The change in units of CPU time implies a context switch to update the 60 | counters in kernel space, and that implies a memory barrier. Thus, 61 | any section that started execution before T0 must be visible by T4. 62 | If we have no visible active section by T4, there was either no such 63 | section, or all such sections are completed. In either case, it is 64 | safe to reclaim for time T0. 65 | 66 | A task can also be outright dead. We detect that by noticing that the 67 | task is either gone from `/proc`, or that the task's creation 68 | timestamp changed. 69 | 70 | Directory layout 71 | ---------------- 72 | 73 | * `rtbr.c`: entry points, simple logic, iterating over all active 74 | records. 75 | * `rtbr_tid.c`: `/proc/pid/stat` tricks to find dead/reborn tasks and 76 | track runqueue status and CPU time usage. 77 | * `rtbr_record.c`: atomically acquire an RTBR record and expose enough 78 | info to detect thread shutdown. 79 | * `rtbr_poll.c`: RTBR poll, both fast path and slow path with all the 80 | scheduler and implied membar trickery. 81 | * `rtbr_impl.c`: record allocation. We CAS race to allocate 82 | geometrically growing arrays of records. Once an array is visible, 83 | we never free it (we reuse records with a stack-as-free-list). 84 | -------------------------------------------------------------------------------- /common/rtbr/rtbr_impl.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "common/an_cc.h" 8 | #include "common/an_malloc.h" 9 | #include "common/rtbr/rtbr_impl.h" 10 | 11 | static AN_MALLOC_DEFINE(slice_token, 12 | .string = "an_rtbr_slice", 13 | .mode = AN_MEMORY_MODE_VARIABLE); 14 | 15 | static struct an_rtbr_record * 16 | slice_get_record(struct an_rtbr_slice *slice, const struct an_rtbr_tid_info *info) 17 | { 18 | uint64_t allocated; 19 | uint64_t n_records; 20 | uint64_t record_id; 21 | struct an_rtbr_record *record; 22 | 23 | allocated = ck_pr_load_64(&slice->allocated_records); 24 | n_records = slice->n_records; 25 | if (allocated >= n_records) { 26 | return NULL; 27 | } 28 | 29 | allocated = ck_pr_faa_64(&slice->allocated_records, 1); 30 | if (allocated >= n_records) { 31 | return NULL; 32 | } 33 | 34 | record = &slice->records[allocated]; 35 | record_id = slice->id_offset + allocated; 36 | if (ck_pr_load_64(&record->id) != record_id) { 37 | /* Racy writes are OK, they're all identical. */ 38 | ck_pr_store_64(&record->id, record_id); 39 | } 40 | 41 | if (an_rtbr_record_acquire(record, info)) { 42 | return record; 43 | } 44 | 45 | return NULL; 46 | } 47 | 48 | static struct an_rtbr_slice * 49 | an_rtbr_slice_create(size_t size, uint64_t offset) 50 | { 51 | struct an_rtbr_slice *ret; 52 | size_t byte_count = sizeof(*ret) + size * sizeof(ret->records[0]); 53 | 54 | assert(size > 0 && (size & (size - 1)) == 0); 55 | assert(((byte_count - sizeof(*ret)) / sizeof(ret->records[0])) == size); 56 | 57 | ret = an_calloc_region(slice_token, 1, byte_count); 58 | assert(ret != NULL); 59 | 60 | ret->n_records = size; 61 | ret->id_offset = offset; 62 | return ret; 63 | } 64 | 65 | static void 66 | an_rtbr_slice_destroy(struct an_rtbr_slice *slice) 67 | { 68 | 69 | an_free(slice_token, slice); 70 | return; 71 | } 72 | 73 | static struct an_rtbr_slice * 74 | ensure_slice(struct an_rtbr_global *global, size_t i) 75 | { 76 | struct an_rtbr_slice *ret; 77 | struct an_rtbr_slice *copy; 78 | size_t size; 79 | uint64_t offset = 0; 80 | 81 | ret = an_pr_load_ptr(&global->slices[i]); 82 | ck_pr_fence_load(); 83 | if (AN_CC_LIKELY(ret != NULL)) { 84 | return ret; 85 | } 86 | 87 | if (i > 0) { 88 | struct an_rtbr_slice *prev; 89 | 90 | prev = an_pr_load_ptr(&global->slices[i - 1]); 91 | assert(prev != NULL); 92 | ck_pr_fence_load(); 93 | offset = prev->id_offset + prev->n_records; 94 | } 95 | 96 | { 97 | unsigned __int128 temp_size = SLICE_INITIAL_SIZE; 98 | size_t max_size = (UINT_MAX / 2) + 1; 99 | 100 | temp_size <<= i; 101 | if (i > 32 || temp_size > max_size) { 102 | size = max_size; 103 | } else { 104 | size = (size_t)temp_size; 105 | } 106 | } 107 | 108 | assert(offset + size > offset); 109 | copy = an_rtbr_slice_create(size, offset); 110 | ck_pr_fence_store(); 111 | 112 | /* coverity[overrun-local : FALSE] */ 113 | if (ck_pr_cas_ptr_value(&global->slices[i], NULL, copy, &ret)) { 114 | return copy; 115 | } 116 | 117 | an_rtbr_slice_destroy(copy); 118 | ck_pr_fence_load(); 119 | return ret; 120 | } 121 | 122 | static void 123 | an_rtbr_init(struct an_rtbr_global *global) 124 | { 125 | 126 | ck_spinlock_lock(&global->lock); 127 | if (ck_pr_load_8(&global->initialized) != 0) { 128 | goto out; 129 | } 130 | 131 | (void)pthread_atfork(NULL, NULL, an_rtbr_reinit); 132 | ck_stack_init(&global->freelist); 133 | ck_pr_store_8(&global->initialized, 1); 134 | 135 | out: 136 | ck_spinlock_unlock(&global->lock); 137 | return; 138 | } 139 | 140 | struct an_rtbr_record * 141 | an_rtbr_get_record(struct an_rtbr_global *global) 142 | { 143 | struct an_rtbr_tid_info info; 144 | struct an_rtbr_record *ret; 145 | size_t cur_slice; 146 | 147 | if (AN_CC_UNLIKELY(ck_pr_load_8(&global->initialized) == 0)) { 148 | an_rtbr_init(global); 149 | } 150 | 151 | info = an_rtbr_tid_info(0); 152 | while (1) { 153 | struct ck_stack_entry *entry; 154 | 155 | entry = ck_stack_pop_mpmc(&global->freelist); 156 | if (entry == NULL) { 157 | break; 158 | } 159 | 160 | ret = an_rtbr_record_container(entry); 161 | if (an_rtbr_record_acquire(ret, &info)) { 162 | return ret; 163 | } 164 | } 165 | 166 | cur_slice = ck_pr_load_32(&global->cur_slice); 167 | for (size_t i = cur_slice; i < ARRAY_SIZE(global->slices); i++) { 168 | struct an_rtbr_slice *slice; 169 | 170 | slice = ensure_slice(global, i); 171 | ret = slice_get_record(slice, &info); 172 | if (ret != NULL) { 173 | if (i != cur_slice) { 174 | ck_pr_store_32(&global->cur_slice, i); 175 | } 176 | 177 | return ret; 178 | } 179 | } 180 | 181 | assert(0 && "Much more than 2^32 concurrent threads?!"); 182 | return NULL; 183 | } 184 | 185 | void 186 | an_rtbr_reinit_impl(struct an_rtbr_global *global) 187 | { 188 | 189 | for (size_t i = 0; i < ARRAY_SIZE(global->slices); i++) { 190 | an_rtbr_slice_destroy(global->slices[i]); 191 | } 192 | 193 | memset(global, 0, sizeof(*global)); 194 | ck_spinlock_init(&global->lock); 195 | ck_stack_init(&global->freelist); 196 | global->initialized = true; 197 | ck_pr_fence_store(); 198 | return; 199 | } 200 | -------------------------------------------------------------------------------- /common/rtbr/rtbr_impl.h: -------------------------------------------------------------------------------- 1 | #ifndef RTBR_IMPL_H 2 | #define RTBR_IMPL_H 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "common/rtbr/rtbr.h" 13 | 14 | /* 15 | * Shouldn't need more than a million cycles to spin up 32 16 | * cores. Experimentally, it's on the order of ~300K cycles for 4 17 | * sockets. 18 | */ 19 | #define RTBR_DELAY_TICKS (1ULL << 20) 20 | #define RTBR_HARD_POLL_PERIOD_MS 10ULL /* Let's not try to poll hard more than once every 10 milliseconds. */ 21 | #define RTBR_POLL_LOG_PERIOD_MS 1000ULL /* Only syslog about polling every ~second. */ 22 | #define SLICE_INITIAL_SIZE 8ULL 23 | 24 | struct an_rtbr_tid_info { 25 | uint64_t tid; 26 | bool running; 27 | bool dead; 28 | uint64_t start_time; 29 | uint64_t total_time; 30 | }; 31 | 32 | struct an_rtbr_record { 33 | struct { 34 | /* tid + start_time. Odds of ABA seem minuscule (: */ 35 | uint64_t lock[2]; 36 | uint64_t id; 37 | /* Private values. */ 38 | /* Value is even if inactive, odd if active. */ 39 | uint64_t self_epoch; 40 | /* Priority list of active sections. Head is oldest. */ 41 | TAILQ_HEAD(, an_rtbr_section) active; 42 | uint64_t active_count; 43 | char info[32]; 44 | /* Queue of cleanups. Head is oldest. */ 45 | STAILQ_HEAD(, an_rtbr_entry) limbo; 46 | uint64_t limbo_count; 47 | /* Local snapshot of the global epoch (safe to reclaim) */ 48 | uint64_t global_epoch; 49 | struct ck_stack_entry stack_entry; 50 | } CK_CC_CACHELINE; 51 | /* Shared values: written by an_rtbr_poll_hard under lock. */ 52 | /* Latest time we know the record was definitely inactive. */ 53 | uint64_t last_safe; 54 | uint64_t total_time; /* snapshot of total_time value */ 55 | uint64_t as_of; /* at timestamp = as_of */ 56 | uint64_t last_self_epoch; /* snapshot taken by the poll thread(s). */ 57 | } CK_CC_CACHELINE; 58 | 59 | CK_STACK_CONTAINER(struct an_rtbr_record, stack_entry, an_rtbr_record_container); 60 | 61 | /* 62 | * A slice is a fixed size array of records with a lock-free bump pointer. 63 | * 64 | * We get a growable set of stable records by incrementally allocating 65 | * slices (geometric growth) and publishing them to rtbr_global.slices 66 | * with a wait-free CAS sequence. 67 | */ 68 | struct an_rtbr_slice { 69 | uint64_t allocated_records; /* bump index; updated with FAA, so may exceed n_records. */ 70 | uint64_t n_records; /* slice capacity */ 71 | uint64_t id_offset; /* record id for slice->records[i] is id_offset + i. */ 72 | struct an_rtbr_record records[]; 73 | }; 74 | 75 | struct an_rtbr_global { 76 | /* Only accessed when creating threads or polling hard. */ 77 | ck_spinlock_t lock; 78 | uint32_t cur_slice; 79 | uint64_t last_hard_poll; 80 | uint64_t record_count; 81 | 82 | /* Read on the fast path. */ 83 | struct { 84 | uint8_t initialized; 85 | uint64_t minimal_epoch; 86 | } CK_CC_CACHELINE; 87 | 88 | /* Read and sometimes written on the fast path. */ 89 | uint64_t global_epoch; 90 | /* Mostly read-only. */ 91 | struct an_rtbr_slice *slices[32]; 92 | /* Write churn when creating threads. */ 93 | struct ck_stack freelist CK_CC_ALIGN(16); 94 | }; 95 | 96 | bool an_rtbr_record_acquire(struct an_rtbr_record *, const struct an_rtbr_tid_info *); 97 | struct an_rtbr_record *an_rtbr_get_record(struct an_rtbr_global *); 98 | struct an_rtbr_tid_info an_rtbr_tid_info(pid_t tid); 99 | bool an_rtbr_poll_hard(struct an_rtbr_global *, struct an_rtbr_record *self); 100 | uint64_t an_rtbr_poll_easy(struct an_rtbr_global *); 101 | void an_rtbr_reinit(void); 102 | void an_rtbr_reinit_impl(struct an_rtbr_global *); 103 | #endif /* !RTBR_IMPL_H */ 104 | -------------------------------------------------------------------------------- /common/rtbr/rtbr_record.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "common/an_cc.h" 5 | #include "common/rtbr/rtbr_impl.h" 6 | 7 | bool 8 | an_rtbr_record_acquire(struct an_rtbr_record *record, const struct an_rtbr_tid_info *info) 9 | { 10 | uint64_t expected[2] = {0, 0}; 11 | uint64_t new[2]; 12 | bool ret; 13 | 14 | assert(info->dead == false); 15 | assert(info->tid != 0); 16 | assert(info->start_time != 0); 17 | 18 | new[0] = info->tid; 19 | new[1] = info->start_time; 20 | ret = ck_pr_cas_64_2(record->lock, expected, new); 21 | if (ret) { 22 | an_pr_store_ptr(&record->stack_entry.next, NULL); 23 | ck_pr_fence_load(); 24 | ck_pr_fence_store(); 25 | 26 | TAILQ_INIT(&record->active); 27 | ck_pr_store_64(&record->active_count, 0); 28 | 29 | STAILQ_INIT(&record->limbo); 30 | ck_pr_store_64(&record->limbo_count, 0); 31 | } 32 | 33 | return ret; 34 | } 35 | -------------------------------------------------------------------------------- /common/rtbr/rtbr_tid.c: -------------------------------------------------------------------------------- 1 | /* 2 | * RTBR TID: poll /proc/ for scheduling information on a task: 3 | * 1. is there such a task? 4 | * 2. what is the status of the task? 5 | * 3. when was the task started? 6 | * 4. how much CPU time (user and system) has the task used? 7 | * 8 | * Item 3 provides ABA protection against the kernel reusing tids, and 9 | * item 4 is interesting because it is only incremented after a 10 | * context switch. If we observe a change in the values, the thread 11 | * went through at least one context switch, which also implies a 12 | * memory barrier. 13 | */ 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include "common/rtbr/rtbr_impl.h" 28 | #include "common/util.h" 29 | 30 | /* -(1ULL << 127). Should be big enough (: */ 31 | #define MAX_INT_STRING "-170141183460469231731687303715884105728 " 32 | 33 | #define INPUT_STRING \ 34 | MAX_INT_STRING /* %d pid */ \ 35 | "(0123456789abcdef) " /* comm (16 chars) */ \ 36 | "R " /* %c state */ \ 37 | MAX_INT_STRING /* ppid */ \ 38 | MAX_INT_STRING /* pgrp */ \ 39 | MAX_INT_STRING /* session */ \ 40 | MAX_INT_STRING /* tty_nr */ \ 41 | MAX_INT_STRING /* tpgid */ \ 42 | MAX_INT_STRING /* flags */ \ 43 | MAX_INT_STRING /* minflt */ \ 44 | MAX_INT_STRING /* cminflt */ \ 45 | MAX_INT_STRING /* majflt */ \ 46 | MAX_INT_STRING /* cmajflt */ \ 47 | MAX_INT_STRING /* "%llu " &utime */ \ 48 | MAX_INT_STRING /* "%llu " &stime */ \ 49 | MAX_INT_STRING /* cutime */ \ 50 | MAX_INT_STRING /* cstime */ \ 51 | MAX_INT_STRING /* priority */ \ 52 | MAX_INT_STRING /* nice */ \ 53 | MAX_INT_STRING /* num threads */ \ 54 | MAX_INT_STRING /* itrealvalue */ \ 55 | MAX_INT_STRING /* "%llu " &starttime */ 56 | 57 | 58 | static pid_t 59 | gettid(void) 60 | { 61 | 62 | return syscall(__NR_gettid); 63 | } 64 | 65 | struct an_rtbr_tid_info 66 | an_rtbr_tid_info(pid_t tid) 67 | { 68 | char buf[sizeof(INPUT_STRING)]; 69 | char path[256]; 70 | struct an_rtbr_tid_info ret; 71 | int fd = -1; 72 | 73 | if (tid == 0) { 74 | tid = gettid(); 75 | } 76 | 77 | memset(&ret, 0, sizeof(ret)); 78 | ret.tid = tid; 79 | 80 | { 81 | int n; 82 | 83 | n = snprintf(path, sizeof(path), "/proc/%"PRIu64"/stat", (uint64_t)tid); 84 | assert((size_t)n < sizeof(path)); 85 | } 86 | 87 | fd = open(path, O_RDONLY | O_CLOEXEC); 88 | if (fd < 0) { 89 | ret.dead = true; 90 | goto out; 91 | } 92 | 93 | memset(buf, 0, sizeof(buf)); 94 | an_readall(fd, buf, sizeof(buf) - 1); 95 | 96 | { 97 | unsigned long long start_time; 98 | unsigned long long utime; 99 | unsigned long long stime; 100 | int n; 101 | char state; 102 | 103 | n = sscanf(buf, 104 | "%*d " /* pid */ 105 | "%*s " /* comm */ 106 | "%c " /* &state */ 107 | "%*d " /* ppid */ 108 | "%*d " /* pgrp */ 109 | "%*d " /* session */ 110 | "%*d " /* tty_nr */ 111 | "%*d " /* tpgid */ 112 | "%*u " /* flags */ 113 | "%*u " /* minflt */ 114 | "%*u " /* cminflt */ 115 | "%*u " /* majflt */ 116 | "%*u " /* cmajflt */ 117 | "%llu " /* &utime */ 118 | "%llu " /* &stime */ 119 | "%*d " /* cutime */ 120 | "%*d " /* cstime */ 121 | "%*d " /* priority */ 122 | "%*d " /* nice */ 123 | "%*d " /* num threads */ 124 | "%*d " /* itrealvalue */ 125 | "%llu " /* &starttime */, 126 | &state, &utime, &stime, &start_time); 127 | if (n != 4) { 128 | ret.dead = true; 129 | goto out; 130 | } 131 | 132 | ret.running = (state == 'R'); 133 | /* Z: zombie. x/X: "dead" (some linuxism). */ 134 | ret.dead = ((state == 'Z') || (state == 'x') || (state == 'X')); 135 | ret.start_time = (uint64_t)start_time; 136 | ret.total_time = (uint64_t)utime + (uint64_t)stime; 137 | } 138 | 139 | out: 140 | if (fd >= 0) { 141 | close(fd); 142 | } 143 | 144 | return ret; 145 | } 146 | -------------------------------------------------------------------------------- /common/x86_64/x86_64.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "common/an_md.h" 11 | #include "common/util.h" 12 | #include "common/x86_64/cpuid.h" 13 | 14 | static uint64_t 15 | an_md_x86_64_i_rdtsc_fast(void) 16 | { 17 | uint64_t eax, edx; 18 | 19 | __asm__ __volatile__("rdtsc" : "=a"(eax), "=d"(edx)); 20 | return ((edx << 32) | eax); 21 | } 22 | 23 | static uint64_t 24 | an_md_x86_64_i_rdtsc(void) 25 | { 26 | uint64_t eax = 0, edx; 27 | 28 | __asm__ __volatile__("cpuid;" 29 | "rdtsc;" 30 | : "+a" (eax), "=d" (edx) 31 | : 32 | : "%ecx", "%ebx", "memory"); 33 | 34 | __asm__ __volatile__("xorl %%eax, %%eax;" 35 | "cpuid;" 36 | : 37 | : 38 | : "%eax", "%ebx", "%ecx", "%edx", "memory"); 39 | 40 | return ((edx << 32) | eax); 41 | } 42 | 43 | static uint64_t 44 | an_md_x86_64_i_rdtscp(void) 45 | { 46 | uint64_t eax, edx; 47 | 48 | __asm__ __volatile__("rdtscp" 49 | : "=a" (eax), "=d" (edx) 50 | : 51 | : "%ecx", "memory"); 52 | 53 | return ((edx << 32) | eax); 54 | } 55 | 56 | an_md_rdtsc_t * 57 | an_md_probe_rdtsc(const char **description, bool fast) 58 | { 59 | 60 | /* 61 | * Intel processors typically have a synchronized clock across 62 | * sockets. This is not the case for AMD, so we cannot rely on 63 | * it as a timer source. 64 | */ 65 | if (cpuid_vendor() != CPUID_VENDOR_INTEL) { 66 | return NULL; 67 | } 68 | 69 | if (fast == true && cpuid_feature(CPUID_FEATURE_TSC) == true) { 70 | if (description != NULL) { 71 | *description = "rdtsc_fast"; 72 | } 73 | 74 | return an_md_x86_64_i_rdtsc_fast; 75 | } 76 | 77 | if (cpuid_feature(CPUID_FEATURE_RDTSCP) == true) { 78 | if (description != NULL) { 79 | *description = "rdtscp"; 80 | } 81 | 82 | return an_md_x86_64_i_rdtscp; 83 | } 84 | 85 | if (cpuid_feature(CPUID_FEATURE_TSC) == true) { 86 | if (description != NULL) { 87 | *description = "rdtsc"; 88 | } 89 | 90 | return an_md_x86_64_i_rdtsc; 91 | } 92 | 93 | return NULL; 94 | } 95 | 96 | static unsigned long long 97 | scan_brand_for_frequency(char *buf, const char *suffix, double scale) 98 | { 99 | size_t freq_index = 0; 100 | size_t suffix_index = 0; 101 | 102 | for (;;) { 103 | char *found; 104 | 105 | found = strcasestr(&buf[suffix_index + 1], suffix); 106 | if (found == NULL) { 107 | break; 108 | } 109 | 110 | suffix_index = found - buf; 111 | } 112 | 113 | if (suffix_index == 0) { 114 | return 0; 115 | } 116 | 117 | for (freq_index = suffix_index; freq_index > 0; ) { 118 | freq_index--; 119 | 120 | if (isblank(buf[freq_index])) { 121 | break; 122 | } 123 | } 124 | 125 | { 126 | double parse; 127 | char old; 128 | 129 | errno = 0; 130 | old = buf[suffix_index]; 131 | buf[suffix_index] = '\0'; 132 | 133 | parse = strtod(&buf[freq_index], NULL); 134 | 135 | buf[suffix_index] = old; 136 | if (errno != 0 || isfinite(parse) == 0 || parse <= 0) { 137 | return 0; 138 | } 139 | 140 | return (unsigned long long)(parse * scale); 141 | } 142 | } 143 | 144 | unsigned long long 145 | an_md_scale_invariant_rdtsc(void) 146 | { 147 | static const struct { 148 | const char *str; 149 | double scale; 150 | } options[] = { 151 | { "thz", 1e12 }, 152 | { "ghz", 1e9 }, 153 | { "mhz", 1e6 } 154 | }; 155 | char brand_string[64] = { 0 }; 156 | unsigned long long ret; 157 | 158 | if (cpuid_vendor() != CPUID_VENDOR_INTEL) { 159 | return 0; 160 | } 161 | 162 | if (cpuid_feature(CPUID_FEATURE_INV_TSC) == false) { 163 | return 0; 164 | } 165 | 166 | ret = cpuid_core_frequency(); 167 | if (ret != 0) { 168 | return ret; 169 | } 170 | 171 | cpuid_brand(brand_string, sizeof(brand_string)); 172 | for (size_t i = 0; i < ARRAY_SIZE(options); i++) { 173 | ret = scan_brand_for_frequency(brand_string, options[i].str, options[i].scale); 174 | if (ret != 0) { 175 | break; 176 | } 177 | } 178 | 179 | return ret; 180 | } 181 | -------------------------------------------------------------------------------- /debian.changelog: -------------------------------------------------------------------------------- 1 | acf (0.1-1) UNRELEASED; urgency=medium 2 | 3 | * Initial release. 4 | 5 | -- Mobai Zhang Tue, 19 Apr 2016 00:43:55 +0000 6 | -------------------------------------------------------------------------------- /debian.compat: -------------------------------------------------------------------------------- 1 | 9 2 | -------------------------------------------------------------------------------- /debian.control: -------------------------------------------------------------------------------- 1 | Source: acf 2 | Maintainer: Mobai Zhang 3 | Section: misc 4 | Priority: optional 5 | Standards-Version: 3.9.2 6 | Build-Depends: debhelper (>= 9) 7 | 8 | Package: acf 9 | Architecture: any 10 | Depends: ${shlibs:Depends}, 11 | ${misc:Depends} 12 | Description: AppNexus Realtime Platform's common C library 13 | -------------------------------------------------------------------------------- /debian.copyright: -------------------------------------------------------------------------------- 1 | Files: * 2 | Copyright: 2015 AppNexus, Inc 3 | License: Apache-2.0 4 | 5 | Files: common/an_qsort.inc 6 | Copyright: 1992-2015 The FreeBSD Project, modifications by AppNexus, Inc. 7 | License: BSD-2-clause 8 | 9 | Files: common/an_table_hash.h 10 | Copyright: Public Domain, Austin Appleby 11 | License: public-domain 12 | 13 | Files: common/net/protocol/http-parser/* 14 | Copyright: 2009 NGINX, Joyent, Inc. and other Node contributors. 15 | License: Expat 16 | 17 | Files: common/x86_64/cpuid.* 18 | Copyright: 2009-2011 Samy Al Bahra, modifications by AppNexus, Inc. 19 | License: BSD-2-clause 20 | 21 | Files: cmake/FindCheck.cmake 22 | Copyright: 2007 Daniel Gollub , 2007-2009 Bjoern Ricks 23 | License: BSD-3-clause 24 | 25 | Files: include/an_charset.* 26 | Copyright: 2011 Joseph A. Adams 27 | License: Expat 28 | 29 | Files: cmake/Findcodecov.cmake, cmake/FindGcov.cmake, cmake/FindLcov.cmake, cmake/llvm-cov-wrapper 30 | Copyright: 2015-2017 RWTH Aachen University, Federal Republic of Germany 31 | License: BSD-3-clause 32 | -------------------------------------------------------------------------------- /debian.rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | 3 | export DESTDIR=$(CURDIR)/debian/acf 4 | export DH_COMPAT=9 5 | export DH_VERBOSE=1 6 | 7 | DEBVERS := $(shell dpkg-parsechangelog | sed -n -e 's/^Version: //p') 8 | VERSION := $(shell echo '$(DEBVERS)' | sed -e 's/^[[:digit:]]*://' -e 's/[~-].*//') 9 | 10 | %: 11 | dh $@ --buildsystem=cmake 12 | 13 | override_dh_auto_configure: 14 | dh_auto_configure -- -DDISABLE_TESTS=ON -DCMAKE_BUILD_TYPE=RELWITHDEBINFO -DCMAKE_ACF_VERSION=$(VERSION) 15 | -------------------------------------------------------------------------------- /example/Makefile: -------------------------------------------------------------------------------- 1 | test:main.c 2 | gcc -o example -std=gnu99 -I../src/ -I../build main.c -lcommon -L../build/lib 3 | 4 | clean: 5 | rm test 6 | 7 | check: 8 | LD_PRELOAD=../build/lib/libcommon.so ./test 9 | -------------------------------------------------------------------------------- /example/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "an_array.h" 5 | #include "an_allocator.h" 6 | 7 | struct foo { 8 | int x; 9 | }; 10 | 11 | AN_ARRAY(foo, foo_list) 12 | 13 | static void 14 | print_array(AN_ARRAY_INSTANCE(foo_list) *a) 15 | { 16 | printf("foo_array: \n"); 17 | struct foo *cursor; 18 | AN_ARRAY_FOREACH(a, cursor) { 19 | printf("%d ", cursor->x); 20 | } 21 | printf("\n"); 22 | } 23 | 24 | int main(int argc, char **argv) { 25 | AN_ARRAY_INSTANCE(foo_list) foo_array; 26 | AN_ARRAY_INIT(foo_list, &foo_array, 8); 27 | 28 | print_array(&foo_array); 29 | 30 | struct foo foo1 = { .x = 1 }; 31 | AN_ARRAY_PUSH(foo_list, &foo_array, &foo1); 32 | struct foo foo2 = { .x = 2 }; 33 | AN_ARRAY_PUSH(foo_list, &foo_array, &foo2); 34 | 35 | print_array(&foo_array); 36 | 37 | AN_ARRAY_DEINIT(foo_list, &foo_array); 38 | return 0; 39 | } 40 | -------------------------------------------------------------------------------- /include/an_allocator.h: -------------------------------------------------------------------------------- 1 | #ifndef AN_ALLOCATOR_H 2 | #define AN_ALLOCATOR_H 3 | 4 | #include "an_cc.h" 5 | 6 | AN_EXTERN_C_BEGIN 7 | 8 | #include "acf_export.h" 9 | 10 | #include 11 | #include 12 | 13 | struct an_allocator { 14 | void *(*malloc)(const void *ctx, size_t size, void *return_addr); 15 | void *(*calloc)(const void *ctx, size_t nmemb, size_t size, void *return_addr); 16 | void *(*realloc)(const void *ctx, void *address, size_t size_from, size_t size_to, void *return_addr); 17 | void (*free)(const void *ctx, void *ptr, void *return_addr); 18 | }; 19 | 20 | static inline void * 21 | an_allocator_malloc(const struct an_allocator *a, size_t size, void *return_addr) 22 | { 23 | return a->malloc(a, size, return_addr); 24 | } 25 | 26 | static inline void * 27 | an_allocator_calloc(const struct an_allocator *a, size_t nmemb, size_t size, void *return_addr) 28 | { 29 | return a->calloc(a, nmemb, size, return_addr); 30 | } 31 | 32 | static inline void * 33 | an_allocator_realloc(const struct an_allocator *a, void *address, size_t size_from, size_t size_to, void *return_addr) 34 | { 35 | return a->realloc(a, address, size_from, size_to, return_addr); 36 | } 37 | 38 | static inline void 39 | an_allocator_free(const struct an_allocator *a, void *ptr, void *return_addr) 40 | { 41 | a->free(a, ptr, return_addr); 42 | } 43 | 44 | char *an_allocator_strdup(const struct an_allocator *a, const char *s); 45 | 46 | char *an_allocator_strndup(const struct an_allocator *a, const char *s, size_t n); 47 | 48 | #define AN_MALLOC(IF, N) \ 49 | an_allocator_malloc((IF), (N), __builtin_return_address(0)) 50 | 51 | #define AN_CALLOC(IF, N, SIZE) \ 52 | an_allocator_calloc((IF), (N), (SIZE), __builtin_return_address(0)) 53 | 54 | #define AN_REALLOC(IF, ADDR, SIZE_FROM, SIZE_TO) \ 55 | an_allocator_realloc((IF), (ADDR), (SIZE_FROM), (SIZE_TO), __builtin_return_address(0)) 56 | 57 | #define AN_FREE(IF, ADDR) \ 58 | an_allocator_free((IF), (ADDR), __builtin_return_address(0)) 59 | 60 | #define AN_STRDUP(IF, S) \ 61 | an_allocator_strdup((IF), (S)) 62 | 63 | #define AN_STRNDUP(IF, S, N) \ 64 | an_allocator_strndup((IF), (S), (N)) 65 | 66 | 67 | /* 68 | * Default allocator uses plain malloc, calloc, realloc, and free 69 | */ 70 | ACF_EXPORT const struct an_allocator *an_default_allocator(void); 71 | extern const struct an_allocator default_allocator; 72 | 73 | AN_EXTERN_C_END 74 | 75 | #endif 76 | -------------------------------------------------------------------------------- /include/an_bitmap.h: -------------------------------------------------------------------------------- 1 | #ifndef AN_BITMAP_H 2 | #define AN_BITMAP_H 3 | 4 | #include "an_cc.h" 5 | 6 | AN_EXTERN_C_BEGIN 7 | 8 | #include 9 | #include 10 | 11 | #define AN_STATIC_BITMAP_T(name, size) uint64_t name[(size + 63UL) / 64] 12 | 13 | #define AN_STATIC_BITMAP_CLEAR(name, size) memset(name, 0, ((size + 63UL) / 64) * sizeof(uint64_t)) 14 | 15 | AN_CC_UNUSED static void 16 | an_static_bitmap_set(uint64_t *bitmap, unsigned i) 17 | { 18 | uint64_t mask = 1UL << (i % 64); 19 | bitmap[i / 64] |= mask; 20 | } 21 | 22 | AN_CC_UNUSED static void 23 | an_static_bitmap_unset(uint64_t *bitmap, unsigned i) 24 | { 25 | uint64_t mask = 1UL << (i % 64); 26 | bitmap[i / 64] &= ~mask; 27 | } 28 | 29 | AN_CC_UNUSED static bool 30 | an_static_bitmap_is_set(const uint64_t *bitmap, unsigned i) 31 | { 32 | return ((bitmap[i / 64]) & (1UL << (i % 64))) != 0; 33 | } 34 | 35 | AN_CC_UNUSED static bool 36 | an_static_bitmap_is_empty(const uint64_t *bitmap, size_t len) 37 | { 38 | size_t blocks = (len + 63UL) / 64; 39 | 40 | for (size_t i = 0; i < blocks; i++) { 41 | if (bitmap[i] != 0) { 42 | return false; 43 | } 44 | } 45 | 46 | return true; 47 | } 48 | 49 | AN_EXTERN_C_END 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /include/an_charset.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2011 Joseph A. Adams (joeyadams3.14159@gmail.com) 3 | All rights reserved. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | */ 23 | 24 | /* 25 | * NOTE: This file has been modified from its original form to remove 26 | * functions that we (AppNexus) do not use. 27 | */ 28 | 29 | #ifndef AN_CHARSET_H 30 | #define AN_CHARSET_H 31 | 32 | #include "an_cc.h" 33 | 34 | AN_EXTERN_C_BEGIN 35 | 36 | #include 37 | #include 38 | 39 | #include "acf_export.h" 40 | 41 | /** 42 | * @brief Validate that the given char is valid UTF-8 43 | * 44 | * @param s Start of the UTF-8 character 45 | * @param e End of the string containing the UTF-8 character 46 | * 47 | * @return The byte-length of the UTF-8 character, 0 if invalid 48 | */ 49 | ACF_EXPORT int an_is_utf8(const char *s, const char *e); 50 | 51 | /** Utf8 string statistics */ 52 | struct an_utf8_stats 53 | { 54 | bool is_valid; /**< True if the string has valid utf-8 content */ 55 | size_t total_code_point_count; /**< Total number of code points in the string. 56 | If @a is_valid is false, number of code points before error was detected */ 57 | size_t wide_code_point_count; /**< Number of wide (>= bytes in size) code points encountered */ 58 | size_t parsed_length; /**< Number of characters, successfully parsed from the input string */ 59 | }; 60 | 61 | /** 62 | * @brief Calculates the stats on the utf-8 encoded string pointed to by 63 | * parameter @a str. See @a utf8_stats 64 | * @param str Pointer to the utf-8 string to calculate stats 65 | * @param byte_length Byte length of the utf-8 string 66 | * @return @a utf8_stats structure with stats collected 67 | */ 68 | ACF_EXPORT struct an_utf8_stats an_utf8_stats_get(const char *str, size_t byte_length); 69 | 70 | /** 71 | * @brief Validate the given UTF-8 string. If it contains '\0' characters, it is still valid. 72 | * 73 | * @param str String to validate 74 | * @param byte_length Byte-length of the string 75 | * 76 | * @return True if valid UTF-8 string, false otherwise 77 | */ 78 | ACF_EXPORT bool an_utf8_validate(const char *str, size_t byte_length); 79 | 80 | AN_EXTERN_C_END 81 | 82 | #endif 83 | -------------------------------------------------------------------------------- /include/an_itoa.h: -------------------------------------------------------------------------------- 1 | #ifndef AN_ITOA_H 2 | #define AN_ITOA_H 3 | /** 4 | * Fast unsigned integer to decimal string conversion code. The code 5 | * ONLY WORKS ON LITTLE ENDIAN machines, and will be slow on hardware 6 | * with bad support for unaligned stores. 7 | * 8 | * Unlike regular itoa, the result is *not* NUL-terminated, and the 9 | * routines are allowed to write garbage bytes in the destination 10 | * buffer after the decimal string, as long as they stay in bounds: 11 | * `an_itoa` never writes past `out[9]`, and `an_ltoa` past `out[19]`. 12 | * 13 | * Porting this to big endian should only necessitate a byteswap 14 | * before writing to memory, but we've never needed to support BE. 15 | */ 16 | 17 | #include "an_cc.h" 18 | 19 | AN_EXTERN_C_BEGIN 20 | 21 | #include 22 | 23 | #include "acf_export.h" 24 | 25 | /** 26 | * @brief Output a non-NUL terminated string representation of x to 27 | * out, using at most 10 characters. 28 | * @result a pointer to the character after the last character in the 29 | * representation of x. 30 | * 31 | * The string representation may have trailing noise, but never writes 32 | * past out[9] and the return value disregards this padding. 33 | */ 34 | ACF_EXPORT char *an_itoa(char *out, uint32_t x); 35 | 36 | /** 37 | * @brief Output a non-NUL terminated string representation of x to 38 | * out, using at most 20 characters. 39 | * @result a pointer to the character after the last character in the 40 | * representation of x. 41 | * 42 | * Again, there may be trailing noise bytes after the return value of 43 | * an_ltoa, but this function never writes past out[19]. 44 | */ 45 | ACF_EXPORT char *an_ltoa(char *out, uint64_t x); 46 | 47 | AN_EXTERN_C_END 48 | #endif 49 | -------------------------------------------------------------------------------- /include/an_syslog.h: -------------------------------------------------------------------------------- 1 | #ifndef AN_SYSLOG_H 2 | #define AN_SYSLOG_H 3 | 4 | #include "an_cc.h" 5 | 6 | AN_EXTERN_C_BEGIN 7 | 8 | #include "acf_export.h" 9 | 10 | ACF_EXPORT void AN_CC_WEAK an_syslog(int priority, const char *format, ...) __attribute__((format(printf, 2, 3))); 11 | 12 | AN_EXTERN_C_END 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /include/an_util.h: -------------------------------------------------------------------------------- 1 | #ifndef AN_UTIL_H 2 | #define AN_UTIL_H 3 | 4 | #include "an_cc.h" 5 | 6 | AN_EXTERN_C_BEGIN 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include "acf_export.h" 13 | 14 | #ifndef __cplusplus 15 | 16 | #ifndef max 17 | #define max(a, b) \ 18 | ({ \ 19 | __typeof__(a) _a = (a); \ 20 | __typeof__(b) _b = (b); \ 21 | _a > _b ? _a : _b; \ 22 | }) 23 | #endif 24 | 25 | #ifndef min 26 | #define min(a, b) \ 27 | ({ \ 28 | __typeof__(a) _a = (a); \ 29 | __typeof__(b) _b = (b); \ 30 | _b > _a ? _a : _b; \ 31 | }) 32 | #endif 33 | 34 | #endif /* __cplusplus */ 35 | 36 | /* from bithacks */ 37 | ACF_EXPORT uint64_t an_next_power_of_2(uint64_t x); 38 | 39 | ACF_EXPORT void an_safe_fill(char dest[], const char *source, size_t max_len); 40 | 41 | ACF_EXPORT void an_safe_strncpy(char *dest, const char *src, size_t num); 42 | 43 | /** 44 | * print the current time in log format 45 | * 46 | * @param tm time struct containing the current time 47 | * @param log_time buffer to store time in 48 | * @param size of the buffer 49 | */ 50 | ACF_EXPORT void an_time_print(struct tm *tm, char log_time[], int len); 51 | 52 | ACF_EXPORT void an_time_to_str(time_t time, char log_time[], int len); 53 | 54 | AN_EXTERN_C_END 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /src/an_allocator.c: -------------------------------------------------------------------------------- 1 | #include "an_allocator.h" 2 | 3 | #include 4 | #include 5 | 6 | static void * 7 | an_default_malloc(const void *ctx, size_t size, void *return_addr) 8 | { 9 | (void)ctx; 10 | (void)return_addr; 11 | 12 | return malloc(size); 13 | } 14 | 15 | static void * 16 | an_default_calloc(const void *ctx, size_t nmemb, size_t size, void *return_addr) 17 | { 18 | (void)ctx; 19 | (void)return_addr; 20 | 21 | return calloc(nmemb, size); 22 | } 23 | 24 | static void * 25 | an_default_realloc(const void *ctx, void *address, size_t size_from, size_t size_to, void *return_addr) 26 | { 27 | (void)ctx; 28 | (void)return_addr; 29 | (void)size_from; 30 | 31 | return realloc(address, size_to); 32 | } 33 | 34 | static void 35 | an_default_free(const void *ctx, void *ptr, void *return_addr) 36 | { 37 | (void)ctx; 38 | (void)return_addr; 39 | 40 | free(ptr); 41 | } 42 | 43 | ACF_EXPORT const struct an_allocator default_allocator = { 44 | .malloc = an_default_malloc, 45 | .calloc = an_default_calloc, 46 | .realloc = an_default_realloc, 47 | .free = an_default_free 48 | }; 49 | 50 | ACF_EXPORT const struct an_allocator * 51 | an_default_allocator() 52 | { 53 | 54 | return &default_allocator; 55 | } 56 | 57 | ACF_EXPORT char * 58 | an_allocator_strdup(const struct an_allocator *a, const char *s) 59 | { 60 | if (s == NULL) { 61 | return NULL; 62 | } 63 | 64 | size_t len = strlen(s); 65 | char *r = AN_MALLOC(a, len + 1); 66 | memcpy(r, s, len); 67 | r[len] = '\0'; 68 | 69 | return r; 70 | } 71 | 72 | ACF_EXPORT char * 73 | an_allocator_strndup(const struct an_allocator *a, const char *s, size_t n) 74 | { 75 | if (s == NULL) { 76 | return NULL; 77 | } 78 | 79 | size_t len = strnlen(s, n); 80 | char *r = AN_MALLOC(a, len + 1); 81 | memcpy(r, s, len); 82 | r[len] = '\0'; 83 | 84 | return r; 85 | } 86 | -------------------------------------------------------------------------------- /src/an_array.c: -------------------------------------------------------------------------------- 1 | #include "an_array.h" 2 | #include "an_allocator.h" 3 | 4 | ACF_EXPORT const struct an_allocator *an_array_allocator = &default_allocator; 5 | 6 | ACF_EXPORT void 7 | an_array_set_allocator(const struct an_allocator *allocator) 8 | { 9 | an_array_allocator = allocator; 10 | } 11 | -------------------------------------------------------------------------------- /src/an_charset.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2011 Joseph A. Adams (joeyadams3.14159@gmail.com) 3 | All rights reserved. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | */ 23 | 24 | /* 25 | * NOTE: This file has been modified from its original form to remove 26 | * functions that we (AppNexus) do not use. 27 | */ 28 | 29 | #include 30 | 31 | #include "an_charset.h" 32 | 33 | ACF_EXPORT struct an_utf8_stats 34 | an_utf8_stats_get(const char *str, size_t byte_length) 35 | { 36 | struct an_utf8_stats rc = { .is_valid = false }; 37 | 38 | const char *s = str; 39 | const char *e = str + byte_length; 40 | int len; 41 | 42 | for (; s < e; s += len) { 43 | len = an_is_utf8(s, e); 44 | if (len == 0) { 45 | return rc; 46 | } 47 | 48 | if (len > 1) { 49 | rc.wide_code_point_count++; 50 | } 51 | rc.parsed_length += len; 52 | rc.total_code_point_count++; 53 | } 54 | assert(s == e); 55 | 56 | rc.is_valid = true; 57 | return rc; 58 | } 59 | 60 | ACF_EXPORT bool 61 | an_utf8_validate(const char *str, size_t byte_length) 62 | { 63 | const struct an_utf8_stats s = an_utf8_stats_get(str, byte_length); 64 | 65 | return s.is_valid; 66 | } 67 | 68 | /* 69 | * This function implements the syntax given in RFC3629, which is 70 | * the same as that given in The Unicode Standard, Version 6.0. 71 | * 72 | * It has the following properties: 73 | * 74 | * * All codepoints U+0000..U+10FFFF may be encoded, 75 | * except for U+D800..U+DFFF, which are reserved 76 | * for UTF-16 surrogate pair encoding. 77 | * * UTF-8 byte sequences longer than 4 bytes are not permitted, 78 | * as they exceed the range of Unicode. 79 | * * The sixty-six Unicode "non-characters" are permitted 80 | * (namely, U+FDD0..U+FDEF, U+xxFFFE, and U+xxFFFF). 81 | */ 82 | ACF_EXPORT int 83 | an_is_utf8(const char *s, const char *e) 84 | { 85 | unsigned char c = *s++; 86 | 87 | if (c <= 0x7F) { /* 00..7F */ 88 | return 1; 89 | } else if (c <= 0xC1) { /* 80..C1 */ 90 | /* Disallow overlong 2-byte sequence. */ 91 | return 0; 92 | } else if (c <= 0xDF) { /* C2..DF */ 93 | /* Make sure the character isn't clipped. */ 94 | if (e - s < 1) { 95 | return 0; 96 | } 97 | 98 | /* Make sure subsequent byte is in the range 0x80..0xBF. */ 99 | if (((unsigned char)*s++ & 0xC0) != 0x80) { 100 | return 0; 101 | } 102 | 103 | return 2; 104 | } else if (c <= 0xEF) { /* E0..EF */ 105 | /* Make sure the character isn't clipped. */ 106 | if (e - s < 2) { 107 | return 0; 108 | } 109 | 110 | /* Disallow overlong 3-byte sequence. */ 111 | if (c == 0xE0 && (unsigned char)*s < 0xA0) { 112 | return 0; 113 | } 114 | 115 | /* Disallow U+D800..U+DFFF. */ 116 | if (c == 0xED && (unsigned char)*s > 0x9F) { 117 | return 0; 118 | } 119 | 120 | /* Make sure subsequent bytes are in the range 0x80..0xBF. */ 121 | if (((unsigned char)*s++ & 0xC0) != 0x80) { 122 | return 0; 123 | } 124 | 125 | if (((unsigned char)*s++ & 0xC0) != 0x80) { 126 | return 0; 127 | } 128 | 129 | return 3; 130 | } else if (c <= 0xF4) { /* F0..F4 */ 131 | /* Make sure the character isn't clipped. */ 132 | if (e - s < 3) { 133 | return 0; 134 | } 135 | 136 | /* Disallow overlong 4-byte sequence. */ 137 | if (c == 0xF0 && (unsigned char)*s < 0x90) { 138 | return 0; 139 | } 140 | 141 | /* Disallow codepoints beyond U+10FFFF. */ 142 | if (c == 0xF4 && (unsigned char)*s > 0x8F) { 143 | return 0; 144 | } 145 | 146 | /* Make sure subsequent bytes are in the range 0x80..0xBF. */ 147 | if (((unsigned char)*s++ & 0xC0) != 0x80) { 148 | return 0; 149 | } 150 | 151 | if (((unsigned char)*s++ & 0xC0) != 0x80) { 152 | return 0; 153 | } 154 | 155 | if (((unsigned char)*s++ & 0xC0) != 0x80) { 156 | return 0; 157 | } 158 | 159 | return 4; 160 | } else { /* F5..FF */ 161 | return 0; 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /src/an_syslog.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "an_syslog.h" 5 | 6 | void 7 | an_syslog(int priority, const char *format, ...) 8 | { 9 | va_list args; 10 | 11 | va_start(args, format); 12 | syslog(priority, format, args); 13 | va_end(args); 14 | } 15 | -------------------------------------------------------------------------------- /src/an_util.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "an_util.h" 9 | 10 | /* from bithacks */ 11 | uint64_t 12 | an_next_power_of_2(uint64_t x) 13 | { 14 | 15 | --x; 16 | x |= x >> 1; 17 | x |= x >> 2; 18 | x |= x >> 4; 19 | x |= x >> 8; 20 | x |= x >> 16; 21 | x |= x >> 32; 22 | 23 | return ++x; 24 | } 25 | 26 | void 27 | an_safe_fill(char dest[], const char *source, size_t max_len) 28 | { 29 | 30 | if (max_len == 0 || source == NULL || *source == '\0') { 31 | dest[0] = '\0'; 32 | return; 33 | } 34 | 35 | 36 | if (max_len >= 4 && strcasecmp(source, "NULL") == 0) { 37 | dest[0] = '\0'; 38 | return; 39 | } 40 | 41 | /* leave room for NULL terminator */ 42 | strncpy(dest, source, max_len - 1); 43 | dest[max_len - 1] = '\0'; 44 | } 45 | 46 | void 47 | an_safe_strncpy(char *dest, const char *src, size_t num) 48 | { 49 | 50 | if (dest == NULL || num == 0) { 51 | return; 52 | } 53 | 54 | if (src == NULL) { 55 | dest[0] = '\0'; 56 | return; 57 | } 58 | 59 | strncpy(dest, src, num-1); 60 | dest[num-1] = '\0'; 61 | } 62 | 63 | /** 64 | * print the current time in log format 65 | * 66 | * @param tm time struct containing the current time 67 | * @param log_time buffer to store time in 68 | * @param size of the buffer 69 | */ 70 | void 71 | an_time_print(struct tm *tm, char log_time[], int len) 72 | { 73 | 74 | snprintf(log_time, len, "%04d-%02d-%02d %02d:%02d:%02d", 75 | tm->tm_year + 1900, tm->tm_mon + 1, 76 | tm->tm_mday, tm->tm_hour, tm->tm_min, 77 | tm->tm_sec); 78 | } 79 | 80 | 81 | void 82 | an_time_to_str(time_t time, char log_time[], int len) 83 | { 84 | static __thread time_t cached_time = 0; 85 | static __thread struct tm cached_tm; 86 | 87 | /* avoid calling localtime_r unless our time has changed in this thread */ 88 | if (cached_time != time) { 89 | cached_time = time; 90 | localtime_r(&cached_time, &cached_tm); 91 | } 92 | 93 | an_time_print(&cached_tm, log_time, len); 94 | } 95 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include(CheckTest) 2 | 3 | find_package(Check REQUIRED) 4 | 5 | set(CHECK_LIBS acf ${CMAKE_THREAD_LIBS_INIT}) 6 | 7 | CHECK_TEST(check_an_allocator LIBS ${CHECK_LIBS} SRC check_an_allocator.c) 8 | CHECK_TEST(check_an_array LIBS ${CHECK_LIBS} SRC check_an_array.c) 9 | CHECK_TEST(check_an_array_cpp LIBS ${CHECK_LIBS} SRC check_an_array_cpp.cc) 10 | CHECK_TEST(check_an_charset LIBS ${CHECK_LIBS} SRC check_an_charset.c) 11 | CHECK_TEST(check_an_itoa LIBS ${CHECK_LIBS} SRC check_an_itoa.c) 12 | CHECK_TEST(check_an_util LIBS ${CHECK_LIBS} SRC check_an_util.c) 13 | CHECK_TEST(check_bitmap LIBS ${CHECK_LIBS} SRC check_bitmap.c) 14 | -------------------------------------------------------------------------------- /test/check_an_allocator.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "an_allocator.h" 9 | 10 | #ifdef __APPLE__ 11 | #include 12 | #define malloc_usable_size malloc_size 13 | #else 14 | #include 15 | #endif 16 | 17 | static struct allocator_stats { 18 | size_t n_malloc; 19 | size_t n_calloc; 20 | size_t n_realloc; 21 | size_t n_free; 22 | } allocator_stats; 23 | 24 | static void * 25 | check_calloc(const void *ctx, size_t nmemb, size_t size, void *return_addr) 26 | { 27 | (void)ctx; 28 | (void)return_addr; 29 | 30 | allocator_stats.n_calloc++; 31 | return calloc(nmemb, size); 32 | } 33 | 34 | static void * 35 | check_malloc(const void *ctx, size_t size, void *return_addr) 36 | { 37 | (void)ctx; 38 | (void)return_addr; 39 | 40 | allocator_stats.n_malloc++; 41 | return malloc(size); 42 | } 43 | 44 | static void * 45 | check_realloc(const void *ctx, void *address, size_t size_from, size_t size_to, void *return_addr) 46 | { 47 | (void)ctx; 48 | (void)size_from; 49 | (void)return_addr; 50 | 51 | allocator_stats.n_realloc++; 52 | return realloc(address, size_to); 53 | } 54 | 55 | static void 56 | check_free(const void *ctx, void *ptr, void *return_addr) 57 | { 58 | (void)ctx; 59 | (void)return_addr; 60 | 61 | allocator_stats.n_free++; 62 | free(ptr); 63 | } 64 | 65 | static void 66 | test_allocator(const struct an_allocator *a) 67 | { 68 | int *data; 69 | 70 | data = AN_MALLOC(a, 10); 71 | ck_assert(data != NULL); 72 | ck_assert(malloc_usable_size(data) > 0); 73 | AN_FREE(a, data); 74 | 75 | data = AN_CALLOC(a, 10, sizeof(int)); 76 | ck_assert(data != NULL); 77 | ck_assert(malloc_usable_size(data) > 0); 78 | for (size_t i = 0; i < 10; i++) { 79 | ck_assert(data[i] == 0); 80 | } 81 | AN_FREE(a, data); 82 | 83 | 84 | data = AN_MALLOC(a, 10); 85 | ck_assert(data != NULL); 86 | size_t original_usable_size = malloc_usable_size(data); 87 | ck_assert(original_usable_size > 0); 88 | 89 | data = AN_REALLOC(a, data, 10, sysconf(_SC_PAGESIZE) * 4); 90 | ck_assert(data != NULL); 91 | ck_assert(malloc_usable_size(data) > original_usable_size); 92 | 93 | AN_FREE(a, data); 94 | 95 | char *s = AN_STRDUP(a, "testing"); 96 | ck_assert(s != NULL); 97 | ck_assert(strcmp(s, "testing") == 0); 98 | AN_FREE(a, s); 99 | 100 | s = AN_STRNDUP(a, "testing", 4); 101 | ck_assert(s != NULL); 102 | ck_assert(strcmp(s, "test") == 0); 103 | AN_FREE(a, s); 104 | 105 | s = AN_STRDUP(a, NULL); 106 | ck_assert(s == NULL); 107 | 108 | s = AN_STRNDUP(a, NULL, 4); 109 | ck_assert(s == NULL); 110 | } 111 | 112 | START_TEST(default_allocator_test) 113 | { 114 | test_allocator(an_default_allocator()); 115 | } 116 | END_TEST 117 | 118 | START_TEST(custom_allocator_test) 119 | { 120 | struct an_allocator check_allocator = { 121 | .malloc = check_malloc, 122 | .calloc = check_calloc, 123 | .realloc = check_realloc, 124 | .free = check_free 125 | }; 126 | 127 | memset(&allocator_stats, 0, sizeof(allocator_stats)); 128 | 129 | test_allocator(&check_allocator); 130 | 131 | ck_assert(allocator_stats.n_calloc > 0); 132 | ck_assert(allocator_stats.n_malloc > 0); 133 | ck_assert(allocator_stats.n_realloc > 0); 134 | ck_assert(allocator_stats.n_free > 0); 135 | } 136 | END_TEST 137 | 138 | int 139 | main(int argc, char *argv[]) 140 | { 141 | SRunner *sr; 142 | Suite *suite = suite_create("an_allocator"); 143 | TCase *tc = tcase_create("test_an_allocator"); 144 | 145 | tcase_add_test(tc, default_allocator_test); 146 | tcase_add_test(tc, custom_allocator_test); 147 | 148 | suite_add_tcase(suite, tc); 149 | 150 | sr = srunner_create(suite); 151 | srunner_set_xml(sr, "check/check_an_allocator.xml"); 152 | srunner_set_fork_status(sr, CK_FORK); 153 | srunner_run_all(sr, CK_NORMAL); 154 | 155 | return srunner_ntests_failed(sr); 156 | } 157 | -------------------------------------------------------------------------------- /test/check_an_array_cpp.cc: -------------------------------------------------------------------------------- 1 | #include "an_array.h" 2 | 3 | struct node { 4 | int value; 5 | }; 6 | 7 | AN_ARRAY(node, test); 8 | AN_ARRAY_PRIMITIVE(int, test2); 9 | 10 | int 11 | main(int argc, char *argv[]) 12 | { 13 | return 0; 14 | } 15 | -------------------------------------------------------------------------------- /test/check_an_charset.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "an_charset.h" 10 | 11 | void 12 | test_stat(const char *str, size_t len, const struct an_utf8_stats e, const char *ctx, int line) 13 | { 14 | struct an_utf8_stats o = an_utf8_stats_get(str, len); 15 | 16 | printf("%s: testing [%s], len:%zu\n", __FUNCTION__, str, len); 17 | ck_assert_msg(o.is_valid == e.is_valid, "stats.is_valid expected to be %s, but observed otherwise (%s:%d)", 18 | e.is_valid ? "true" : "false", ctx, line); 19 | ck_assert_msg(o.total_code_point_count == e.total_code_point_count, "stats.total_code_point_count expected to be %zu, " 20 | "but observed %zu (%s:%d)", e.total_code_point_count, o.total_code_point_count, ctx, line); 21 | ck_assert_msg(o.wide_code_point_count == e.wide_code_point_count, "stats.wide_code_point_count expected to be %zu, " 22 | "but observed %zu (%s:%d)", e.wide_code_point_count, o.wide_code_point_count, ctx, line); 23 | ck_assert_msg(o.parsed_length == e.parsed_length, "stats.parsed_length expected to be %zu, " 24 | "but observed %zu (%s:%d)", e.parsed_length, o.parsed_length, ctx, line); 25 | } 26 | 27 | START_TEST(test_stats) 28 | { 29 | const char *s; 30 | 31 | s = "\u0430\u0410abc"; 32 | test_stat(s, strlen(s), (struct an_utf8_stats){ .is_valid = true, .total_code_point_count = 5, 33 | .wide_code_point_count = 2, .parsed_length = strlen(s) }, __FUNCTION__, __LINE__); 34 | 35 | s = "\u0430\u0410"; 36 | test_stat(s, strlen(s), (struct an_utf8_stats){ .is_valid = true, .total_code_point_count = 2, 37 | .wide_code_point_count = 2, .parsed_length = strlen(s) }, __FUNCTION__, __LINE__); 38 | 39 | s = "ABCabc"; 40 | test_stat(s, strlen(s), (struct an_utf8_stats){ .is_valid = true, .total_code_point_count = 6, 41 | .wide_code_point_count = 0, .parsed_length = strlen(s) }, __FUNCTION__, __LINE__); 42 | 43 | s = "\xED\xA0\xBF"; /* invalid surrogate */ 44 | test_stat(s, strlen(s), (struct an_utf8_stats){ .is_valid = false, .total_code_point_count = 0, 45 | .wide_code_point_count = 0, .parsed_length = 0 }, __FUNCTION__, __LINE__); 46 | 47 | s = "\u0430A\xED\xA0\xBF"; /* invalid surrogate */ 48 | test_stat(s, strlen(s), (struct an_utf8_stats){ .is_valid = false, .total_code_point_count = 2, 49 | .wide_code_point_count = 1, .parsed_length = 3 }, __FUNCTION__, __LINE__); 50 | } 51 | END_TEST 52 | 53 | START_TEST(utf8_validate) 54 | { 55 | #define _UTF8_VALIDATE(s, r) ck_assert(an_utf8_validate(s, strlen(s)) == (r)) 56 | 57 | _UTF8_VALIDATE("abc", true); 58 | _UTF8_VALIDATE("\u0430\u0410", true); 59 | _UTF8_VALIDATE("\u0430\u0410abc", true); 60 | 61 | _UTF8_VALIDATE("\xc3\x28", false); 62 | _UTF8_VALIDATE("\xa0\xa1", false); 63 | 64 | _UTF8_VALIDATE("\xe2\x28\xa1", false); 65 | _UTF8_VALIDATE("\xe2\x82\x28", false); 66 | 67 | _UTF8_VALIDATE("\xf0\x28\x8c\xbc", false); 68 | _UTF8_VALIDATE("\xf0\x90\x28\xbc", false); 69 | _UTF8_VALIDATE("\xf0\x28\x8c\x28", false); 70 | 71 | #undef _UTF8_VALIDATE 72 | } 73 | END_TEST 74 | 75 | 76 | int 77 | main(int argc, char** argv) 78 | { 79 | Suite *suite = suite_create("check_an_charset"); 80 | 81 | TCase *tc = tcase_create("test_an_charset"); 82 | tcase_add_test(tc, test_stats); 83 | tcase_add_test(tc, utf8_validate); 84 | suite_add_tcase(suite, tc); 85 | 86 | SRunner *sr = srunner_create(suite); 87 | srunner_set_xml(sr, "check/check_an_charset"); 88 | srunner_set_fork_status(sr, CK_FORK); 89 | srunner_run_all(sr, CK_NORMAL); 90 | int num_failed = srunner_ntests_failed(sr); 91 | srunner_free(sr); 92 | 93 | return num_failed; 94 | } 95 | -------------------------------------------------------------------------------- /test/check_an_itoa.c: -------------------------------------------------------------------------------- 1 | #ifdef NDEBUG 2 | #undef NDEBUG 3 | #endif 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "an_itoa.h" 13 | 14 | static void 15 | test_itoa(uint32_t x) 16 | { 17 | char buf[23]; 18 | unsigned long long test; 19 | char *end; 20 | 21 | memset(buf, 'X', 23); 22 | end = an_itoa(buf + 1, x); 23 | assert(end - (buf + 1) <= 10); 24 | assert(buf[0] == 'X'); 25 | assert(buf[1] != 'X'); 26 | assert(buf[1] != ' '); 27 | assert(*(end - 1) != 'X'); 28 | assert('0' <= *(end - 1)); 29 | assert(*(end - 1) <= '9'); 30 | while (*end == '\0') end++; 31 | assert(*end == 'X'); 32 | assert(end - (buf + 1) <= 10); 33 | *end = '\0'; 34 | 35 | assert(sscanf(buf + 1, " %llu", &test) == 1); 36 | assert(test == x); 37 | } 38 | 39 | static void 40 | test_ltoa(uint64_t x) 41 | { 42 | char buf[23]; 43 | unsigned long long test; 44 | char *end; 45 | 46 | memset(buf, 'X', 23); 47 | end = an_ltoa(buf + 1, x); 48 | assert(end - (buf + 1) <= 20); 49 | assert(buf[0] == 'X'); 50 | assert(buf[1] != 'X'); 51 | assert(buf[1] != ' '); 52 | assert(*(end - 1) != 'X'); 53 | assert('0' <= *(end - 1)); 54 | assert(*(end - 1) <= '9'); 55 | while (*end == '\0') end++; 56 | assert(end - (buf + 1) <= 20); 57 | assert(*end == 'X'); 58 | *end = '\0'; 59 | 60 | assert(sscanf(buf + 1, " %llu", &test) == 1); 61 | assert(test == x); 62 | } 63 | 64 | int 65 | main(int argc, char **argv) 66 | { 67 | uint64_t hi; 68 | ssize_t range = 128; 69 | 70 | /* Fewer test by default. */ 71 | if (argc > 1) { 72 | range = 1024 * 1024; 73 | } 74 | 75 | #define TEST(I) do { \ 76 | uint64_t test_value = (I); \ 77 | \ 78 | test_itoa(test_value); \ 79 | test_ltoa(test_value); \ 80 | } while (0) 81 | 82 | { 83 | char buf[23]; 84 | char *end; 85 | 86 | end = an_itoa(buf, 0); 87 | assert(end == &buf[1]); 88 | assert(buf[0] == '0'); 89 | 90 | end = an_ltoa(buf, 0); 91 | assert(end == &buf[1]); 92 | assert(buf[0] == '0'); 93 | } 94 | 95 | /* Test around powers of 10. */ 96 | hi = 1; 97 | for (size_t i = 0; i <= 20; i++, hi *= 10) { 98 | printf("Testing around power of 10: %"PRIu64"\n", hi); 99 | for (ssize_t j = -range; j <= range; j++) { 100 | TEST(hi + j); 101 | TEST(-(hi + j)); 102 | } 103 | } 104 | 105 | /* Test around powers of 2. */ 106 | hi = 1; 107 | for (size_t i = 0; i <= 64; i++, hi *= 2) { 108 | printf("Testing around power of 2: %"PRIu64"\n", hi); 109 | for (long j = -range; j <= range; j++) { 110 | TEST(hi + j); 111 | TEST(-(hi + j)); 112 | } 113 | } 114 | 115 | /* No exhaustive test by default. */ 116 | if (argc > 1) { 117 | size_t limit = UINT32_MAX; 118 | 119 | for (size_t i = 0; i <= limit; i++) { 120 | if (i % (8 * 1024 * 1024) == 0) { 121 | printf("Exhaustive testing: %05.2f%%\n", 122 | 100.0 * i / limit); 123 | } 124 | 125 | TEST(i); 126 | test_ltoa(-i); 127 | } 128 | } 129 | 130 | #undef TEST 131 | 132 | return 0; 133 | } 134 | -------------------------------------------------------------------------------- /test/check_an_util.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "an_util.h" 8 | 9 | static bool 10 | is_power_of_2(uint64_t x) 11 | { 12 | return ((x != 0) && !(x & (x - 1))); 13 | } 14 | 15 | START_TEST(test_an_next_power_of_2) 16 | { 17 | 18 | for (uint64_t i = 1; i < INT16_MAX; i++) { 19 | uint64_t v = an_next_power_of_2(i); 20 | ck_assert_msg(is_power_of_2(v)); 21 | ck_assert(v >= i); 22 | ck_assert(v/2 < i); 23 | } 24 | } 25 | END_TEST 26 | 27 | START_TEST(test_an_safe_fill) 28 | { 29 | char buf[32]; 30 | memset(buf, '1', sizeof(buf)); 31 | an_safe_fill(buf, NULL, sizeof(buf)); 32 | ck_assert(strlen(buf) == 0); 33 | 34 | memset(buf, '1', sizeof(buf)); 35 | an_safe_fill(buf, "\0", sizeof(buf)); 36 | ck_assert(strlen(buf) == 0); 37 | 38 | memset(buf, '1', sizeof(buf)); 39 | an_safe_fill(buf, "one", 0); 40 | ck_assert(strlen(buf) == 0); 41 | 42 | memset(buf, '1', sizeof(buf)); 43 | an_safe_fill(buf, "NULL", sizeof(buf)); 44 | ck_assert(strlen(buf) == 0); 45 | 46 | memset(buf, '1', sizeof(buf)); 47 | an_safe_fill(buf, "one", sizeof(buf)); 48 | ck_assert(strlen(buf) == 3); 49 | 50 | char src[sizeof(buf) * 2]; 51 | memset(src, '1', sizeof(src)); 52 | memset(buf, '1', sizeof(buf)); 53 | an_safe_fill(buf, src, sizeof(buf)); 54 | ck_assert(strlen(buf) == sizeof(buf)-1); 55 | } 56 | END_TEST 57 | 58 | START_TEST(test_an_safe_strncpy) 59 | { 60 | char buf[32]; 61 | memset(buf, '1', sizeof(buf)); 62 | an_safe_strncpy(buf, NULL, sizeof(buf)); 63 | ck_assert(strlen(buf) == 0); 64 | 65 | memset(buf, '1', sizeof(buf)); 66 | an_safe_strncpy(buf, "\0", sizeof(buf)); 67 | ck_assert(strlen(buf) == 0); 68 | 69 | memset(buf, '1', sizeof(buf)); 70 | buf[sizeof(buf) - 1] = '\0'; 71 | an_safe_strncpy(buf, "one", 0); 72 | ck_assert(strlen(buf) == sizeof(buf) - 1); 73 | 74 | memset(buf, '1', sizeof(buf)); 75 | an_safe_strncpy(buf, "one", sizeof(buf)); 76 | ck_assert(strlen(buf) == 3); 77 | 78 | char src[sizeof(buf) * 2]; 79 | memset(src, '1', sizeof(src)); 80 | memset(buf, '1', sizeof(buf)); 81 | an_safe_strncpy(buf, src, sizeof(buf)); 82 | ck_assert(strlen(buf) == sizeof(buf)-1); 83 | } 84 | END_TEST 85 | 86 | START_TEST(test_an_time_print) 87 | { 88 | char buf[1024]; 89 | struct tm tm; 90 | 91 | time_t x = 1429887925; 92 | gmtime_r(&x, &tm); 93 | 94 | memset(buf, 0, sizeof(buf)); 95 | an_time_print(&tm, buf, sizeof(buf)); 96 | ck_assert(strcmp(buf, "2015-04-24 15:05:25") == 0); 97 | } 98 | END_TEST 99 | 100 | START_TEST(test_an_time_to_str) 101 | { 102 | char buf[1024]; 103 | char buf2[1024]; 104 | time_t x = 1429887925; 105 | 106 | memset(buf, 0, sizeof(buf)); 107 | an_time_to_str(x, buf, sizeof(buf)); 108 | 109 | memset(buf2, 0, sizeof(buf2)); 110 | an_time_to_str(x, buf2, sizeof(buf)); 111 | ck_assert(strcmp(buf, buf2) == 0); 112 | } 113 | END_TEST 114 | 115 | 116 | int 117 | main(int argc, char *argv[]) 118 | { 119 | SRunner *sr; 120 | Suite *suite = suite_create("an_util"); 121 | TCase *tc = tcase_create("test_an_util"); 122 | 123 | tcase_add_test(tc, test_an_next_power_of_2); 124 | tcase_add_test(tc, test_an_safe_fill); 125 | tcase_add_test(tc, test_an_safe_strncpy); 126 | tcase_add_test(tc, test_an_time_print); 127 | tcase_add_test(tc, test_an_time_to_str); 128 | 129 | suite_add_tcase(suite, tc); 130 | 131 | sr = srunner_create(suite); 132 | srunner_set_xml(sr, "check/check_an_util.xml"); 133 | srunner_set_fork_status(sr, CK_FORK); 134 | srunner_run_all(sr, CK_NORMAL); 135 | 136 | return srunner_ntests_failed(sr); 137 | } 138 | -------------------------------------------------------------------------------- /test/check_bitmap.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "an_bitmap.h" 6 | 7 | START_TEST(test_sanity) { 8 | fprintf(stdout, "\n\nBegin test_sanity\n"); 9 | fail_unless(1==1, "sanity test suite"); 10 | } 11 | END_TEST 12 | 13 | START_TEST(test_an_static_bitmap_set) { 14 | fprintf(stdout, "\n\nBegin test_an_static_bitmap_set\n"); 15 | AN_STATIC_BITMAP_T(bitmap, 31415); 16 | AN_STATIC_BITMAP_CLEAR(bitmap, 31415); 17 | fail_if(an_static_bitmap_is_set(bitmap, 68)); 18 | an_static_bitmap_set(bitmap, 68); 19 | fail_if(!an_static_bitmap_is_set(bitmap, 68)); 20 | } 21 | END_TEST 22 | 23 | START_TEST(test_an_static_bitmap_unset) { 24 | fprintf(stdout, "\n\nBegin test_an_static_bitmap_unset\n"); 25 | AN_STATIC_BITMAP_T(bitmap, 31415); 26 | AN_STATIC_BITMAP_CLEAR(bitmap, 31415); 27 | an_static_bitmap_set(bitmap, 68); 28 | fail_if(!an_static_bitmap_is_set(bitmap, 68)); 29 | an_static_bitmap_unset(bitmap, 68); 30 | fail_if(an_static_bitmap_is_set(bitmap, 68)); 31 | } 32 | END_TEST 33 | 34 | START_TEST(test_an_static_clear) { 35 | fprintf(stdout, "\n\nBegin test_an_static_clear\n"); 36 | AN_STATIC_BITMAP_T(bitmap, 31415); 37 | an_static_bitmap_set(bitmap, 68); 38 | an_static_bitmap_set(bitmap, 168); 39 | an_static_bitmap_set(bitmap, 30068); 40 | fail_if(!an_static_bitmap_is_set(bitmap, 68)); 41 | fail_if(!an_static_bitmap_is_set(bitmap, 168)); 42 | fail_if(!an_static_bitmap_is_set(bitmap, 30068)); 43 | AN_STATIC_BITMAP_CLEAR(bitmap, 31415); 44 | for (unsigned i = 0; i < 31415; i++) { 45 | fail_if(an_static_bitmap_is_set(bitmap, i)); 46 | } 47 | fail_if(!an_static_bitmap_is_empty(bitmap, 31415)); 48 | } 49 | END_TEST 50 | 51 | START_TEST(test_an_static_front2back) { 52 | fprintf(stdout, "\n\nBegin test_an_static_front2back\n"); 53 | 54 | // A non-aligned bitmap of reasonably large size 55 | AN_STATIC_BITMAP_T(bitmap, 20000); 56 | AN_STATIC_BITMAP_CLEAR(bitmap, 20000); 57 | for (unsigned i = 0; i < 20000; i++) { 58 | if (i % 1023 == 0) { 59 | an_static_bitmap_set(bitmap, i); 60 | } 61 | } 62 | for (unsigned i = 0; i < 20000; i++) { 63 | if (i % 1023 == 0) { 64 | fail_unless(an_static_bitmap_is_set(bitmap, i)); 65 | } 66 | else { 67 | fail_unless(!an_static_bitmap_is_set(bitmap, i)); 68 | } 69 | } 70 | an_static_bitmap_unset(bitmap, 0); 71 | an_static_bitmap_unset(bitmap, 2046); 72 | for (unsigned i = 0; i < 20000; i++) { 73 | if (i == 0 || i == 2046 || i % 1023 != 0) { 74 | fail_unless(!an_static_bitmap_is_set(bitmap, i)); 75 | } 76 | else { 77 | fail_unless(an_static_bitmap_is_set(bitmap, i)); 78 | } 79 | } 80 | } 81 | END_TEST 82 | 83 | int 84 | main(int argc, char **argv) 85 | { 86 | Suite* suite = suite_create("common/check_bitmap"); 87 | 88 | TCase* tc = tcase_create("test_bitmap"); 89 | tcase_add_test(tc, test_sanity); 90 | 91 | tcase_add_test(tc, test_an_static_bitmap_set); 92 | tcase_add_test(tc, test_an_static_bitmap_unset); 93 | tcase_add_test(tc, test_an_static_clear); 94 | tcase_add_test(tc, test_an_static_front2back); 95 | suite_add_tcase(suite, tc); 96 | 97 | SRunner *sr = srunner_create(suite); 98 | srunner_set_xml(sr, "check/check_bitmap.xml"); 99 | srunner_set_fork_status(sr, CK_FORK); 100 | srunner_run_all(sr, CK_NORMAL); 101 | int num_failed = srunner_ntests_failed(sr); 102 | 103 | srunner_free(sr); 104 | 105 | return num_failed; 106 | } 107 | -------------------------------------------------------------------------------- /third_party/http-parser/AUTHORS: -------------------------------------------------------------------------------- 1 | # Authors ordered by first contribution. 2 | Ryan Dahl 3 | Jeremy Hinegardner 4 | Sergey Shepelev 5 | Joe Damato 6 | tomika 7 | Phoenix Sol 8 | Cliff Frey 9 | Ewen Cheslack-Postava 10 | Santiago Gala 11 | Tim Becker 12 | Jeff Terrace 13 | Ben Noordhuis 14 | Nathan Rajlich 15 | Mark Nottingham 16 | Aman Gupta 17 | Tim Becker 18 | Sean Cunningham 19 | Peter Griess 20 | Salman Haq 21 | Cliff Frey 22 | Jon Kolb 23 | Fouad Mardini 24 | Paul Querna 25 | Felix Geisendörfer 26 | koichik 27 | Andre Caron 28 | Ivo Raisr 29 | James McLaughlin 30 | David Gwynne 31 | Thomas LE ROUX 32 | Randy Rizun 33 | Andre Louis Caron 34 | Simon Zimmermann 35 | Erik Dubbelboer 36 | Martell Malone 37 | Bertrand Paquet 38 | BogDan Vatra 39 | Peter Faiman 40 | Corey Richardson 41 | Tóth Tamás 42 | Cam Swords 43 | Chris Dickinson 44 | Uli Köhler 45 | Charlie Somerville 46 | Patrik Stutz 47 | Fedor Indutny 48 | runner 49 | Alexis Campailla 50 | David Wragg 51 | Vinnie Falco 52 | Alex Butum 53 | Rex Feng 54 | Alex Kocharin 55 | Mark Koopman 56 | Helge Heß 57 | Alexis La Goutte 58 | George Miroshnykov 59 | Maciej Małecki 60 | Marc O'Morain 61 | Jeff Pinner 62 | Timothy J Fontaine 63 | Akagi201 64 | Romain Giraud 65 | Jay Satiro 66 | Arne Steen 67 | Kjell Schubert 68 | -------------------------------------------------------------------------------- /third_party/http-parser/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | http_parser.c is based on src/http/ngx_http_parse.c from NGINX copyright 2 | Igor Sysoev. 3 | 4 | Additional changes are licensed under the same terms as NGINX and 5 | copyright Joyent, Inc. and other Node contributors. All rights reserved. 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to 9 | deal in the Software without restriction, including without limitation the 10 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 11 | sell copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 23 | IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /third_party/http-parser/bench.c: -------------------------------------------------------------------------------- 1 | /* Copyright Fedor Indutny. All rights reserved. 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy 4 | * of this software and associated documentation files (the "Software"), to 5 | * deal in the Software without restriction, including without limitation the 6 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 | * sell copies of the Software, and to permit persons to whom the Software is 8 | * furnished to do so, subject to the following conditions: 9 | * 10 | * The above copyright notice and this permission notice shall be included in 11 | * all copies or substantial portions of the Software. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | * IN THE SOFTWARE. 20 | */ 21 | #include "http_parser.h" 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | static const char data[] = 28 | "POST /joyent/http-parser HTTP/1.1\r\n" 29 | "Host: github.com\r\n" 30 | "DNT: 1\r\n" 31 | "Accept-Encoding: gzip, deflate, sdch\r\n" 32 | "Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.6,en;q=0.4\r\n" 33 | "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) " 34 | "AppleWebKit/537.36 (KHTML, like Gecko) " 35 | "Chrome/39.0.2171.65 Safari/537.36\r\n" 36 | "Accept: text/html,application/xhtml+xml,application/xml;q=0.9," 37 | "image/webp,*/*;q=0.8\r\n" 38 | "Referer: https://github.com/joyent/http-parser\r\n" 39 | "Connection: keep-alive\r\n" 40 | "Transfer-Encoding: chunked\r\n" 41 | "Cache-Control: max-age=0\r\n\r\nb\r\nhello world\r\n0\r\n\r\n"; 42 | static const size_t data_len = sizeof(data) - 1; 43 | 44 | static int on_info(http_parser* p) { 45 | return 0; 46 | } 47 | 48 | 49 | static int on_data(http_parser* p, const char *at, size_t length) { 50 | return 0; 51 | } 52 | 53 | static http_parser_settings settings = { 54 | .on_message_begin = on_info, 55 | .on_headers_complete = on_info, 56 | .on_message_complete = on_info, 57 | .on_header_field = on_data, 58 | .on_header_value = on_data, 59 | .on_url = on_data, 60 | .on_status = on_data, 61 | .on_body = on_data 62 | }; 63 | 64 | int bench(int iter_count, int silent) { 65 | struct http_parser parser; 66 | int i; 67 | int err; 68 | struct timeval start; 69 | struct timeval end; 70 | float rps; 71 | 72 | if (!silent) { 73 | err = gettimeofday(&start, NULL); 74 | assert(err == 0); 75 | } 76 | 77 | for (i = 0; i < iter_count; i++) { 78 | size_t parsed; 79 | http_parser_init(&parser, HTTP_REQUEST); 80 | 81 | parsed = http_parser_execute(&parser, &settings, data, data_len); 82 | assert(parsed == data_len); 83 | } 84 | 85 | if (!silent) { 86 | err = gettimeofday(&end, NULL); 87 | assert(err == 0); 88 | 89 | fprintf(stdout, "Benchmark result:\n"); 90 | 91 | rps = (float) (end.tv_sec - start.tv_sec) + 92 | (end.tv_usec - start.tv_usec) * 1e-6f; 93 | fprintf(stdout, "Took %f seconds to run\n", rps); 94 | 95 | rps = (float) iter_count / rps; 96 | fprintf(stdout, "%f req/sec\n", rps); 97 | fflush(stdout); 98 | } 99 | 100 | return 0; 101 | } 102 | 103 | int main(int argc, char** argv) { 104 | if (argc == 2 && strcmp(argv[1], "infinite") == 0) { 105 | for (;;) 106 | bench(5000000, 1); 107 | return 0; 108 | } else { 109 | return bench(5000000, 0); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /third_party/http-parser/contrib/parsertrace.c: -------------------------------------------------------------------------------- 1 | /* Based on src/http/ngx_http_parse.c from NGINX copyright Igor Sysoev 2 | * 3 | * Additional changes are licensed under the same terms as NGINX and 4 | * copyright Joyent, Inc. and other Node contributors. All rights reserved. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | /* Dump what the parser finds to stdout as it happen */ 26 | 27 | #include "http_parser.h" 28 | #include 29 | #include 30 | #include 31 | 32 | int on_message_begin(http_parser* _) { 33 | (void)_; 34 | printf("\n***MESSAGE BEGIN***\n\n"); 35 | return 0; 36 | } 37 | 38 | int on_headers_complete(http_parser* _) { 39 | (void)_; 40 | printf("\n***HEADERS COMPLETE***\n\n"); 41 | return 0; 42 | } 43 | 44 | int on_message_complete(http_parser* _) { 45 | (void)_; 46 | printf("\n***MESSAGE COMPLETE***\n\n"); 47 | return 0; 48 | } 49 | 50 | int on_url(http_parser* _, const char* at, size_t length) { 51 | (void)_; 52 | printf("Url: %.*s\n", (int)length, at); 53 | return 0; 54 | } 55 | 56 | int on_header_field(http_parser* _, const char* at, size_t length) { 57 | (void)_; 58 | printf("Header field: %.*s\n", (int)length, at); 59 | return 0; 60 | } 61 | 62 | int on_header_value(http_parser* _, const char* at, size_t length) { 63 | (void)_; 64 | printf("Header value: %.*s\n", (int)length, at); 65 | return 0; 66 | } 67 | 68 | int on_body(http_parser* _, const char* at, size_t length) { 69 | (void)_; 70 | printf("Body: %.*s\n", (int)length, at); 71 | return 0; 72 | } 73 | 74 | void usage(const char* name) { 75 | fprintf(stderr, 76 | "Usage: %s $type $filename\n" 77 | " type: -x, where x is one of {r,b,q}\n" 78 | " parses file as a Response, reQuest, or Both\n", 79 | name); 80 | exit(EXIT_FAILURE); 81 | } 82 | 83 | int main(int argc, char* argv[]) { 84 | enum http_parser_type file_type; 85 | 86 | if (argc != 3) { 87 | usage(argv[0]); 88 | } 89 | 90 | char* type = argv[1]; 91 | if (type[0] != '-') { 92 | usage(argv[0]); 93 | } 94 | 95 | switch (type[1]) { 96 | /* in the case of "-", type[1] will be NUL */ 97 | case 'r': 98 | file_type = HTTP_RESPONSE; 99 | break; 100 | case 'q': 101 | file_type = HTTP_REQUEST; 102 | break; 103 | case 'b': 104 | file_type = HTTP_BOTH; 105 | break; 106 | default: 107 | usage(argv[0]); 108 | } 109 | 110 | char* filename = argv[2]; 111 | FILE* file = fopen(filename, "r"); 112 | if (file == NULL) { 113 | perror("fopen"); 114 | goto fail; 115 | } 116 | 117 | fseek(file, 0, SEEK_END); 118 | long file_length = ftell(file); 119 | if (file_length == -1) { 120 | perror("ftell"); 121 | goto fail; 122 | } 123 | fseek(file, 0, SEEK_SET); 124 | 125 | char* data = malloc(file_length); 126 | if (fread(data, 1, file_length, file) != (size_t)file_length) { 127 | fprintf(stderr, "couldn't read entire file\n"); 128 | free(data); 129 | goto fail; 130 | } 131 | 132 | http_parser_settings settings; 133 | memset(&settings, 0, sizeof(settings)); 134 | settings.on_message_begin = on_message_begin; 135 | settings.on_url = on_url; 136 | settings.on_header_field = on_header_field; 137 | settings.on_header_value = on_header_value; 138 | settings.on_headers_complete = on_headers_complete; 139 | settings.on_body = on_body; 140 | settings.on_message_complete = on_message_complete; 141 | 142 | http_parser parser; 143 | http_parser_init(&parser, file_type); 144 | size_t nparsed = http_parser_execute(&parser, &settings, data, file_length); 145 | free(data); 146 | 147 | if (nparsed != (size_t)file_length) { 148 | fprintf(stderr, 149 | "Error: %s (%s)\n", 150 | http_errno_description(HTTP_PARSER_ERRNO(&parser)), 151 | http_errno_name(HTTP_PARSER_ERRNO(&parser))); 152 | goto fail; 153 | } 154 | 155 | return EXIT_SUCCESS; 156 | 157 | fail: 158 | fclose(file); 159 | return EXIT_FAILURE; 160 | } 161 | -------------------------------------------------------------------------------- /third_party/http-parser/contrib/url_parser.c: -------------------------------------------------------------------------------- 1 | #include "http_parser.h" 2 | #include 3 | #include 4 | 5 | void 6 | dump_url (const char *url, const struct http_parser_url *u) 7 | { 8 | unsigned int i; 9 | 10 | printf("\tfield_set: 0x%x, port: %u\n", u->field_set, u->port); 11 | for (i = 0; i < UF_MAX; i++) { 12 | if ((u->field_set & (1 << i)) == 0) { 13 | printf("\tfield_data[%u]: unset\n", i); 14 | continue; 15 | } 16 | 17 | printf("\tfield_data[%u]: off: %u, len: %u, part: %.*s\n", 18 | i, 19 | u->field_data[i].off, 20 | u->field_data[i].len, 21 | u->field_data[i].len, 22 | url + u->field_data[i].off); 23 | } 24 | } 25 | 26 | int main(int argc, char ** argv) { 27 | struct http_parser_url u; 28 | int len, connect, result; 29 | 30 | if (argc != 3) { 31 | printf("Syntax : %s connect|get url\n", argv[0]); 32 | return 1; 33 | } 34 | len = strlen(argv[2]); 35 | connect = strcmp("connect", argv[1]) == 0 ? 1 : 0; 36 | printf("Parsing %s, connect %d\n", argv[2], connect); 37 | 38 | result = http_parser_parse_url(argv[2], len, connect, &u); 39 | if (result != 0) { 40 | printf("Parse error : %d\n", result); 41 | return result; 42 | } 43 | printf("Parse ok, result : \n"); 44 | dump_url(argv[2], &u); 45 | return 0; 46 | } -------------------------------------------------------------------------------- /third_party/http-parser/http_parser.gyp: -------------------------------------------------------------------------------- 1 | # This file is used with the GYP meta build system. 2 | # http://code.google.com/p/gyp/ 3 | # To build try this: 4 | # svn co http://gyp.googlecode.com/svn/trunk gyp 5 | # ./gyp/gyp -f make --depth=`pwd` http_parser.gyp 6 | # ./out/Debug/test 7 | { 8 | 'target_defaults': { 9 | 'default_configuration': 'Debug', 10 | 'configurations': { 11 | # TODO: hoist these out and put them somewhere common, because 12 | # RuntimeLibrary MUST MATCH across the entire project 13 | 'Debug': { 14 | 'defines': [ 'DEBUG', '_DEBUG' ], 15 | 'cflags': [ '-Wall', '-Wextra', '-O0', '-g', '-ftrapv' ], 16 | 'msvs_settings': { 17 | 'VCCLCompilerTool': { 18 | 'RuntimeLibrary': 1, # static debug 19 | }, 20 | }, 21 | }, 22 | 'Release': { 23 | 'defines': [ 'NDEBUG' ], 24 | 'cflags': [ '-Wall', '-Wextra', '-O3' ], 25 | 'msvs_settings': { 26 | 'VCCLCompilerTool': { 27 | 'RuntimeLibrary': 0, # static release 28 | }, 29 | }, 30 | } 31 | }, 32 | 'msvs_settings': { 33 | 'VCCLCompilerTool': { 34 | }, 35 | 'VCLibrarianTool': { 36 | }, 37 | 'VCLinkerTool': { 38 | 'GenerateDebugInformation': 'true', 39 | }, 40 | }, 41 | 'conditions': [ 42 | ['OS == "win"', { 43 | 'defines': [ 44 | 'WIN32' 45 | ], 46 | }] 47 | ], 48 | }, 49 | 50 | 'targets': [ 51 | { 52 | 'target_name': 'http_parser', 53 | 'type': 'static_library', 54 | 'include_dirs': [ '.' ], 55 | 'direct_dependent_settings': { 56 | 'defines': [ 'HTTP_PARSER_STRICT=0' ], 57 | 'include_dirs': [ '.' ], 58 | }, 59 | 'defines': [ 'HTTP_PARSER_STRICT=0' ], 60 | 'sources': [ './http_parser.c', ], 61 | 'conditions': [ 62 | ['OS=="win"', { 63 | 'msvs_settings': { 64 | 'VCCLCompilerTool': { 65 | # Compile as C++. http_parser.c is actually C99, but C++ is 66 | # close enough in this case. 67 | 'CompileAs': 2, 68 | }, 69 | }, 70 | }] 71 | ], 72 | }, 73 | 74 | { 75 | 'target_name': 'http_parser_strict', 76 | 'type': 'static_library', 77 | 'include_dirs': [ '.' ], 78 | 'direct_dependent_settings': { 79 | 'defines': [ 'HTTP_PARSER_STRICT=1' ], 80 | 'include_dirs': [ '.' ], 81 | }, 82 | 'defines': [ 'HTTP_PARSER_STRICT=1' ], 83 | 'sources': [ './http_parser.c', ], 84 | 'conditions': [ 85 | ['OS=="win"', { 86 | 'msvs_settings': { 87 | 'VCCLCompilerTool': { 88 | # Compile as C++. http_parser.c is actually C99, but C++ is 89 | # close enough in this case. 90 | 'CompileAs': 2, 91 | }, 92 | }, 93 | }] 94 | ], 95 | }, 96 | 97 | { 98 | 'target_name': 'test-nonstrict', 99 | 'type': 'executable', 100 | 'dependencies': [ 'http_parser' ], 101 | 'sources': [ 'test.c' ] 102 | }, 103 | 104 | { 105 | 'target_name': 'test-strict', 106 | 'type': 'executable', 107 | 'dependencies': [ 'http_parser_strict' ], 108 | 'sources': [ 'test.c' ] 109 | } 110 | ] 111 | } 112 | --------------------------------------------------------------------------------