pax_global_header00006660000000000000000000000064136764532040014524gustar00rootroot0000000000000052 comment=a89596ce224438c136ef0336a81c51262cad9cd3 libcork-1.0.0-rc3/000077500000000000000000000000001367645320400136345ustar00rootroot00000000000000libcork-1.0.0-rc3/.buzzy/000077500000000000000000000000001367645320400150755ustar00rootroot00000000000000libcork-1.0.0-rc3/.buzzy/links.yaml000066400000000000000000000000531367645320400170770ustar00rootroot00000000000000- git://github.com/dcreager/buzzy-core.git libcork-1.0.0-rc3/.buzzy/package.yaml000066400000000000000000000001211367645320400173460ustar00rootroot00000000000000name: libcork build_dependencies: - pkg-config - check >= 0.9.4 license: BSD libcork-1.0.0-rc3/.clang-format000066400000000000000000000034541367645320400162150ustar00rootroot00000000000000Language: Cpp AlignAfterOpenBracket: Align AlignConsecutiveAssignments: false AlignConsecutiveDeclarations: false AlignEscapedNewlines: Right AlignOperands: true AlignTrailingComments: true AllowAllParametersOfDeclarationOnNextLine: true AllowShortBlocksOnASingleLine: false AllowShortCaseLabelsOnASingleLine: false AllowShortFunctionsOnASingleLine: Inline AllowShortIfStatementsOnASingleLine: false AllowShortLoopsOnASingleLine: false AlwaysBreakAfterDefinitionReturnType: TopLevel AlwaysBreakAfterReturnType: TopLevel AlwaysBreakBeforeMultilineStrings: true AlwaysBreakTemplateDeclarations: true BinPackArguments: true BinPackParameters: true BreakBeforeBinaryOperators: None BreakBeforeBraces: WebKit BreakBeforeTernaryOperators: false BreakStringLiterals: false ColumnLimit: 80 ContinuationIndentWidth: 8 ConstructorInitializerAllOnOneLineOrOnePerLine: true DerivePointerAlignment: false DisableFormat: false ExperimentalAutoDetectBinPacking: false IncludeCategories: - Regex: '^<.*\.h>' Priority: 1 - Regex: '^<.*' Priority: 2 - Regex: '.*' Priority: 3 IncludeIsMainRegex: '([-_](test|unittest))?$' IndentCaseLabels: true IndentWidth: 4 IndentWrappedFunctionNames: false KeepEmptyLinesAtTheStartOfBlocks: false MacroBlockBegin: '' MacroBlockEnd: '' MaxEmptyLinesToKeep: 2 NamespaceIndentation: None PenaltyBreakBeforeFirstCallParameter: 1 PenaltyBreakComment: 300 PenaltyBreakString: 1000 PenaltyExcessCharacter: 1000000 PenaltyReturnTypeOnItsOwnLine: 0 PointerAlignment: Left ReflowComments: false SortIncludes: true SpaceAfterCStyleCast: true SpaceBeforeAssignmentOperators: true SpaceBeforeParens: ControlStatements SpaceInEmptyParentheses: false SpacesBeforeTrailingComments: 2 SpacesInCStyleCastParentheses: false SpacesInParentheses: false SpacesInSquareBrackets: false TabWidth: 8 UseTab: Never libcork-1.0.0-rc3/.github/000077500000000000000000000000001367645320400151745ustar00rootroot00000000000000libcork-1.0.0-rc3/.github/workflows/000077500000000000000000000000001367645320400172315ustar00rootroot00000000000000libcork-1.0.0-rc3/.github/workflows/ci.yml000066400000000000000000000015451367645320400203540ustar00rootroot00000000000000name: Continuous integration on: push: branches: - master pull_request: schedule: - cron: "0 0 1,15 * *" jobs: test: runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest] arch: [amd64, i386] compiler: [clang, gcc] flavor: - autotools - cmake - shared-cmake - fallback-u128 - cmake-from-dist env: ARCH: ${{ matrix.arch }} COMPILER: ${{ matrix.compiler }} FLAVOR: ${{ matrix.flavor }} OS_NAME: ${{ matrix.os }} steps: - name: Checkout code uses: actions/checkout@v2 - name: Fetch everything (for automatic version detection) run: git fetch --prune --unshallow - name: Install dependencies run: scripts/install - name: Test harness run: scripts/test libcork-1.0.0-rc3/.gitignore000066400000000000000000000011151367645320400156220ustar00rootroot00000000000000# autotools stuff /aclocal.m4 /autom4te.cache/ /build-aux/compile /build-aux/config.guess /build-aux/config.sub /build-aux/depcomp /build-aux/install-sh /build-aux/ltmain.sh /build-aux/missing /build-aux/test-driver /config.h.in /configure /Makefile.in /m4/libtool.m4 /m4/lt*.m4 # Dist tarballs libcork-*.tar.xz # In-source build artefacts *.a *.o *.la *.lo .deps .dirstamp .libs/ /config.h /config.log /config.status /cork-* /libcork.la /libtool /Makefile /stamp-h1 test*.log test*.trs /tests/test-* !/tests/test-*.c # Common out-of-source build locations /.build* /build* !/build-aux libcork-1.0.0-rc3/AUTHORS000066400000000000000000000007341367645320400147100ustar00rootroot00000000000000# This is the list of libcork authors for copyright purposes. # # This does not necessarily list everyone who has contributed code, since in # some cases, their employer may be the copyright holder. To see the full list # of contributors, see the revision history in source control. DDoSolitary Google Inc. Jan Weiß Nathan French Redjack, LLC Roger Shimizu Toby DiPasquale libcork-1.0.0-rc3/CMakeLists.txt000066400000000000000000000074341367645320400164040ustar00rootroot00000000000000# -*- coding: utf-8 -*- # ---------------------------------------------------------------------- # Copyright © 2011, libcork authors # Please see the COPYING file in this distribution for license details. # ---------------------------------------------------------------------- cmake_minimum_required(VERSION 2.6) set(PROJECT_NAME libcork) set(RELEASE_DATE 2015-09-03) project(${PROJECT_NAME}) enable_testing() set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") find_package(ParseArguments) find_package(Prereqs) find_package(CTargets) #----------------------------------------------------------------------- # Retrieve the current version number execute_process( COMMAND ${CMAKE_SOURCE_DIR}/build-aux/calculate version ${CMAKE_SOURCE_DIR} .version-stamp WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} RESULT_VARIABLE VERSION_RESULT OUTPUT_VARIABLE VERSION OUTPUT_STRIP_TRAILING_WHITESPACE ) if(VERSION_RESULT) message(FATAL_ERROR "Cannot determine version number: " ${VERSION_RESULT}) endif(VERSION_RESULT) message(STATUS "Current version: " ${VERSION}) string(REGEX REPLACE "-.*" "-dev" BASE_VERSION "${VERSION}") if(BASE_VERSION MATCHES "^([0-9]+)\\.([0-9]+)\\.([0-9]+)(-dev)?$") set(CORK_VERSION_MAJOR "${CMAKE_MATCH_1}") set(CORK_VERSION_MINOR "${CMAKE_MATCH_2}") set(CORK_VERSION_PATCH "${CMAKE_MATCH_3}") else(BASE_VERSION MATCHES "^([0-9]+)\\.([0-9]+)\\.([0-9]+)(-dev)?$") message(FATAL_ERROR "Invalid version number: ${VERSION}") endif(BASE_VERSION MATCHES "^([0-9]+)\\.([0-9]+)\\.([0-9]+)(-dev)?$") set(CORK_VERSION "${VERSION}") execute_process( COMMAND ${CMAKE_SOURCE_DIR}/build-aux/calculate commit ${CMAKE_SOURCE_DIR} .commit-stamp WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} RESULT_VARIABLE GIT_SHA1_RESULT OUTPUT_VARIABLE CORK_GIT_SHA1 OUTPUT_STRIP_TRAILING_WHITESPACE ) if(GIT_SHA1_RESULT) message(FATAL_ERROR "Cannot determine git commit: " ${GIT_SHA1_RESULT}) endif(GIT_SHA1_RESULT) message(STATUS "Current revision: " ${CORK_GIT_SHA1}) include(GNUInstallDirs) #----------------------------------------------------------------------- # Set some options if(APPLE) if (NOT CMAKE_INSTALL_NAME_DIR) set(CMAKE_INSTALL_NAME_DIR "${CMAKE_INSTALL_PREFIX}/lib") endif (NOT CMAKE_INSTALL_NAME_DIR) endif(APPLE) if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel." FORCE) endif(NOT CMAKE_BUILD_TYPE) set(ENABLE_SHARED YES CACHE BOOL "Whether to build a shared library") set(ENABLE_SHARED_EXECUTABLES NO CACHE BOOL "Whether to link executables using shared libraries") set(ENABLE_SHARED_TESTS NO CACHE BOOL "Whether to link test cases using shared libraries") set(ENABLE_STATIC YES CACHE BOOL "Whether to build a static library") if(NOT CMAKE_INSTALL_LIBDIR) set(CMAKE_INSTALL_LIBDIR lib CACHE STRING "The base name of the installation directory for libraries") endif(NOT CMAKE_INSTALL_LIBDIR) if(CMAKE_C_COMPILER_ID STREQUAL "GNU") add_definitions(-Wall -Werror) elseif(CMAKE_C_COMPILER_ID STREQUAL "Clang") add_definitions(-Wall -Werror) elseif(CMAKE_C_COMPILER_ID STREQUAL "Intel") add_definitions(-Wall -Werror) endif(CMAKE_C_COMPILER_ID STREQUAL "GNU") #----------------------------------------------------------------------- # Check for prerequisite libraries find_package(Threads) set(THREADS_LDFLAGS "${CMAKE_THREAD_LIBS_INIT}") set(THREADS_STATIC_LDFLAGS "${CMAKE_THREAD_LIBS_INIT}") set(PTHREAD_LIBS "${CMAKE_THREAD_LIBS_INIT}") #----------------------------------------------------------------------- # Include our subdirectories add_subdirectory(include) add_subdirectory(share) add_subdirectory(src) add_subdirectory(tests) add_subdirectory(docs/old) libcork-1.0.0-rc3/COPYING000066400000000000000000000027641367645320400147000ustar00rootroot00000000000000Copyright © 2011-2017, libcork authors All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: • Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. • Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. • Neither the names of the libcork authors nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. libcork-1.0.0-rc3/INSTALL000066400000000000000000000033221367645320400146650ustar00rootroot00000000000000Installation instructions ========================= The libcork library is written in ANSI C. It uses cmake as its build manager. Prerequisite libraries ---------------------- To build libcork, you need the following libraries installed on your system: * pkg-config * check (http://libcheck.github.io/check) Building from source -------------------- The libcork library uses cmake as its build manager. In most cases, you should be able to build the source code using the following: $ mkdir build $ cd build $ cmake .. -DCMAKE_INSTALL_PREFIX=$PREFIX $ make $ make test $ make install You might have to run the last command using sudo, if you need administrative privileges to write to the $PREFIX directory. Shared and static libraries --------------------------- You can use the `ENABLE_SHARED` and `ENABLE_STATIC` cmake options to control whether or not to install shared and static versions of libcork, respectively. By default, both are installed. You can use the `ENABLE_SHARED_EXECUTABLE` cmake option to control whether the programs that we install link with libcork's shared library or static library. (Note that this can override the value of `ENABLE_SHARED`; if you ask for the programs to link with the shared library, then we have to install that shared library for the programs to work properly.) By default, we link with libcork statically. So, as an example, if you wanted to only build and install the shared library, and to have our programs link with that shared library, you'd replace the cmake command with the following: $ cmake .. \ -DCMAKE_INSTALL_PREFIX=$PREFIX \ -DENABLE_SHARED=YES \ -DENABLE_STATIC=NO \ -DENABLE_SHARED_EXECUTABLES=YES libcork-1.0.0-rc3/Makefile.am000066400000000000000000000300561367645320400156740ustar00rootroot00000000000000# -*- coding: utf-8 -*- # ------------------------------------------------------------------------------ # Copyright © 2018, libcork authors # Please see the COPYING file in this distribution for license details. # ------------------------------------------------------------------------------ #------------------------------------------------------------------------------- # Dist hooks dist-hook: dist-check-git-version dist-stamps #------------------------------------------------------------------------------- # .commit-stamp and .version-stamp dist-check-git-version: : # Verify that the version that configure.ac recorded matches the : # current calculated version. @git_ver=`$(top_srcdir)/build-aux/calculate version $(top_srcdir) $(top_srcdir)/.version-stamp`; \ if test "x$${git_ver}" != "x$(PACKAGE_VERSION)"; then \ echo "ERROR: PACKAGE_VERSION and 'git describe' version do not match:"; \ echo " current 'git describe' version: $${git_ver}"; \ echo " current PACKAGE_VERSION: $(PACKAGE_VERSION)"; \ echo "Update PACKAGE_VERSION by running $(top_srcdir)/autogen.sh."; \ rm -rf "$(top_srcdir)/autom4te.cache"; \ exit 1; \ fi dist-stamps: @: # Generate version stamps for dist tarball. @echo $(CORK_GIT_SHA1) > $(distdir)/.commit-stamp @echo $(VERSION) > $(distdir)/.version-stamp #------------------------------------------------------------------------------- # Preliminaries ACLOCAL_AMFLAGS = -I m4 AM_CPPFLAGS = \ -I$(srcdir)/include \ -I$(builddir)/include lib_LTLIBRARIES = libcork.la bin_PROGRAMS = cork-hash check_PROGRAMS = CLEANFILES = EXTRA_DIST = EXTRA_PROGRAMS = TESTS = #------------------------------------------------------------------------------- # Extras EXTRA_DIST += \ CMakeLists.txt \ README.markdown \ build-aux/calculate \ cmake \ docs \ extras \ share \ include/CMakeLists.txt \ src/CMakeLists.txt \ src/libcork.pc.in \ tests/CMakeLists.txt #------------------------------------------------------------------------------- # libcork libcork_includedir = $(includedir)/libcork cli_includedir = $(includedir)/libcork/cli config_includedir = $(includedir)/libcork/config core_includedir = $(includedir)/libcork/core ds_includedir = $(includedir)/libcork/ds helpers_includedir = $(includedir)/libcork/helpers os_includedir = $(includedir)/libcork/os threads_includedir = $(includedir)/libcork/threads libcork_include_HEADERS = \ include/libcork/cli.h \ include/libcork/config.h \ include/libcork/core.h \ include/libcork/ds.h \ include/libcork/os.h \ include/libcork/threads.h cli_include_HEADERS = \ include/libcork/cli/commands.h config_include_HEADERS = \ include/libcork/config/arch.h \ include/libcork/config/gcc.h \ include/libcork/config/macosx.h \ include/libcork/config/bsd.h \ include/libcork/config/linux.h \ include/libcork/config/config.h \ include/libcork/config/version.h core_include_HEADERS = \ include/libcork/core/hash.h \ include/libcork/core/error.h \ include/libcork/core/allocator.h \ include/libcork/core/u128.h \ include/libcork/core/attributes.h \ include/libcork/core/byte-order.h \ include/libcork/core/callbacks.h \ include/libcork/core/timestamp.h \ include/libcork/core/gc.h \ include/libcork/core/net-addresses.h \ include/libcork/core/types.h \ include/libcork/core/id.h \ include/libcork/core/api.h \ include/libcork/core/mempool.h ds_include_HEADERS = \ include/libcork/ds/hash-table.h \ include/libcork/ds/array.h \ include/libcork/ds/managed-buffer.h \ include/libcork/ds/ring-buffer.h \ include/libcork/ds/stream.h \ include/libcork/ds/bitset.h \ include/libcork/ds/buffer.h \ include/libcork/ds/slice.h \ include/libcork/ds/dllist.h helpers_include_HEADERS = \ include/libcork/helpers/posix.h \ include/libcork/helpers/errors.h \ include/libcork/helpers/gc.h os_include_HEADERS = \ include/libcork/os/files.h \ include/libcork/os/process.h \ include/libcork/os/subprocess.h threads_include_HEADERS = \ include/libcork/threads/atomics.h \ include/libcork/threads/basics.h libcork_la_SOURCES = \ src/libcork/cli/commands.c \ src/libcork/core/allocator.c \ src/libcork/core/error.c \ src/libcork/core/gc.c \ src/libcork/core/hash.c \ src/libcork/core/id.c \ src/libcork/core/ip-address.c \ src/libcork/core/mempool.c \ src/libcork/core/timestamp.c \ src/libcork/core/u128.c \ src/libcork/core/version.c \ src/libcork/ds/array.c \ src/libcork/ds/bitset.c \ src/libcork/ds/buffer.c \ src/libcork/ds/dllist.c \ src/libcork/ds/file-stream.c \ src/libcork/ds/hash-table.c \ src/libcork/ds/managed-buffer.c \ src/libcork/ds/ring-buffer.c \ src/libcork/ds/slice.c \ src/libcork/ds/stream.c \ src/libcork/posix/directory-walker.c \ src/libcork/posix/env.c \ src/libcork/posix/exec.c \ src/libcork/posix/files.c \ src/libcork/posix/process.c \ src/libcork/posix/subprocess.c \ src/libcork/pthreads/thread.c pkgconfig_DATA = src/libcork.pc libcork_la_CPPFLAGS = $(AM_CPPFLAGS) $(CPPFLAGS) -DCORK_API=CORK_EXPORT libcork_la_CFLAGS = $(AM_CFLAGS) $(CFLAGS) -DCORK_API=CORK_EXPORT libcork_la_LDFLAGS = $(AM_LDFLAGS) $(LDFLAGS) -version-info 17:0:1 #----------------------------------------------------------------------- # Utility commands cork_hash_SOURCES = src/cork-hash/cork-hash.c cork_hash_LDADD = libcork.la #----------------------------------------------------------------------- # Tests # Standalone tests standalone_tests = \ test-array \ test-bitset \ test-buffer \ test-core \ test-dllist \ test-files \ test-gc \ test-hash-table \ test-managed-buffer \ test-mempool \ test-ring-buffer \ test-slice \ test-subprocess \ test-threads \ test-u128 EXTRA_DIST += tests/create-u128-test-cases.py EXTRA_PROGRAMS += $(standalone_tests) if RUN_TESTS check_PROGRAMS += $(standalone_tests) TESTS += $(standalone_tests) tests_LDADD_ = libcork.la $(CHECK_LIBS) tests_LDFLAGS_ = $(LDFLAGS) -static test_array_SOURCES = tests/test-array.c tests/helpers.h test_array_LDADD = $(tests_LDADD_) test_array_LDFLAGS = $(tests_LDFLAGS_) test_bitset_SOURCES = tests/test-bitset.c tests/helpers.h test_bitset_LDADD = $(tests_LDADD_) test_bitset_LDFLAGS = $(tests_LDFLAGS_) test_buffer_SOURCES = tests/test-buffer.c tests/helpers.h test_buffer_LDADD = $(tests_LDADD_) test_buffer_LDFLAGS = $(tests_LDFLAGS_) test_core_SOURCES = tests/test-core.c tests/helpers.h test_core_LDADD = $(tests_LDADD_) test_core_LDFLAGS = $(tests_LDFLAGS_) test_dllist_SOURCES = tests/test-dllist.c tests/helpers.h test_dllist_LDADD = $(tests_LDADD_) test_dllist_LDFLAGS = $(tests_LDFLAGS_) test_files_SOURCES = tests/test-files.c tests/helpers.h test_files_LDADD = $(tests_LDADD_) test_files_LDFLAGS = $(tests_LDFLAGS_) test_gc_SOURCES = tests/test-gc.c tests/helpers.h test_gc_LDADD = $(tests_LDADD_) test_gc_LDFLAGS = $(tests_LDFLAGS_) test_hash_table_SOURCES = tests/test-hash-table.c tests/helpers.h test_hash_table_LDADD = $(tests_LDADD_) test_hash_table_LDFLAGS = $(tests_LDFLAGS_) test_managed_buffer_SOURCES = tests/test-managed-buffer.c tests/helpers.h test_managed_buffer_LDADD = $(tests_LDADD_) test_managed_buffer_LDFLAGS = $(tests_LDFLAGS_) test_mempool_SOURCES = tests/test-mempool.c tests/helpers.h test_mempool_LDADD = $(tests_LDADD_) test_mempool_LDFLAGS = $(tests_LDFLAGS_) test_ring_buffer_SOURCES = tests/test-ring-buffer.c tests/helpers.h test_ring_buffer_LDADD = $(tests_LDADD_) test_ring_buffer_LDFLAGS = $(tests_LDFLAGS_) test_slice_SOURCES = tests/test-slice.c tests/helpers.h test_slice_LDADD = $(tests_LDADD_) test_slice_LDFLAGS = $(tests_LDFLAGS_) test_subprocess_SOURCES = tests/test-subprocess.c tests/helpers.h test_subprocess_LDADD = $(tests_LDADD_) test_subprocess_LDFLAGS = $(tests_LDFLAGS_) test_threads_SOURCES = tests/test-threads.c tests/helpers.h test_threads_LDADD = $(tests_LDADD_) test_threads_LDFLAGS = $(tests_LDFLAGS_) test_u128_SOURCES = tests/test-u128.c tests/helpers.h test_u128_CPPFLAGS = -I$(builddir)/tests $(AM_CPPFLAGS) test_u128_LDADD = $(tests_LDADD_) test_u128_LDFLAGS = $(tests_LDFLAGS_) u128_tests = \ tests/u128-tests-eq.c.in \ tests/u128-tests-ne.c.in \ tests/u128-tests-lt.c.in \ tests/u128-tests-le.c.in \ tests/u128-tests-gt.c.in \ tests/u128-tests-ge.c.in \ tests/u128-tests-shl.c.in \ tests/u128-tests-shr.c.in \ tests/u128-tests-add.c.in \ tests/u128-tests-sub.c.in CLEANFILES += $(u128_tests) tests/u128-tests-eq.c.in: tests/create-u128-test-cases.py $(AM_V_GEN) $(PYTHON) $< eq $@ tests/u128-tests-ne.c.in: tests/create-u128-test-cases.py $(AM_V_GEN) $(PYTHON) $< ne $@ tests/u128-tests-lt.c.in: tests/create-u128-test-cases.py $(AM_V_GEN) $(PYTHON) $< lt $@ tests/u128-tests-le.c.in: tests/create-u128-test-cases.py $(AM_V_GEN) $(PYTHON) $< le $@ tests/u128-tests-gt.c.in: tests/create-u128-test-cases.py $(AM_V_GEN) $(PYTHON) $< gt $@ tests/u128-tests-ge.c.in: tests/create-u128-test-cases.py $(AM_V_GEN) $(PYTHON) $< ge $@ tests/u128-tests-shl.c.in: tests/create-u128-test-cases.py $(AM_V_GEN) $(PYTHON) $< shl $@ tests/u128-tests-shr.c.in: tests/create-u128-test-cases.py $(AM_V_GEN) $(PYTHON) $< shr $@ tests/u128-tests-add.c.in: tests/create-u128-test-cases.py $(AM_V_GEN) $(PYTHON) $< add $@ tests/u128-tests-sub.c.in: tests/create-u128-test-cases.py $(AM_V_GEN) $(PYTHON) $< sub $@ tests/test-u128.c: $(u128_tests) endif RUN_TESTS # Cram tests TEST_EXTENSIONS = .t EXTRA_DIST += \ tests/ccram \ tests/cram.py \ tests/test-input.txt cram_tests = \ tests/cork-hash.t \ tests/cork-initializer.t \ tests/cork-test/cleanup.t \ tests/cork-test/directory-watcher.t \ tests/cork-test/help1-c1-s1.t \ tests/cork-test/help1-c1-s2.t \ tests/cork-test/help1-c1.t \ tests/cork-test/help1-c2.t \ tests/cork-test/help1-root.t \ tests/cork-test/help2-c1-s1.t \ tests/cork-test/help2-c1-s2.t \ tests/cork-test/help2-c1.t \ tests/cork-test/help2-c2.t \ tests/cork-test/help2-root.t \ tests/cork-test/help3-c1-s1.t \ tests/cork-test/help3-c1-s2.t \ tests/cork-test/help3-c1.t \ tests/cork-test/help3-c2.t \ tests/cork-test/help3-root.t \ tests/cork-test/no-command-c1.t \ tests/cork-test/no-command-root.t \ tests/cork-test/run-c1-s1-f.t \ tests/cork-test/run-c1-s1-f-t.t \ tests/cork-test/run-c1-s1.t \ tests/cork-test/run-c1-s1-test.t \ tests/cork-test/run-c1-s1-t.t \ tests/cork-test/run-c1-s2-file.t \ tests/cork-test/run-c1-s2-f.t \ tests/cork-test/run-c1-s2.t \ tests/cork-test/run-c2.t \ tests/cork-test/run-find-01.t \ tests/cork-test/run-find-all-01.t \ tests/cork-test/run-mkdir-01.t \ tests/cork-test/run-paths-01.t \ tests/cork-test/run-pwd-01.t \ tests/cork-test/run-rm-01.t \ tests/cork-test/run-sub-01.t \ tests/cork-test/run-sub-02.t \ tests/cork-test/run-sub-03.t \ tests/cork-test/run-sub-04.t \ tests/cork-test/run-sub-05.t \ tests/cork-test/run-sub-06.t EXTRA_DIST += $(cram_tests) if RUN_TESTS TESTS += $(cram_tests) T_LOG_COMPILER = $(srcdir)/tests/ccram AM_T_LOG_FLAGS = \ --python $(PYTHON) \ --root $(srcdir) \ --cram $(srcdir)/tests/cram.py \ --tests check_PROGRAMS += cork-initializer cork_initializer_SOURCES = \ src/cork-initializer/init1.c \ src/cork-initializer/init2.c \ src/cork-initializer/main.c cork_initializer_LDADD = libcork.la check_PROGRAMS += cork-test cork_test_SOURCES = src/cork-test/cork-test.c cork_test_LDADD = libcork.la endif # If you have test failures during distcheck, cram won't be able to print out # the diff, since `make distcheck` causes the unpacked distribution tarball to # be read-only. Uncomment the following to (temporarily) make the unpacked # tests directory writeable, so that you can see what caused the test failure. #distcheck-hook: # chmod -R ug+w $(distdir)/tests # Test harness @VALGRIND_CHECK_RULES@ if !RUN_TESTS check-local: $(error Cannot run test suite without check and Python installed!) endif libcork-1.0.0-rc3/README.markdown000066400000000000000000000070021367645320400163340ustar00rootroot00000000000000# libcork [![Build Status](https://travis-ci.org/dcreager/libcork.svg?branch=master)](https://travis-ci.org/dcreager/libcork) So what is libcork, exactly? It's a “simple, easily embeddable, cross-platform C library”. It falls roughly into the same category as [glib](http://library.gnome.org/devel/glib/) or [APR](http://apr.apache.org/) in the C world; the STL, [POCO](http://pocoproject.org/), or [QtCore](http://qt.nokia.com/) in the C++ world; or the standard libraries of any decent dynamic language. So if libcork has all of these comparables, why a new library? Well, none of the C++ options are really applicable here. And none of the C options work, because one of the main goals is to have the library be highly modular, and useful in resource-constrained systems. Once we describe some of the design decisions that we've made in libcork, you'll hopefully see how this fits into an interesting niche of its own. ## Using libcork There are two primary ways to use libcork in your own software project: as a _shared library_, or _embedded_. When you use libcork as a shared library, you install it just like you would any other C library. We happen to use CMake as our build system, so you follow the usual CMake recipe to install the library. (See the [INSTALL](INSTALL) file for details.) All of the libcork code is contained within a single shared library (called libcork.so, libcork.dylib, or cork.dll, depending on the system). We also install a pkg-config file that makes it easy to add the appropriate compiler flags in your own build scripts. So, you use pkg-config to find libcork's include and library files, link with libcork, and you're good to go. The alternative is to embed libcork into your own software project's directory structure. In this case, your build scripts compile the libcork source along with the rest of your code. This has some advantages for resource-constrained systems, since (assuming your compiler and linker are any good), you only include the libcork routines that you actually use. And if your toolchain supports link-time optimization, the libcork routines can be optimized into the rest of your code. Which should you use? That's really up to you. Linking against the shared library adds a runtime dependency, but gives you the usual benefits of shared libraries: the library in memory is shared across each program that uses it; you can install a single bug-fix update and all libcork programs automatically take advantage of the new release; etc. The embedding option is great if you really need to make your library as small as possible, or if you don't want to add that runtime dependency. ## Design decisions Note that having libcork be **easily** embeddable has some ramifications on the library's design. In particular, we don't want to make any assumptions about which build system you're embedding libcork into. We happen to use CMake, but you might be using autotools, waf, scons, or any number of others. Most cross-platform libraries follow the autotools model of performing some checks at compile time (maybe during a separate “configure” phase, maybe not) to choose the right API implementation for the current platform. Since we can't assume a build system, we have to take a different approach, and do as many checks as we can using the C preprocessor. Any check that we can't make in the preprocessor has to be driven by a C preprocessor macro definition, which you (the libcork user) are responsible for checking for and defining. So we need to have as few of those as possible. libcork-1.0.0-rc3/autogen.sh000077500000000000000000000010201367645320400156260ustar00rootroot00000000000000#!/bin/sh # ------------------------------------------------------------------------------ # Copyright © 2020, libcork authors. # Please see the COPYING file in this distribution for license details. # ------------------------------------------------------------------------------ set -e cd "$(dirname "$0")" AUTORECONF=${AUTORECONF:-autoreconf} ACLOCAL=${ACLOCAL:-aclocal} AUTOCONF=${AUTOCONF:-autoconf} AUTOHEADER=${AUTOHEADER:-autoheader} AUTOMAKE=${AUTOMAKE:-automake} "${AUTORECONF}" --verbose --install --force -I m4 libcork-1.0.0-rc3/build-aux/000077500000000000000000000000001367645320400155265ustar00rootroot00000000000000libcork-1.0.0-rc3/build-aux/calculate000077500000000000000000000027221367645320400174140ustar00rootroot00000000000000#!/bin/sh # ---------------------------------------------------------------------- # Copyright © 2011, libcork authors # All rights reserved. # # Please see the COPYING file in this distribution for license details. # ---------------------------------------------------------------------- # Usage: # calculate version|commit [top_srcdir] [path to stamp file] # # Calculates the current version number. When run from a distribution tarball, # we get the version number from the .version-stamp file (that our `make dist` # target ensures that it creates). When run from a local git repository, we get # the version number via `git describe`. set -e WHAT="$1" case "$WHAT" in version) ;; commit) ;; *) echo "Unknown option $WHAT" >&2; exit 1;; esac top_srcdir="${2-.}" export GIT_DIR="${top_srcdir}/.git" # First try the stamp file STAMP_FILE="$3" if [ -f "$STAMP_FILE" ]; then version=$(cat "$STAMP_FILE") if [ -z "$version" ]; then echo "Invalid stamp file" >&2 exit 1 fi printf "%s" "$version" exit 0 fi # Fall back on `git describe` case "$WHAT" in version) closest_tag=$(git describe --abbrev=0) full_version=$(git describe --abbrev=7 --dirty) if test "$closest_tag" = "$full_version"; then version="$closest_tag" else version="$closest_tag-git" fi ;; commit) version=$(git rev-parse HEAD);; esac if [ -z "$version" ]; then echo "Cannot find the version from git" >&2 exit 1 fi printf "%s" "$version" libcork-1.0.0-rc3/cmake/000077500000000000000000000000001367645320400147145ustar00rootroot00000000000000libcork-1.0.0-rc3/cmake/FindCTargets.cmake000066400000000000000000000200441367645320400202330ustar00rootroot00000000000000# -*- coding: utf-8 -*- # ---------------------------------------------------------------------- # Copyright © 2015, libcork authors # Please see the COPYING file in this distribution for license details. # ---------------------------------------------------------------------- #----------------------------------------------------------------------- # Configuration options that control all of the below set(ENABLE_SHARED YES CACHE BOOL "Whether to build a shared library") set(ENABLE_SHARED_EXECUTABLES YES CACHE BOOL "Whether to link executables using shared libraries") set(ENABLE_STATIC YES CACHE BOOL "Whether to build a static library") #----------------------------------------------------------------------- # Library, with options to build both shared and static versions function(target_add_shared_libraries TARGET_NAME LIBRARIES LOCAL_LIBRARIES) foreach(lib ${LIBRARIES}) string(REPLACE "-" "_" lib ${lib}) string(TOUPPER ${lib} upperlib) target_link_libraries( ${TARGET_NAME} ${${upperlib}_LDFLAGS} ) endforeach(lib) foreach(lib ${LOCAL_LIBRARIES}) target_link_libraries(${TARGET_NAME} ${lib}-shared) endforeach(lib) endfunction(target_add_shared_libraries) function(target_add_static_libraries TARGET_NAME LIBRARIES LOCAL_LIBRARIES) foreach(lib ${LIBRARIES}) string(REPLACE "-" "_" lib ${lib}) string(TOUPPER ${lib} upperlib) target_link_libraries( ${TARGET_NAME} ${${upperlib}_STATIC_LDFLAGS} ) endforeach(lib) foreach(lib ${LOCAL_LIBRARIES}) target_link_libraries(${TARGET_NAME} ${lib}-static) endforeach(lib) endfunction(target_add_static_libraries) set_property(GLOBAL PROPERTY ALL_LOCAL_LIBRARIES "") function(add_c_library __TARGET_NAME) set(options) set(one_args OUTPUT_NAME PKGCONFIG_NAME VERSION_INFO) set(multi_args LIBRARIES LOCAL_LIBRARIES SOURCES) cmake_parse_arguments(_ "${options}" "${one_args}" "${multi_args}" ${ARGN}) if (__VERSION_INFO MATCHES "^([0-9]+):([0-9]+):([0-9]+)(-dev)?$") set(__VERSION_CURRENT "${CMAKE_MATCH_1}") set(__VERSION_REVISION "${CMAKE_MATCH_2}") set(__VERSION_AGE "${CMAKE_MATCH_3}") else (__VERSION_INFO MATCHES "^([0-9]+):([0-9]+):([0-9]+)(-dev)?$") message(FATAL_ERROR "Invalid library version info: ${__VERSION_INFO}") endif (__VERSION_INFO MATCHES "^([0-9]+):([0-9]+):([0-9]+)(-dev)?$") # Mimic libtool's behavior in calculating SONAME and VERSION from # version-info. # http://git.savannah.gnu.org/cgit/libtool.git/tree/build-aux/ltmain.in?id=722b6af0fad19b3d9f21924ae5aa6dfae5957378#n7042 math(EXPR __SOVERSION "${__VERSION_CURRENT} - ${__VERSION_AGE}") set(__VERSION "${__SOVERSION}.${__VERSION_AGE}.${__VERSION_REVISION}") get_property(ALL_LOCAL_LIBRARIES GLOBAL PROPERTY ALL_LOCAL_LIBRARIES) list(APPEND ALL_LOCAL_LIBRARIES ${__TARGET_NAME}) set_property(GLOBAL PROPERTY ALL_LOCAL_LIBRARIES "${ALL_LOCAL_LIBRARIES}") if (ENABLE_SHARED OR ENABLE_SHARED_EXECUTABLES) add_library(${__TARGET_NAME}-shared SHARED ${__SOURCES}) set_target_properties( ${__TARGET_NAME}-shared PROPERTIES OUTPUT_NAME ${__OUTPUT_NAME} CLEAN_DIRECT_OUTPUT 1 VERSION ${__VERSION} SOVERSION ${__SOVERSION} ) if (CMAKE_VERSION VERSION_GREATER "2.8.11") target_include_directories( ${__TARGET_NAME}-shared PUBLIC ${CMAKE_SOURCE_DIR}/include ${CMAKE_BINARY_DIR}/include ) else (CMAKE_VERSION VERSION_GREATER "2.8.11") include_directories( ${CMAKE_SOURCE_DIR}/include ${CMAKE_BINARY_DIR}/include ) endif (CMAKE_VERSION VERSION_GREATER "2.8.11") target_add_shared_libraries( ${__TARGET_NAME}-shared "${__LIBRARIES}" "${__LOCAL_LIBRARIES}" ) # We have to install the shared library if the user asked us to, or if # the user asked us to link our programs with the shared library. install(TARGETS ${__TARGET_NAME}-shared LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) endif (ENABLE_SHARED OR ENABLE_SHARED_EXECUTABLES) if (ENABLE_STATIC OR NOT ENABLE_SHARED_EXECUTABLES) add_library(${__TARGET_NAME}-static STATIC ${__SOURCES}) set_target_properties( ${__TARGET_NAME}-static PROPERTIES OUTPUT_NAME ${__OUTPUT_NAME} CLEAN_DIRECT_OUTPUT 1 ) if (CMAKE_VERSION VERSION_GREATER "2.8.11") target_include_directories( ${__TARGET_NAME}-static PUBLIC ${CMAKE_SOURCE_DIR}/include ${CMAKE_BINARY_DIR}/include ) else (CMAKE_VERSION VERSION_GREATER "2.8.11") include_directories( ${CMAKE_SOURCE_DIR}/include ${CMAKE_BINARY_DIR}/include ) endif (CMAKE_VERSION VERSION_GREATER "2.8.11") target_add_static_libraries( ${__TARGET_NAME}-static "${__LIBRARIES}" "${__LOCAL_LIBRARIES}" ) endif (ENABLE_STATIC OR NOT ENABLE_SHARED_EXECUTABLES) if (ENABLE_STATIC) # We DON'T have to install the static library if the user asked us to # link our programs statically. install(TARGETS ${__TARGET_NAME}-static ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) endif (ENABLE_STATIC) set(PACKAGE_TARNAME "${PROJECT_NAME}") set(prefix ${CMAKE_INSTALL_PREFIX}) set(exec_prefix "\${prefix}") set(datarootdir "\${prefix}/share") set(includedir "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}") set(libdir "\${exec_prefix}/${CMAKE_INSTALL_LIBDIR}") string(REPLACE "${CMAKE_INSTALL_DATAROOTDIR}/" "" base_docdir "${CMAKE_INSTALL_DOCDIR}") set(docdir "\${datarootdir}/${base_docdir}") configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/${__PKGCONFIG_NAME}.pc.in ${CMAKE_CURRENT_BINARY_DIR}/${__PKGCONFIG_NAME}.pc @ONLY ) install( FILES ${CMAKE_CURRENT_BINARY_DIR}/${__PKGCONFIG_NAME}.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig ) endfunction(add_c_library) #----------------------------------------------------------------------- # Executable function(add_c_executable __TARGET_NAME) set(options SKIP_INSTALL) set(one_args OUTPUT_NAME) set(multi_args LIBRARIES LOCAL_LIBRARIES SOURCES) cmake_parse_arguments(_ "${options}" "${one_args}" "${multi_args}" ${ARGN}) add_executable(${__TARGET_NAME} ${__SOURCES}) if (CMAKE_VERSION VERSION_GREATER "2.8.11") target_include_directories( ${__TARGET_NAME} PUBLIC ${CMAKE_SOURCE_DIR}/include ${CMAKE_BINARY_DIR}/include ) else (CMAKE_VERSION VERSION_GREATER "2.8.11") include_directories( ${CMAKE_SOURCE_DIR}/include ${CMAKE_BINARY_DIR}/include ) endif (CMAKE_VERSION VERSION_GREATER "2.8.11") if (ENABLE_SHARED_EXECUTABLES) target_add_shared_libraries( ${__TARGET_NAME} "${__LIBRARIES}" "${__LOCAL_LIBRARIES}" ) else (ENABLE_SHARED_EXECUTABLES) target_add_static_libraries( ${__TARGET_NAME} "${__LIBRARIES}" "${__LOCAL_LIBRARIES}" ) endif (ENABLE_SHARED_EXECUTABLES) if (NOT __SKIP_INSTALL) install(TARGETS ${__TARGET_NAME} RUNTIME DESTINATION bin) endif (NOT __SKIP_INSTALL) endfunction(add_c_executable) #----------------------------------------------------------------------- # Test case pkgconfig_prereq(check OPTIONAL) function(add_c_test TEST_NAME) get_property(ALL_LOCAL_LIBRARIES GLOBAL PROPERTY ALL_LOCAL_LIBRARIES) add_c_executable( ${TEST_NAME} SKIP_INSTALL OUTPUT_NAME ${TEST_NAME} SOURCES ${TEST_NAME}.c LIBRARIES check LOCAL_LIBRARIES ${ALL_LOCAL_LIBRARIES} ) add_test(${TEST_NAME} ${TEST_NAME}) endfunction(add_c_test) libcork-1.0.0-rc3/cmake/FindParseArguments.cmake000066400000000000000000000032531367645320400214620ustar00rootroot00000000000000# -*- coding: utf-8 -*- # ---------------------------------------------------------------------- # Copyright © 2015, libcork authors # Please see the COPYING file in this distribution for license details. # ---------------------------------------------------------------------- # CMake 2.8.4 and higher gives us cmake_parse_arguments out of the box. For # earlier versions (RHEL5!) we have to define it ourselves. (The definition # comes from .) if (CMAKE_VERSION VERSION_LESS "2.8.4") MACRO(CMAKE_PARSE_ARGUMENTS prefix arg_names option_names) SET(DEFAULT_ARGS) FOREACH(arg_name ${arg_names}) SET(${prefix}_${arg_name}) ENDFOREACH(arg_name) FOREACH(option ${option_names}) SET(${prefix}_${option} FALSE) ENDFOREACH(option) SET(current_arg_name DEFAULT_ARGS) SET(current_arg_list) FOREACH(arg ${ARGN}) SET(larg_names ${arg_names}) LIST(FIND larg_names "${arg}" is_arg_name) IF (is_arg_name GREATER -1) SET(${prefix}_${current_arg_name} ${current_arg_list}) SET(current_arg_name ${arg}) SET(current_arg_list) ELSE (is_arg_name GREATER -1) SET(loption_names ${option_names}) LIST(FIND loption_names "${arg}" is_option) IF (is_option GREATER -1) SET(${prefix}_${arg} TRUE) ELSE (is_option GREATER -1) SET(current_arg_list ${current_arg_list} ${arg}) ENDIF (is_option GREATER -1) ENDIF (is_arg_name GREATER -1) ENDFOREACH(arg) SET(${prefix}_${current_arg_name} ${current_arg_list}) ENDMACRO(CMAKE_PARSE_ARGUMENTS) else (CMAKE_VERSION VERSION_LESS "2.8.4") include(CMakeParseArguments) endif (CMAKE_VERSION VERSION_LESS "2.8.4") libcork-1.0.0-rc3/cmake/FindPrereqs.cmake000066400000000000000000000056571367645320400201550ustar00rootroot00000000000000# -*- coding: utf-8 -*- # ---------------------------------------------------------------------- # Copyright © 2015, libcork authors # Please see the COPYING file in this distribution for license details. # ---------------------------------------------------------------------- #----------------------------------------------------------------------- # Configuration options that control all of the below set(PKG_CONFIG_PATH CACHE STRING "pkg-config search path") if (PKG_CONFIG_PATH) set(ENV{PKG_CONFIG_PATH} "${PKG_CONFIG_PATH}:$ENV{PKG_CONFIG_PATH}") endif (PKG_CONFIG_PATH) #----------------------------------------------------------------------- # pkg-config prerequisites find_package(PkgConfig) function(pkgconfig_prereq DEP) set(options OPTIONAL) set(one_args) set(multi_args) cmake_parse_arguments(_ "${options}" "${one_args}" "${multi_args}" ${ARGN}) string(REGEX REPLACE "[<>=].*" "" SHORT_NAME "${DEP}") string(REPLACE "-" "_" SHORT_NAME "${SHORT_NAME}") string(TOUPPER ${SHORT_NAME} UPPER_SHORT_NAME) string(TOLOWER ${SHORT_NAME} LOWER_SHORT_NAME) set(USE_CUSTOM_${UPPER_SHORT_NAME} NO CACHE BOOL "Whether you want to provide custom details for ${LOWER_SHORT_NAME}") if (NOT USE_CUSTOM_${UPPER_SHORT_NAME}) set(PKG_CHECK_ARGS) if (NOT __OPTIONAL) list(APPEND PKG_CHECK_ARGS REQUIRED) endif (NOT __OPTIONAL) list(APPEND PKG_CHECK_ARGS ${DEP}) pkg_check_modules(${UPPER_SHORT_NAME} ${PKG_CHECK_ARGS}) endif (NOT USE_CUSTOM_${UPPER_SHORT_NAME}) include_directories(${${UPPER_SHORT_NAME}_INCLUDE_DIRS}) link_directories(${${UPPER_SHORT_NAME}_LIBRARY_DIRS}) endfunction(pkgconfig_prereq) #----------------------------------------------------------------------- # find_library prerequisites function(library_prereq LIB_NAME) set(options OPTIONAL) set(one_args) set(multi_args) cmake_parse_arguments(_ "${options}" "${one_args}" "${multi_args}" ${ARGN}) string(REPLACE "-" "_" SHORT_NAME "${LIB_NAME}") string(TOUPPER ${SHORT_NAME} UPPER_SHORT_NAME) string(TOLOWER ${SHORT_NAME} LOWER_SHORT_NAME) set(USE_CUSTOM_${UPPER_SHORT_NAME} NO CACHE BOOL "Whether you want to provide custom details for ${LOWER_SHORT_NAME}") if (USE_CUSTOM_${UPPER_SHORT_NAME}) include_directories(${${UPPER_SHORT_NAME}_INCLUDE_DIRS}) link_directories(${${UPPER_SHORT_NAME}_LIBRARY_DIRS}) if (NOT ${UPPER_SHORT_NAME}_STATIC_LDFLAGS) set(${UPPER_SHORT_NAME}_STATIC_LDFLAGS ${${UPPER_SHORT_NAME}_LDFLAGS} PARENT_SCOPE) endif (NOT ${UPPER_SHORT_NAME}_STATIC_LDFLAGS) else (USE_CUSTOM_${UPPER_SHORT_NAME}) find_library(${UPPER_SHORT_NAME}_LDFLAGS ${LIB_NAME}) set(${UPPER_SHORT_NAME}_STATIC_LDFLAGS ${${UPPER_SHORT_NAME}_LDFLAGS} PARENT_SCOPE) endif (USE_CUSTOM_${UPPER_SHORT_NAME}) endfunction(library_prereq) libcork-1.0.0-rc3/configure.ac000066400000000000000000000044701367645320400161270ustar00rootroot00000000000000# -*- coding: utf-8 -*- # ------------------------------------------------------------------------------ # Copyright © 2018, libcork authors # Please see the COPYING file in this distribution for license details. # ------------------------------------------------------------------------------ AC_INIT([libcork], m4_esyscmd([build-aux/calculate version . .version-stamp]), [info@libcork.io]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_SRCDIR([src/libcork/core/version.c]) AM_INIT_AUTOMAKE([foreign no-dist-gzip dist-xz subdir-objects]) AM_MAINTAINER_MODE([enable]) # Allow packagers to disable if they want AM_SILENT_RULES([yes]) LT_INIT # Generating version-stamp files AC_PATH_PROG(GIT, [git]) if test -z "$GIT" ; then AC_MSG_FAILURE([cannot find 'git']) fi # Versions AC_PROG_AWK AC_PROG_SED BASE_VERSION=`AS_ECHO([$VERSION]) | sed -e 's/\-.*//'` AC_SUBST(CORK_VERSION_MAJOR, [`AS_ECHO([$BASE_VERSION]) | $AWK -F. '{print $1}'`]) AC_SUBST(CORK_VERSION_MINOR, [`AS_ECHO([$BASE_VERSION]) | $AWK -F. '{print $2}'`]) AC_SUBST(CORK_VERSION_PATCH, [`AS_ECHO([$BASE_VERSION]) | $AWK -F. '{print $3}'`]) AC_SUBST(CORK_VERSION, ["$VERSION"]) AC_SUBST(CORK_GIT_SHA1, m4_esyscmd([build-aux/calculate commit . .commit-stamp])) AC_CONFIG_FILES([include/libcork/config/version.h]) AC_PROG_CC AC_PROG_CC_C99 # TAP support AC_PROG_AWK # Threads AX_PTHREAD LIBS="$PTHREAD_LIBS $LIBS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" CC="$PTHREAD_CC" # pkg-config PKG_INSTALLDIR AC_CONFIG_FILES([src/libcork.pc]) # Tests AM_PATH_PYTHON([2.3], , [:]) PKG_CHECK_MODULES(CHECK, [check], [check=yes], [check=no]) AM_CONDITIONAL(RUN_TESTS, [test "$PYTHON" != : -a $check = yes]) # Valgrind support AX_VALGRIND_DFLT([memcheck], [on]) AX_VALGRIND_DFLT([helgrind], [off]) AX_VALGRIND_DFLT([drd], [off]) AX_VALGRIND_DFLT([sgcheck], [off]) AX_VALGRIND_CHECK() # Turn on fatal warnings by default; you can override this by setting CPPFLAGS # to something else when running configure. : ${CPPFLAGS="-Wall -Werror"} AC_OUTPUT([Makefile]) cat < One of the biggest hassles in writing C code is memory management. libcork's memory allocation API tries to simplify this task as much as possible. This is still C, so you still have to manage allocated memory manually — for instance, by keeping careful track of which section of code "owns" any memory that you've allocated from heap, and is therefore responsible for freeing it. But we *can* make it easier to handle memory allocation failures, and provide helper macros for certain common allocation tasks. There is another `important use case`_ that we also want to support: giving application writers complete control over how the libraries they use allocate and deallocate memory. libcork :ref:`provides ` this capability, giving you control over how, for instance, a hash table allocates its internal buckets. If you're writing a library that links with libcork as a shared library, you'll get this behavior for free; if the application writer customizes how libcork allocates memory, your library will pick up that customization as well. If you're embedding libcork, so that your library's clients can't tell (or care) that you're using libcork, then you'll want to expose your own similar customization interface. .. _important use case: https://blog.mozilla.org/nnethercote/2013/11/08/libraries-should-permit-custom-allocators/ .. _allocation-api: Allocating memory ================= The simplest part of the API is the part responsible for actually allocating and deallocating memory. When using this part of the API, you don't have to worry about customization at all; the functions described here will automatically "do the right thing" based on how your library or application is configured. The biggest thing to worry about is how to handle memory allocation failures. We provide two strategies, "guaranteed" and "recoverable". The most common use case is that running out of memory is a Really Bad Thing, and there's nothing we can do to recover. In this case, it doesn't make sense to check for memory allocation failures throughout your code, since you can't really do anything if it does happen. The "guaranteed" family of functions handles that error checking for you, and guarantees that if the allocation function returns, it will return a valid piece of memory. If the allocation fails, the function will never return. That allows you to right simple and safe code like the following:: struct my_type *instance = cork_new(struct my_type); /* Just start using instance; don't worry about verifying that it's * not NULL! */ On the other hand, you might be writing some code that can gracefully handle a memory allocation failure. You might try to allocate a super-huge cache, for instance; if you can't allocate the cache, your code will still work, it will just be a bit slower. In this case, you *want* to be able to detect memory allocation failures, and handle them in whatever way is appropriate. The "recoverable" family of functions will return a ``NULL`` pointer if allocation fails. .. note:: libcork itself uses the guaranteed functions for all internal memory allocation. Guaranteed allocation --------------------- The functions in this section are guaranteed to return a valid newly allocated pointer. If memory allocation fails, the functions will not return. .. function:: void \*cork_malloc(size_t size) void \*cork_calloc(size_t count, size_t size) void \*cork_realloc(void \*ptr, size_t old_size, size_t new_size) type \*cork_new(TYPE type) The first three functions mimic the standard ``malloc``, ``calloc``, and ``realloc`` functions to allocate (or reallocate) some memory, with the added guarantee that they will always return a valid newly allocated pointer. ``cork_new`` is a convenience function for allocating an instance of a particular type; it is exactly equivalent to:: cork_malloc(sizeof(type)) Note that with ``cork_realloc``, unlike the standard ``realloc`` function, you must provide the old size of the memory region, in addition to the requested new size. Each allocation function has a corresponding deallocation function that you must use to free the memory when you are done with it: use :c:func:`cork_free` to free memory allocated using ``cork_malloc`` or ``cork_realloc``; use :c:func:`cork_cfree` to free memory allocated using ``cork_calloc``; and use :c:func:`cork_delete` to free memory allocated using ``cork_new``. .. note:: Note that the possible memory leak in the standard ``realloc`` function doesn't apply here, since we're going to abort the whole program if the reallocation fails. Recoverable allocation ---------------------- The functions in this section will return a ``NULL`` pointer if any memory allocation fails, allowing you to recover from the error condition, if possible. .. function:: void \*cork_xmalloc(size_t size) void \*cork_xcalloc(size_t count, size_t size) void \*cork_xrealloc(void \*ptr, size_t old_size, size_t new_size) void \*cork_xreallocf(void \*ptr, size_t old_size, size_t new_size) type \*cork_xnew(TYPE type) The first three functions mimic the standard ``malloc``, ``calloc``, ``realloc`` functions. ``cork_xreallocf`` mimics the common ``reallocf`` function from BSD. These functions return ``NULL`` if the memory allocation fails. (Note that unlike the standard functions, they do **not** set ``errno`` to ``ENOMEM``; the only indication you have of an error condition is a ``NULL`` return value.) Note that with ``cork_xrealloc`` and ``cork_xreallocf``, unlike the standard ``realloc`` function, you must provide the old size of the memory region, in addition to the requested new size. ``cork_xreallocf`` is more safe than the standard ``realloc`` function. A common idiom when calling ``realloc`` is:: void *ptr = /* from somewhere */; /* UNSAFE! Do not do this! */ ptr = realloc(ptr, new_size); This is unsafe! The ``realloc`` function returns a ``NULL`` pointer if the reallocation fails. By assigning directly into *ptr*, you'll get a memory leak in these situations. The ``cork_xreallocf`` function, on the other hand, will automatically free the existing pointer if the reallocation fails, eliminating the memory leak:: void *ptr = /* from somewhere */; /* This is safe. Do this. */ ptr = cork_xreallocf(ptr, new_size); /* Check whether ptr is NULL before using it! */ Each allocation function has a corresponding deallocation function that you must use to free the memory when you are done with it: use :c:func:`cork_free` to free memory allocated using ``cork_xmalloc``, ``cork_xrealloc``, or ``cork_xreallocf``; use :c:func:`cork_cfree` to free memory allocated using ``cork_xcalloc``; and use :c:func:`cork_delete` to free memory allocated using ``cork_xnew``. Deallocation ------------ Since this is C, you must free any memory region once you're done with it. You must use one of the functions from this section to free any memory that you created using any of the allocation functions described previously. .. function:: void cork_free(void \*ptr, size_t size) void cork_cfree(void \*ptr, size_t count, size_t size) void cork_delete(TYPE type, void \*ptr) Frees a region of memory allocated by one of libcork's allocation functions. Note that unlike the standard ``free`` function, you must provide the size of the allocated region when it's freed, as well as when it's created. Most of the time this isn't an issue, since you're either freeing a region whose size is known at compile time, or you're already keeping track of the size of a dynamically sized memory region for some other reason. You should use ``cork_free`` to free memory allocated using :c:func:`cork_malloc`, :c:func:`cork_realloc`, :c:func:`cork_xmalloc`, :c:func:`cork_xrealloc`, or :c:func:`cork_xreallocf`. You should use ``cork_cfree`` to free memory allocated using :c:func:`cork_calloc` or :c:func:`cork_xcalloc`. You should use ``cork_delete`` to free memory allocated using :c:func:`cork_new` or :c:func:`cork_xnew`. Duplicating strings ------------------- .. function:: const char \*cork_strdup(const char \*str) const char \*cork_strndup(const char \*str, size_t size) const char \*cork_xstrdup(const char \*str) const char \*cork_xstrndup(const char \*str, size_t size) These functions mimic the standard ``strdup`` function. They create a copy of an existing C string, allocating exactly as much memory is needed to hold the copy. The ``strdup`` variants calculate the size of *str* using ``strlen``. For the ``strndup`` variants, *str* does not need to be ``NUL``-terminated, and you must pass in its *size*. (Note that is different than the standard ``strndup``, where *str* must be ``NUL``-terminated, and which copies **at most** *size* bytes. Our version always copies **exactly** *size* bytes.) The result is guaranteed to be ``NUL``-terminated, even if the source *str* is not. You shouldn't modify the contents of the copied string. You must use :c:func:`cork_strfree()` to free the string when you're done with it. The ``x`` variant returns a ``NULL`` pointer if the allocation fails; the non-\ ``x`` variant is guaranteed to return a valid pointer to a copied string. .. function:: void cork_strfree(const char \*str) Frees *str*, which must have been created using :c:func:`cork_strdup()` or :c:func:`cork_xstrdup()`. .. _libcork-allocators: Customizing how libcork allocates ================================= The ``cork_alloc`` type encapsulates a particular memory allocation scheme. To customize how libcork allocates memory, you create a new instance of this type, and then use :c:func:`cork_set_allocator` to register it with libcork. .. function:: void cork_set_allocator(const struct cork_alloc \*alloc) Override which :ref:`allocator instance ` libcork will use to create and free memory. We will take control of *alloc*; you must not free it yourself after passing it to this function. You can only call this function at most once. This function is **not** thread-safe; it's only safe to call before you've called **any** other libcork function (or any function from any other library that uses libcork. (The only exceptions are libcork functions that take in a :c:type:`cork_alloc` parameter or return a :c:type:`cork_alloc` result; these functions are safe to call before calling ``cork_set_allocator``.) .. var:: const struct cork_alloc \*cork_allocator The current :ref:`allocator instance ` that libcork will use to create and free memory. .. _allocators: Writing a custom allocator -------------------------- .. type:: struct cork_alloc The ``cork_alloc`` type contains several methods for performing different allocation and deallocation operations. You are only required to provide implementations of ``xmalloc`` and ``free``; we can provide default implementations of all of the other methods in terms of those two. You can provide optimized versions of the other methods, if appropriate. .. function:: struct cork_alloc \*cork_alloc_new_alloc(const struct cork_alloc \*parent) ``cork_alloc_new`` creates a new allocator instance. The new instance will itself be allocated using *parent*. You must provide implementations of at least the ``xmalloc`` and ``free`` methods. You can also override our default implementations of any of the other methods. This function is **not** thread-safe; it's only safe to call before you've called **any** other libcork function (or any function from any other library that uses libcork. (The only exceptions are libcork functions that take in a :c:type:`cork_alloc` parameter or return a :c:type:`cork_alloc` result; these functions are safe to call before calling ``cork_set_allocator``.) The new allocator instance will automatically be freed when the process exits. If you registered a *user_data* pointer for your allocation methods (via :c:func:`cork_alloc_set_user_data`), it will be freed using the *free_user_data* method you provided. If you create more than one ``cork_alloc`` instance in the process, they will be freed in the reverse order that they were created. .. note:: In your allocator implementation, you cannot assume that the rest of the libcork allocation framework has been set up yet. So if your allocator needs to allocate, you must not use the usual :c:func:`cork_malloc` family of functions; instead you should use the ``cork_alloc_malloc`` variants to explicitly allocate memory using your new allocator's *parent*. .. function:: void cork_alloc_set_user_data(struct cork_alloc \*alloc, void \*user_data, cork_free_f free_user_data) Provide a *user_data* pointer, which will be passed unmodified to each allocation method that you register. You can also provide an optional *free_user_data* method, which we will use to free the *user_data* instance when the allocator itself is freed. .. function:: void cork_alloc_set_calloc(struct cork_alloc \*alloc, cork_alloc_calloc_f calloc) void cork_alloc_set_xcalloc(struct cork_alloc \*alloc, cork_alloc_calloc_f calloc) .. type:: void \*(\*cork_alloc_calloc_f)(const struct cork_alloc \*alloc, size_t count, size_t size) These methods are used to implement the :c:func:`cork_calloc` and :c:func:`cork_xcalloc` functions. Your must allocate and return ``count * size`` bytes of memory. You must ensure that every byte in this region is initialized to ``0``. The ``calloc`` variant must always return a valid pointer; if memory allocation fails, it must not return. The ``xcalloc`` variant should return ``NULL`` if allocation fails. .. function:: void cork_alloc_set_malloc(struct cork_alloc \*alloc, cork_alloc_malloc_f malloc) void cork_alloc_set_xmalloc(struct cork_alloc \*alloc, cork_alloc_malloc_f malloc) .. type:: void \*(\*cork_alloc_malloc_f)(const struct cork_alloc \*alloc, size_t size) These methods are used to implement the :c:func:`cork_malloc` and :c:func:`cork_xmalloc` functions. You must allocate and return *size* bytes of memory. The ``malloc`` variant must always return a valid pointer; if memory allocation fails, it must not return. The ``xmalloc`` variant should return ``NULL`` if allocation fails. .. function:: void cork_alloc_set_realloc(struct cork_alloc \*alloc, cork_alloc_realloc_f realloc) void cork_alloc_set_xrealloc(struct cork_alloc \*alloc, cork_alloc_realloc_f realloc) .. type:: void \*(\*cork_alloc_realloc_f)(const struct cork_alloc \*alloc, void \*ptr, size_t old_size, size_t new_size) These methods are used to implement the :c:func:`cork_realloc`, :c:func:`cork_xrealloc`, and :c:func:`cork_xreallocf` functions. You must reallocate *ptr* to contain *new_size* bytes of memory and return the reallocated pointer. *old_size* will be the previously allocated size of *ptr*. The ``realloc`` variant must always return a valid pointer; if memory reallocation fails, it must not return. The ``xrealloc`` variant should return ``NULL`` if reallocation fails. .. function:: void cork_alloc_set_free(struct cork_alloc \*alloc, cork_alloc_free_f free) .. type:: void \*(\*cork_alloc_free_f)(const struct cork_alloc \*alloc, void \*ptr, size_t size) These methods are used to implement the :c:func:`cork_free`, :c:func:`cork_cfree`, and :c:func:`cork_delete` functions. You must deallocate *ptr*. *size* will be the allocated size of *ptr*. libcork-1.0.0-rc3/docs/old/array.rst000066400000000000000000000142251367645320400172160ustar00rootroot00000000000000.. _array: **************** Resizable arrays **************** .. highlight:: c :: #include This section defines a resizable array class, similar to C++'s ``std::vector`` or Java's ``ArrayList`` classes. Our arrays can store any fixed-size element. The arrays automatically resize themselves as necessary to store the elements that you add. .. type:: cork_array(element_type) A resizable array that contains elements of type *element_type*. .. function:: void cork_array_init(cork_array(T) \*array) Initializes a new array. You should allocate *array* yourself, presumably on the stack or directly within some other data type. The array will start empty. .. function:: void cork_array_done(cork_array(T) \*array) Finalizes an array, freeing any storage that was allocated to hold the arrays elements. .. function:: size_t cork_array_size(cork_array(T) \*array) Returns the number of elements in *array*. .. function:: bool cork_array_is_empty(cork_array(T) \*array) Returns whether *array* has any elements. .. function:: void cork_array_void(cork_array(T) \*array) Removes all elements from *array*. .. function:: T* cork_array_elements(cork_array(T) \*array) Returns a pointer to the underlying array of elements in *array*. The elements are guaranteed to be contiguous, just like in a normal C array, but the particular pointer that is returned in **not** guaranteed to be consistent across function calls that modify the contents of the array. .. function:: T cork_array_at(cork_array(T) \*array, size_t index) Returns the element in *array* at the given *index*. Like accessing a normal C array, we don't do any bounds checking. The result is a valid lvalue, so it can be directly assigned to:: cork_array(int64_t) array; cork_array_append(array, 5, err); cork_array_at(array, 0) = 12; .. function:: void cork_array_append(cork_array(T) \*array, T element) Appends *element* to the end of *array*, reallocating the array's storage if necessary. If you have an ``init`` or ``reset`` callback for *array*, it will be used to initialize the space that was allocated for the new element, and then *element* will be directly copied into that space (using ``memcpy`` or an equivalent). If that is not the right copy behavior for the elements of *array*, then you should use :c:func:`cork_array_append_get` instead, and fill in the allocated element directly. .. function:: T \*cork_array_append_get(cork_array(T) \*array) Appends a new element to the end of *array*, reallocating the array's storage if necessary, returning a pointer to the new element. .. function:: int cork_array_ensure_size(cork_array(T) \*array, size_t desired_count) Ensures that *array* has enough allocated space to store *desired_count* elements, reallocating the array's storage if needed. The actual size and existing contents of the array aren't changed. .. function:: int cork_array_copy(cork_array(T) \*dest, cork_array(T) \*src, cork_copy_f \*copy, void \*user_data) Copy elements from *src* to *dest*. If you provide a *copy* function, it will be called on each element to perform the copy. If not, we'll use ``memcpy`` to bulk-copy the elements. If you've provided :ref:`callbacks ` for *dest*, then those callbacks will be called appropriately. We'll call the ``remove`` callback for any existing entries (will be overwritten by the copy). We'll call ``init`` or ``reuse`` on each element entry before it's copied. .. type:: typedef int (\*cork_copy_f)(void \*user_data, void \*dest, const void \*src) .. function:: size_t cork_array_element_size(cork_array(T) \*array) Returns the size of the elements that are stored in *array*. You won't normally need to call this, since you can just use ``sizeof(T)``. .. _array-callbacks: Initializing and finalizing elements ------------------------------------ You can provide callback functions that will be used to automatically initialize and finalize the elements of a resizable array. .. function:: void cork_array_set_init(cork_array(T) \*array, cork_init_f init) void cork_array_set_done(cork_array(T) \*array, cork_done_f done) void cork_array_set_reuse(cork_array(T) \*array, cork_init_f reuse) void cork_array_set_remove(cork_array(T) \*array, cork_done_f remove) void cork_array_set_callback_data(cork_array(T) \*array, void \*user_data, cork_free_f free_user_data) Set one of the callback functions for *array*. There are two pairs of callbacks: ``init`` and ``done``, and ``reuse`` and ``remove``. Within each pair, one callback is used to initialize an element of the array, while the other is used to finalize it. The ``init`` callback is used to initialize an element when its array entry is used for the first time. If you then shrink the array (via :c:func:`cork_array_clear`, for instance), and then append elements again, you will reuse array entries; in this case, the ``reset`` callback is used instead. (Having separate ``init`` and ``reuse`` callbacks can be useful when the elements are complex objects with deep memory requirements. If you use the ``init`` callback to allocate that memory, and use the ``reset`` callback to "clear" it, then you can reduce some of the memory allocation overhead.) Similarly, the ``remove`` callback is used when an element is removed from the array, but the space that the element used isn't being reclaimed yet. The ``done`` callback, on the other hand, is used when the array entry is reclaimed and freed. All of the callbacks take in an additional *user_data* parameter, in addition to the array entries themselves. You provide that parameter by calling the :c:func:`cork_array_set_callback_data` function. If you pass in a *free_user_data* function, then we will use that function to free the *user_data* when the array itself is finalized. .. type:: typedef void (\*cork_init_f)(void \*user_data, void \*value) typedef void (\*cork_done_f)(void \*user_data, void \*value) typedef void (\*cork_free_f)(void \*value) libcork-1.0.0-rc3/docs/old/attributes.rst000066400000000000000000000066361367645320400202750ustar00rootroot00000000000000.. _attributes: ******************* Compiler attributes ******************* .. highlight:: c :: #include The macros in this section define compiler-agnostic versions of several common compiler attributes. .. function:: CORK_LIKELY(expression) CORK_UNLIKELY(expression) Indicate that the given Boolean *expression* is likely to be ``true`` or ``false``, respectively. The compiler can sometimes use this information to generate more efficient code. .. macro:: CORK_ATTR_CONST Declare a “constant” function. The return value of a constant function can only depend on its parameters. This is slightly more strict than a “pure” function (declared by :c:macro:`CORK_ATTR_PURE`); a constant function is not allowed to read from global variables, whereas a pure function is. .. note:: Note that the compiler won't verify that your function meets the requirements of a constant function. Instead, this attribute notifies the compiler of your intentions, which allows the compiler to assume more about your function when optimizing code that calls it. :: int square(int x) CORK_ATTR_CONST; .. macro:: CORK_ATTR_MALLOC Declare a function that returns a newly allocated pointer. The compiler can use this information to generate more accurate aliasing information, since it can infer that the result of the function cannot alias any other existing pointer. :: void *custom_malloc(size_t size) CORK_ATTR_MALLOC; .. macro:: CORK_ATTR_NOINLINE Declare that a function shouldn't be eligible for inlining. .. macro:: CORK_ATTR_PRINTF(format_index, args_index) Declare a function that takes in ``printf``\ -like parameters. *format_index* is the index (starting from 1) of the parameter that contains the ``printf`` format string. *args_index* is the index of the first parameter that contains the data to format. .. macro:: CORK_ATTR_PURE Declare a “pure” function. The return value of a pure function can only depend on its parameters, and on global variables. :: static int _next_id; int get_next_id(void) CORK_ATTR_PURE; .. macro:: CORK_ATTR_SENTINEL Declare a var-arg function whose last parameter must be a ``NULL`` sentinel value. When the compiler supports this attribute, it will check the actual parameters whenever this function is called, and ensure that the last parameter is a ``NULL``. .. macro:: CORK_ATTR_UNUSED Declare a entity that might not be used. This lets you keep ``-Wall`` activated in several cases where you're obligated to define something that you don't intend to use. :: CORK_ATTR_UNUSED static void unused_function(void) { CORK_ATTR_UNUSED int unused_value; } .. macro:: CORK_INITIALIZER(func_name) CORK_FINALIZER(func_name) Declare a ``static`` function that will be automatically called at program startup (for ``CORK_INITIALIZER``) or shutdown (for ``CORK_FINALIZER``). If there are multiple initializer functions linked into a program, there is no guarantee about the order in which the functions will be called. :: #include #include static cork_array(int) array; CORK_INITIALIZER(init_array) { cork_array_init(&array); } CORK_FINALIZER(done_array) { cork_array_done(&array); } libcork-1.0.0-rc3/docs/old/basic-types.rst000066400000000000000000000062271367645320400203260ustar00rootroot00000000000000.. _basic-types: *********** Basic types *********** .. highlight:: c :: #include The types in this section ensure that the C99 integer types are available, regardless of platform. We also define some preprocessor macros that give the size of the non-fixed-size standard types. In addition, libcork defines some useful low-level types: .. toctree:: :maxdepth: 1 int128 net-addresses timestamps hash-values unique-ids Integral types ============== .. type:: bool A boolean. Where possible, we simply include ```` to get this type. It might be ``typedef``\ ed to ``int``\ . We also make sure that the following constants are defined: .. var:: bool false bool true .. type:: int8_t uint8_t int16_t uint16_t int32_t uint32_t int64_t uint64_t Signed and unsigned, fixed-size integral types. .. type:: intptr_t uintptr_t Signed and unsigned integers that are guaranteed to be big enough to hold a type-cast ``void *``\ . .. type:: size_t An unsigned integer big enough to hold the size of a memory object, or a maximal array index. .. type:: ptrdiff_t A signed integer big enough to hold the difference between two pointers. Size macros =========== .. macro:: CORK_SIZEOF_SHORT CORK_SIZEOF_INT CORK_SIZEOF_LONG CORK_SIZEOF_POINTER The size (in bytes) of the ``short``, ``int``, ``long``, and ``void *`` types, respectively. .. _embedded-struct: Embedded ``struct``\ s ====================== Quite often a callback function or API will take in a pointer to a particular ``struct``, with the expectation that you can embed that ``struct`` into some other type for extension purposes. Kind of a bastardized subclassing mechanism for C code. The doubly-linked list module is a perfect example; you're meant to embed :c:type:`cork_dllist_item` within the linked list element type. You can use the following macro to obtain the pointer to the containing (“subclass”) ``struct``, when given a pointer to the contained (“superclass”) ``struct``: .. function:: struct_type \*cork_container_of(field_type \*field, TYPE struct_type, FIELD field_name) The *struct_type* parameter must be the name of a ``struct`` type, *field_name* must be the name of some field within that ``struct``, and *field* must be a pointer to an instance of that field. The macro returns a pointer to the containing ``struct``. So, given the following definitions:: struct superclass { int a; }; struct subclass { int b; struct superclass parent; }; struct subclass instance; then the following identity holds:: cork_container_of(&instance.parent, struct subclass, parent) == &instance .. note:: When the superclass ``struct`` appears as the first element of the subclass ``struct``, you can obtain the same effect using a simple type-cast. However, the ``cork_container_of`` macro is more robust, since it also works when the superclass ``struct`` appears later on in the subclass ``struct``. libcork-1.0.0-rc3/docs/old/bitset.rst000066400000000000000000000046671367645320400174030ustar00rootroot00000000000000.. _bits: ******** Bit sets ******** .. highlight:: c :: #include This sections defines a type for storing an array of bits. This data structure is most often used to implement a set of integers. It is particularly good when you expect your sets to be *dense*. You should not use a bitset if the number of possibly elements is outrageously large, however, since that would cause your bitset to exhaust the available memory. .. type:: struct cork_bitset An array of bits. You should not allocate any instances of this type yourself; use :c:func:`cork_bitset_new` instead. .. member:: size_t bit_count The number of bits that are included in this array. (Each bit can be on or off; this does not give you the number of bits that are *on*, it gives you the number of bits in total, on or off.) .. function:: void cork_bitset_init(struct cork_bitset \*set) Initialize a new bitset instance that you've allocated yourself (usually on the stack). All bits will be initialized to ``0``. .. function:: struct cork_bitset \*cork_bitset_new(size_t bit_count) Create a new bitset with enough space to store the given number of bits. All bits will be initialized to ``0``. .. function:: void cork_bitset_done(struct cork_bitset \*set) Finalize a bitset, freeing any set content that it contains. This function should only be used for bitsets that you allocated yourself, and initialized using :c:func:`cork_bitset_init()`. You must **not** use this function to free a bitset allocated using :c:func:`cork_bitset_new()`. .. function:: void cork_bitset_free(struct cork_bitset \*set) Finalize and deallocate a bitset, freeing any set content that it contains. This function should only be used for bitsets allocated using :c:func:`cork_bitset_new()`. You must **not** use this function to free a bitset initialized using :c:func:`cork_bitset_init()`. .. function:: bool cork_bitset_get(struct cork_bitset \*set, size_t index) Return whether the given bit is on or off in *set*. It is your responsibility to ensure that *index* is within the valid range for *set*. .. function:: void cork_bitset_set(struct cork_bitset \*set, size_t index, bool value) Turn the given bit on or off in *set*. It is your responsibility to ensure that *index* is within the valid range for *set*. .. function:: void cork_bitset_clear(struct cork_bitset \*set) Turn off of the bits in *set*. libcork-1.0.0-rc3/docs/old/buffer.rst000066400000000000000000000274531367645320400173600ustar00rootroot00000000000000.. _buffer: ************************ Resizable binary buffers ************************ .. highlight:: c :: #include This section defines a resizable binary buffer type. This class can also be used to construct C strings, when you don't know the size of the string in advance. This class is not reference counted; we assume that there's a single owner of the buffer. The contents of a :c:type:`cork_buffer` are fully mutable. If you want to turn the buffer into something that's safe to pass between threads, you can use the :c:func:`cork_buffer_to_slice()` or :c:func:`cork_buffer_to_managed_buffer()` functions to create an immutable managed wrapper around the buffer. You can read the contents of the buffer by accessing the :c:member:`buf ` and :c:member:`size ` fields directly. However, to modify the contents of a buffer, you should use the mutator functions described below, since they take care of automatically resizing the underlying buffer when necessary. .. note:: This class always creates its own copy of any data added to the buffer; there aren't any methods for wrapping existing buffers without copying. If you want to do that, you should use :ref:`managed-buffer` or :ref:`slice`. .. type:: struct cork_buffer A resizable binary buffer. .. member:: void \*buf The current contents of the buffer. .. member:: size_t size The current size of the buffer. .. function:: void cork_buffer_init(struct cork_buffer \*buffer) struct cork_buffer CORK_BUFFER_INIT() Initialize a new buffer instance that you've allocated yourself (usually on the stack). The ``CORK_BUFFER_INIT`` version can only be used as a static initializer. The preallocated ``cork_buffer`` instance that you provide doesn't include space for the content of the buffer; this will be allocated automatically as content is added. .. function:: struct cork_buffer \*cork_buffer_new(void) Allocate and initialize a new buffer instance. .. function:: void cork_buffer_done(struct cork_buffer \*buffer) Finalize a buffer, freeing any content that it contains. This function should only be used for buffers that you allocated yourself, and initialized using :c:func:`cork_buffer_init()` or :c:func:`CORK_BUFFER_INIT()`. You must **not** use this function to free a buffer allocated using :c:func:`cork_buffer_new()`. .. function:: void cork_buffer_free(struct cork_buffer \*buffer) Finalize and deallocate a buffer, freeing any content that it contains. This function should only be used for buffers allocated using :c:func:`cork_buffer_new()`. You must **not** use this function to free a buffer initialized using :c:func:`cork_buffer_init()` or :c:func:`CORK_BUFFER_INIT()`. .. function:: bool cork_buffer_equal(const struct cork_buffer \*buffer1, const struct cork_buffer \*buffer2) Compare two buffers for equality. .. function:: void cork_buffer_ensure_size(struct cork_buffer \*buffer, size_t desired_size) Ensure that a buffer has allocated enough space to store at least *desired_size* bytes. We won't shrink the size of the buffer's internal storage; if the buffer has already allocated at least *desired_size* bytes, the function acts as a no-op. .. function:: uint8_t cork_buffer_byte(struct cork_buffer \*buffer, size_t index) char cork_buffer_char(struct cork_buffer \*buffer, size_t index) Return the byte or character at the given index in *buffer*. Mutator functions ----------------- Most of the mutator functions defined in this section come in two variants: a ``_set`` function, which clears the buffer before adding new content, and an ``_append`` function, which retains the old content, adding the new content to the end of the buffer. Each mutator function will automatically append an extra ``NUL`` byte to the end of whatever content is placed into the buffer. However, this ``NUL`` byte will **not** be included in the :c:member:`size ` of the buffer. This ensures that the contents of any ``cork_buffer`` can be used as a ``NUL``\ -terminated C string (assuming that there aren't any internal ``NUL``\ s), even if the buffer is constructed from a data source that doesn't include ``NUL`` terminators. .. function:: void cork_buffer_clear(struct cork_buffer \*buffer) Clear a buffer. This does not free any storage that the buffer has allocated; this storage will be reused if you add contents back to the buffer. .. function:: void cork_buffer_truncate(struct cork_buffer \*buffer, size_t length) Truncate a buffer so that contains no more than *length* bytes. If the buffer is already shorter than this, it is not modified. .. function:: void cork_buffer_copy(struct cork_buffer \*dest, const struct cork_buffer \*src) void cork_buffer_append_copy(struct cork_buffer \*dest, const struct cork_buffer \*src) Copy the contents of the *src* buffer into *dest*. The ``_set`` variant clears the buffer first, while the ``_append`` variant adds *src* to whatever content is already there. .. function:: void cork_buffer_set(struct cork_buffer \*buffer, const void \*src, size_t length) void cork_buffer_append(struct cork_buffer \*buffer, const void \*src, size_t length) Copy the contents of *src* into a buffer. The ``_set`` variant clears the buffer first, while the ``_append`` variant adds *src* to whatever content is already there. .. function:: void cork_buffer_set_string(struct cork_buffer \*buffer, const char \*str) void cork_buffer_append_string(struct cork_buffer \*buffer, const char \*str) void cork_buffer_set_literal(struct cork_buffer \*buffer, const char \*str) void cork_buffer_append_literal(struct cork_buffer \*buffer, const char \*str) Copy the contents of *str* (which must be a ``NUL``\ -terminated C string) into a buffer. The ``_set`` variants clears the buffer first, while the ``_append`` variants adds *str* to whatever content is already there. The ``_literal`` variants only work when *str* is a C string literal; we use the ``sizeof`` operator to determine the length of the string at compile time. The ``_string`` variants work with any C string; we use the builtin ``strlen`` function to determine the length of the string. .. function:: void cork_buffer_printf(struct cork_buffer \*buffer, const char \*format, ...) void cork_buffer_vprintf(struct cork_buffer \*buffer, const char \*format, va_list args) void cork_buffer_append_printf(struct cork_buffer \*buffer, const char \*format, ...) void cork_buffer_append_vprintf(struct cork_buffer \*buffer, const char \*format, va_list args) Format data according to a ``printf`` format string, placing the result into a buffer. The ``_append`` variants add the formatted string to whatever content is already in the buffer; the non-\ ``_append`` variants clear the buffer first. The ``_printf`` variants are vararg functions, and take in the format string's data as direct parameters. The ``_vprintf`` variants can be used within another vararg function, and let you pass in the format string's data as a C99-standard ``va_list`` instance. Pretty-printing --------------- We also provide several helper functions for adding pretty-printed content to a ``cork_buffer``. .. function:: void cork_buffer_append_indent(struct cork_buffer \*buffer, size_t indent) Append *indent* spaces to *buffer*. .. function:: void cork_buffer_append_c_string(struct cork_buffer \*buffer, const char \*str, size_t length) Append the C string literal representation of *str* to *buffer*. This will include opening and closing double quotes, and any non-printable characters will be escaped. (We will use the standard letter-based escapes where possible, and fall back on ``"\xXX"`` hexadecimal escapes for other non-printable characters.) The result is guaranteed to stay on a single line, since any embedded newlines will be converted to a ``\n`` escape sequence. .. function:: void cork_buffer_append_hex_dump(struct cork_buffer \*buffer, size_t indent, const char \*str, size_t length) void cork_buffer_append_multiline(struct cork_buffer \*buffer, size_t indent, const char \*str, size_t length) void cork_buffer_append_binary(struct cork_buffer \*buffer, size_t indent, const char \*str, size_t length) Append a pretty-printed representation of *str* to *buffer*. All of these functions can produce multiple lines of output. All lines except for the first will be prefaced with *indent* space characters. The final line will **not** have a trailing newline. The ``hex_dump`` variant will output a hex-dump representation of *str*. This will include the hexadecimal representation of each byte, and the actual character of any printable byte. The ``multiline`` variant appends the raw content of *str* to the buffer, without making any attempt to sanitize non-printable characters. (That means you should only call this variant if you know that *str* contains only printable characters.) If *str* itself spans multiple lines, then we'll insert indentation to make sure that we satisfy the indentation rules described above. The ``binary`` variant autodetects how to best render *str*. If it contains any non-printable characters, then we'll use the ``hex_dump`` representation. If it spans multiple lines, we'll use the ``multiline`` representation. Otherwise, we'll append the content directly without any modification. Other binary data structures ---------------------------- The ``cork_buffer`` class is the only binary data class that is mutable; this comes at the cost of only being usable by a single owner thread or function at a time. Once you have constructed a binary string or payload using a ``cork_buffer``, you can use the functions in this section to produce a corresponding instance of one of libcork's sharable, immutable binary data types. .. function:: struct cork_managed_buffer \*cork_buffer_to_managed_buffer(struct cork_buffer \*buffer) Create a new :ref:`managed buffer ` to manage the contents of a ``cork_buffer`` instance. *buffer* must have been allocated on the heap (i.e., using :c:func:`cork_buffer_new()`, and not :c:func:`cork_buffer_init()`). We take ownership of *buffer*, regardless of whether we're able to successfully create a new :c:type:`cork_managed_buffer` instance. You must **not** try to free *buffer* yourself. .. function:: int cork_buffer_to_slice(struct cork_buffer \*buffer, struct cork_slice \*slice) Initialize a new :ref:`slice ` to manage the contents of *buffer*. *buffer* must have been allocated on the heap (i.e., using :c:func:`cork_buffer_new()`, and not :c:func:`cork_buffer_init()`). We take ownership of *buffer*, regardless of whether we're able to successfully create a new :c:type:`cork_managed_buffer` instance. You must **not** try to free *buffer* yourself. The slice will point into the contents of a new :ref:`managed buffer ` instance. The managed buffer isn't returned directly, though you can create additional slices into it using the usual :c:type:`cork_slice` methods. Regardless of whether we can initialize the slice successfully, you **must** call :c:func:`cork_slice_finish()` on *slice* when you're done with the slice. .. function:: struct cork_stream_consumer \*cork_buffer_to_stream_consumer(struct cork_buffer \*buffer) Create a new stream consumer that appends any received data into *buffer*. We do **not** take control of *buffer*. You retain responsibility for freeing the buffer, and you must ensure that it remains allocated and valid for the entire lifetime of the stream consumer that we return. libcork-1.0.0-rc3/docs/old/byte-order.rst000066400000000000000000000142351367645320400201550ustar00rootroot00000000000000.. _byte-order: ********** Byte order ********** .. highlight:: c :: #include This section contains definitions for determining the endianness of the host system, and for byte-swapping integer values of various sizes. Endianness detection ==================== .. macro:: CORK_LITTLE_ENDIAN CORK_BIG_ENDIAN CORK_HOST_ENDIANNESS CORK_OTHER_ENDIANNESS The ``CORK_HOST_ENDIANNESS`` macro can be used to determine the endianness of the host system. It will be equal to either ``CORK_LITTLE_ENDIAN`` or ``CORK_BIG_ENDIAN``. (The actual values don't matter; you should always compare against the predefined constants.) The ``CORK_OTHER_endianness`` macro is defined to be the opposite endianness as ``CORK_HOST_ENDIANNESS``. A common use case would be something like:: #if CORK_HOST_endianness == CORK_LITTLE_ENDIAN /* do something to little-endian values */ #else /* do something to big-endian values */ #endif .. macro:: CORK_HOST_ENDIANNESS_NAME CORK_OTHER_ENDIANNESS_NAME These macros give you a human-readable name of the host's endianness. You can use this in debugging messages. .. note:: You should *not* use these macros to detect the endianness of the system, since we might change their definitions at some point to support localization. For that, use :macro:`CORK_LITTLE_ENDIAN` and :macro:`CORK_BIG_ENDIAN`. Byte swapping ============= Swapping arbitrary expressions ------------------------------ All of the macros in this section take in an rvalue (i.e., any arbitrary expression) as a parameter. The result of the swap is returned as the value of the macro. .. function:: uint16_t CORK_SWAP_UINT16(uint16_t value) uint32_t CORK_SWAP_UINT32(uint32_t value) uint64_t CORK_SWAP_UINT64(uint64_t value) cork_u128 CORK_SWAP_UINT128(cork_128 value) These functions always perform a byte-swap, regardless of the endianness of the host system. .. function:: uint16_t CORK_UINT16_BIG_TO_HOST(uint16_t value) uint32_t CORK_UINT32_BIG_TO_HOST(uint32_t value) uint64_t CORK_UINT64_BIG_TO_HOST(uint64_t value) cork_u128 CORK_UINT128_BIG_TO_HOST(cork_u128 value) These functions convert a big-endian (or network-endian) value into host endianness. (I.e., they only perform a swap if the current host is little-endian.) .. function:: uint16_t CORK_UINT16_HOST_TO_BIG(uint16_t value) uint32_t CORK_UINT32_HOST_TO_BIG(uint32_t value) uint64_t CORK_UINT64_HOST_TO_BIG(uint64_t value) cork_u128 CORK_UINT128_HOST_TO_BIG(cork_u128 value) These functions convert a host-endian value into big (or network) endianness. (I.e., they only perform a swap if the current host is little-endian.) .. function:: uint16_t CORK_UINT16_LITTLE_TO_HOST(uint16_t value) uint32_t CORK_UINT32_LITTLE_TO_HOST(uint32_t value) uint64_t CORK_UINT64_LITTLE_TO_HOST(uint64_t value) cork_u128 CORK_UINT128_LITTLE_TO_HOST(cork_u128 value) These functions convert a little-endian value into host endianness. (I.e., they only perform a swap if the current host is big-endian.) .. function:: uint16_t CORK_UINT16_HOST_TO_LITTLE(uint16_t value) uint32_t CORK_UINT32_HOST_TO_LITTLE(uint32_t value) uint64_t CORK_UINT64_HOST_TO_LITTLE(uint64_t value) cork_u128 CORK_UINT128_HOST_TO_LITTLE(cork_u128 value) These functions convert a host-endian value into little endianness. (I.e., they only perform a swap if the current host is big-endian.) Swapping values in place ------------------------ The macros in this section swap an integer *in place*, which means that the original value is overwritten with the result of the swap. To support this, you must pass in an *lvalue* as the parameter to the macro. (Note that you don't pass in a *pointer* to the original value; these operations are implemented as macros, and you just need to provide a reference to the variable to be swapped.) .. function:: void CORK_SWAP_UINT16_IN_PLACE(uint16_t &value) void CORK_SWAP_UINT32_IN_PLACE(uint32_t &value) void CORK_SWAP_UINT64_IN_PLACE(uint64_t &value) void CORK_SWAP_UINT128_IN_PLACE(cork_u128 &value) These functions always perform a byte-swap, regardless of the endianness of the host system. .. function:: void CORK_UINT16_BIG_TO_HOST_IN_PLACE(uint16_t &value) void CORK_UINT32_BIG_TO_HOST_IN_PLACE(uint32_t &value) void CORK_UINT64_BIG_TO_HOST_IN_PLACE(uint64_t &value) void CORK_UINT128_BIG_TO_HOST_IN_PLACE(cork_u128 &value) These functions convert a big-endian (or network-endian) value into host endianness, and vice versa. (I.e., they only perform a swap if the current host is little-endian.) .. function:: void CORK_UINT16_HOST_TO_BIG_IN_PLACE(uint16_t &value) void CORK_UINT32_HOST_TO_BIG_IN_PLACE(uint32_t &value) void CORK_UINT64_HOST_TO_BIG_IN_PLACE(uint64_t &value) void CORK_UINT128_HOST_TO_BIG_IN_PLACE(cork_u128 &value) These functions convert a host-endian value into big (or network) endianness. (I.e., they only perform a swap if the current host is little-endian.) .. function:: void CORK_UINT16_LITTLE_TO_HOST_IN_PLACE(uint16_t &value) void CORK_UINT32_LITTLE_TO_HOST_IN_PLACE(uint32_t &value) void CORK_UINT64_LITTLE_TO_HOST_IN_PLACE(uint64_t &value) void CORK_UINT128_LITTLE_TO_HOST_IN_PLACE(cork_u128 &value) These functions convert a little-endian value into host endianness, and vice versa. (I.e., they only perform a swap if the current host is big-endian.) .. function:: void CORK_UINT16_HOST_TO_LITTLE_IN_PLACE(uint16_t &value) void CORK_UINT32_HOST_TO_LITTLE_IN_PLACE(uint32_t &value) void CORK_UINT64_HOST_TO_LITTLE_IN_PLACE(uint64_t &value) void CORK_UINT128_HOST_TO_LITTLE_IN_PLACE(cork_u128 &value) These functions convert a host-endian value into little endianness. (I.e., they only perform a swap if the current host is big-endian.) libcork-1.0.0-rc3/docs/old/cli.rst000066400000000000000000000330031367645320400166420ustar00rootroot00000000000000.. _cli: ********************* Command-line programs ********************* .. highlight:: c :: #include The functions in this section let you easily create complex command-line applications that include subcommands, in the style of the ``git`` or ``svn`` programs. Overview ======== If you're designing an application where you want to provide command-line access to many different operations or use cases, the simplest solution is to create a separate executable for each one. This can clutter up the user's ``$PREFIX/bin`` directory, however, and can add complexity to your code base. Many projects instead create a single “super-command” executable, which includes within it all of the operations that you want to support. You choose specific operations by selecting a *subcommand* on the command line. .. type:: struct cork_command An opaque type describing one of the subcommands in an executable. So, for instance, if you were writing a library for manipulating sets of objects, you could define several subcommands of a single ``set`` executable: .. code-block:: none $ set add $ set query $ set remove $ set union -o $ set print avro $ set print json Each of these operations acts in exactly the same as if they were defined as separate executables: .. code-block:: none $ set-add $ set-query $ set-remove $ set-union -o $ set-print-avro $ set-print-json Note that you're not limited to one level of subcommands. The ``set print`` subcommand, for instance, itself contains two subcommands: ``avro`` and ``json``. Leaf commands ============= A *leaf command* is a subcommand that represents one operation in your executable. In the example above, there are six leaf commands: ``set add``, ``set query``, ``set remove``, ``set union``, ``set print avro``, and ``set print json``. To define a leaf command, you use the following macro: .. macro:: cork_leaf_command(const char \*name, const char \*short_description, const char \*usage, const char \*full_help, cork_option_parser parse_options, run) Returns :c:type:`cork_command` instance that defines a leaf command. *name* is the name of the leaf command; this is the word that the user must type on the command-line to select this command. (For ``set add``, this would be ``add``; for ``set print avro``, this would be ``avro``.) *short_description*, *usage*, and *full_help* should be static strings, and will be used to produce various forms of :ref:`help text ` for the subcommand. *short_description* should fit into one line; this will be used as the short description of this leaf command when we print out a list of all of the subcommands that are in the command set that this leaf belongs to. *usage* will be printed whenever we need to print out a usage synopsis. This should describe the options and arguments to the leaf command; it will be printed after the full name of the subcommand. (For instance, using the example above, the ``set add`` command's usage text would be `` ``.) *full_help* should be a longer, multi-line string that describes the subcommand *in full detail*. We will automatically preface the help text with the usage summary for the command. *parse_options* is a function that will be used to parse any command-line options that appear *after* the subcommand's name on the command line. (See :ref:`below ` for more details.) This can be ``NULL`` if the subcommand does not have any options. *run* is the function that will be called to actually execute the command. Any options will have already been processed by the *parse_options* function; you should stash the option values into global or file-scope variables, and then use the contents of those variables in this function. Your *run* function must be an instance of the :c:type:`cork_leaf_command_run` function type: .. type:: void (\*cork_leaf_command_run)(int argc, char \*\*argv) The *argc* and *argv* parameters will describe any values that appear on the command line after the name of the leaf command. This will *not* include any options that were processed by the command's *parse_options* function. As an example, we could define the ``set add`` command as follows:: static void set_add_run(int argc, char **argv); #define SET_ADD_SHORT "Adds an element to a set" #define SET_ADD_USAGE " " #define SET_ADD_FULL \ "Loads in a set from , and adds to the set. The\n" \ "new set will be written back out to .\n" static struct cork_command set_add = cork_leaf_command("add", SET_ADD_SHORT, SET_ADD_USAGE, SET_ADD_FULL, NULL, set_add_run); static void set_add_run(int argc, char **argv) { /* Verify that the user gave both required options... */ if (argc < 1) { cork_command_show_help(&set_add, "Missing set filename."); exit(EXIT_FAILURE); } if (argc < 2) { cork_command_show_help(&set_add, "Missing element to add."); exit(EXIT_FAILURE); } /* ...and no others. */ if (argc > 2) { cork_command_show_help(&set_add, "Too many values on command line."); exit(EXIT_FAILURE); } /* At this point, will be in argv[0], will be in * argv[1]. */ /* Do what needs to be done */ exit(EXIT_SUCCESS); } There are a few interesting points to make. First, note that we use preprocessor macros to define all of the help text for the command. Also, note that *each* line (including the last) of the full help text needs to have a trailing newline included in the string literal. Lastly, note that we still have to perform some final validation of the command line arguments given by the user. If the user hasn't satisfied the subcommand's requirements, we use the :c:func:`cork_command_show_help` function to print out a nice error message (including a usage summary of the subcommand), and then we halt the executable using the standard ``exit`` function. Command sets ============ A *command set* is a collection of subcommands. Every executable will have at least one command set, for the root executable itself. It's also possible to have nested command sets. In our example above, ``set`` and ``set print`` are both command sets. To define a command set, you use the following macro: .. macro:: cork_command_set(const char \*name, const char \*short_description, cork_option_parser parse_options, struct cork_command \*\*subcommands) Returns :c:type:`cork_command` instance that defines a command set. *name* is the name of the command set; this is the word that the user must type on the command-line to select this set of commands. If the user only specifies the name of the command set, then we'll print out a list of this set's subcommands, along with their short descriptions. (For instance, running ``set`` on its own would describe the ``set add``, ``set query``, ``set remove``, ``set union``, and ``set print`` subcommands. Running ``set print`` on its own would describe the ``set print avro`` and ``set print json`` commands.) *short_description*, should be a static strings, and will be used to produce various forms of :ref:`help text ` for the command set. *short_description* should fit into one line; this will be used as the short description of this command when we print out a list of all of the subcommands that are in the command set that this command belongs to. *parse_options* is a function that will be used to parse any command-line options that appear *after* the command set's name on the command line, but *before* the name of one of the set's subcommands. (See :ref:`below ` for more details.) This can be ``NULL`` if the command set does not have any options. *subcommands* should be an array of :c:type:`cork_command` pointers. The array **must** have a ``NULL`` pointer as its last element. The order of the subcommands in the array will effect the order that the commands are listed in the command set's help text. As an example, we could define the ``set print`` command set as follows:: /* Assuming set_print_avro and set_print_json were already defined * previously, using cork_leaf_command: */ struct cork_command set_print_avro = cork_leaf_command(...); struct cork_command set_print_json = cork_leaf_command(...); /* "set print" command set */ static struct cork_command *set_print_subcommands[] = { &set_print_avro, &set_print_json, NULL }; #define SET_PRINT_SHORT \ "Print out the contents of a set in a variety of formats" static struct cork_command set_print = cork_command_set("print", SET_PRINT_SHORT, NULL, &set_print_subcommands); You must define your executable's top level of subcommands as a command set as well. For instance, we could define the ``set`` command set as follows:: static struct cork_command *root_subcommands[] = { &set_add, &set_query, &set_remove, &set_union, &set_print, NULL }; static struct cork_command root = cork_command_set("set", NULL, NULL, &root_subcommands); Note that we don't need to provide a short description for the root command, since it doesn't belong to any command sets. Running the commands ==================== Once you've defined all of your subcommands, your executable's ``main`` function is trivial:: int main(int argc, char **argv) { return cork_command_main(&root, argc, argv); } .. function:: int cork_command_main(struct cork_command \*root, int argc, char \*\*argv) Runs a subcommand, as defined by the command-line arguments given by *argc* and *argv*. *root* should define the root command set for the executable. .. _cli-help: Help text ========= The command-line programs created with this framework automatically support generating several flavors of help text for its subcommands. You don't need to do anything special, except for ensuring that the actual help text that you provide to the :c:macro:`cork_leaf_command` and :c:macro:`cork_command_set` macros defined is intelligble and useful. Your executable will automatically include a ``help`` command in every command set, as well as ``--help`` and ``-h`` options in every command set and leaf command. So all of the following would print out the help text for the ``set add`` command: .. code-block:: none $ set help add $ set add --help $ set add -h And all of the following would print out the list of ``set print`` subcommands: .. code-block:: none $ set help print $ set print --help $ set print -h You can also print out the help text for a command explicitly by calling the following function: .. function:: void cork_command_show_help(struct cork_command \*command, const char \*message) Prints out help text for *command*. (If it's a leaf command, this is the full help text. If it's a command set, it's a list of the set's subcommands.) We will preface the help text with *message* if it's non-``NULL``. (The message should not include a trailing newline.) .. _cli-options: Option parsing ============== Leaf commands and command sets both let you provide a function that parse command-line options for the given command. We don't prescribe any particular option parsing library, you just need to conform to the interface described in this section. (Note that the standard ``getopt`` and ``getopt_long`` functions can easily be used in an option parsing function.) .. type:: int (\*cork_option_parser)(int argc, char \*\*argv) Should parse any command-line options that can appear at this point in the executable's command line. (The options must appear immediately after the name of the command that this function belongs to. See below for several examples.) Your function must look for and process any options that appear at the beginning of *argv*. If there are any errors processing the options, you should print out an error message (most likely via :c:func:`cork_command_show_help`) and exit the program, using the standard ``exit`` function, with an exit code of ``EXIT_FAILURE``. If there aren't any errors processing the options, you should return the number of *argv* elements that were consumed while processing the options. We will use this return value to update *argc* and *argv* beforing continuing with subcommand selection and argument processing. (Note that ``getopt``'s ``optind`` variable is exactly what you need for the return value.) As mentioned above, different option parsing functions are used to parse options from a particular point in the command line. Given the following command: .. code-block:: none $ set --opt1 print --opt2 avro --opt3 --opt4=foo The ``--opt1`` option would be parsed by the ``set`` command's parser. The ``--opt2`` option would be parsed by the ``set print`` command's parser. The ``--opt3`` and ``-opt4=foo`` options would be parsed by the ``set print avro`` command's parser. And the ```` argument would be parsed by the ``set print avro`` command's *run* function. libcork-1.0.0-rc3/docs/old/conf.py000066400000000000000000000037661367645320400166550ustar00rootroot00000000000000# -*- coding: utf-8 -*- import sys, os extensions = ['sphinx.ext.mathjax'] source_suffix = '.rst' master_doc = 'index' project_name = u'libcork' project_slug = u'libcork' company = u'libcork authors' copyright_years = u'2011-2017' default_role = 'c:func' primary_domain = 'c' rst_epilog = """ .. |project_name| replace:: """ + project_name + """ """ # Intersphinx stuff # If your documentation uses intersphinx to link to other Sphinx # documentation sets, uncomment and fill in the following. # #intersphinx_mapping = { # 'libcork': ('http://libcork.readthedocs.org/en/latest/', None), #} # Our CMake build scripts will insert overrides below if the prereq # libraries have installed their Sphinx documentation locally. DO NOT # uncomment out the last line of this block; we need it commented so # that this conf.py file still works if CMake doesn't do its # substitution thing. # @INTERSPHINX_OVERRIDES@ #---------------------------------------------------------------------- # Everything below here shouldn't need to be changed. release = None version = None # Give CMake a chance to insert a version number # @VERSION_FOR_CONF_PY@ # Otherwise grab version from git if version is None: import re import subprocess release = str(subprocess.check_output(["git", "describe"]).rstrip()) version = re.sub(r"-dev.*$", "-dev", release) # Project details project = project_name copyright = copyright_years+u', '+company templates_path = ['_templates'] exclude_patterns = ['_build'] pygments_style = 'sphinx' html_theme = 'default' html_style = 'docco-sphinx.css' html_static_path = ['_static'] htmlhelp_basename = project_slug+'-doc' latex_documents = [ ('index', project_slug+'.tex', project_name+u' Documentation', company, 'manual'), ] man_pages = [ ('index', 'libcork', u'libcork documentation', [u'libcork authors'], 1) ] texinfo_documents = [ ('index', 'libcork', u'libcork documentation', u'libcork authors', 'libcork', 'One line description of project.', 'Miscellaneous'), ] libcork-1.0.0-rc3/docs/old/config.rst000066400000000000000000000137251367645320400173510ustar00rootroot00000000000000.. _config: ******************* Configuring libcork ******************* .. highlight:: c :: #include Several libcork features have different implementations on different platforms. Since we want libcork to be easily embeddable into projects with a wide range of build systems, we try to autodetect which implementations to use, using only the C preprocessor and the predefined macros that are available on the current system. This module provides a layer of indirection, with all of the preprocessor-based autodetection in one place. This module's task is to define a collection of libcork-specific configuration macros, which all other libcork modules will use to select which implementation to use. This design also lets you skip the autodetection, and provide values for the configuration macros directly. This is especially useful if you're embedding libcork into another project, and already have a ``configure`` step in your build system that performs platform detection. See :c:macro:`CORK_CONFIG_SKIP_AUTODETECT` for details. .. note:: The autodetection logic is almost certainly incomplete. If you need to port libcork to another platform, this is where an important chunk of edits will take place. Patches are welcome! .. _configuration-macros: Configuration macros ==================== This section lists all of the macros that are defined by libcork's autodetection logic. Other libcork modules will use the values of these macros to choose among the possible implementations. .. macro:: CORK_CONFIG_VERSION_MAJOR CORK_CONFIG_VERSION_MINOR CORK_CONFIG_VERSION_PATCH The libcork library version, with each part of the version number separated out into separate macros. .. macro:: CORK_CONFIG_VERSION_STRING The libcork library version, encoded as a single string. .. macro:: CORK_CONFIG_REVISION The git SHA-1 commit identifier of the libcork version that you're using. .. macro:: CORK_CONFIG_ARCH_X86 CORK_CONFIG_ARCH_X64 CORK_CONFIG_ARCH_PPC Exactly one of these macros should be defined to ``1`` to indicate the architecture of the current platform. All of the other macros should be defined to ``0`` or left undefined. The macros correspond to the following architectures: ============ ================================================ Macro suffix Architecture ============ ================================================ ``X86`` 32-bit Intel (386 or greater) ``X64`` 64-bit Intel/AMD (AMD64/EM64T, *not* IA-64) ``PPC`` 32-bit PowerPC ============ ================================================ .. macro:: CORK_CONFIG_HAVE_GCC_ASM Whether the GCC `inline assembler`_ syntax is available. (This doesn't imply that the compiler is specifically GCC.) Should be defined to ``0`` or ``1``. .. _inline assembler: http://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html .. macro:: CORK_CONFIG_HAVE_GCC_ATTRIBUTES Whether the GCC-style syntax for `compiler attributes`_ is available. (This doesn't imply that the compiler is specifically GCC.) Should be defined to ``0`` or ``1``. .. _compiler attributes: http://gcc.gnu.org/onlinedocs/gcc/Attribute-Syntax.html .. macro:: CORK_CONFIG_HAVE_GCC_ATOMICS Whether GCC-style `atomic intrinsics`_ are available. (This doesn't imply that the compiler is specifically GCC.) Should be defined to ``0`` or ``1``. .. _atomic intrinsics: http://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Atomic-Builtins.html .. macro:: CORK_CONFIG_HAVE_GCC_INT128 Whether the GCC-style `128-bit integer`_ types (``__int128`` and ``unsigned __int128``) are available. (This doesn't imply that the compiler is specifically GCC.) Should be defined to ``0`` or ``1``. .. _128-bit integer: http://gcc.gnu.org/onlinedocs/gcc/_005f_005fint128.html .. macro:: CORK_CONFIG_HAVE_GCC_MODE_ATTRIBUTE Whether GCC-style `machine modes`_ are available. (This doesn't imply that the compiler is specifically GCC.) Should be defined to ``0`` or ``1``. .. _machine modes: http://gcc.gnu.org/onlinedocs/gcc-4.8.1/gccint/Machine-Modes.html#Machine-Modes .. macro:: CORK_CONFIG_HAVE_GCC_STATEMENT_EXPRS Whether GCC-style `statement expressions`_ are available. (This doesn't imply that the compiler is specifically GCC.) Should be defined to ``0`` or ``1``. .. _statement expressions: http://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html .. macro:: CORK_CONFIG_HAVE_REALLOCF Whether this platform defines a ``reallocf`` function in ``stdlib.h``. ``reallocf`` is a BSD extension to the standard ``realloc`` function that frees the existing pointer if a reallocation fails. If this function exists, we can use it to implement :func:`cork_realloc`. .. macro:: CORK_CONFIG_IS_BIG_ENDIAN CORK_CONFIG_IS_LITTLE_ENDIAN Whether the current system is big-endian or little-endian. Exactly one of these macros should be defined to ``1``; the other should be defined to ``0``. .. _skipping-autodetection: Skipping autodetection ====================== .. macro:: CORK_CONFIG_SKIP_AUTODETECT If you want to skip libcork's autodetection logic, then you are responsible for providing the appropriate values for all of the macros defined in :ref:`configuration-macros`. To do this, have your build system define this macro, with a value of ``1``. This will override the default value of ``0`` provided in the ``libcork/config/config.h`` header file. Then, create (or have your build system create) a ``libcork/config/custom.h`` header file. You can place this file anywhere in your header search path. We will load that file instead of libcork's autodetection logic. Place the appropriate definitions for each of the configuration macros into this file. If needed, you can generate this file as part of the ``configure`` step of your build system; the only requirement is that it's available once you start compiling the libcork source files. libcork-1.0.0-rc3/docs/old/dllist.rst000066400000000000000000000253461367645320400174010ustar00rootroot00000000000000.. _dllist: ******************* Doubly-linked lists ******************* .. highlight:: c :: #include This section defines a doubly-linked list data structure. The structure is “invasive”, since you must place an instance of the :c:type:`cork_dllist_item` type into the type whose instances will be stored in the list. The list itself is represented by the :c:type:`cork_dllist` type. As an example, we could define the following types for storing groups, as well as the users within each group:: struct group { const char *group_name; struct cork_dllist members; }; struct user { const char *username; const char *real_name; struct cork_dllist_item list; }; Note that both ``cork_dllist`` and ``cork_dllist_item`` are embedded directly into our domain-specific types. This means that every list operation defined in this section is guaranteed to succeed, since no memory operations will be involved. (The list and any items will have already been allocated before you try to call the list function.) Like with any embedded ``struct``, you can use the :c:func:`cork_container_of` macro to obtain a pointer to a ``struct user`` if you're given a pointer to a :c:type:`cork_dllist_item`. .. type:: struct cork_dllist A doubly-linked list. The list itself is represented by a sentinel element, representing the empty list. .. type:: struct cork_dllist_item An element of a doubly-linked list. This type will usually be embedded within the type whose instances will be stored in the list. .. member:: struct cork_dllist_item \*next struct cork_dllist_item \*prev A pointer to the next (or previous) element in the list. If this element marks the end (or beginning) of the list, then *next* (or *prev*) will point to the list's sentinel value. .. function:: void cork_dllist_init(struct cork_dllist \*list) struct cork_dllist CORK_DLLIST_INIT(SYMBOL name) Initializes a doubly-linked list. The list will initially be empty. The second variant is a static initializer, that lets you initialize a list at compile time, rather than runtime. You must pass in the name of the list for this to work, since we need to be able to extract pointers into the list object. Querying a list --------------- .. function:: size_t cork_dllist_size(const struct cork_dllist \*list) Returns the number of elements in *list*. This operation runs in :math:`O(n)` time. .. function:: bool cork_dllist_is_empty(struct cork_dllist \*list) Returns whether *list* is empty. This operation runs in :math:`O(1)` time. Editing a list -------------- .. function:: void cork_dllist_add_to_head(struct cork_dllist \*list, struct cork_dllist_item \*element) void cork_dllist_add_to_tail(struct cork_dllist \*list, struct cork_dllist_item \*element) Adds *element* to *list*. The ``_head`` variant adds the new element to the beginning of the list; the ``_tail`` variant adds it to the end. You are responsible for allocating the list element yourself, most likely by allocating the ``struct`` that you've embedded :c:type:`cork_dllist_item` into. .. note:: This function assumes that *element* isn't already a member of a different list. You're responsible for calling :c:func:`cork_dllist_remove()` if this isn't the case. (If you don't, the other list will become malformed.) This operation runs in :math:`O(1)` time. .. function:: void cork_dllist_add_after(struct cork_dllist_item \*pred, struct cork_dllist_item \*element) void cork_dllist_add_before(struct cork_dllist_item \*succ, struct cork_dllist_item \*element) Adds *element* to the same list that *pred* or *succ* belong to. The ``_after`` variant ensures that *element* appears in the list immediately after *pred*. The ``_before`` variant ensures that *element* appears in the list immediately before *succ*. .. note:: This function assumes that *element* isn't already a member of a different list. You're responsible for calling :c:func:`cork_dllist_remove()` if this isn't the case. (If you don't, the other list will become malformed.) This operation runs in :math:`O(1)` time. .. function:: void cork_dllist_add_list_to_head(struct cork_dllist \*dest, struct cork_dllist \*src) void cork_dllist_add_list_to_tail(struct cork_dllist \*dest, struct cork_dllist \*src) Moves all of the elements in *src* to *dest*. The ``_head`` variant moves the elements to the beginning of *dest*; the ``_tail`` variant moves them to the end. After these functions return, *src* will be empty. This operation runs in :math:`O(1)` time. .. function:: void cork_dllist_remove(struct cork_dllist_item \*element) Removes *element* from the list that it currently belongs to. (Note that you don't have to pass in a pointer to that list.) .. note:: You must not call this function on a list's sentinel element. This operation runs in :math:`O(1)` time. Iterating through a list ------------------------ There are two strategies you can use to access all of the elements in a doubly-linked list: *visiting* and *iterating*. With visiting, you write a visitor function, which will be applied to each element in the list. (In this case, libcork controls the loop that steps through each element.) .. function:: int cork_dllist_visit(struct cork_dllist \*list, void \*user_data, cork_dllist_visit_f \*func) Apply a function to each element in *list*. The function is allowed to remove the current element from the list; this will not affect our ability to iterate through the remainder of the list. The function will be given a pointer to the :c:type:`cork_dllist_item` for each element; you can use :c:func:`cork_container_of()` to get a pointer to the actual element type. If your visitor function ever returns a non-zero value, we will abort the iteration and return that value from ``cork_dllist_visit``. If your function always returns ``0``, then you will visit all of the elements in *list*, and we'll return ``0`` from ``cork_dllist_visit``. .. type:: int cork_dllist_visit_f(void \*user_data, struct cork_dllist_item \*element) A function that can be applied to each element in a doubly-linked list. For instance, you can manually calculate the number of elements in a list as follows (assuming you didn't want to use the built-in :c:func:`cork_dllist_size()` function, of course):: static int count_elements(void *user_data, struct cork_dllist_item *element) { size_t *count = ud; (*count)++; return 0; } struct cork_dllist *list = /* from somewhere */; size_t count = 0; cork_dllist_visit(list, &count, count_elements); /* returns 0 */ /* the number of elements is now in count */ The second strategy is to iterate through the elements yourself. .. macro:: cork_dllist_foreach(struct cork_dllist \*list, struct cork_dllist_item &\*curr, struct cork_dllist_item &\*next, TYPE element_type, TYPE &\*element, FIELD item_field) cork_dllist_foreach_void(struct cork_dllist \*list, struct cork_dllist_item &\*curr, struct cork_dllist_item &\*next) Iterate through each element in *list*, executing a statement for each one. You must declare two variables of type ``struct cork_dllist_item *``, and pass in their names as *curr* and *next*. (You'll usually call the variables ``curr`` and ``next``, too.) For the ``_void`` variant, your statement can only use these :c:type:`cork_dllist_item` variables to access the current list element. You can use :c:func:`cork_container_of` to get a pointer to the actual element type. For the non-``_void`` variant, we'll automatically call :c:func:`cork_container_of` for you. *element_type* should be the actual element type, which must contain an embedded :c:func:`cork_dllist_item` field. *item_field* should be the name of this embedded field. You must allocate a pointer to the element type, and pass in its name as *element*. For instance, you can use these macros calculate the number of elements as follows:: struct cork_dllist *list = /* from somewhere */; struct cork_dllist *curr; struct cork_dllist *next; size_t count = 0; cork_dllist_foreach_void(list, curr, next) { count++; } /* the number of elements is now in count */ We're able to use :c:macro:`cork_dllist_foreach_void` since we don't need to access the contents of each element to calculate how many of theo there are. If we wanted to calculuate a sum, however, we'd have to use :c:macro:`cork_dllist_foreach`:: struct element { unsigned int value; struct cork_dllist_item item; }; struct cork_dllist *list = /* from somewhere */; struct cork_dllist *curr; struct cork_dllist *next; struct element *element; unsigned int sum = 0; cork_dllist_foreach(list, curr, next, struct element, element, item) { sum += element->value; } /* the sum of the elements is now in sum */ If the ``foreach`` macros don't provide what you need, you can also iterate through the list manually. .. function:: struct cork_dllist_item \*cork_dllist_head(struct cork_dllist \*list) struct cork_dllist_item \*cork_dllist_start(struct cork_dllist \*list) Returns the element at the beginning of *list*. If *list* is empty, then the ``_head`` variant will return ``NULL``, while the ``_start`` variant will return the list's sentinel element. .. function:: struct cork_dllist_item \*cork_dllist_tail(struct cork_dllist \*list) struct cork_dllist_item \*cork_dllist_end(struct cork_dllist \*list) Returns the element at the end of *list*. If *list* is empty, then the ``_tail`` variant will return ``NULL``, while the ``_end`` variant will return the list's sentinel element. .. function:: bool cork_dllist_is_start(struct cork_dllist \*list, struct cork_dllist_item \*element) bool cork_dllist_is_end(struct cork_dllist \*list, struct cork_dllist_item \*element) Returns whether *element* marks the start (or end) of *list*. With these functions, manually counting the list elements looks like:: struct cork_dllist *list = /* from somewhere */; struct cork_dllist_item *curr; size_t count = 0; for (curr = cork_dllist_start(list); !cork_dllist_is_end(list, curr); curr = curr->next) { count++; } /* the number of elements is now in count */ You can also count the elements in reverse order:: struct cork_dllist *list = /* from somewhere */; struct cork_dllist_item *curr; size_t count = 0; for (curr = cork_dllist_end(list); !cork_dllist_is_start(list, curr); curr = curr->prev) { count++; } /* the number of elements is now in count */ libcork-1.0.0-rc3/docs/old/ds.rst000066400000000000000000000004731367645320400165060ustar00rootroot00000000000000.. _ds: *************** Data structures *************** .. highlight:: c :: #include libcork includes implementations of a number of useful data structures. .. toctree:: :maxdepth: 1 array bitset slice managed-buffer buffer stream dllist hash-table ring-buffer libcork-1.0.0-rc3/docs/old/errors.rst000066400000000000000000000444141367645320400174170ustar00rootroot00000000000000.. _errors: *************** Error reporting *************** .. highlight:: c :: #include This section defines an API for reporting error conditions. It's loosely modeled on the POSIX ``errno`` mechanism. The standard POSIX approach for reporting errors is to return an integer status code, and to store error codes into the ``errno`` global variable. This approach has a couple of drawbacks. The first is that you --- or really, your C library --- has to ensure that ``errno`` is placed in thread-local storage, so that separate threads have their own error condition variables. The second, and in our mind more important, is that the set of error codes is fixed and platform-dependent. It's difficult to add new error codes to represent application-level error conditions. The libcork error API is a way around this. Like standard POSIX-conforming functions, you return an integer status code from any function that might need to report an error to its caller. The status return code is simple: ``0`` indicates success, ``-1`` indicates failure. When an error occurs, you can use the functions in this section to get more information about the error: an *error code*, and human-readable string description of the error. The POSIX ``errno`` values, while hard to extend, are perfectly well-defined for most platforms; therefore, any ``errno`` value supported by your system's C library is a valid libcork error code. To support new application-specific error codes, an error code can also be the hash of some string describing the error. This “hash of a string” approach makes it easy to define new error codes without needing any centralized mechanism for assigning IDs to the various codes. Moreover, it's very unlikely that a hashed error code will conflict with some existing POSIX ``errno`` value, or with any other hashed error codes. .. note:: We correctly maintain a separate error condition for each thread in the current process. This is all hidden by the functions in this section; it's safe to call them from multiple threads simultaneously. Calling a function that can return an error ------------------------------------------- There are two basic forms for a function that can produce an error. The first is if the function returns a single pointer as its result:: TYPE * my_function(/* parameters */); The second is for any other function:: int my_function(/* parameters */); If an error occurs, the function will return either ``NULL`` or ``-1``, depending on its return type. Success will be indicated by a non-\ ``NULL`` pointer or a ``0``. (More complex return value schemes are possible, if the function needs to signal more than a simple “success” or “failure”; in that case, you'll need to check the function's documentation for details.) If you want to know specifics about the error, there are several functions that you can use to interrogate the current error condition. .. function:: bool cork_error_occurred(void) Returns whether an error has occurred. .. function:: cork_error cork_error_code(void) Returns the error code of the current error condition. If no error has occurred, the result will be :c:macro:`CORK_ERROR_NONE`. .. function:: const char \*cork_error_message(void) Returns the human-readable string description the current error condition. If no error occurred, the result of this function is undefined. You can use the ``cork_error_prefix`` family of functions to add additional context to the beginning of an error message. .. function:: void cork_error_prefix_printf(const char \*format, ...) void cork_error_prefix_string(const char \*string) void cork_error_prefix_vprintf(const char \*format, va_list args) Prepends some additional text to the current error condition. When you're done checking the current error condition, you clear it so that later calls to :c:func:`cork_error_occurred` and friends don't re-report this error. .. function:: void cork_error_clear(void) Clears the current error condition. Writing a function that can return an error ------------------------------------------- When writing a function that might produce an error condition, your function signature should follow one of the two standard patterns described above:: int my_function(/* parameters */); TYPE * my_function(/* parameters */); You should return ``-1`` or ``NULL`` if an error occurs, and ``0`` or a non-\ ``NULL`` pointer if it succeeds. If ``NULL`` is a valid “successful” result of the function, you should use the first form, and define a ``TYPE **`` output parameter to return the actual pointer value. (If you're using the first form, you can use additional return codes if there are other possible results besides a simple “success” and “failure”.) If your function results in an error, you need to fill in the current error condition using the ``cork_error_set`` family of functions: .. function:: void cork_error_set_printf(cork_error ecode, const char \*format, ...) void cork_error_set_string(cork_error ecode, const char \*string) void cork_error_set_vprintf(cork_error ecode, const char \*format, va_list args) Fills in the current error condition. The error condition is defined by the error code *ecode*. The human-readable description is constructed from *string*, or from *format* and any additional parameters, depending on which variant you use. As an example, the :ref:`IP address ` parsing functions fill in :c:macro:`CORK_PARSE_ERROR` error conditions when you try to parse a malformed address:: const char *str = /* the string that's being parsed */; cork_error_set_printf (CORK_PARSE_ERROR, "Invalid IP address: %s", str); If a particular kind of error can be raised in several places throughout your code, it can be useful to define a helper function for filling in the current error condition:: static void cork_ip_address_parse_error(const char *version, const char *str) { cork_error_set_printf (CORK_PARSE_ERROR, "Invalid %s address: %s", version, str); } Error-checking macros --------------------- There can be a lot of repetitive code when calling functions that return error conditions. We provide a collection of helper macros that make it easier to write this code. .. note:: Unlike most libcork modules, these macros are **not** automatically defined when you include the ``libcork/core.h`` header file, since they don't include a ``cork_`` prefix. Because of this, we don't want to pollute your namespace unless you ask for the macros. To do so, you must explicitly include their header file:: #include Additional debugging output ~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. macro:: CORK_PRINT_ERRORS If you define this macro to ``1`` before including :file:`libcork/helpers/errors.h`, then we'll output the current function name, file, and line number, along with the description of the error, to stderr whenever an error is detected by one of the macros described in this section. Returning a default error code ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If you follow one of the standard function signature patterns described above, then your function will either return an ``int`` or some pointer type, and errors will be signalled by a return value of ``-1`` or ``NULL``. If so, you can use the macros in this section to automatically return the appropriate error return value if a nested function call returns an error. With these macros, you won't have a chance to inspect the error condition when an error occurs, so you should pass in your own *err* parameter when calling the nested function. (The mnemonic for remembering these macro names is that they all start with ``rXY_``. The ``r`` indicates that they automatically “return”. The second character indicates whether *your* function returns an ``int`` or a pointer. The third character indicates whether the function you're *calling* returns an ``int`` or a pointer.) .. function:: void rie_check(call) Call a function whose return value isn't enough to check for an error, when your function returns an ``int``. We'll use :c:func:`cork_error_occurred` to check for an error. If the nested function call returns an error, we propagate that error on. .. function:: void rii_check(call) Call a function that returns an ``int`` error indicator, when your function also returns an ``int``. If the nested function call returns an error, we propagate that error on. .. function:: void rip_check(call) Call a function that returns a pointer, when your function returns an ``int``. If the nested function call returns an error, we propagate that error on. .. function:: void rpe_check(call) Call a function whose return value isn't enough to check for an error, when your function returns a pointer. We'll use :c:func:`cork_error_occurred` to check for an error. If the nested function call returns an error, we propagate that error on. .. function:: void rpi_check(call) Call a function that returns an ``int`` error indicator, when your function returns a pointer. If the nested function call returns an error, we propagate that error on. .. function:: void rpp_check(call) Call a function that returns a pointer, when your function also returns a pointer. If the nested function call returns an error, we propagate that error on. Returning a non-standard return value ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If your function doesn't have a standard signature, or it uses additional return values besides ``0``, ``1``, ``NULL``, and valid pointers, then you can use the macros in this section to return a custom return value in case of an error. With these macros, you won't have a chance to inspect the error condition when an error occurs, so you should pass in your own *err* parameter when calling the nested function. (The mnemonic for remembering these macro names is that they all start with ``xY_``. The ``x`` doesn't standard for anything in particular. The second character indicates whether the function you're *calling* returns an ``int`` or a pointer. We don't need separate macros for *your* function's return type, since you provide a return value explicitly.) .. function:: void xe_check(retval, call) Call a function whose return value isn't enough to check for an error. If the nested function call raises an error, we propagate that error on, and return *retval* from the current function. .. function:: void xi_check(retval, call) Call a function that returns an ``int`` error indicator. If the nested function call raises an error, we propagate that error on, and return *retval* from the current function. .. function:: void xp_check(retval, call) Call a function that returns a pointer. If the nested function call raises an error, we propagate that error on, and return *retval* from the current function. Post-processing when an error occurs ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If you need to perform some post-processing when a nested function returns an error, you can use the functions in this section. They will automatically jump to the current scope's ``error`` label whenever an error occurs. (The mnemonic for remembering these macro names is that they all start with ``eY_``. The ``e`` indicates that they'll jump to the ``error`` label. The second character indicates whether the function you're *calling* returns an ``int`` or a pointer. We don't need separate macros for *your* function's return type, since the macros won't automatically return anything.) .. function:: void ei_check(call) Call a function whose return value isn't enough to check for an error. If the nested function call raises an error, we automatically jump to the current scope's ``error`` label. .. function:: void ei_check(call) Call a function that returns an ``int`` error indicator. If the nested function call raises an error, we automatically jump to the current scope's ``error`` label. .. function:: void ep_check(call) Call a function that returns a pointer. If the nested function call raises an error, we automatically jump to the current scope's ``error`` label. Calling POSIX functions ~~~~~~~~~~~~~~~~~~~~~~~ The :c:func:`cork_system_error_set` function automatically translates a POSIX error (specified in the standard ``errno`` variable) into a libcork error condition (which will be reported by :c:func:`cork_error_occurred` and friends). We also define several helper macros for calling a POSIX function and automatically checking its result. :: #include .. note:: For all of these macros, the ``EINTR`` POSIX error is handled specially. This error indicates that a system call was interrupted by a signal, and that the call should be retried. The macros do not translate ``EINTR`` errors into libcork errors; instead, they will retry the ``call`` until the statement succeeds or returns a non-``EINTR`` error. .. function:: void rii_check_posix(call) Call a function that returns an ``int`` error indicator, when your function also returns an ``int``. If the nested function call returns a POSIX error, we translate it into a libcork error and return a libcork error code. .. function:: void rip_check_posix(call) Call a function that returns a pointer, when your function returns an ``int``. If the nested function call returns a POSIX error, we translate it into a libcork error and return a libcork error code. .. function:: void rpi_check_posix(call) Call a function that returns an ``int`` error indicator, when your function returns a pointer. If the nested function call returns a POSIX error, we translate it into a libcork error and return a libcork error code. .. function:: void rpp_check_posix(call) Call a function that returns a pointer, when your function also returns a pointer. If the nested function call returns a POSIX error, we translate it into a libcork error and return a libcork error code. .. function:: void ei_check_posix(call) Call a function that returns an ``int`` error indicator. If the nested function call raises a POSIX error, we translate it into a libcork error and automatically jump to the current scope's ``error`` label. .. function:: void ep_check_posix(call) Call a function that returns a pointer. If the nested function call raises a POSIX error, we translate it into a libcork error and automatically jump to the current scope's ``error`` label. Defining new error codes ------------------------ If none of the built-in error codes suffice for an error condition that you need to report, you'll have to define our own. As mentioned above, each libcork error code is either a predefined POSIX ``errno`` value, or a hash some of string identifying a custom error condition. Typically, you will create a macro in one of your public header files, whose value will be your new custom error code. If this is the case, you can use the macro name itself to create the hash value for the error code. This is what we do for the non-POSIX builtin errors; for instance, the value of the :c:macro:`CORK_PARSE_ERROR` error code macro is the hash of the string ``CORK_PARSE_ERROR``. Given this string, you can produce the error code's hash value using the :ref:`cork-hash ` command that's installed with libcork:: $ cork-hash CORK_PARSE_ERROR 0x95dfd3c8 It's incredibly unlikely that the hash value for your new error code will conflict with any other custom hash-based error codes, or with any predefined POSIX ``errno`` values. With your macro name and hash value ready, defining the new error code is simple:: #define CORK_PARSE_ERROR 0x95dfd3c8 You should also provide a helper macro that makes it easier to report new instances of this error condition:: #define cork_parse_error(...) \ cork_error_set_printf(CORK_PARSE_ERROR, __VA_ARGS__) .. type:: uint32_t cork_error An identifier for a particular error condition. This will either be a predefined POSIX ``errno`` value, or the hash of a unique string describing the error condition. With your error class and code defined, you can fill in error instances using :c:func:`cork_error_set_printf` and friends. Builtin errors -------------- In addition to all of the predefined POSIX ``errno`` values, we also provide error codes for a handful of common error conditions. You should feel free to use these in your libraries and applications, instead of creating custom error codes, if they apply. .. macro:: CORK_ERROR_NONE A special error code that signals that no error occurred. .. macro:: CORK_PARSE_ERROR The provided input violates the rules of the language grammar or file format (or anything else, really) that you're trying to parse. .. function:: void cork_parse_error(const char *format*, ...) .. macro:: CORK_REDEFINED CORK_UNDEFINED Useful when you have a container type that must ensure that there is only one entry for any given key. .. function:: void cork_redefined(const char *format*, ...) void cork_undefined(const char *format*, ...) .. macro:: CORK_UNKNOWN_ERROR Some error occurred, but we don't have any other information about the error. .. function:: void cork_unknown_error(void) The error description will include the name of the current function. We also provide some helper functions for setting these built-in errors: .. function:: void cork_system_error_set(void) void cork_system_error_set_explicit(int err) Fills in the current libcork error condition with information from a POSIX ``errno`` value. The human-readable description of the error will be obtained from the standard ``strerror`` function. With the ``_explicit`` variant, you provide the ``errno`` value directly; for the other variant, we get the error code from the C library's ``errno`` variable. .. function:: void cork_abort(const char \*fmt, ...) Aborts the current program with an error message given by *fmt* and any additional parameters. .. function:: void cork_unreachable(void) Aborts the current program with a message indicating that the code path should be unreachable. This can be useful in the ``default`` clause of a ``switch`` statement if you can ensure that one of the non-``default`` branches will always be selected. libcork-1.0.0-rc3/docs/old/files.rst000066400000000000000000000373471367645320400172140ustar00rootroot00000000000000.. _files: ********************* Files and directories ********************* .. highlight:: c :: #include The functions in this section let you interact with files and directories in the local filesystem. Paths ===== We provide several functions for constructing and handling paths into the local filesystem. .. type:: struct cork_path Represents a path in the local filesystem. The path can be relative or absolute. The paths don't have to refer to existing files or directories. .. function:: struct cork_path \*cork_path_new(const char \*path) struct cork_path \*cork_path_clone(const struct cork_path \*other) Construct a new path object from the given path string, or as a copy of another path object. .. function:: void cork_path_free(struct cork_path \*path) Free a path object. .. function:: const char \*cork_path_get(const struct cork_path \*path) Return the string content of a path. This is not normalized in any way. The result is guaranteed to be non-``NULL``, but may refer to an empty string. The return value belongs to the path object; you must not modify the contents of the string, nor should you try to free the underlying memory. .. function:: struct cork_path \*cork_path_absolute(const struct cork_path \*other) int cork_path_make_absolute(struct cork_path \path) Convert a relative path into an absolute path. The first variant constructs a new path object to hold the result; the second variant overwritesthe contents of *path*. If there is a problem obtaining the current working directory, these functions will return an error condition. .. function:: struct cork_path \*cork_path_join(const struct cork_path \*path, const char \*more) struct cork_path \*cork_path_join_path(const struct cork_path \*path, const struct cork_path \*more) void \*cork_path_append(struct cork_path \path, const char \*more) void \*cork_path_append_path(struct cork_path \*path, const struct cork_path \*more) Concatenate two paths together. The ``join`` variants create a new path object containing the concatenated results. The ``append`` variants overwrite the contents of *path* with the concatenated results. .. function:: struct cork_path \*cork_path_basename(const struct cork_path \*path) void \*cork_path_set_basename(struct cork_path \*path) Extract the base name of *path*. This is the portion after the final trailing slash. The first variant constructs a new path object to hold the result; the second variant overwritesthe contents of *path*. .. note:: These functions return a different result than the standard ``basename(3)`` function. We consider a trailing slash to be significant, whereas ``basename(3)`` does not:: basename("a/b/c/") == "c" cork_path_basename("a/b/c/") == "" .. function:: struct cork_path \*cork_path_dirname(const struct cork_path \*path) void \*cork_path_set_dirname(struct cork_path \*path) Extract the directory name of *path*. This is the portion before the final trailing slash. The first variant constructs a new path object to hold the result; the second variant overwritesthe contents of *path*. .. note:: These functions return a different result than the standard ``dirname(3)`` function. We consider a trailing slash to be significant, whereas ``dirname(3)`` does not:: dirname("a/b/c/") == "a/b" cork_path_dirname("a/b/c/") == "a/b/c" Lists of paths ============== .. type:: struct cork_path_list A list of paths in the local filesystem. .. function:: struct cork_path_list \*cork_path_list_new_empty(void) struct cork_path_list \*cork_path_list_new(const char \*list) Create a new list of paths. The first variant creates a list that is initially empty. The second variant takes in a colon-separated list of paths as a single string, and adds each of those paths to the new list. .. function:: void cork_path_list_free(struct cork_path_list \*list) Free a path list. .. function:: void cork_path_list_add(struct cork_path_list \*list, struct cork_path \*path) Add *path* to *list*. The list takes control of the path instance; you must not try to free *path* yourself. .. function:: size_t cork_path_list_size(const struct cork_path_list \*list) Return the number of paths in *list*. .. function:: const struct cork_path \*cork_path_list_get(const struct cork_path_list \*list, size_t index) Return the path in *list* at the given *index*. The list still owns the path instance that's returned; you must not try to free it or modify its contents. .. function:: const char \*cork_path_list_to_string(const struct cork_path_list \*list) Return a string containing all of the paths in *list* separated by colons. .. function:: struct cork_file \*cork_path_list_find_file(const struct cork_path_list \*list, const char \*rel_path) struct cork_file_list \*cork_path_list_find_files(const struct cork_path_list \*list, const char \*rel_file) Search for a file in a list of paths. *rel_path* gives the path of the sought-after file, relative to each of the directories in *list*. The first variant returns a :c:type:`cork_file` instance for the first match. In no file can be found, it returns ``NULL`` and sets an error condition. The second variant returns a :c:type:`cork_file_list` instance containing all of the matches. In no file can be found, we return an empty list. (Unlike the first variant, this is not considered an error.) Standard paths ============== .. function:: struct cork_path \*cork_path_home(void) Return a :c:type:`cork_path` that refers to the current user's home directory. If we can't determine the current user's home directory, we set an error condition and return ``NULL``. On POSIX systems, this directory is determined by the ``HOME`` environment variable. .. function:: struct cork_path_list \*cork_path_config_paths(void) Return a :c:type:`cork_path_list` that includes all of the standard directories that can be used to store configuration files. This includes a user-specific directory that allows the user to override any global configuration files. On POSIX systems, these directories are defined XDG Base Directory Specification. .. function:: struct cork_path_list \*cork_path_data_paths(void) Return a :c:type:`cork_path_list` that includes all of the standard directories that can be used to store application data files. This includes a user-specific directory that allows the user to override any global data files. On POSIX systems, these directories are defined XDG Base Directory Specification. .. function:: struct cork_path \*cork_path_user_cache_path(void) Return a :c:type:`cork_path` that refers to a directory that can be used to store cache files created on behalf of the current user. This directory should only be used to store data that you can reproduce if needed. On POSIX systems, these directories are defined XDG Base Directory Specification. .. function:: struct cork_path \*cork_path_user_runtime_path(void) Return a :c:type:`cork_path` that refers to a directory that can be used to store small runtime management files on behalf of the current user. On POSIX systems, these directories are defined XDG Base Directory Specification. Files ===== .. type:: struct cork_file Represents a file on the local filesystem. The file in question does not necessarily have to exist; you can use :c:type:`cork_file` instances to refer to files that you have not yet created, for instance. .. type:: typedef unsigned int cork_file_mode Represents a Unix-style file permission set. .. function:: struct cork_file \*cork_file_new(const char \*path) struct cork_file \*cork_file_new_from_path(struct cork_path \*path) Create a new :c:type:`cork_file` instance to represent the file with the given *path*. The ``_from_path`` variant uses an existing :c:type:`cork_path` instance to specify the path. The new file instance will take control of the :c:type`cork_path` instance, so you should not try to free it yourself. .. function:: void cork_file_free(struct cork_file \*file) Free a file instance. .. function:: const struct cork_path \*cork_file_path(struct cork_file \*file) Return the path of a file. The :c:type:`cork_path` instance belongs to the file; you must not try to modify or free the path instance. .. function:: int cork_file_exists(struct cork_file \*file, bool \*exists) Check whether a file exists in the filesystem, storing the result in *exists*. The function returns an error condition if we are unable to determine whether the file exists --- for instance, because you do not have permission to look into one of the containing directories. .. function:: int cork_file_type(struct cork_file \*file, enum cork_file_type \*type) Return what kind of file the given :c:type:`cork_file` instance refers to. The function returns an error condition if there is an error accessing the file --- for instance, because you do not have permission to look into one of the containing directories. If the function succeeds, it will fill in *type* with one of the following values: .. type:: enum cork_file_type .. member:: CORK_FILE_MISSING *file* does not exist. .. member:: CORK_FILE_REGULAR *file* is a regular file. .. member:: CORK_FILE_DIRECTORY *file* is a directory. .. member:: CORK_FILE_SYMLINK *file* is a symbolic link. .. member:: CORK_FILE_UNKNOWN We can access *file*, but we do not know what type of file it is. .. function:: int cork_file_remove(struct cork_file \*file, unsigned int flags) Remove *file* from the filesystem. *flags* must be the bitwise OR (``|``) of the following flags. (Use ``0`` if you do not want any of the flags.) .. macro:: CORK_FILE_PERMISSIVE If this flag is given, then it is not considered an error if *file* does not exist. If the flag is not given, then the function function returns an error if *file* doesn't exist. (This mimics the standard ``rm -f`` command.) .. macro:: CORK_FILE_RECURSIVE If this flag is given, and *file* refers to a directory, then the function will automatically remove the directory and all of its contents. If the flag is not given, and *file* refers to a directory, then the directory must be empty for this function to succeed. If *file* does not refer to a directory, this flag has no effect. (This mimics the standard ``rmdir -r`` command.) Directories =========== Certain functions can only be applied to a :c:type:`cork_file` instance that refers to a directory. .. function:: int cork_file_mkdir(struct cork_file \*directory, cork_file_mode mode, unsigned int flags) Create a new directory in the filesystem, with permissions given by *mode*. *flags* must be the bitwise OR (``|``) of the following flags. (Use ``0`` if you do not want any of the flags.) .. macro:: CORK_FILE_PERMISSIVE If this flag is given, then it is not considered an error if *directory* already exists. If the flag is not given, then the function function returns an error if *directory* exists. (This mimics part of the standard ``mkdir -p`` command.) .. macro:: CORK_FILE_RECURSIVE If this flag is given, then the function will ensure that all of the parent directories of *directory* exist, creating them if necessary. Each directory created will have permissions given by *mode*. (This mimics part of the standard ``mkdir -p`` command.) .. function:: int cork_file_iterate_directory(struct cork_file \*directory, cork_file_directory_iterator iterator, void \*user_data) Call *iterator* for each file or subdirectory contained in *directory* (not including the directory's ``.`` and ``..`` entries). This function does not recurse into any subdirectories; it only iterates through the immediate children of *directory*. If your iteration function returns a non-zero result, we will abort the iteration and return that value. Otherwise, if each call to the iteration function returns ``0``, then we will return ``0`` as well. *iterator* must be an instance of the following function type: .. type:: typedef int (\*cork_file_directory_iterator)(struct cork_file \*child, const char \*rel_name, void \*user_data) Called for each child entry in *directory*. *child* will be a file instance referring to the child entry. *rel_name* gives the relative name of the child entry within its parent *directory*. Lists of files ============== .. type:: struct cork_file_list A list of files in the local filesystem. .. function:: struct cork_file_list \*cork_file_list_new_empty(void) struct cork_file_list \*cork_file_list_new(struct cork_path_list \*path_list) Create a new list of files. The first variant creates a list that is initially empty. The second variant adds a new file instance for each of the paths in *path_list*. .. function:: void cork_file_list_free(struct cork_file_list \*list) Free a file list. .. function:: void cork_file_list_add(struct cork_file_list \*list, struct cork_file \*file) Add *file* to *list*. The list takes control of the file instance; you must not try to free *file* yourself. .. function:: size_t cork_file_list_size(const struct cork_file_list \*list) Return the number of files in *list*. .. function:: struct cork_file \*cork_file_list_get(const struct cork_file_list \*list, size_t index) Return the file in *list* at the given *index*. The list still owns the file instance that's returned; you must not try to free it. Directory walking ================= .. function:: int cork_walk_directory(const char \*path, struct cork_dir_walker \*walker) Walk through the contents of a directory. *path* can be an absolute or relative path. If it's relative, it will be interpreted relative to the current directory. If *path* doesn't exist, or there are any problems reading the contents of the directory, we'll set an error condition and return ``-1``. To process the contents of the directory, you must provide a *walker* object, which contains several callback methods that we will call when files and subdirectories of *path* are encountered. Each method should return ``0`` on success. Unless otherwise noted, if we receive any other return result, we will abort the directory walk, and return that same result from the :c:func:`cork_walk_directory` call itself. In all of the following methods, *base_name* will be the base name of the entry within its immediate subdirectory. *rel_path* will be the relative path of the entry within the *path* that you originally asked to walk through. *full_path* will the full path to the entry, including *path* itself. .. type:: struct cork_dir_walker .. member:: int (\*file)(struct cork_dir_walker \*walker, const char \*full_path, const char \*rel_path, const char \*base_name) Called when a regular file is encountered. .. member:: int (\*enter_directory)(struct cork_dir_walker \*walker, const char \*full_path, const char \*rel_path, const char \*base_name) Called when a subdirectory of *path* of encountered. If you don't want to recurse into this directory, return :c:data:`CORK_SKIP_DIRECTORY`. .. macro:: CORK_SKIP_DIRECTORY .. member:: int (\*leave_directory)(struct cork_dir_walker \*walker, const char \*full_path, const char \*rel_path, const char \*base_name) Called when a subdirectory has been fully processed. libcork-1.0.0-rc3/docs/old/gc.rst000066400000000000000000000406171367645320400164750ustar00rootroot00000000000000.. _gc: ************************************ Reference-counted garbage collection ************************************ .. highlight:: c :: #include The functions in this section implement a reference counting garbage collector. The garbage collector handles reference cycles correctly. It is **not** a conservative garbage collector — i.e., we don't assume that every word inside an object might be a pointer. Instead, each garbage-collected object must provide a *recursion function* that knows how to delve down into any child objects that it references. The garbage collector is **not** thread-safe. If your application is multi-threaded, each thread will (automatically) have its own garbage collection context. There are two strategies that you can use when using the garbage collector in a multi-threaded application: * Have a single “master” thread be responsible for the lifecycle of every object. This thread is the only one allowed to interact with the garbage collector. **No** other threads are allowed to call any of the functions in this section, including the :c:func:`cork_gc_incref()` and :c:func:`cork_gc_decref()` functions. Other threads are allowed to access the objects that are managed by the garbage collector, but the master thread must ensure that all objects are live whenever another thread attempts to use them. This will require some kind of thread-safe communication or synchronization between the master thread and the worker threads. * Have a separate garbage collector per thread. Each object is “owned” by a single thread, and the object is managed by that thread's garbage collector. As with the first strategy, other threads can use any object, as long as the object's owner thread is able to guarantee that the object will be live for as long as it's needed. (Eventually we'll also support migrating an object from one garbage collector to another, but that feature isn't currently implemented.) The garbage collection implementation is based on the algorithm described in §3 of [1]_. .. [1] Bacon, DF and Rajan VT. *Concurrent cycle collection in reference counted systems*. Proc. ECOOP 2001. LNCS 2072. Creating a garbage collector ============================ .. function:: void cork_gc_init(void) Initalizes a garbage collection context for the current thread. Usually, you can allocate this on the stack of your ``main`` function:: int main(int argc, char ** argv) { cork_gc_init(); // use the GC context // and free it when you're done cork_gc_done(); } It's not required that you call this function at all; if you don't, we'll automatically initialize a garbage collection context for the current thread the first time you try to allocate a garbage-collected object. You can call this function, though, if you want to have more control over when the initialization occurs. .. function:: void cork_gc_done(void) Finalize the garbage collection context for the current thread. All objects created in this thread will be freed when this function returns. You must call this function in each thread that allocates garbage-collected objects, just before that thread finishes. (If your application is single-threaded, then you must call this function before the ``main`` function finishes.) If you don't, you'll almost certainly get memory leaks. Managing garbage-collected objects ================================== A garbage collection context can't be used to manage arbitrary objects, since each garbage-collected class must define some callback functions for interacting with the garbage collector. (The :ref:`next section ` contains more details.) Each garbage-collected class will provide its own constructor function for instantiating a new instance of that class. There aren't any explicit destructors for garbage-collected objects; instead you manage “references” to the objects. Each pointer to a garbage-collected object is a reference, and each object maintains a count of the references to itself. A newly constructed object starts with a reference count of ``1``. Whenever you save a pointer to a garbage-collected object, you should increase the object's reference count. When you're done with the pointer, you decrease its reference count. When the reference count drops to ``0``, the garbage collector frees the object. .. function:: void \*cork_gc_incref(void \*obj) Increments the reference count of an object *obj* that is managed by the current thread's garbage collector. We always return *obj* as a result, which allows you to use the following idiom:: struct my_obj * my_copy_of_obj = cork_gc_incref(obj); .. function:: void cork_gc_decref(void \*obj) Decrements the reference count of an object *obj* that is managed by the current thread's garbage collector If the reference count drops to ``0``, then the garbage collector will free the object. .. note:: It's safe to call this function with a ``NULL`` *obj* pointer; in this case, the function acts as a no-op. .. _borrow-ref: Borrowing a reference --------------------- While the strategy mentioned above implies that you should call :c:func:`cork_gc_incref()` and :c:func:`cork_gc_decref()` for *every* pointer to a garbage-collected object, you can sometimes get away without bumping the reference count. In particular, you can often *borrow* an existing reference to an object, if you can guarantee that the borrowed reference will exist for as long as you need access to the object. The most common example of this when you pass in a garbage-collected object as the parameter to a function:: int use_new_reference(struct my_obj *obj) { /* Here we're being pedantically correct, and incrementing obj's * reference count since we've got our own pointer to the object. */ cork_gc_incref(obj); /* Do something useful with obj */ /* And now that we're done with it, decrement the reference count. */ cork_gc_decref(obj); } int borrowed_reference(struct my_obj *obj) { /* We can assume that the caller has a valid reference to obj, so * we're just going to borrow that reference. */ /* Do something useful with obj */ } In this example, ``borrowed_reference`` doesn't need to update *obj*\ 's reference count. We assume that the caller has a valid reference to *obj* when it makes the call to ``borrowed_reference``. Moreover, we know that the caller can't possibly release this reference (via :c:func:`cork_gc_decref()`) until ``borrowed_reference`` returns. Since we can guarantee that the caller's reference to *obj* will exist for the entire duration of ``borrowed_reference``, we don't need to protect it with an ``incref``/``decref`` pair. .. _steal-ref: Stealing a reference -------------------- Another common pattern is for a “parent” object to maintain a reference to a “child” object. (For example, a container class might maintain references to all of the elements in the container, assuming that the container and elements are all garbage-collected objects.) When you have a network of objects like this, the parent object's constructor will usually take in a pointer to the child object as a parameter. If we strictly follow the basic referencing counting rules described above, you'll end up with something like:: struct child *child = child_new(); struct parent *parent = parent_new(child); cork_gc_decref(child); The ``child_new`` constructor gives us a reference to *child*. The ``parent_new`` constructor then creates a new reference to *child*, which will be stored somewhere in *parent*. We no longer need our own reference to *child*, so we immediately decrement its reference count. This is a common enough occurrence that many constructor functions will instead *steal* the reference passed in as a parameter. This means that the constructor takes control of the caller's reference. This allows us to rewrite the example as:: struct parent *parent = parent_new_stealing(child_new()); For functions that steal a reference, the caller **cannot** assume that the object pointed to by the stolen reference exists when the function returns. (If there's an error in ``parent_new_stealing``, for instance, it must release the stolen reference to *child* to prevent a memory leak.) If a function is going to steal a reference, but you also need to use the object after the function call returns, then you need to explicitly increment the reference count *before* calling the function:: struct child *child = child_new(); struct parent *parent = parent_new_stealing(cork_gc_incref(child)); /* Do something with child. */ /* And then release our reference when we're done. */ cork_gc_decref(child); .. note:: It's important to point out that not every constructor will steal the references passed in as parameters. Moreover, there are some constructors that steal references for some parameters but not for others. It entirely depends on what the “normal” use case is for the constructor. If you're almost always going to pass in a child object that was just created, and that will always be accessed via the parent, then the constructor will usually steal the reference. If the child can be referenced by many parents, then the constructor will usually *not* steal the reference. The documentation for each constructor function will explicitly state which references are stolen and which objects it creates new references for. .. _new-gc-class: Writing a new garbage-collected class ===================================== When you are creating a new class that you want to be managed by a garbage collector, there are two basic steps you need to follow: * Implement a set of callback functions that allow the garbage collector to interact with objects of the new class. * Use the garbage collector's allocation functions to allocate storage for instance of your class. You won't need to write a public destructor function, since objects of the new class will be destroyed automatically when the garbage collector determines that they're no longer needed. Garbage collector callback interface ------------------------------------ Each garbage-collected class must provide an implementation of the “callback interface”: .. type:: struct cork_gc_obj_iface .. member:: void (\*free)(void \*obj) This callback is called when a garbage-collected object is about to be freed. You can perform any special cleanup steps in this callback. You do **not** need to deallocate the object's storage, and you do **not** need to release any references that you old to other objects. Both of these steps will be taken care of for you by the garbage collector. If your class doesn't need any additional finalization steps, this entry in the callback interface can be ``NULL``. .. member:: void (\*recurse)(struct cork_gc \*gc, void \*obj, cork_gc_recurser recurse, void \*ud) This callback is how you inform the garbage collector of your references to other garbage-collected objects. The garbage collector will call this function whenever it needs to traverse through a graph of object references. Your implementation of this callback should just call *recurse* with each garbage-collected object that you hold a reference to. You must pass in *gc* as the first parameter to each call to *recurse*, and *ud* as the third parameter. Note that it's fine to call *recurse* with a ``NULL`` object pointer, which makes it slightly easier to write implementations of this callback. If instances of your class can never contain references to other garbage-collected objects, this entry in the callback interface can be ``NULL``. .. type:: void (\*cork_gc_recurser)(struct cork_gc \*gc, void \*obj, void \*ud) An opaque callback provided by the garbage collector when it calls an object's :c:member:`~cork_gc_obj_iface.recurse` method. .. type:: struct cork_gc An opaque type representing the current thread's garbage-collection context. You'll only need to use this type when implementing a :c:member:`~cork_gc_obj_iface.recurse` function. .. _gc-macros: Helper macros ~~~~~~~~~~~~~ There are several macros declared in :file:`libcork/helpers/gc.h` that make it easier to define the garbage-collection interface for a new class. .. note:: Unlike most libcork modules, these macros are **not** automatically defined when you include the ``libcork/core.h`` header file, since they don't include a ``cork_`` prefix. Because of this, we don't want to pollute your namespace unless you ask for the macros. To do so, you must explicitly include their header file:: #include .. macro:: _free_(SYMBOL name) _recurse_(SYMBOL name) These macros declare the *free* and *recurse* methods for a new class. The functions will be declared with exactly the signatures and parameter names shown in the entries for the :c:member:`~cork_gc_obj_iface.free` and :c:member:`~cork_gc_obj_iface.recurse` methods. You will almost certainly not need to refer to the method implementations directly, since you can use the :c:macro:`_gc_*_ <_gc_>` macros below to declare the interface struct. But if you do, they'll be called :samp:`{[name]}__free` and :samp:`{[name]}__recurse`. (Note the double underscore.) .. macro:: _gc_(SYMBOL name) _gc_no_free_(SYMBOL name) _gc_no_recurse_(SYMBOL name) _gc_leaf_(SYMBOL name) Define the garbage-collection interface struct for a new class. If you defined both ``free`` and ``recurse`` methods, you should use the ``_gc_`` variant. If you only defined one of the methods, you should use ``_gc_no_free_`` or ``_gc_no_recurse_``. If you didn't define either method, you should use ``_gc_free_``. Like the method definitions, you probably won't need to refer to the interface struct directly, since you can use the :c:func:`cork_gc_new` macro to allocate new instances of the new class. But if you do, it will be called :samp:`{[name]}__gc`. (Note the double underscore.) As an example, we can use these macros to define a new tree class:: #include struct tree { const char *name; struct tree *left; struct tree *right; }; _free_(tree) { struct tree *self = obj; cork_strfree(self->name); } _recurse_(tree) { struct tree *self = obj; recurse(self->left, ud); recurse(self->right, ud); } _gc_(tree); Allocating new garbage-collected objects ---------------------------------------- In your garbage-collected class's constructor, you must use one of the following functions to allocate the object's storage. (The garbage collector hides some additional state in the object's memory region, so you can't allocate the storage using ``malloc`` or :c:func:`cork_new()` directly.) .. function:: void \*cork_gc_alloc(size_t instance_size, struct cork_gc_obj_iface \*iface) Allocates a new garbage-collected object that is *instance_size* bytes large. *iface* should be a pointer to a callback interface for the object. If there are any problems allocating the new instance, the program will abort. .. function:: type \*cork_gc_new_iface(TYPE type, struct cork_gc_obj_iface \*iface) Allocates a new garbage-collected instance of *type*. The size of the memory region to allocate is calculated using the ``sizeof`` operator, and the result will be automatically cast to ``type *``. *iface* should be a pointer to a callback interface for the object. If there are any problems allocating the new instance, the program will abort. .. function:: struct name \*cork_gc_new(SYMBOL name) Allocates a new garbage-collected instance of :samp:`struct {[name]}`. (Note that you don't pass in the ``struct`` part of the type name.) We assume that the garbage collection interface was created using one of the :c:macro:`_gc_*_ <_gc_>` macros, using the same *name* parameter. Using these functions, could instantiate our example tree class as follows:: struct tree * tree_new(const char *name) { struct tree *self = cork_gc_new(tree); self->name = cork_strdup(name); self->left = NULL; self->right = NULL; return self; } libcork-1.0.0-rc3/docs/old/hash-table.rst000066400000000000000000000421241367645320400201070ustar00rootroot00000000000000.. _hash-table: *********** Hash tables *********** .. highlight:: c :: #include This section defines a hash table class. Our hash table implementation is based on the public domain hash table package written in the late 1980's by Peter Moore at UC Berkeley. The keys and values of a libcork hash table are both represented by ``void *`` pointers. You can also store integer keys or values, as long as you use the :c:type:`intptr_t` or :c:type:`uintptr_t` integral types. (These are the only integer types guaranteed by the C99 standard to fit within the space used by a ``void *``.) The keys of the hash table can be any arbitrary type; you must provide two functions that control how key pointers are used to identify entries in the table: the *hasher* (:c:type:`cork_hash_f`) and the *comparator* (:c:type:`cork_equals_f`). It's your responsibility to ensure that these two functions are consistent with each other — i.e., if two keys are equal according to your comparator, they must also map to the same hash value. (The inverse doesn't need to be true; it's fine for two keys to have the same hash value but not be equal.) .. type:: struct cork_hash_table A hash table. .. function:: struct cork_hash_table \*cork_hash_table_new(size_t initial_size, unsigned int flags) Creates a new hash table instance. If you know roughly how many entries you're going to add to the hash table, you can pass this in as the *initial_size* parameter. If you don't know how many entries there will be, you can use ``0`` for this parameter instead. You will most likely need to provide a hashing function and a comparison function for the new hash table (using :c:func:`cork_hash_table_set_hash` and :c:func:`cork_hash_table_set_equals`), which will be used to compare key values of the entries in the table. If you do not provide your own functions, the default functions will compare key pointers as-is without interpreting what they point to. The *flags* field is currently unused, and should be ``0``. In the future, this parameter will be used to let you customize the behavior of the hash table. .. function:: void cork_hash_table_free(struct cork_hash_table \*table) Frees a hash table. If you have provided a :c:func:`free_key ` or :c:func:`free_value ` callback for *table*, then we'll automatically free any remaining keys and/or values. .. type:: struct cork_hash_table_entry The contents of an entry in a hash table. .. member:: void \*key The key for this entry. There won't be any other entries in the hash table with the same key, as determined by the comparator function that you provide. .. member:: void \*value The value for this entry. The entry's value is completely opaque to the hash table; we'll never need to compare or interrogate the values in the table. .. member:: cork_hash hash The hash value for this entry's key. This field is strictly read-only. Callback functions ------------------ You can use the callback functions in this section to customize the behavior of a hash table. .. function:: void cork_hash_table_set_user_data(struct cork_hash_table \*table, void \*user_data, cork_free_f free_user_data) Lets you provide an opaque *user_data* pointer to each of the hash table's callbacks. This lets you provide additional state, other than the hash table itself to those callbacks. If *free_user_data* is not ``NULL``, then the hash table will take control of *user_data*, and will use the *free_user_data* function to free it when the hash table is destroyed. Key management ~~~~~~~~~~~~~~ .. function:: void cork_hash_table_set_hash(struct cork_hash_table \*table, void \*user_data, cork_hash_f hash) The hash table will use the ``hash`` callback to calculate a hash value for each key. .. type:: cork_hash (\*cork_hash_f)(void \*user_data, const void \*key) .. note:: It's important to use a hash function that has a uniform distribution of hash values for the set of values you expect to use as hash table keys. In particular, you *should not* rely on there being a prime number of hash table bins to get the desired uniform distribution. The :ref:`hash value functions ` that we provide have uniform distribution (and are fast), and should be safe to use for most key types. .. function:: void cork_hash_table_set_equals(struct cork_hash_table \*table, void \*user_data, cork_equals_f equals) The hash table will use the ``equals`` callback to compare keys. .. type:: bool (\*cork_equals_f)(void \*user_data, const void \*key1, const void \*key2) Built-in key types ~~~~~~~~~~~~~~~~~~ We also provide a couple of specialized constructors for common key types, which prevents you from having to duplicate common hashing and comparison functions. .. function:: struct cork_hash_table \*cork_string_hash_table_new(size_t initial_size, unsigned int flags) Create a hash table whose keys will be C strings. .. function:: struct cork_hash_table \*cork_pointer_hash_table_new(size_t initial_size, unsigned int flags) Create a hash table where keys should be compared using standard pointer equality. (In other words, keys should only be considered equal if they point to the same physical object.) Automatically freeing entries ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. function:: void cork_hash_table_set_free_key(struct cork_hash_table \*table, void \*user_data, cork_free_f free_key) void cork_hash_table_set_free_value(struct cork_hash_table \*table, void \*user_data, cork_free_f free_value) If you provide ``free_key`` and/or ``free_value`` callbacks, then the hash table will take ownership of any keys and values that you add. The hash table will use these callbacks to free each key and value when entries are explicitly deleted (via :c:func:`cork_hash_table_delete` or :c:func:`cork_hash_table_clear`), and when the hash table itself is destroyed. Adding and retrieving entries ----------------------------- There are several functions that can be used to add or retrieve entries from a hash table. Each one has slightly different semantics; you should read through them all before deciding which one to use for a particular use case. .. note:: Each of these functions comes in two variants. The “normal” variant will use the hash table's :c:func:`hash ` callback to calculate the hash value for the *key* parameter. This is the normal way to interact with a hash table. When using the ``_hash`` variant, you must calculate the hash value for each key yourself, and pass in this hash value as an extra parameter. The hash table's :c:func:`hash ` callback is not invoked. This can be more efficient, if you've already calculated or cached the hash value. It is your responsibility to make sure that the hash values you provide are consistent, just like when you write a :c:func:`hash ` callback. .. function:: void \*cork_hash_table_get(const struct cork_hash_table \*table, const void \*key) void \*cork_hash_table_get_hash(const struct cork_hash_table \*table, cork_hash hash, const void \*key) Retrieves the value in *table* with the given *key*. We return ``NULL`` if there's no corresponding entry in the table. This means that, using this function, you can't tell the difference between a missing entry, and an entry that's explicitly mapped to ``NULL``. If you need to distinguish those cases, you should use :c:func:`cork_hash_table_get_entry()` instead. .. function:: struct cork_hash_table_entry \*cork_hash_table_get_entry(const struct cork_hash_table \*table, const void \*key) struct cork_hash_table_entry \*cork_hash_table_get_entry_hash(const struct cork_hash_table \*table, cork_hash hash, const void \*key) Retrieves the entry in *table* with the given *key*. We return ``NULL`` if there's no corresponding entry in the table. You are free to update the :c:member:`key ` and :c:member:`value ` fields of the entry. However, you must ensure that any new key is considered “equal” to the old key, according to the hasher and comparator functions that you provided for this hash table. .. function:: struct cork_hash_table_entry \*cork_hash_table_get_or_create(struct cork_hash_table \*table, void \*key, bool \*is_new) struct cork_hash_table_entry \*cork_hash_table_get_or_create_hash(struct cork_hash_table \*table, cork_hash hash, void \*key, bool \*is_new) Retrieves the entry in *table* with the given *key*. If there is no entry with the given key, it will be created. (If we can't create the new entry, we'll return ``NULL``.) We'll fill in the *is_new* output parameter to indicate whether the entry is new or not. If a new entry is created, its value will initially be ``NULL``, but you can update this as necessary. You can also update the entry's key, though you must ensure that any new key is considered “equal” to the old key, according to the hasher and comparator functions that you provided for this hash table. This is necessary, for instance, if the *key* parameter that we search for was allocated on the stack. We can't save this stack key into the hash table, since it will disapppear as soon as the calling function finishes. Instead, you must create a new key on the heap, which can be saved into the entry. For efficiency, you'll only want to allocate this new heap-stored key if the entry is actually new, especially if there will be a lot successful lookups of existing keys. .. function:: int cork_hash_table_put(struct cork_hash_table \*table, void \*key, void \*value, bool \*is_new, void \*\*old_key, void \*\*old_value) int cork_hash_table_put_hash(struct cork_hash_table \*table, cork_hash hash, void \*key, void \*value, bool \*is_new, void \*\*old_key, void \*\*old_value) Add an entry to a hash table. If there is already an entry with the given key, we will overwrite its key and value with the *key* and *value* parameters. If the *is_new* parameter is non-\ ``NULL``, we'll fill it in to indicate whether the entry is new or already existed in the table. If the *old_key* and/or *old_value* parameters are non-\ ``NULL``, we'll fill them in with the existing key and value. This can be used, for instance, to finalize an overwritten key or value object. .. function:: void cork_hash_table_delete_entry(struct cork_hash_table \*table, struct cork_hash_table_entry \*entry) Removes *entry* from *table*. You must ensure that *entry* refers to a valid, existing entry in the hash table. This function can be more efficient than :c:func:`cork_hash_table_delete` if you've recently retrieved a hash table entry using :c:func:`cork_hash_table_get_or_create` or :c:func:`cork_hash_table_get_entry`, since we won't have to search for the entry again. .. function:: bool cork_hash_table_delete(struct cork_hash_table \*table, const void \*key, void \*\*deleted_key, void \*\*deleted_value) bool cork_hash_table_delete_hash(struct cork_hash_table \*table, cork_hash hash, const void \*key, void \*\*deleted_key, void \*\*deleted_value) Removes the entry with the given *key* from *table*. If there isn't any entry with the given key, we'll return ``false``. If the *deleted_key* and/or *deleted_value* parameters are non-\ ``NULL``, we'll fill them in with the deleted key and value. This can be used, for instance, to finalize the key or value object that was stored in the hash table entry. If you have provided a :c:func:`free_key ` or :c:func:`free_value ` callback for *table*, then we'll automatically free the key and/or value of the deleted entry. (This happens before ``cork_hash_table_delete`` returns, so you must not provide a *deleted_key* and/or *deleted_value* in this case.) Other operations ---------------- .. function:: size_t cork_hash_table_size(const struct cork_hash_table \*table) Returns the number of entries in a hash table. .. function:: void cork_hash_table_clear(struct cork_hash_table \*table) Removes all of the entries in a hash table, without finalizing the hash table itself. If you have provided a :c:func:`free_key ` or :c:func:`free_value ` callback for *table*, then we'll automatically free any remaining keys and/or values. .. function:: int cork_hash_table_ensure_size(struct cork_hash_table \*table, size_t desired_count) Ensures that *table* has enough space to efficiently store a certain number of entries. This can be used to reduce (or eliminate) the number of resizing operations needed to add a large number of entries to the table, when you know in advance roughly how many entries there will be. Iterating through a hash table ------------------------------ There are two strategies you can use to access all of the entries in a hash table: *mapping* and *iterating*. Iteration order ~~~~~~~~~~~~~~~ Regardless of whether you use the mapping or iteration functions, we guarantee that the collection of items will be processed in the same order that they were added to the hash table. Mapping ~~~~~~~ With mapping, you write a mapping function that will be applied to each entry in the table. (In this case, libcork controls the loop that steps through each entry.) .. function:: void cork_hash_table_map(struct cork_hash_table \*table, void \*user_data, cork_hash_table_map_f map) Applies the *map* function to each entry in a hash table. The *map* function's :c:type:`cork_hash_table_map_result` return value can be used to influence the iteration. .. type:: enum cork_hash_table_map_result (\*cork_hash_table_map_f)(void \*user_data, struct cork_hash_table_entry \*entry) The function that will be applied to each entry in a hash table. The function's return value can be used to influence the iteration: .. type:: enum cork_hash_table_map_result .. var:: CORK_HASH_TABLE_CONTINUE Continue the current :c:func:`cork_hash_table_map()` operation. If there are any remaining elements, the next one will be passed into another call of the *map* function. .. var:: CORK_HASH_TABLE_ABORT Stop the current :c:func:`cork_hash_table_map()` operation. No more entries will be processed after this one, even if there are remaining elements in the hash table. .. var:: CORK_HASH_TABLE_DELETE Continue the current :c:func:`cork_hash_table_map()` operation, but first delete the entry that was just processed. If there are any remaining elements, the next one will be passed into another call of the *map* function. For instance, you can manually calculate the number of entries in a hash table as follows (assuming you didn't want to use the built-in :c:func:`cork_hash_table_size()` function, of course):: static enum cork_hash_table_map_result count_entries(void *user_data, struct cork_hash_table_entry *entry) { size_t *count = user_data; (*count)++; return CORK_HASH_TABLE_MAP_CONTINUE; } struct cork_hash_table *table = /* from somewhere */; size_t count = 0; cork_hash_table_map(table, &count, count_entries); /* the number of entries is now in count */ Iterating ~~~~~~~~~ The second strategy is to iterate through the entries yourself. Since the internal struture of the :c:type:`cork_hash_table` type is opaque (and slightly more complex than a simple array), you have to use a special “iterator” type to manage the manual iteration. Note that unlike when using a mapping function, it is **not** safe to delete entries in a hash table as you manually iterate through them. .. type:: struct cork_hash_table_iterator A helper type for manually iterating through the entries in a hash table. All of the fields in this type are private. You'll usually allocate this type on the stack. .. function:: void cork_hash_table_iterator_init(struct cork_hash_table \*table, struct cork_hash_table_iterator \*iterator) Initializes a new iterator for the given hash table. .. function:: struct cork_hash_table_entry \*cork_hash_table_iterator_next(struct cork_hash_table_iterator \*iterator) Returns the next entry in *iterator*\ 's hash table. If you've already iterated through all of the entries in the table, we'll return ``NULL``. With these functions, manually counting the hash table entries looks like:: struct cork_hash_table *table = /* from somewhere */; struct cork_hash_table_iterator iter; struct cork_hash_table_entry *entry; size_t count = 0; cork_hash_table_iterator_init(table, &iter); while ((entry = cork_hash_table_iterator_next(&iter)) != NULL) { count++; } /* the number of elements is now in count */ libcork-1.0.0-rc3/docs/old/hash-values.rst000066400000000000000000000062551367645320400203240ustar00rootroot00000000000000.. _hash-values: *********** Hash values *********** .. highlight:: c :: #include The functions in this section can be used to produce fast, good hash values. .. note:: For the curious, libcork currently uses the public-domain `MurmurHash3 `_ as its hash implementation. Hashing in C code ----------------- A common pattern would be something along the lines of:: struct my_type { int a; long b; double c; size_t name_length; const char *name; }; cork_hash my_type_hash(const struct my_type *self) { /* hash of "struct my_type" */ cork_hash hash = 0xd4a130d8; hash = cork_hash_variable(hash, self->a); hash = cork_hash_variable(hash, self->b); hash = cork_hash_variable(hash, self->c); hash = cork_hash_buffer(hash, self->name, self->name_length); return hash; } In this example, the seed value (``0xd4a130d8``) is the hash of the constant string ``"struct my_type"``. You can produce seed values like this using the :ref:`cork-hash ` script described below:: $ cork-hash "struct my_type" 0xd4a130d8 .. type:: uint32_t cork_hash .. function:: cork_hash cork_hash_buffer(cork_hash seed, const void \*src, size_t len) cork_hash cork_hash_variable(cork_hash seed, TYPE val) Incorporate the contents of the given binary buffer or variable into a hash value. For the ``_variable`` variant, *val* must be an lvalue visible in the current scope. The hash values produces by these functions can change over time, and might not be consistent across different platforms. The only guarantee is that hash values will be consistest for the duration of the current process. .. function:: cork_hash cork_stable_hash_buffer(cork_hash seed, const void \*src, size_t len) cork_hash cork_stable_hash_variable(cork_hash seed, TYPE val) Stable versions of :c:func:`cork_hash_buffer` and :c:func:`cork_hash_variable`. We guarantee that the hash values produced by this function will be consistent across different platforms, and across different versions of the libcork library. .. type:: cork_big_hash .. function:: cork_big_hash cork_big_hash_buffer(cork_big_hash seed, const void \*src, size_t len) Incorporate the contents of the given binary buffer into a "big" hash value. A big hash value has a much larger space of possible hash values (128 bits vs 32). .. function:: bool cork_big_hash_equal(cork_big_hash hash1, cork_big_hash hash2) Compare two big hash values for equality. .. _cork-hash: Hashing from the command line ----------------------------- Several parts of libcork use hash values as identifiers; you use a unique string to identify part of your code, and use the hash of that string as the actual identifier value. We provide a command-line utility that you can use to produce these hash values: .. code-block:: none cork-hash .. describe:: The string to hash. This should be provided as a single argument on the command line, so if your string contains spaces or other shell meta-characters, you must enclose the string in quotes. libcork-1.0.0-rc3/docs/old/index.rst000066400000000000000000000024521367645320400172060ustar00rootroot00000000000000.. _index: |project_name| documentation ============================ This is the documentation for |project_name| |release|, last updated |today|. Introduction ------------ So what is libcork, exactly? It's a “simple, easily embeddable, cross-platform C library”. It falls roughly into the same category as glib_ or APR_ in the C world; the STL, POCO_, or QtCore_ in the C++ world; or the standard libraries of any decent dynamic language. So if libcork has all of these comparables, why a new library? Well, none of the C++ options are really applicable here. And none of the C options work, because one of the main goals is to have the library be highly modular, and useful in resource-constrained systems. Once we describe some of the design decisions that we've made in libcork, you'll hopefully see how this fits into an interesting niche of its own. .. _glib: http://library.gnome.org/devel/glib/ .. _APR: http://apr.apache.org/ .. _POCO: http://pocoproject.org/ .. _QtCore: http://qt.nokia.com/ Contents -------- .. toctree:: :maxdepth: 2 config versions visibility basic-types byte-order attributes allocation errors gc mempool ds cli files process subprocess threads Indices and tables ------------------ * :ref:`genindex` * :ref:`search` libcork-1.0.0-rc3/docs/old/int128.rst000066400000000000000000000120511367645320400171200ustar00rootroot00000000000000.. _int128: **************** 128-bit integers **************** .. highlight:: c :: #include We provide an API for working with unsigned, 128-bit integers. Unlike libraries like GMP_, our goal is not to support arbitrarily large integers, but to provide optimized support for this one specific integer type. We might add support for additional large integer types in the future, as need arises, but the focus will always be on a small number of specific types, and not on arbitrary sizes. For that, use GMP. .. _GMP: http://gmplib.org/ .. type:: cork_u128 An unsigned, 128-bit integer. You can assume that instances of this type will be exactly 16 bytes in size, and that the integer value will be stored in host-endian order. This type is currently implemented as a ``struct``, but none of its members are part of the public API. Initialization ============== .. function:: cork_u128 cork_u128_from_32(uint32_t i0, uint32_t i1, uint32_t i2, uint32_t i3) cork_u128 cork_u128_from_64(uint64_t i0, uint64_t i1) Return a 128-bit integer initialized with the given portions. The various *iX* pieces are given in big-endian order, regardless of the host's endianness. For instance, both of the following initialize an integer to :math:`2^{64}`:: cork_u128 value1 = cork_u128_from_32(0, 1, 0, 0); cork_u128 value2 = cork_u128_from_64(1, 0); Accessing subsets ================= .. function:: &uint8_t cork_u128_be8(cork_u128 value, unsigned int index) &uint16_t cork_u128_be16(cork_u128 value, unsigned int index) &uint32_t cork_u128_be32(cork_u128 value, unsigned int index) &uint64_t cork_u128_be64(cork_u128 value, unsigned int index) Returns a reference to a portion of a 128-bit integer. Regardless of the host's endianness, the indices are counted in big-endian order — i.e., an *index* of ``0`` will always return the most-significant portion of *value*. The result is a valid lvalue, so you can assign to it to update the contents of *value*:: cork_u128 value; cork_u128_be64(value, 0) = 4; cork_u128_be64(value, 1) = 16; Arithmetic ========== All of the functions in this section are implemented as macros or inline functions, so you won't incur any function-call overhead when using them. .. function:: cork_u128 cork_u128_add(cork_128 a, cork_u128 b) cork_u128 cork_u128_sub(cork_128 a, cork_u128 b) Add or subtract two 128-bit integers, returning the result. :: cork_u128 a = cork_u128_from_32(0, 10); cork_u128 b = cork_u128_from_32(0, 3); cork_u128 c = cork_u128_add(a, b); cork_u128 d = cork_u128_sub(a, b); // c == 13 && d == 7 Comparison ========== All of the functions in this section are implemented as macros or inline functions, so you won't incur any function-call overhead when using them. .. function:: bool cork_u128_eq(cork_128 a, cork_u128 b) bool cork_u128_ne(cork_128 a, cork_u128 b) bool cork_u128_lt(cork_128 a, cork_u128 b) bool cork_u128_le(cork_128 a, cork_u128 b) bool cork_u128_gt(cork_128 a, cork_u128 b) bool cork_u128_ge(cork_128 a, cork_u128 b) Compare two 128-bit integers. These functions correspond, respectively, to the ``==``, ``!=``, ``<``, ``<=``, ``>``, and ``>=`` operators. :: cork_u128 a = cork_u128_from_32(0, 10); cork_u128 b = cork_u128_from_32(0, 3); // cork_u128_eq(a, b) → false // cork_u128_ne(a, b) → true // cork_u128_eq(a, a) → true // cork_u128_gt(a, b) → true // cork_u128_ge(a, a) → true // and so on Printing ======== .. function:: const char \*cork_u128_to_decimal(char \*buf, cork_u128 value) const char \*cork_u128_to_hex(char \*buf, cork_u128 value) const char \*cork_u128_to_padded_hex(char \*buf, cork_u128 value) Write the string representation of *value* into *buf*. The ``decimal`` and ``hex`` variants do not include any padding in the result. The ``padded_hex`` variant pads the result with ``0`` characters so that the string representation of every :c:type:`cork_u128` has the same width. You must provide the buffer that the string representation will be rendered into. (This ensures that these functions are thread-safe.) The return value will be some portion of this buffer, but might not be *buf* itself. You are responsible for ensuring that *buf* is large enough to hold the string representation of any valid 128-bit integer. The :c:macro:`CORK_U128_DECIMAL_LENGTH` and :c:macro:`CORK_U128_HEX_LENGTH` macros can be helpful for this:: char buf[CORK_U128_DECIMAL_LENGTH]; cork_u128 value = cork_u128_from_32(0, 125); printf("%s\n", cork_u128_to_decimal(buf, value)); // prints "125\n" .. macro:: CORK_U128_DECIMAL_LENGTH CORK_U128_HEX_LENGTH The maximum length of the decimal or hexadecimal string representation of a 128-bit integer, including a ``NUL`` terminator. libcork-1.0.0-rc3/docs/old/managed-buffer.rst000066400000000000000000000110301367645320400207320ustar00rootroot00000000000000.. _managed-buffer: ********************** Managed binary buffers ********************** .. highlight:: c :: #include This section defines an interface for handling reference-counted binary buffers. The :c:type:`cork_managed_buffer` type wraps a buffer with a simple reference count, and takes care of freeing the necessary resources when the reference count drops to zero. There should only be a single :c:type:`cork_managed_buffer` instance for any given buffer, regardless of how many threads or functions access that buffer. Each thread or function that uses the buffer does so via a :c:type:`cork_slice` instance. This type is meant to be allocated directly on the stack (or in some other managed storage), and keeps a pointer to the managed buffer instance that it slices. As its name implies, a slice can refer to a subset of the buffer. .. type:: struct cork_managed_buffer A “managed buffer”, which wraps a buffer with a simple reference count. Managed buffer consumers should consider all of the fields of this class private. Managed buffer implementors should fill in this fields when constructing a new ``cork_managed_buffer`` instance. .. member:: const void \*buf The buffer that this instance manages. .. member:: size_t size The size of :c:member:`buf`. .. member:: volatile int ref_count A reference count for the buffer. If this drops to ``0``, the buffer will be finalized. .. member:: struct cork_managed_buffer_iface \*iface The managed buffer implementation for this instance. .. function:: struct cork_managed_buffer \*cork_managed_buffer_ref(struct cork_managed_buffer \*buf) Atomically increase the reference count of a managed buffer. This function is thread-safe. .. function:: void cork_managed_buffer_unref(struct cork_managed_buffer \*buf) Atomically decrease the reference count of a managed buffer. If the reference count falls to ``0``, the instance is freed. This function is thread-safe. .. function:: int cork_managed_buffer_slice(struct cork_slice \*dest, struct cork_managed_buffer \*buffer, size_t offset, size_t length) int cork_managed_buffer_slice_offset(struct cork_slice \*dest, struct cork_managed_buffer \*buffer, size_t offset) Initialize a new slice that refers to a subset of a managed buffer. The *offset* and *length* parameters identify the subset. (For the ``_slice_offset`` variant, the *length* is calculated automatically to include all of the managed buffer content starting from *offset*.) If these parameters don't refer to a valid portion of the buffer, we return ``false``, and you must not try to deference the slice's :c:member:`buf ` pointer. If the slice is valid, we return ``true``. Regardless of whether the new slice is valid, you **must** ensure that you call :c:func:`cork_slice_finish()` when you are done with the slice. Predefined managed buffer implementations ----------------------------------------- .. function:: struct cork_managed_buffer \*cork_managed_buffer_new_copy(const void \*buf, size_t size) Make a copy of *buf*, and allocate a new managed buffer to manage this copy. The copy will automatically be freed when the managed buffer's reference count drops to ``0``. .. type:: void (\*cork_managed_buffer_freer)(void \*buf, size_t size) A finalization function for a managed buffer created by :c:func:`cork_managed_buffer_new()`. .. function:: struct cork_managed_buffer \*cork_managed_buffer_new(const void \*buf, size_t size, cork_managed_buffer_freer free) Allocate a new managed buffer to manage an existing buffer (*buf*). The existing buffer is *not* copied; the new managed buffer instance takes control of it. When the managed buffer's reference count drops to ``0``, it will call *free* to finalize *buf*. This is a helper function, and keeps you from having to write a complete custom managed buffer implementation when you don't need to store any additional state in the managed buffer object. .. note:: The *free* function is *not* responsible for freeing the ``cork_managed_buffer`` instance itself. Custom managed buffer implementations ------------------------------------- .. type:: struct cork_managed_buffer_iface The interface of methods that managed buffer implementations must provide. .. member:: void (\*free)(struct cork_managed_buffer \*self) Free the contents of a managed buffer, and the ``cork_managed_buffer`` instance itself. libcork-1.0.0-rc3/docs/old/mempool.rst000066400000000000000000000155711367645320400175550ustar00rootroot00000000000000.. _mempool: ************ Memory pools ************ .. highlight:: c :: #include The functions in this section let you define *memory pools*, which allow you to reduce the overhead of allocating and freeing large numbers of small objects. Instead of generating a ``malloc`` call for each individual object, the memory pool allocates a large *block* of memory, and then subdivides this block of memory into objects of the desired size. The free objects in the memory pool are linked together in a singly-linked list, which means that allocation and deallocation is usually a (very small) constant-time operation. .. note:: Memory pools are *not* thread safe; if you have multiple threads allocating objects of the same type, they'll need separate memory threads. Basic interface --------------- .. type:: struct cork_mempool A memory pool. All of the objects created by the memory pool will be the same size; this size is provided when you initialize the memory pool. .. function:: struct cork_mempool \*cork_mempool_new_size(size_t element_size) struct cork_mempool \*cork_mempool_new(TYPE type) Allocate a new memory pool. The size of the objects allocated by the memory pool is given either as an explicit *element_size*, or by giving the *type* of the objects. The blocks allocated by the memory pool will be of a default size (currently 4Kb). .. function:: struct cork_mempool \*cork_mempool_new_size_ex(size_t element_size, size_t block_size) struct cork_mempool \*cork_mempool_new_ex(TYPE type, size_t block_size) Allocate a new memory pool. The size of the objects allocated by the memory pool is given either as an explicit *element_size*, or by giving the *type* of the objects. The blocks allocated by the memory pool will be *block_size* bytes large. .. function:: void cork_mempool_free(struct cork_mempool \*mp) Free a memory pool. You **must** have already freed all of the objects allocated by the pool; if you haven't, then this function will cause the current process to abort. .. function:: void \*cork_mempool_new_object(struct cork_mempool \*mp) Allocate a new object from the memory pool. .. function:: void cork_mempool_free_object(struct cork_mempool \*mp, void \*ptr) Free an object that was allocated from the memory pool. .. _mempool-lifecycle: Initializing and finalizing objects ----------------------------------- When you free an object that was allocated via a memory pool, the memory for that object isn't actually freed immediately. (That's kind of the reason that you're using a memory pool in the first place.) This means that if your object contains any fields that are expensive to initialize and finalize, it can make sense to postpone the finalization of those fields until the memory for the object itself is actually freed. As an example, let's say you have the following type that you're going to allocate via a memory pool:: struct my_data { struct cork_buffer scratch_space; int age; }; Our first attempt at a constructor and destructor would then be:: static cork_mempool *pool; pool = cork_mempool_new(struct my_data); struct my_data * my_data_new(void) { struct my_data *self = cork_mempool_new_object(pool); if (self == NULL) { return NULL; } cork_buffer_init(&self->scratch_space); return self; } void my_data_free(struct my_data *self) { cork_buffer_done(&self->scratch_space); cork_mempool_free_object(pool, self); } What's interesting about this example is that the ``scratch_space`` field, being a :c:type:`cork_buffer`, allocates some space internally to hold whatever data we're building up in the buffer. When we call :c:func:`cork_buffer_done` in our destructor, that memory is returned to the system. Later on, when we allocate a new ``my_data``, the :c:func:`cork_mempool_new_object` call in our constructor might get this same physical instance back. We'll then proceed to re-initialize the ``scratch_space`` buffer, which will then reallocate its internal buffer space as we use the type. Since we're using a memory pool to reuse the memory for the ``my_data`` instance, we might as well try to reuse the memory for the ``scratch_space`` field, as well. To do this, you provide initialization and finalization callbacks: .. function:: void cork_mempool_set_user_data(struct cork_mempool \*mp, void \*user_data, cork_free_f free_user_data) void cork_mempool_set_init_object(struct cork_mempool \*mp, cork_init_f init_object) void cork_mempool_set_done_object(struct cork_mempool \*mp, cork_done_f done_object) Provide callback functions that will be used to initialize and finalize each object created by the memory pool. So, instead of putting the initialization logic into our constructor, we put it into the ``init_object`` function. Similarly, the finalization logic goes into ``done_object``, and not our destructor:: static void my_data_init(void *user_data, void *vself) { struct my_data *self = vself; cork_buffer_init(&self->scratch_space); return 0; } static void my_data_done(void *user_data, void *vself) { struct my_data *self = vself; cork_buffer_done(&self->scratch_space); } static cork_mempool *pool; pool = cork_mempool_new(pool, struct my_data); cork_mempool_set_init_object(pool, my_data_init); cork_mempool_set_done_object(pool, my_data_done); struct my_data * my_data_new(void) { return cork_mempool_new_object(pool); } void my_data_free(struct my_data *self) { cork_mempool_free_object(pool, self); } In this implementation, the ``scratch_space`` buffer is initialized when the memory for an instance is first allocated, and it's not finalized until the memory for the instance is returned to the system. (Which basically means "when the memory pool itself is freed".) A caveat with this approach is that we've no longer guaranteed that the ``scratch_space`` buffer is empty when ``my_data_new`` returns — if we're reusing an existing object, then the contents of the "previous" object's buffer will still be there. We can either make sure that consumers of ``my_data`` don't assume anything about the contents of ``scratch_space``, or better yet, we can *reset* the fields in our constructor object:: struct my_data * my_data_new(void) { struct my_data *self = cork_mempool_new_object(pool); cork_buffer_clear(&self->scratch_space); return self; } In this example, we can reset the buffer just by clearing it. If resetting is more involved, it can sometimes be better to leave the instance in a "messy" state, and have your clients not make assumptions. But if you do this, make sure to be clear about it in your documentation. libcork-1.0.0-rc3/docs/old/net-addresses.rst000066400000000000000000000126311367645320400206400ustar00rootroot00000000000000.. _net-addresses: ***************** Network addresses ***************** .. highlight:: c :: #include IP addresses ------------ libcork provides C types for storing IPv4 and IPv6 addresses, as well as a union type for storing a generic IP address, regardless of whether it's IPv4 or IPv6. (This lets you distinguish between an IPv4 address and the equivalent ``::ffff:0:0/96`` IPv4-mapped IPv6 address.) .. type:: struct cork_ipv4 struct cork_ipv6 An IPv4 or IPv6 address. The address is stored in memory exactly as it would be if sent over a network connection — i.e., in network-endian order, regardless of the endianness of the current host. The types are also guaranteed to be exactly the size of an actual IPv4 or IPv6 address (without additional padding), so they can be embedded directly into ``struct`` types that represent binary disk/wire formats. The contents of these types should be considered opaque. You should use the accessor functions defined below to interact with the IP address. .. type:: struct cork_ip A single union type that can contain either an IPv4 or IPv6 address. This type contains a discriminator field, so you can't use it directly in a binary disk/wire format type. .. member:: unsigned int version Either ``4`` or ``6``, indicating whether the current IP address is an IPv4 address or an IPv6 address. .. member:: struct cork_ipv4 ip.v4 struct cork_ipv6 ip.v6 Gives you access to the underlying :c:type:`cork_ipv4` or :c:type:`cork_ipv6` instance for the current address. It's your responsibility to check the :c:member:`cork_ip.version` field and only access the union branch that corresponds to the current IP version. .. function:: void cork_ipv4_copy(struct cork_ipv4 \*addr, const void \*src) void cork_ipv6_copy(struct cork_ipv6 \*addr, const void \*src) void cork_ip_from_ipv4(struct cork_ip \*addr, const void \*src) void cork_ip_from_ipv6(struct cork_ip \*addr, const void \*src) Initializes a :c:type:`cork_ipv4`, :c:type:`cork_ipv6`, or :c:type:`cork_ip` instance from an existing IP address somewhere in memory. The existing address doesn't have to be an instance of the :c:type:`cork_ipv4` or :c:type:`cork_ipv6` types, but it does have to be a well-formed address. (For IPv4, it must be 4 bytes long; for IPv6, 16 bytes long. And in both cases, the address must already be in network-endian order, regardless of the host's endianness.) .. function:: int cork_ipv4_init(struct cork_ipv4 \*addr, const char \*str) int cork_ipv6_init(struct cork_ipv6 \*addr, const char \*str) int cork_ip_init(struct cork_ip \*addr, const char \*str) Initializes a :c:type:`cork_ipv4`, :c:type:`cork_ipv6`, or :c:type:`cork_ip` instance from the string representation of an IP address. *str* must point to a string containing a well-formed IP address. (Dotted-quad for an IPv4, and colon-hex for IPv6.) Moreover, the version of the IP address in *str* must be compatible with the function that you call: it can't be an IPv6 address if you call ``cork_ipv4_init``, and it can't be an IPv4 address if you call ``cork_ipv6_init``. If *str* doesn't represent a valid address (of a compatible IP version), then we leave *addr* unchanged, fill in the current error condition with a :c:data:`CORK_NET_ADDRESS_PARSE_ERROR` error, and return ``-1``. .. function:: bool cork_ipv4_equal(const struct cork_ipv4 \*addr1, const struct cork_ipv4 \*addr2) bool cork_ipv6_equal(const struct cork_ipv6 \*addr1, const struct cork_ipv6 \*addr2) bool cork_ip_equal(const struct cork_ip \*addr1, const struct cork_ip \*addr2) Checks two IP addresses for equality. .. macro:: CORK_IPV4_STRING_LENGTH CORK_IPV6_STRING_LENGTH CORK_IP_STRING_LENGTH The maximum length of the string representation of an IPv4, IPv6, or generic IP address, including a ``NUL`` terminator. .. function:: void cork_ipv4_to_raw_string(const struct cork_ipv4 \*addr, char \*dest) void cork_ipv6_to_raw_string(const struct cork_ipv6 \*addr, char \*dest) void cork_ip_to_raw_string(const struct cork_ip \*addr, char \*dest) Fills in *dest* with the string representation of an IPv4, IPv6, or generic IP address. You are responsible for ensuring that *dest* is large enough to hold the string representation of any valid IP address of the given version. The :c:macro:`CORK_IPV4_STRING_LENGTH`, :c:macro:`CORK_IPV6_STRING_LENGTH`, and :c:macro:`CORK_IP_STRING_LENGTH` macros can be helpful for this:: char buf[CORK_IPV4_STRING_LENGTH]; struct cork_ipv4 addr; cork_ipv4_to_raw_string(&addr, buf); .. function:: bool cork_ipv4_is_valid_network(const struct cork_ipv4 \*addr, unsigned int cidr_prefix) bool cork_ipv6_is_valid_network(const struct cork_ipv6 \*addr, unsigned int cidr_prefix) bool cork_ip_is_valid_network(const struct cork_ipv6 \*addr, unsigned int cidr_prefix) Checks an IP address for alignment with a CIDR block prefix. For example, 10.1.2.4/24 is invalid, but 10.1.2.4/30 is valid. .. macro:: CORK_NET_ADDRESS_ERROR CORK_NET_ADDRESS_PARSE_ERROR The error class and codes used for the :ref:`error conditions ` described in this section. libcork-1.0.0-rc3/docs/old/process.rst000066400000000000000000000173621367645320400175630ustar00rootroot00000000000000.. _processes: ********* Processes ********* .. highlight:: c :: #include The functions in this section let you interact with the current running process. Cleanup functions ~~~~~~~~~~~~~~~~~ Often you will need to perform some cleanup tasks whenever the current process terminates normally. The functions in this section let you do that. .. function:: void cork_cleanup_at_exit(int priority, cork_cleanup_function function) void cork_cleanup_at_exit_named(const char \*name, int priority, cork_cleanup_function function) Register a *function* that should be called when the current process terminates. When multiple functions are registered, the order in which they are called is determined by their *priority* values --- functions with lower priorities will be called first. If any functions have the same priority value, there is no guarantee about the order in which they will be called. All cleanup functions must conform to the following signature: .. type:: void (\*cork_cleanup_function)(void) The ``_named`` variant lets you provide an explicit name for the cleanup function, which currently is only used when printing out debug messages. The plain variant automatically detects the name of *function*, so that you don't have to provide it explicitly. .. _env: Environment variables ~~~~~~~~~~~~~~~~~~~~~ .. type:: struct cork_env A collection of environment variables that can be passed to subprocesses. .. function:: struct cork_env \*cork_env_new(void) Create a new, empty collection of environment variables. .. function:: struct cork_env \*cork_env_clone_current(void) Create a new :c:type:`cork_env` containing all of the environment variables in the current process's environment list. .. function:: void cork_env_free(struct cork_env \*env) Free a collection of environment variables. .. function:: const char \*cork_env_get(struct cork_env \*env, const char \*name) Return the value of the environment variable with the given *name*. If there is no variable with that name, return ``NULL``. If *env* is ``NULL``, then the variable is retrieved from the current process environment; otherwise, it is retrieved from *env*. .. function:: void cork_env_add(struct cork_env \*env, const char \*name, const char \*value) void cork_env_add_printf(struct cork_env \*env, const char \*name, const char \*format, ...) void cork_env_add_vprintf(struct cork_env \*env, const char \*name, const char \*format, va_list args) Add a new environment variable with the given *name* and *value*. If there is already a variable with that name, it is overwritten. We make a copy of both *name* and *variable*, so it is safe to pass in temporary or reusable strings for either. The ``printf`` and ``vprintf`` variants construct the new variable's value from a ``printf``-like format string. If *env* is ``NULL``, then the new variable is added to the current process environment; otherwise, it is added to *env*. .. function:: void cork_env_remove(struct cork_env \*env, const char \*name) Remove the environment variable with the given *name*, if it exists. If there isn't any variable with that name, do nothing. If *env* is ``NULL``, then the variable is removed from the current process environment; otherwise, it is removed from *env*. .. function:: void cork_env_replace_current(struct cork_env \*env) Replace the current process's environment list with the contents of *env*. .. _exec: Executing another program ~~~~~~~~~~~~~~~~~~~~~~~~~ .. type:: struct cork_exec A specification for executing another program. .. function:: struct cork_exec \*cork_exec_new(const char \*program) struct cork_exec \*cork_exec_new_with_params(const char \*program, ...) struct cork_exec \*cork_exec_new_with_param_array(const char \*program, char \* const \*params) Create a new specification for executing *program*. *program* must either be an absolute path to an executable on the local filesystem, or the name of an executable that should be found in the current ``PATH``. The first variant creates a specification that initially doesn't contain any parameters to pass into the new program. The second variant allows you to pass in each argument as a separate parameter; you must ensure that you terminate the list of parameters with a ``NULL`` pointer. The third variant allows you to pass in a ``NULL``-terminated array of strings to use as an initial parameter list. For all three variants, you can add additional parameters before executing the new program via the :c:func:`cork_add_param` function. .. note:: Most programs will expect the first parameter to be the name of the program being executed. The :c:func:`cork_exec_new_with_params` function will automatically fill in this first parameter for you. The other constructor functions do not; when using them, it is your responsibility to provide this parameter, just like any other parameters to pass into the program. This function does not actually execute the program; that is handled by the :c:func:`cork_exec_run` function. .. function:: void cork_exec_free(struct cork_exec \*exec) Free an execution specification. You normally won't need to call this function; normally you'll replace the current process with the new program (by calling :c:func:`cork_exec_run`), which means you won't have a chance to free the specification object. .. function:: const char \*cork_exec_description(struct cork_exec \*exec) Return a string description of the program described by an execution specification. .. function:: void cork_exec_add_param(struct cork_exec \*exec, const char \*param) Add a parameter to the parameter list that will be passed into the new program. .. function:: void cork_exec_set_env(struct cork_exec \*exec, struct cork_env \*env) Provide a set of environment variables that will be passed into the new program. The subprocess's environment will contain only those variables defined in *env*. You can use the :c:func:`cork_env_clone_current` function to create a copy of the current process's environment, to use it as a base to add new variables or remove unsafe variables. We will take control of *env*, so you must **not** call :c:func:`cork_env_free` to free the environment yourself. If you don't call this function for a specification object, the new program will use the same environment as the calling process. .. function:: void cork_exec_set_cwd(struct cork_exec \*exec, const char \directory) Change the working directory that the new program will be called from. If you don't call this function for a specification object, the new program will be executed in the same working directory as the calling process. .. function:: const char \*cork_exec_program(struct cork_exec \*exec) size_t \*cork_exec_param_count(struct cork_exec \*exec) const char \*cork_exec_param(struct cork_exec \*exec, size_t index) struct cork_env \*cork_exec_env(struct cork_exec \*exec) const char \*cork_exec_cwd(struct cork_exec \*exec) Accessor functions that allow you to retrieve the contents of an execution specification. The :c:func:`cork_exec_env` and :c:func:`cork_exec_cwd` functions might return ``NULL``, if there isn't an environment or working directory specified. .. function:: int cork_exec_run(struct cork_exec \*exec) Execute the program specified by *exec*, replacing the current process. If we can successfully start the new program, this function will not return. If there are any errors starting the program, this function will return an error condition. libcork-1.0.0-rc3/docs/old/ring-buffer.rst000066400000000000000000000056021367645320400203050ustar00rootroot00000000000000.. _ring-buffer: ************ Ring buffers ************ .. highlight:: c :: #include .. type:: struct cork_ring_buffer A ring buffer data structure that can be easily embedded into other types. All of the fields in ``cork_ring_buffer`` are private; you should only access the contents of the ring buffer via the functions defined below. The elements of a ring buffer are ``void *`` pointers. (You can also store integers via the :c:type:`intptr_t` and :c:type:`uintptr_t` types.) Ring buffers have a fixed capacity, which must be specified when the ring buffer instance is initialized. You cannot add extra space to an existing ring buffer. Ring buffers implement a FIFO queue structure; elements will be returned by :c:func:`cork_ring_buffer_pop()` in the same order that they're added by :c:func:`cork_ring_buffer_add()`. .. function:: int cork_ring_buffer_init(struct cork_ring_buffer \*buf, size_t size) struct cork_ring_buffer \*cork_ring_buffer_new(size_t size) Initializes a ring buffer instance, having a capacity of *size* elements. The ``_init`` version should be used to initialize an instance you allocated yourself on the stack. The ``_new`` version will allocate an instance from the heap. If memory allocation fails in either function, the program will abort with an error. .. function:: void cork_ring_buffer_done(struct cork_ring_buffer \*buf) void cork_ring_buffer_free(struct cork_ring_buffer \*buf) Finalizes a ring buffer instance. The ``_done`` variant should be used to finalize an instance that you allocated yourself (i.e., on the stack). The ``_free`` version should be used on instance allocated on the heap by using :c:func:`cork_hash_table_new()`. Nothing special is done to any remaining elements in the ring buffer; if they need to be finalized, you should do that yourself before calling this function. .. function:: bool cork_ring_buffer_is_empty(struct cork_ring_buffer \*buf) bool cork_ring_buffer_is_full(struct cork_ring_buffer \*buf) Returns whether the ring buffer is empty or full. (You cannot add elements to a full ring buffer, and you cannot pop elemenst from an empty one.) .. function:: int cork_ring_buffer_add(struct cork_ring_buffer \*buf, void \*element) Adds *element* to a ring buffer. If the ring buffer is full, we return ``-1``, and the ring buffer will be unchanged. Otherwise we return ``0``. .. function:: void \*cork_ring_buffer_pop(struct cork_ring_buffer \*buf) void \*cork_ring_buffer_peek(struct cork_ring_buffer \*buf) Returns the next element in the ring buffer. If the ring buffer is empty, we return ``NULL``. The ``_pop`` variant will remove the returned element from the ring buffer before returning it; the ``_peek`` variant will leave the element in the ring buffer. libcork-1.0.0-rc3/docs/old/slice.rst000066400000000000000000000256061367645320400172040ustar00rootroot00000000000000.. _slice: ************* Binary slices ************* .. highlight:: c :: #include This section defines an interface for safely referring to the contents of a binary buffer, without needing to know where the buffer came from. In addition to accessing the contents of the underlying buffer, slices support three operations: * *Copying* initializes a new slice object to point at the same underlying buffer as the current slice. Depending on how the underlying buffer is implemented, this doesn't necessarily involve actual copying; for instance, the :c:type:`cork_managed_buffer` type implements this operation by incrementing the reference count of the managed buffer. * *Slicing* updates the current slice to point at a subset of its current contents. This doesn't affect the underlying buffer. * *Freeing* releases the resources used by the slice, possibly freeing the underlying buffer. These operations are represented by the *slice interface* (:c:type:`cork_slice_iface`). To write a new slice implementation, you just need to provide an instance of this interface type. .. note:: There's no generic constructor or initialization function for slices; instead, you'll create a slice from some other data structure, using a function specific to that data structure. (An example is the :c:func:`cork_buffer_to_slice()` function, which initializes a slice from a :ref:`resizable buffer `.) .. type:: struct cork_slice A specific window into a portion of an underlying binary buffer. Instances of this class do not need to be (and almost never are) allocated on the heap; you can define an instance directly on the stack, or in some other kind of managed storage. .. note:: It is **very important** that you ensure that :c:func:`cork_slice_finish()` is called whenever you are done with a slice — if you don't, there's a very good chance that the underlying buffer will never be freed. Yes, yes, it's unfortunate that C doesn't have ``try``/``finally`` or RAII, but suck it up and make sure that :c:func:`cork_slice_finish()` gets called. .. member:: const void \*buf The beginning of the sliced portion of the underlying buffer. .. member:: size_t size The size of the sliced portion of the underlying buffer. .. member:: struct cork_slice_iface \*iface The slice implementation of the underlying buffer. For slice consumers, this field should be considered private. For slice implementors, you should fill in this field with your slice interface. .. member:: void \*user_data An opaque pointer used by the slice implementation. For slice consumers, this field should be considered private. For slice implementors, you can use this field to point at the underlying buffer (and/or any additional metadata that you need.) .. function:: void cork_slice_clear(struct cork_slice \*slice) Clear a slice object. This fills in a slice instance so that it's “empty”. You should not try to call any of the slice methods on an empty slice, nor should you try to dereference the slice's :c:member:`buf ` pointer. An empty slice is equivalent to a ``NULL`` pointer. .. function:: bool cork_slice_is_empty(struct cork_slice \*slice) Return whether a slice is empty. .. function:: int cork_slice_copy(struct cork_slice \*dest, struct cork_slice \*src, size_t offset, size_t length) int cork_slice_copy_offset(struct cork_slice \*dest, struct cork_slice \*src, size_t offset) int cork_slice_copy_fast(struct cork_slice \*dest, struct cork_slice \*src, size_t offset, size_t length) int cork_slice_copy_offset_fast(struct cork_slice \*dest, struct cork_slice \*src, size_t offset) Initialize a new slice that refers to a subset of an existing slice. The *offset* and *length* parameters identify the subset. (For the ``_copy_offset`` variant, the *length* is calculated automatically to include all of the original slice content starting from *offset*.) For the ``_fast`` variants, we **don't** verify that the *offset* and *length* parameters refer to a valid subset of the slice. This is your responsibility. For the non-\ ``_fast`` variants, we perform a bounds check for you, and return an error if the requested slice is invalid. Regardless of whether the new slice is valid, you **must** ensure that you call :c:func:`cork_slice_finish()` on *dest* when you are done with it. .. function:: int cork_slice_light_copy(struct cork_slice \*dest, const struct cork_slice \*src, size_t offset, size_t length) int cork_slice_light_copy_offset(struct cork_slice \*dest, const struct cork_slice \*src, size_t offset) int cork_slice_light_copy_fast(struct cork_slice \*dest, const struct cork_slice \*src, size_t offset, size_t length) int cork_slice_light_copy_offset_fast(struct cork_slice \*dest, const struct cork_slice \*src, size_t offset) Initialize a new slice that refers to a subset of an existing slice. By calling a ``_light_copy`` function instead of a ``_copy`` function, you are guaranteeing that *dest* will not outlive *src* — i.e., it is your responsibility to ensure that you call :c:func:`cork_slice_finish` on *dest* before you call it on *src*. This guarantee lets slice implementations make a more light-weight copy of the slice: for instance, by not having to make a copy of the underlying buffer. The *offset* and *length* parameters identify the subset. (For the ``_light_copy_offset`` variant, the *length* is calculated automatically to include all of the original slice content starting from *offset*.) For the ``_fast`` variants, we **don't** verify that the *offset* and *length* parameters refer to a valid subset of the slice. This is your responsibility. For the non-\ ``_fast`` variants, we perform a bounds check for you, and return an error if the requested slice is invalid. Regardless of whether the new slice is valid, you **must** ensure that you call :c:func:`cork_slice_finish()` on *dest* when you are done with it. .. function:: int cork_slice_slice(struct cork_slice \*slice, size_t offset, size_t length) int cork_slice_slice_offset(struct cork_slice \*slice, size_t offset) int cork_slice_slice_fast(struct cork_slice \*slice, size_t offset, size_t length) int cork_slice_slice_offset_fast(struct cork_slice \*slice, size_t offset) Update a slice to refer to a subset of its contents. The *offset* and *length* parameters identify the subset. (For the ``_slice_offset`` variant, the *length* is calculated automatically to include all of the original slice content starting from *offset*.) For the ``_fast`` variants, we **don't** verify that the *offset* and *length* parameters refer to a valid subset of the slice. This is your responsibility. For the non-\ ``_fast`` variants, we perform a bounds check for you, and return an error if the requested slice is invalid. .. function:: void cork_slice_finish(struct cork_slice \*slice) Finalize a slice, freeing the underlying buffer if necessary. .. function:: int cork_slice_equal(const struct cork_slice \*slice1, const struct cork_slice \*slice2) Compare the contents of two slices for equality. (The *contents* of the slices are compared, not their pointers; this is the slice equivalent of ``memcmp``, not the ``==`` operator.) Slice interface --------------- .. type:: struct cork_slice_iface The interface of methods that slice implementations must provide. .. member:: void (\*free)(struct cork_slice \*self) Called when the slice should be freed. If necessary, you should free the contents of the underlying buffer. (If the buffer contents can be shared, it's up to you to keep track of when the contents are safe to be freed.) This function pointer can be ``NULL`` if you don't need to free any underlying buffer. .. member:: int (\*copy)(struct cork_slice \*dest, const struct cork_slice \*src, size_t offset, size_t length) int (\*light_copy)(struct cork_slice \*dest, const struct cork_slice \*src, size_t offset, size_t length) Create a copy of a slice. You can assume that *offset* and *length* refer to a valid subset of *src*\ 's content. For the ``light_copy`` method, the caller guarantees that the new light copy (*dest*) will not outlive the original slice (*src*). For some slice implementations, this lets you create a more light-weight copy — for instance, by not having to make an actualy copy of the underlying buffer. .. member:: int (\*slice)(struct cork_slice \*self, size_t offset, size_t length) Update *self* to point at a different subset of the underlying buffer. You can assume that *offset* and *length* refer to a valid subset of the buffer. (They will be relative to *self*\ 's existing slice, and not to the original buffer.) This function pointer can be ``NULL`` if you don't need to do anything special to the underlying buffer; in this case, :c:func:`cork_slice_slice()` and :c:func:`cork_slice_slice_offset()` will update the slice's *buf* and *size* fields for you. Built-in slice implementations ------------------------------ Several libcork classes can be used to initialize a slice: * :ref:`Managed buffers ` via the :c:func:`cork_managed_buffer_slice` function * :ref:`Resizable buffers ` via the :c:func:`cork_buffer_to_slice` function You can also initialize a slice to point at an existing buffer: .. function:: void cork_slice_init_static(struct cork_slice \*dest, const void \*buf, size_t size) Initializes *dest* to point at the given static buffer. Since the buffer is static, and guaranteed to always exist, the slice's :c:member:`~cork_slice.copy` method doesn't copy the underlying data, it just creates a new pointer to the existing buffer. .. note:: You can also use this function to refer to a non-static buffer, but then you take responsibility for ensuring that the underlying buffer exists for at least as long as the slice, and any copies made of the slice. As with all slices, you **must** ensure that you call :c:func:`cork_slice_finish` when you're done with the slice. .. function:: void cork_slice_init_copy_once(struct cork_slice \*dest, const void \*buf, size_t size) Initializes *dest* to point at the given buffer. If any copies are made of the slice, then we create a :ref:`managed copy ` of the underlying buffer. This means that you only have to ensure that *buf* exists for as long as the original *dest* slice is used. As with all slices, you **must** ensure that you call :c:func:`cork_slice_finish` when you're done with the slice. libcork-1.0.0-rc3/docs/old/stream.rst000066400000000000000000000177411367645320400174010ustar00rootroot00000000000000.. _stream: ***************** Stream processing ***************** .. highlight:: c :: #include Stream producers ---------------- A *producer* of binary data should take in a pointer to a :c:type:`cork_stream_consumer` instance. Any data that is produced by the stream is then sent into the consumer instance for processing. Once the stream has been exhausted (for instance, by reaching the end of a file), you signal this to the consumer. During both of these steps, the consumer is able to signal error conditions; for instance, a stream consumer that parses a particular file format might return an error condition if the stream of data is malformed. If possible, the stream producer can try to recover from the error condition, but more often, the stream producer will simply pass the error back up to its caller. .. function:: int cork_stream_consumer_data(struct cork_stream_consumer \*consumer, const void \*buf, size_t size, bool is_first_chunk) Send the next chunk of data into a stream consumer. You only have to ensure that *buf* is valid for the duration of this function call; the stream consumer is responsible for saving a copy of the data if it needs to be processed later. In particular, this means that it's perfectly safe for *buf* to refer to a stack-allocated memory region. .. function:: int cork_stream_consumer_eof(struct cork_stream_consumer \*consumer) Notify the stream consumer that the end of the stream has been reached. The stream consumer might perform some final validation and error detection at this point. .. function:: void cork_stream_consumer_free(struct cork_stream_consumer \*consumer) Finalize and deallocate a stream consumer. Built-in stream producers ~~~~~~~~~~~~~~~~~~~~~~~~~ We provide several built-in stream producers: .. function:: int cork_consume_fd(struct cork_stream_consumer \*consumer, int fd) int cork_consume_file(struct cork_stream_consumer \*consumer, FILE \*fp) int cork_consume_file_from_path(struct cork_stream_consumer \*consumer, const char \*path, int flags) Read in a file, passing its contents into the given stream consumer. The ``_fd`` and ``_file`` variants consume a file that you've already opened; you are responsible for closing the file after its been consumed. The ``_file_from_path`` variant will open the file for you, using the standard ``open(2)`` function with the given *flags*. This variant will close the file before returning, regardless of whether the file was successfully consumed or not. File stream producer example ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ As an example, we could implement the :c:func:`cork_consume_file` stream producer as follows:: #include #include #include #include #define BUFFER_SIZE 65536 int cork_consume_file(struct cork_stream_consumer *consumer, FILE *fp) { char buf[BUFFER_SIZE]; size_t bytes_read; bool first = true; while ((bytes_read = fread(buf, 1, BUFFER_SIZE, fp)) > 0) { rii_check(cork_stream_consumer_data(consumer, buf, bytes_read, first)); first = false; } if (feof(fp)) { return cork_stream_consumer_eof(consumer); } else { cork_system_error_set(); return -1; } } Note that this stream producer does not take care of opening or closing the ``FILE`` object, nor does it take care of freeing the consumer. (Our actual implementation of :c:func:`cork_consume_file` also correctly handles ``EINTR`` errors, and so is a bit more complex. But this example still works as an illustration of how to pass data into a stream consumer.) .. _stream-consumers: Stream consumers ---------------- To consume data from a stream, you must create a type that implements the :c:type:`cork_stream_consumer` interface. .. type:: struct cork_stream_consumer An interface for consumer a stream of binary data. The producer of the stream will call the :c:func:`cork_stream_consumer_data()` function repeatedly, once for each successive chunk of data in the stream. Once the stream has been exhausted, the producer will call :c:func:`cork_stream_consumer_eof()` to signal the end of the stream. .. member:: int (\*data)(struct cork_stream_consumer \*consumer, const void \*buf, size_t size, bool is_first_chunk) Process the next chunk of data in the stream. *buf* is only guaranteed to be valid for the duration of this function call. If you need to access the contents of the slice later, you must save it somewhere yourself. If there is an error processing this chunk of data, you should return ``-1`` and fill in the current error condition using :c:func:`cork_error_set`. .. member:: int (\*eof)(struct cork_stream_consumer \*consumer) Handle the end of the stream. This allows you to defer any final validation or error detection until all of the data has been processed. If there is an error detected at this point, you should return ``-1`` and fill in the current error condition using :c:func:`cork_error_set`. .. member:: void (\*free)(struct cork_stream_consumer \*consumer) Free the consumer object. Built-in stream consumers ~~~~~~~~~~~~~~~~~~~~~~~~~ We provide several built-in stream consumers: .. function:: struct cork_stream_consumer \*cork_fd_consumer_new(int fd) struct cork_stream_consumer \*cork_file_consumer_new(FILE \*fp) struct cork_stream_consumer \*cork_file_from_path_consumer_new(const char \*path, int flags) Create a stream consumer that appends any data that it receives to a file. The ``_fd`` and ``_file`` variants append to a file that you've already opened; you are responsible for closing the file after the consumer has finished processing data. The ``_file_from_path`` variant will open the file for you, using the standard ``open(2)`` function with the given *flags*. This variant will close the file before returning, regardless of whether the stream consumer successfully processed the data or not. File stream consumer example ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ As an example, we could implement a stream consumer for the :c:func:`cork_file_consumer_new` function as follows:: #include #include #include #include struct cork_file_consumer { /* cork_file_consumer implements the cork_stream_consumer interface */ struct cork_stream_consumer parent; /* the file to write the data into */ FILE *fp; }; static int cork_file_consumer__data(struct cork_stream_consumer *vself, const void *buf, size_t size, bool is_first) { struct file_consumer *self = cork_container_of(vself, struct cork_file_consumer, parent); size_t bytes_written = fwrite(buf, 1, size, self->fp); /* If there was an error writing to the file, then signal this to * the producer */ if (bytes_written == size) { return 0; } else { cork_system_error_set(); return -1; } } static int cork_file_consumer__eof(struct cork_stream_consumer *vself) { /* We don't close the file, so there's nothing special to do at * end-of-stream. */ return 0; } static void cork_file_consumer__free(struct cork_stream_consumer *vself) { struct file_consumer *self = cork_container_of(vself, struct cork_file_consumer, parent); free(self); } struct cork_stream_consumer * cork_file_consumer_new(FILE *fp) { struct cork_file_consumer *self = cork_new(struct cork_file_consumer); self->parent.data = cork_file_consumer__data; self->parent.eof = cork_file_consumer__eof; self->parent.free = cork_file_consumer__free; self->fp = fp; return &self->parent; } Note that this stream consumer does not take care of opening or closing the ``FILE`` object. libcork-1.0.0-rc3/docs/old/subprocess.rst000066400000000000000000000223521367645320400202700ustar00rootroot00000000000000.. _subprocesses: ************ Subprocesses ************ .. highlight:: c :: #include The functions in this section let you fork child processes, run arbitrary commands in them, and collect any output that they produce. Subprocess objects ~~~~~~~~~~~~~~~~~~ .. type:: struct cork_subprocess Represents a child process. There are several functions for creating child processes, described below. .. function:: void cork_subprocess_free(struct cork_subprocess \*sub) Free a subprocess. The subprocess must not currently be executing. Creating subprocesses ~~~~~~~~~~~~~~~~~~~~~ There are several functions that you can use to create and execute child processes. .. function:: struct cork_subprocess \*cork_subprocess_new(void \*user_data, cork_free_f free_user_data, cork_run_f run, struct cork_stream_consumer \*stdout, struct cork_stream_consumer \*stderr, int \*exit_code) struct cork_subprocess \*cork_subprocess_new_exec(struct cork_exec \*exec, struct cork_stream_consumer \*stdout, struct cork_stream_consumer \*stderr, int \*exit_code) Create a new subprocess specification. The first variant will execute the given *run* function in the subprocess. The second variant will execute a new program in the subprocess; the details of the program to execute are given by a :c:type:`cork_exec` specification object. For both of these functions, you can collect the data that the subprocess writes to its stdout and stderr streams by passing in :ref:`stream consumer ` instances for the *stdout* and/or *stderr* parameters. If either (or both) of these parameters is ``NULL``, then the child process will inherit the corresponding output stream from the current process. (Usually, this means that the child's stdout or stderr will be interleaved with the parent's.) If you provide a non-``NULL`` pointer for the *exit_code* parameter, then we will fill in this pointer with the exit code of the subprocess when it finishes. For :c:func:`cork_subprocess_new_exec`, the exit code is the value passed to the builtin ``exit`` function, or the value returned from the subprocess's ``main`` function. For :c:func:`cork_subprocess_new`, the exit code is the value returned from the thread body's *run* function. You can also create *groups* of subprocesses. This lets you start up several subprocesses at the same time, and wait for them all to finish. .. type:: struct cork_subprocess_group A group of subprocesses that will all be executed simultaneously. .. function:: struct cork_subprocess_group \*cork_subprocess_group_new(void) Create a new group of subprocesses. The group will initially be empty. .. function:: void cork_subprocess_group_free(struct cork_subprocess_group \*group) Free a subprocess group. This frees all of the subprocesses in the group, too. If you've started executing the subprocesses in the group, you **must not** call this function until they have finished executing. (You can use the :c:func:`cork_subprocess_group_is_finished` function to see if the group is still executing, and the :c:func:`cork_subprocess_group_abort` to terminate the subprocesses before freeing the group.) .. function:: void cork_subprocess_group_add(struct cork_subprocess_group \*group, struct cork_subprocess \*sub) Add the given subprocess to *group*. The group takes control of the subprocess; you should not try to free it yourself. Once you've created your subprocesses, you can start them executing: .. function:: int cork_subprocess_start(struct cork_subprocess \*sub) int cork_subprocess_group_start(struct cork_subprocess_group \*group) Execute the given subprocess, or all of the subprocesses in *group*. We immediately return once the processes have been started. You can use the :c:func:`cork_subprocess_drain`, :c:func:`cork_subprocess_wait`, :c:func:`cork_subprocess_group_drain`, and :c:func:`cork_subprocess_group_wait` functions to wait for the subprocesses to complete. If there are any errors starting the subprocesses, we'll terminate any subprocesses that we were able to start, set an :ref:`error condition `, and return ``-1``. Since we immediately return after starting the subprocesses, you must somehow wait for them to finish. There are two strategies for doing so. If you don't need to communicate with the subprocesses (by writing to their stdin streams or sending them signals), the simplest strategy is to just wait for them to finish: .. function:: int cork_subprocess_wait(struct cork_subprocess \*sub) int cork_subprocess_group_wait(struct cork_subprocess_group \*group) Wait until the given subprocess, or all of the subprocesses in *group*, have finished executing. While waiting, we'll continue to read data from the subprocesses stdout and stderr streams as we can. If there are any errors reading from the subprocesses, we'll terminate all of the subprocesses that are still executing, set an :ref:`error condition `, and return ``-1``. If the group has already finished, the function doesn't do anything. As an example:: struct cork_subprocess_group *group = /* from somewhere */; /* Wait for the subprocesses to finish */ if (cork_subprocess_group_wait(group) == -1) { /* An error occurred; handle it! */ } /* At this point, we're guaranteed that the subprocesses have all been * terminated; either everything finished successfully, or the subprocesses * were terminated for us when an error was detected. */ cork_subprocess_group_free(group); If you do need to communicate with the subprocesses, then you need more control over when we try to read from their stdout and stderr streams. (The pipes that connect the subprocesses to the parent process are fixed size, and so without careful orchestration, you can easily get a deadlock. Moreover, the right pattern of reading and writing depends on the subprocesses that you're executing, so it's not something that we can handle for you automatically.) .. function:: struct cork_stream_consumer \*cork_subprocess_stdin(struct cork_subprocess \*sub) Return a :ref:`stream consumer ` that lets you write data to the subprocess's stdin. We do not buffer this data in any way; calling :c:func:`cork_stream_consumer_data` immediately tries to write the given data to the subprocess's stdin stream. This can easily lead to deadlock if you do not manage the subprocess's particular orchestration correctly. .. function:: bool cork_subprocess_is_finished(struct cork_subprocess \*sub) bool cork_subprocess_group_is_finished(struct cork_subprocess_group \*group) Return whether the given subprocess, or all of the subprocesses in *group*, have finished executing. .. function:: int cork_subprocess_abort(struct cork_subprocess \*sub) int cork_subprocess_group_abort(struct cork_subprocess_group \*group) Immediately terminate the given subprocess, or all of the subprocesses in *group*. This can be used to clean up if you detect an error condition and need to close the subprocesses early. If the group has already finished, the function doesn't do anything. .. function:: bool cork_subprocess_drain(struct cork_subprocess \*sub) bool cork_subprocess_group_drain(struct cork_subprocess_group \*group) Check the given subprocess, or all of the subprocesses in *group*, for any output on their stdout and stderr streams. We'll read in as much data as we can from all of the subprocesses without blocking, and then return. (Of course, we only do this for those subprocesses that you provided stdout or stderr consumers for.) This function lets you pass data into the subprocesses's stdin streams, or (**TODO: eventually**) send them signals, and handle any orchestration that's necessarily to ensure that the subprocesses don't deadlock. The return value indicates whether any "progress" was made. We will return ``true`` if we were able to read any data from any of the subprocesses, or if we detected that any of the subprocesses exited. If there are any errors reading from the subprocesses, we'll terminate all of the subprocesses that are still executing, set an :ref:`error condition `, and return ``false``. If the group has already finished, the function doesn't do anything. To do this, you continue to "drain" the subprocesses whenever you're ready to read from their stdout and stderr streams. You repeat this in a loop, writing to the stdin streams or sending signals as necessary, until all of the subprocesses have finished:: struct cork_subprocess_group *group = /* from somewhere */; while (!cork_subprocess_group_is_finished(group)) { /* Drain the stdout and stderr streams */ if (cork_subprocess_group_drain(group) == -1) { /* An error occurred; handle it! */ } else { /* Write to the stdin streams or send signals */ } } /* At this point, we're guaranteed that the subprocesses have all been * terminated; either everything finished successfully, or the subprocesses * were terminated for us when an error was detected. */ cork_subprocess_group_free(group); libcork-1.0.0-rc3/docs/old/threads.rst000066400000000000000000000311011367645320400175220ustar00rootroot00000000000000.. _multithreading: ********************** Multithreading support ********************** .. highlight:: c :: #include libcork provides several functions for handling threads and writing thread-aware code in a portable way. .. _thread-ids: Thread IDs ========== .. type:: unsigned int cork_thread_id An identifier for a thread in the current process. This is a portable type; it is not based on the "raw" thread ID used by the underlying thread implementation. This type will always be equivalent to ``unsigned int``, on all platforms. Furthermore, :c:data:`CORK_THREAD_NONE` will always refer to an instance of this type that we guarantee will not be used by any thread. .. var:: cork_thread_id CORK_THREAD_NONE A :c:type:`cork_thread_id` value that will not be used as the ID of any thread. You can use this value to represent "no thread" in any data structures you create. Moreover, we guarantee that ``CORK_THREAD_NONE`` will have the value ``0``, which lets you zero-initialize a data structure containing a :c:type:`cork_thread_id`, and have its initial state automatically represent "no thread". .. function:: cork_thread_id cork_current_thread_get_id(void) Returns the identifier of the currently executing thread. This function works correctly for any thread in the current proces --- including the main thread, and threads that weren't created by :c:func:`cork_thread_new`. .. _threads: Creating threads ================ The functions in this section let you create and start new threads in the current process. Each libcork thread is named and has a unique :ref:`thread ID `. Each thread also contains a ``run`` function, which defines the code that should be executed within the new thread. Every thread goes through the same lifecycle: 1) You create a new thread via :c:func:`cork_thread_new`. At this point, the thread is ready to execute, but isn't automatically started. If you encounter an error before you start the thread, you must use :c:func:`cork_thread_free` to free the thread object. When you create the thread, you give it a :c:type:`cork_run_f` function, which defines the code that will be executed in the new thread. You also provide a ``user_data`` value, which it gives you a place to pass data into and out of the thread. .. note:: Any data passed into and out of the thread via the body instance is not automatically synchronized or thread-safe. You can safely pass in input data before calling :c:type:`cork_thread_new`, and retrieve output data after calling :c:type:`cork_thread_join`, all without requiring any extra synchronization effort. While the thread is executing, however, you must implement your own synchronization or locking to access the contents of the body from some other thread. 2) You start the thread via :c:func:`cork_thread_start`. You must ensure that you don't try to start a thread more than once. Once you've started a thread, you no longer have responsibility for freeing it; you must ensure that you don't call :c:func:`cork_thread_free` on a thread that you've started. 3) Once you've started a thread, you wait for it to finish via :c:func:`cork_thread_join`. Any thread can wait for any other thread to finish, although you are responsible for ensuring that your threads don't deadlock. However, you can only join a particular thread once. .. type:: struct cork_thread A thread within the current process. This type is opaque; you must use the functions defined below to interact with the thread. .. function:: struct cork_thread \*cork_thread_new(const char \*name, void \*user_data, cork_free_f free_user_data, cork_run_f run) Create a new thread with the given *name* that will execute *run*. The thread does not start running immediately. When the thread is started, the *run* function will be called with *user_data* as its only parameter. When the thread finishes (or if it is freed via :c:func:`cork_thread_free` before the thread is started), we'll use the *free_user_data* function to free the *user_data* value. You can provide ``NULL`` if *user_data* shouldn't be freed, or if you want to free it yourself. .. note:: If you provide a *free_user_data* function, it will be called as soon as the thread finished. That means that if you use :c:func:`cork_thread_join` to wait for the thread to finish, the *user_data* value will no longer be valid when :c:func:`cork_thread_join` returns. You must either copy any necessary data out into more a more persistent memory location before the thread finishes, or you should use a ``NULL`` *free_user_data* function and free the *user_data* memory yourself once you're sure the thread has finished. .. function:: void cork_thread_free(struct cork_thread \*thread) Free *thread*. You can only call this function if you haven't started the thread yet. Once you start a thread, the thread is responsible for freeing itself when it finishes. .. function:: struct cork_thread \*cork_current_thread_get(void) Return the :c:type:`cork_thread` instance for the current thread. This function returns ``NULL`` when called from the main thread (i.e., the one created automatically when the process starts), or from a thread that wasn't created via :c:func:`cork_thread_new`. .. function:: const char \* cork_thread_get_name(struct cork_thread \*thread) cork_thread_id cork_thread_get_id(struct cork_thread \*thread) Retrieve information about the given thread. .. function:: int cork_thread_start(struct cork_thread \*thread) Start *thread*. After calling this function, you must not try to free *thread* yourself; the thread will automatically free itself once it has finished executing and has been joined. .. function:: int cork_thread_join(struct cork_thread \*thread) Wait for *thread* to finish executing. If the thread's body's ``run`` function an :ref:`error condition `, we will catch that error and return it ourselves. The thread is automatically freed once it finishes executing. You cannot join a thread that has not been started, and once you've started a thread, you **must** join it exactly once. (If you don't join it, there's no guarantee that it will be freed.) .. _atomics: Atomic operations ================= We provide several platform-agnostic macros for implementing common atomic operations. Addition ~~~~~~~~ .. function:: int cork_int_atomic_add(volatile int \*var, int delta) unsigned int cork_uint_atomic_add(volatile unsigned int \*var, unsigned int delta) size_t cork_size_atomic_add(volatile size_t \*var, size_t delta) Atomically add *delta* to the variable pointed to by *var*, returning the result of the addition. .. function:: int cork_int_atomic_pre_add(volatile int \*var, int delta) unsigned int cork_uint_atomic_pre_add(volatile unsigned int \*var, unsigned int delta) size_t cork_size_atomic_pre_add(volatile size_t \*var, size_t delta) Atomically add *delta* to the variable pointed to by *var*, returning the value from before the addition. Subtraction ~~~~~~~~~~~ .. function:: int cork_int_atomic_sub(volatile int \*var, int delta) unsigned int cork_uint_atomic_sub(volatile unsigned int \*var, unsigned int delta) size_t cork_size_atomic_sub(volatile size_t \*var, size_t delta) Atomically subtract *delta* from the variable pointed to by *var*, returning the result of the subtraction. .. function:: int cork_int_atomic_pre_sub(volatile int \*var, int delta) unsigned int cork_uint_atomic_pre_sub(volatile unsigned int \*var, unsigned int delta) size_t cork_size_atomic_pre_sub(volatile size_t \*var, size_t delta) Atomically subtract *delta* from the variable pointed to by *var*, returning the value from before the subtraction. Compare-and-swap ~~~~~~~~~~~~~~~~ .. function:: int cork_int_cas(volatile int_t \*var, int old_value, int new_value) unsigned int cork_uint_cas(volatile uint_t \*var, unsigned int old_value, unsigned int new_value) size_t cork_size_cas(volatile size_t \*var, size_t old_value, size_t new_value) TYPE \*cork_ptr_cas(TYPE \* volatile \*var, TYPE \*old_value, TYPE \*new_value) Atomically check whether the variable pointed to by *var* contains the value *old_value*, and if so, update it to contain the value *new_value*. We return the value of *var* before the compare-and-swap. (If this value is equal to *old_value*, then the compare-and-swap was successful.) .. _once: Executing something once ======================== The functions in this section let you ensure that a particular piece of code is executed exactly once, even if multiple threads attempt the execution at roughly the same time. .. macro:: cork_once_barrier(name) Declares a barrier that can be used with the :c:func:`cork_once` macro. .. macro:: cork_once(barrier, call) cork_once_recursive(barrier, call) Ensure that *call* (which can be an arbitrary statement) is executed exactly once, regardless of how many times control reaches the call to ``cork_once``. If control reaches the ``cork_once`` call at roughly the same time in multiple threads, exactly one of them will be allowed to execute the code. The call to ``cork_once`` won't return until *call* has been executed. If you have multiple calls to ``cork_once`` that use the same *barrier*, then exactly one *call* will succeed. If the *call* statements are different in those ``cork_once`` invocations, then it's undefined which one gets executed. If the function that contains the ``cork_once`` call is recursive, then you should call the ``_recursive`` variant of the macro. With the ``_recursive`` variant, if the same thread tries to obtain the underlying lock multiple times, the second and later calls will silently succeed. With the regular variant, you'll get a deadlock in this case. These macros are usually used to initialize a static variable that will be shared across multiple threads:: static struct my_type shared_value; static void expensive_initialization(void) { /* do something to initialize shared_value */ } cork_once_barrier(shared_value_once); struct my_type * get_shared_value(void) { cork_once(shared_value_once, expensive_initialization()); return &shared_value; } Each thread can then call ``get_shared_value`` to retrieve a properly initialized instance of ``struct my_type``. Regardless of how many threads call this function, and how often they call it, the value will be initialized exactly once, and will be guaranteed to be initialized before any thread tries to use it. .. _tls: Thread-local storage ==================== The macro in this section can be used to create thread-local storage in a platform-agnostic manner. .. macro:: cork_tls(TYPE type, SYMBOL name) Creates a static function called :samp:`{[name]}_get`, which will return a pointer to a thread-local instance of *type*. This is a static function, so it won't be visible outside of the current compilation unit. When a particular thread's instance is created for the first time, it will be filled with ``0`` bytes. If the actual type needs more complex initialization before it can be used, you can create a wrapper struct that contains a boolean indiciating whether that initialization has happened:: struct wrapper { bool initialized; struct real_type val; }; cork_tls(struct wrapper, wrapper); static struct real_type * real_type_get(void) { struct wrapper * wrapper = wrapper_get(); struct real_type * real_val = &wrapper->val; if (CORK_UNLIKELY(!wrapper->initialized)) { expensive_initialization(real_val); } return real_val; } It's also not possible to provide a finalization function; if your thread-local variable acquires any resources or memory that needs to be freed when the thread finishes, you must make a “thread cleanup” function that you explicitly call at the end of each thread. .. note:: On some platforms, the number of thread-local values that can be created by any given process is limited (i.e., on the order of 128 or 256 values). This means that you should limit the number of thread-local values you create, especially in a library. libcork-1.0.0-rc3/docs/old/timestamps.rst000066400000000000000000000101661367645320400202660ustar00rootroot00000000000000.. _timestamps: ************************* High-precision timestamps ************************* .. highlight:: c :: #include .. type:: uint64_t cork_timestamp A high-precision timestamp type. A timestamp is represented by a 64-bit integer, whose unit is the *gammasecond* (γsec), where :math:`1~\textrm{γsec} = \frac{1}{2^{32}} \textrm{sec}`. With this representation, the upper 32 bits of a timestamp value represent the timestamp truncated (towards zero) to seconds. For this type, we don't concern ourselves with any higher-level issues of clock synchronization or time zones. ``cork_timestamp`` values can be used to represent any time quantity, regardless of which time standard (UTC, GMT, TAI) you use, or whether it takes into account the local time zone. .. function:: void cork_timestamp_init_sec(cork_timestamp \*ts, uint32_t sec) void cork_timestamp_init_gsec(cork_timestamp \*ts, uint32_t sec, uint32_t gsec) void cork_timestamp_init_msec(cork_timestamp \*ts, uint32_t sec, uint32_t msec) void cork_timestamp_init_usec(cork_timestamp \*ts, uint32_t sec, uint32_t usec) void cork_timestamp_init_nsec(cork_timestamp \*ts, uint32_t sec, uint32_t nsec) Initializes a timestamp from a separate seconds part and fractional part. For the ``_sec`` variant, the fractional part will be set to ``0``. For the ``_gsec`` variant, you provide the fractional part in gammaseconds. For the ``_msec``, ``_usec``, and ``_nsec`` variants, the fractional part will be translated into gammaseconds from milliseconds, microseconds, or nanoseconds, respectively. .. function:: void cork_timestamp_init_now(cork_timestamp \*ts) Initializes a timestamp with the current UTC time of day. .. note:: The resolution of this function is system-dependent. .. function:: uint32_t cork_timestamp_sec(const cork_timestamp ts) Returns the seconds portion of a timestamp. .. function:: uint32_t cork_timestamp_gsec(const cork_timestamp ts) uint32_t cork_timestamp_msec(const cork_timestamp ts) uint32_t cork_timestamp_usec(const cork_timestamp ts) uint32_t cork_timestamp_nsec(const cork_timestamp ts) Returns the fractional portion of a timestamp. The variants return the fractional portion in, respectively, gammaseconds, milliseconds, microseconds, or nanoseconds. .. function:: int cork_timestamp_format_utc(const cork_timestamp ts, const char \*format, struct cork_buffer \*buf) int cork_timestamp_format_local(const cork_timestamp ts, const char \*format, struct cork_buffer \*buf) Create the string representation of the given timestamp according to *format*, appending the result to the current contents of *buf*. The ``_utc`` variant assumes that *ts* represents a UTC time, whereas the ``_local`` variant assumes that it represents a time in the local time zone. *format* is a format string whose syntax is similar to that of the POSIX ``strftime`` function. *format* must contain arbitrary text interspersed with ``%`` specifiers, which will be replaced with portions of the timestamp. The following specifiers are recognized (note that this list does **not** include all of the specifiers supported by ``strftime``): ============== ==================================================== Specifier Replacement ============== ==================================================== ``%%`` A literal ``%`` character ``%d`` Day of month (``01``-``31``) ``%[width]f`` Fractional seconds (zero-padded, limited to ``[width]`` digits) ``%H`` Hour in current day (``00``-``23``) ``%m`` Month (``01``-``12``) ``%M`` Minute in current hour (``00``-``59``) ``%s`` Number of seconds since Unix epoch ``%S`` Second in current minute (``00``-``60``) ``%Y`` Four-digit year (including century) ============== ==================================================== If the format string is invalid, we will return an :ref:`error condition `. libcork-1.0.0-rc3/docs/old/unique-ids.rst000066400000000000000000000051101367645320400201540ustar00rootroot00000000000000.. _unique-ids: ****************** Unique identifiers ****************** .. highlight:: c :: #include The functions in this section let you define compile-time unique identifiers. These identifiers are simple C variables, and each one is guaranteed to be unique within the context of a single execution of your program. They are *not* appropriate for use as external identifiers --- for instance, for serializing into long-term storage or sending via a communications channel to another process. .. type:: cork_uid A unique identifier. .. macro:: cork_uid CORK_UID_NONE A unique identifier value that means "no identifier". This is guaranteed to be distinct from all other unique identifiers. It is invalid to call :c:func:`cork_uid_hash`, :c:func:`cork_uid_id`, or :c:func:`cork_uid_name` on this identifier. .. macro:: cork_uid_define(SYMBOL id) cork_uid_define_named(SYMBOL id, const char \*name) You use the :c:func:`cork_uid_define` macro to define a new unique identifier with the given C identifier *id*. The ``_define`` variant also uses *id* as the identifier's human-readable name; the ``_define_named`` variant let's you provide a separate human-readable name. Within the context of a single execution of this program, this identifier is guaranteed to be distinct from any other identifier, regardless of which library the identifiers are defined in. In the same compilation unit, you can then use the C identifier *id* to retrieve the :c:type:`cork_uid` instance for this identifier. .. note:: The unique identifier objects are declared ``static``, so they are only directly visible (using the C identifier *id*) in the same compilation unit as the :c:func:`cork_uid_define` call that created the identifier. The resulting :c:type:`cork_uid` value, however, can be passed around the rest of your code however you want. .. function:: bool cork_uid_equal(const cork_uid id1, const cork_uid id2) Return whether two :c:type:`cork_uid` values refer to the same unique identifier. .. function:: cork_hash cork_uid_hash(const cork_uid id) Return a :ref:`hash value ` for the given identifier. .. function:: const char \*cork_uid_name(const cork_uid id) Return the name of the given unique identifier. Example ======= :: #include #include cork_uid_define(test_id); int main(void) { cork_uid id = test_id; printf("Identifier %p has name %s\n", id, cork_uid_name(id)); return 0; } libcork-1.0.0-rc3/docs/old/versions.rst000066400000000000000000000045371367645320400177550ustar00rootroot00000000000000.. _versions: *************** Library version *************** .. highlight:: c :: #include The macros and functions in this section let you determine the version of the libcork library at compile-time and runtime, and make it easier for you to define similar macros and functions in your own libraries. libcork version --------------- .. macro:: CORK_VERSION The libcork library version, encoded as a single number as follows:: (major * 1000000) + (minor * 1000) + patch For instance, version 1.2.10 would be encoded as 1002010. .. macro:: CORK_VERSION_MAJOR CORK_VERSION_MINOR CORK_VERSION_PATCH The libcork library version, with each part of the version number separated out into separate macros. .. function:: const char \*cork_version_string(void) const char \*cork_revision_string(void) Return the libcork library version or revision as a string. The *version* is the simple three-part version number (``major:minor:patch``). The *revision* is an opaque string that identifies the specific revision in libcork's code history. (Right now, this is a SHA-1 git commit identifier.) .. tip:: Compile-time vs runtime There's an important difference between the :c:macro:`CORK_VERSION` macro and :c:func:`cork_version_string` function, even though they seemingly return the same information. The macro version be evaluated by the preprocessor, and so it will return the version that was available *when your code was compiled*. If you later install a newer (but backwards-compatible) version of libcork, any code that called the macro will still have the original version, and not the new version. The function version, on the other hand, calculates the version information *at runtime*, when the function is actually called. That means that the function result will always give you the current installed libcork version, even as newer versions are installed on the system. Constructing version information -------------------------------- If you're writing a library that uses libcork, it's a good idea to provide your own version information, similar to how libcork does. .. function:: CORK_MAKE_VERSION(major, minor, patch) Assemble a ``major.minor.patch`` version into a single number, using the same pattern as :c:macro:`CORK_VERSION`. libcork-1.0.0-rc3/docs/old/visibility.rst000066400000000000000000000104301367645320400202610ustar00rootroot00000000000000.. _visibility: ***************** Symbol visibility ***************** .. highlight:: c :: #include When writing a shared library, you should always be careful to explicitly mark which functions and other symbols are part of the library's public API, and which should only be used internally by the library. There are a number of benefits to doing this; there is a good summary on the `GCC wiki`_. (Note that even though that summary is on the GCC wiki, the notes apply equally well to other compilers and platforms.) .. _GCC wiki: http://gcc.gnu.org/wiki/Visibility libcork provides several helper macros that make it easier to do this. We use these macros ourselves to define libcork's public API. Defining a library's public API ------------------------------- On some platforms (for instance, on Windows), you must declare each public function and symbol differently depending on whether you're compiling the library that *defines* the symbol, or a library or program that *uses* the symbol. The first is called an *export*, the second an *import*. On other platforms (for instance, GCC on Linux), the declaration of a public symbol is the same regardless of whether you're exporting or importing the symbol. libcork provides macros that let you explicitly declare a symbol as an export or an import in a platform-independent way. .. macro:: CORK_EXPORT CORK_IMPORT Explicitly declare that a symbol should be exported from the current shared library, or imported from some other shared library. However, you will rarely need to use these macros directly. Instead, when writing a new shared library, you should declare a new preprocessor macro (specific to the new library), which you'll use when declaring the library's public API. For instance, if you're creating a new library called *libfoo*, you would declare a new preprocessor macro called ``FOO_API``:: #if !defined(FOO_API) #define FOO_API CORK_IMPORT #endif This ensures that anyone who wants to *use* libfoo doesn't need to do anything special; the ``FOO_API`` macro will default to importing the symbols from libfoo's public API. When *building* libfoo, you must set up your build system to define this variable differently; since you need to *export* the symbols in this case, the ``FOO_API`` macro should be set to ``CORK_EXPORT``. Each build system will have a different way to do this. In CMake, for instance, you'd add the following: .. code-block:: cmake set_target_properties(libfoo PROPERTIES COMPILE_DEFINITIONS FOO_API=CORK_EXPORT ) Then, in all of your header files, you should use your new ``FOO_API`` macro when declaring each function or symbol in the public API:: FOO_API int foo_load_from_file(const char *name); FOO_API void foo_do_something_great(int flags); extern FOO_API const char *foo_name; Local symbols ------------- Normally, if you need a function to be local, and not be exported as part of the library's public API, you can just declare it ``static``:: static int file_local_function(void) { /* This function is not visible outside of this file. */ return 0; } This works great as long as the function is only needed within the current source file. Sometimes, though, you need to define a function that can be used in other source files within the same library, but which shouldn't be visible outside of the library. To do this, you should define the function using the :c:macro:`CORK_LOCAL` macro. .. macro:: CORK_LOCAL Declare a symbol that should be visible in any source file within the current library, but not visible outside of the library. As an example:: CORK_LOCAL int library_local_function(void) { /* This function is visible in other files, but not outside of the * library. */ return 0; } Since you're going to use this function in multiple files, you'll want to declare the function in a header file. However, since the function is not part of the public API, this should *not* be defined in a public header file (that is, one that's installed along with the shared library). Instead, you should include a private header file that's only available in your library's source code archive, and which should not be installed with the other public header files. libcork-1.0.0-rc3/extras/000077500000000000000000000000001367645320400151425ustar00rootroot00000000000000libcork-1.0.0-rc3/extras/hashstring.py000066400000000000000000000040221367645320400176640ustar00rootroot00000000000000# -*- coding: utf-8 -*- # ---------------------------------------------------------------------- # Copyright © 2011, libcork authors # Please see the COPYING file in this distribution for license details. # ---------------------------------------------------------------------- # Calculates the 32-bit MurmurHash3 hash value [1] for a string provided on the # command line. # # [1] http://code.google.com/p/smhasher/wiki/MurmurHash3 def rotl32(a, b): return (((a << (b & 0x1f)) & 0xffffffff) | ((a >> (32 - (b & 0x1f))) & 0xffffffff)) def fmix(h): h = h ^ (h >> 16) h = (h * 0x85ebca6b) & 0xffffffff h = h ^ (h >> 13) h = (h * 0xc2b2ae35) & 0xffffffff h = h ^ (h >> 16) return h def hash(value, seed): import struct length = len(value) num_blocks = length / 4 tail_length = length % 4 fmt = "<" + ("i" * num_blocks) + ("b" * tail_length) vals = struct.unpack(fmt, value) h1 = seed c1 = 0xcc9e2d51 c2 = 0x1b873593 for block in vals[:num_blocks]: k1 = block k1 = (k1 * c1) & 0xffffffff k1 = rotl32(k1, 15) k1 = (k1 * c2) & 0xffffffff h1 = h1 ^ k1 h1 = rotl32(h1, 13) h1 = (h1 * 5 + 0xe6546b64) & 0xffffffff k1 = 0 if tail_length >= 3: k1 = k1 ^ ((vals[num_blocks + 2] << 16) & 0xffffffff) if tail_length >= 2: k1 = k1 ^ ((vals[num_blocks + 1] << 8) & 0xffffffff) if tail_length >= 1: k1 = k1 ^ ( vals[num_blocks] & 0xffffffff) k1 = (k1 * c1) & 0xffffffff k1 = rotl32(k1, 15) k1 = (k1 * c2) & 0xffffffff h1 = h1 ^ k1 h1 = h1 ^ (length & 0xffffffff) return fmix(h1) if __name__ == "__main__": import sys if len(sys.argv) != 2 and len(sys.argv) != 3: print("Usage: hashstring.py []") sys.exit(1) def myhex(v): return hex(v).rstrip("L") if len(sys.argv) == 3: seed = long(sys.argv[2]) & 0xffffffff else: seed = 0 print(myhex(hash(sys.argv[1], seed))) libcork-1.0.0-rc3/include/000077500000000000000000000000001367645320400152575ustar00rootroot00000000000000libcork-1.0.0-rc3/include/CMakeLists.txt000066400000000000000000000013001367645320400200110ustar00rootroot00000000000000# -*- coding: utf-8 -*- # ---------------------------------------------------------------------- # Copyright © 2011, libcork authors # All rights reserved. # # Please see the COPYING file in this distribution for license details. # ---------------------------------------------------------------------- configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/libcork/config/version.h.in ${CMAKE_CURRENT_BINARY_DIR}/libcork/config/version.h ESCAPE_QUOTES @ONLY ) install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/ DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" FILES_MATCHING PATTERN "*.h") install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libcork/config/version.h DESTINATION include/libcork/config) libcork-1.0.0-rc3/include/libcork/000077500000000000000000000000001367645320400167045ustar00rootroot00000000000000libcork-1.0.0-rc3/include/libcork/cli.h000066400000000000000000000007131367645320400176250ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2012, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #ifndef LIBCORK_CLI_H #define LIBCORK_CLI_H /*** include all of the parts ***/ #include #endif /* LIBCORK_CLI_H */ libcork-1.0.0-rc3/include/libcork/cli/000077500000000000000000000000001367645320400174535ustar00rootroot00000000000000libcork-1.0.0-rc3/include/libcork/cli/commands.h000066400000000000000000000025461367645320400214340ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2012, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #ifndef LIBCORK_COMMANDS_H #define LIBCORK_COMMANDS_H #include typedef void (*cork_leaf_command_run)(int argc, char **argv); typedef int (*cork_option_parser)(int argc, char **argv); enum cork_command_type { CORK_COMMAND_SET, CORK_LEAF_COMMAND }; struct cork_command { enum cork_command_type type; const char *name; const char *short_desc; const char *usage_suffix; const char *full_help; int (*parse_options)(int argc, char **argv); struct cork_command **set; cork_leaf_command_run run; }; #define cork_command_set(name, sd, parse_options, set) \ { \ CORK_COMMAND_SET, name, sd, NULL, NULL, \ parse_options, set, NULL \ } #define cork_leaf_command(name, sd, us, fh, parse_options, run) \ { \ CORK_LEAF_COMMAND, name, sd, us, fh, \ parse_options, NULL, run \ } CORK_API void cork_command_show_help(struct cork_command *command, const char *message); CORK_API int cork_command_main(struct cork_command *root, int argc, char **argv); #endif /* LIBCORK_COMMANDS_H */ libcork-1.0.0-rc3/include/libcork/config.h000066400000000000000000000007251367645320400203260ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2011, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #ifndef LIBCORK_CONFIG_H #define LIBCORK_CONFIG_H /*** include all of the parts ***/ #include #endif /* LIBCORK_CONFIG_H */ libcork-1.0.0-rc3/include/libcork/config/000077500000000000000000000000001367645320400201515ustar00rootroot00000000000000libcork-1.0.0-rc3/include/libcork/config/arch.h000066400000000000000000000020421367645320400212350ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2012, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #ifndef LIBCORK_CONFIG_ARCH_H #define LIBCORK_CONFIG_ARCH_H /*----------------------------------------------------------------------- * Platform */ #if defined(__i386__) || defined(_M_IX86) #define CORK_CONFIG_ARCH_X86 1 #else #define CORK_CONFIG_ARCH_X86 0 #endif #if defined(__x86_64__) || defined(_M_X64) #define CORK_CONFIG_ARCH_X64 1 #else #define CORK_CONFIG_ARCH_X64 0 #endif #if defined(__powerpc__) || defined(__ppc__) /* GCC-ish compiler */ #define CORK_CONFIG_ARCH_PPC 1 #elif defined(_M_PPC) /* VS-ish compiler */ #define CORK_CONFIG_ARCH_PPC 1 #elif defined(_ARCH_PPC) /* Something called XL C/C++? */ #define CORK_CONFIG_ARCH_PPC 1 #else #define CORK_CONFIG_ARCH_PPC 0 #endif #endif /* LIBCORK_CONFIG_ARCH_H */ libcork-1.0.0-rc3/include/libcork/config/bsd.h000066400000000000000000000015521367645320400210750ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2013, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #ifndef LIBCORK_CONFIG_BSD_H #define LIBCORK_CONFIG_BSD_H /*----------------------------------------------------------------------- * Endianness */ #include #if BYTE_ORDER == BIG_ENDIAN #define CORK_CONFIG_IS_BIG_ENDIAN 1 #define CORK_CONFIG_IS_LITTLE_ENDIAN 0 #elif BYTE_ORDER == LITTLE_ENDIAN #define CORK_CONFIG_IS_BIG_ENDIAN 0 #define CORK_CONFIG_IS_LITTLE_ENDIAN 1 #else #error "Cannot determine system endianness" #endif #define CORK_HAVE_REALLOCF 1 #define CORK_HAVE_PTHREADS 1 #endif /* LIBCORK_CONFIG_BSD_H */ libcork-1.0.0-rc3/include/libcork/config/config.h000066400000000000000000000033451367645320400215740ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2011, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #ifndef LIBCORK_CONFIG_CONFIG_H #define LIBCORK_CONFIG_CONFIG_H /* If you want to skip autodetection, define this to 1, and provide a * libcork/config/custom.h header file. */ #if !defined(CORK_CONFIG_SKIP_AUTODETECT) #define CORK_CONFIG_SKIP_AUTODETECT 0 #endif #if CORK_CONFIG_SKIP_AUTODETECT /* The user has promised that they'll define everything themselves. */ #include #else /* Otherwise autodetect! */ /**** VERSION ****/ #include /**** ARCHITECTURES ****/ #include /**** PLATFORMS ****/ #if (defined(__unix__) || defined(unix)) && !defined(USG) /* We need this to test for BSD, but it's a good idea to have for * any brand of Unix.*/ #include #endif #if defined(__linux) || defined(__FreeBSD_kernel__) || defined(__GNU__) /* Do some Linux, kFreeBSD or GNU/Hurd specific autodetection. */ #include #elif defined(__APPLE__) && defined(__MACH__) /* Do some Mac OS X-specific autodetection. */ #include #elif defined(BSD) && (BSD >= 199103) /* Do some BSD (4.3 code base or newer)specific autodetection. */ #include #endif /* platforms */ /**** COMPILERS ****/ #if defined(__GNUC__) /* Do some GCC-specific autodetection. */ #include #endif /* compilers */ #endif /* autodetect or not */ #endif /* LIBCORK_CONFIG_CONFIG_H */ libcork-1.0.0-rc3/include/libcork/config/gcc.h000066400000000000000000000057661367645320400210740ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2011, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #ifndef LIBCORK_CONFIG_GCC_H #define LIBCORK_CONFIG_GCC_H /* Figure out the GCC version */ #if defined(__GNUC_PATCHLEVEL__) #define CORK_CONFIG_GCC_VERSION (__GNUC__ * 10000 \ + __GNUC_MINOR__ * 100 \ + __GNUC_PATCHLEVEL__) #else #define CORK_CONFIG_GCC_VERSION (__GNUC__ * 10000 \ + __GNUC_MINOR__ * 100) #endif /*----------------------------------------------------------------------- * Compiler attributes */ /* The GCC assembly syntax has been available basically forever. */ #if !defined(CORK_CONFIG_HAVE_GCC_ASM) #if defined(CORK_CONFIG_GCC_VERSION) #define CORK_CONFIG_HAVE_GCC_ASM 1 #else #define CORK_CONFIG_HAVE_GCC_ASM 0 #endif #endif /* The GCC atomic instrinsics are available as of GCC 4.1.0. */ #if !defined(CORK_CONFIG_HAVE_GCC_ATOMICS) #if CORK_CONFIG_GCC_VERSION >= 40100 #define CORK_CONFIG_HAVE_GCC_ATOMICS 1 #else #define CORK_CONFIG_HAVE_GCC_ATOMICS 0 #endif #endif /* The attributes we want to use are available as of GCC 2.96. */ #if !defined(CORK_CONFIG_HAVE_GCC_ATTRIBUTES) #if CORK_CONFIG_GCC_VERSION >= 29600 #define CORK_CONFIG_HAVE_GCC_ATTRIBUTES 1 #else #define CORK_CONFIG_HAVE_GCC_ATTRIBUTES 0 #endif #endif /* C99 inline is available if we're compiling in C99 mode. */ #if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L #define CORK_CONFIG_HAVE_C99_INLINE 1 #else #define CORK_CONFIG_HAVE_C99_INLINE 0 #endif /* __int128 seems to be available on 64-bit platforms as of GCC 4.6. The * attribute((mode(TI))) syntax seems to be available as of 4.1. */ #if !defined(CORK_CONFIG_HAVE_GCC_INT128) #if CORK_CONFIG_ARCH_X64 && CORK_CONFIG_GCC_VERSION >= 40600 #define CORK_CONFIG_HAVE_GCC_INT128 1 #else #define CORK_CONFIG_HAVE_GCC_INT128 0 #endif #endif #if !defined(CORK_CONFIG_HAVE_GCC_MODE_ATTRIBUTE) #if CORK_CONFIG_ARCH_X64 && CORK_CONFIG_GCC_VERSION >= 40100 #define CORK_CONFIG_HAVE_GCC_MODE_ATTRIBUTE 1 #else #define CORK_CONFIG_HAVE_GCC_MODE_ATTRIBUTE 0 #endif #endif /* Statement expressions have been available since GCC 3.1. */ #if !defined(CORK_CONFIG_HAVE_GCC_STATEMENT_EXPRS) #if CORK_CONFIG_GCC_VERSION >= 30100 #define CORK_CONFIG_HAVE_GCC_STATEMENT_EXPRS 1 #else #define CORK_CONFIG_HAVE_GCC_STATEMENT_EXPRS 0 #endif #endif /* Thread-local storage has been available since GCC 3.3, but not on Mac * OS X. */ #if !defined(CORK_CONFIG_HAVE_THREAD_STORAGE_CLASS) #if !(defined(__APPLE__) && defined(__MACH__)) #if CORK_CONFIG_GCC_VERSION >= 30300 #define CORK_CONFIG_HAVE_THREAD_STORAGE_CLASS 1 #else #define CORK_CONFIG_HAVE_THREAD_STORAGE_CLASS 0 #endif #else #define CORK_CONFIG_HAVE_THREAD_STORAGE_CLASS 0 #endif #endif #endif /* LIBCORK_CONFIG_GCC_H */ libcork-1.0.0-rc3/include/libcork/config/linux.h000066400000000000000000000015641367645320400214670ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2011, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #ifndef LIBCORK_CONFIG_LINUX_H #define LIBCORK_CONFIG_LINUX_H /*----------------------------------------------------------------------- * Endianness */ #include #if __BYTE_ORDER == __BIG_ENDIAN #define CORK_CONFIG_IS_BIG_ENDIAN 1 #define CORK_CONFIG_IS_LITTLE_ENDIAN 0 #elif __BYTE_ORDER == __LITTLE_ENDIAN #define CORK_CONFIG_IS_BIG_ENDIAN 0 #define CORK_CONFIG_IS_LITTLE_ENDIAN 1 #else #error "Cannot determine system endianness" #endif #define CORK_HAVE_REALLOCF 0 #define CORK_HAVE_PTHREADS 1 #endif /* LIBCORK_CONFIG_LINUX_H */ libcork-1.0.0-rc3/include/libcork/config/macosx.h000066400000000000000000000015671367645320400216250ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2011, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #ifndef LIBCORK_CONFIG_MACOSX_H #define LIBCORK_CONFIG_MACOSX_H /*----------------------------------------------------------------------- * Endianness */ #include #if BYTE_ORDER == BIG_ENDIAN #define CORK_CONFIG_IS_BIG_ENDIAN 1 #define CORK_CONFIG_IS_LITTLE_ENDIAN 0 #elif BYTE_ORDER == LITTLE_ENDIAN #define CORK_CONFIG_IS_BIG_ENDIAN 0 #define CORK_CONFIG_IS_LITTLE_ENDIAN 1 #else #error "Cannot determine system endianness" #endif #define CORK_HAVE_REALLOCF 1 #define CORK_HAVE_PTHREADS 1 #endif /* LIBCORK_CONFIG_MACOSX_H */ libcork-1.0.0-rc3/include/libcork/config/version.h.in000066400000000000000000000014401367645320400224130ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2015, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #ifndef LIBCORK_CONFIG_VERSION_H #define LIBCORK_CONFIG_VERSION_H /*----------------------------------------------------------------------- * Library version */ #define CORK_CONFIG_VERSION_MAJOR @CORK_VERSION_MAJOR@ #define CORK_CONFIG_VERSION_MINOR @CORK_VERSION_MINOR@ #define CORK_CONFIG_VERSION_PATCH @CORK_VERSION_PATCH@ #define CORK_CONFIG_VERSION_STRING "@CORK_VERSION@" #define CORK_CONFIG_REVISION "@CORK_GIT_SHA1@" #endif /* LIBCORK_CONFIG_VERSION_H */ libcork-1.0.0-rc3/include/libcork/core.h000066400000000000000000000015441367645320400200110ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2011, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #ifndef LIBCORK_CORE_H #define LIBCORK_CORE_H /*** include all of the parts ***/ #include #include #include #include #include #include #include #include #include #include #include #include #include #endif /* LIBCORK_CORE_H */ libcork-1.0.0-rc3/include/libcork/core/000077500000000000000000000000001367645320400176345ustar00rootroot00000000000000libcork-1.0.0-rc3/include/libcork/core/allocator.h000066400000000000000000000260121367645320400217660ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2011, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #ifndef LIBCORK_CORE_ALLOCATOR_H #define LIBCORK_CORE_ALLOCATOR_H #include #include #include #include #include #include #include /*----------------------------------------------------------------------- * Allocator interface */ struct cork_alloc; typedef void * (*cork_alloc_calloc_f)(const struct cork_alloc *alloc, size_t count, size_t size); typedef void * (*cork_alloc_malloc_f)(const struct cork_alloc *alloc, size_t size); /* Must not free `ptr` if allocation fails. */ typedef void * (*cork_alloc_realloc_f)(const struct cork_alloc *alloc, void *ptr, size_t old_size, size_t new_size); typedef void (*cork_alloc_free_f)(const struct cork_alloc *alloc, void *ptr, size_t size); struct cork_alloc { const struct cork_alloc *parent; void *user_data; cork_free_f free_user_data; cork_alloc_calloc_f calloc; cork_alloc_malloc_f malloc; cork_alloc_realloc_f realloc; cork_alloc_calloc_f xcalloc; cork_alloc_malloc_f xmalloc; cork_alloc_realloc_f xrealloc; cork_alloc_free_f free; }; /* NOT thread-safe; must be called before most other libcork functions. * Allocator will automatically be freed at process exit. */ CORK_API struct cork_alloc * cork_alloc_new_alloc(const struct cork_alloc *parent); CORK_API void cork_alloc_set_user_data(struct cork_alloc *alloc, void *user_data, cork_free_f free_user_data); /* These variants must always return a valid pointer. If allocation fails, they * should abort the process or transfer control in some other way to an error * handler or cleanup routine. * * If you only provide implementations of the `x` variants, we'll provide * default implementations of these that abort the process if a memory * allocation fails. */ CORK_API void cork_alloc_set_calloc(struct cork_alloc *alloc, cork_alloc_calloc_f calloc); CORK_API void cork_alloc_set_malloc(struct cork_alloc *alloc, cork_alloc_malloc_f malloc); CORK_API void cork_alloc_set_realloc(struct cork_alloc *alloc, cork_alloc_realloc_f realloc); /* These variants can return a NULL pointer if allocation fails. */ CORK_API void cork_alloc_set_xcalloc(struct cork_alloc *alloc, cork_alloc_calloc_f xcalloc); CORK_API void cork_alloc_set_xmalloc(struct cork_alloc *alloc, cork_alloc_malloc_f xmalloc); CORK_API void cork_alloc_set_xrealloc(struct cork_alloc *alloc, cork_alloc_realloc_f xrealloc); CORK_API void cork_alloc_set_free(struct cork_alloc *alloc, cork_alloc_free_f free); /* Low-level use of an allocator. */ CORK_ATTR_MALLOC CORK_INLINE void * cork_alloc_calloc(const struct cork_alloc *alloc, size_t count, size_t size) { return alloc->calloc(alloc, count, size); } CORK_ATTR_MALLOC CORK_INLINE void * cork_alloc_malloc(const struct cork_alloc *alloc, size_t size) { return alloc->malloc(alloc, size); } CORK_ATTR_MALLOC CORK_INLINE void * cork_alloc_realloc(const struct cork_alloc *alloc, void *ptr, size_t old_size, size_t new_size) { return alloc->realloc(alloc, ptr, old_size, new_size); } CORK_ATTR_MALLOC CORK_INLINE void * cork_alloc_xcalloc(const struct cork_alloc *alloc, size_t count, size_t size) { return alloc->xcalloc(alloc, count, size); } CORK_ATTR_MALLOC CORK_INLINE void * cork_alloc_xmalloc(const struct cork_alloc *alloc, size_t size) { return alloc->xmalloc(alloc, size); } CORK_ATTR_MALLOC CORK_INLINE void * cork_alloc_xrealloc(const struct cork_alloc *alloc, void *ptr, size_t old_size, size_t new_size) { return alloc->xrealloc(alloc, ptr, old_size, new_size); } CORK_ATTR_MALLOC CORK_INLINE void * cork_alloc_xreallocf(const struct cork_alloc *alloc, void *ptr, size_t old_size, size_t new_size) { void *result = alloc->xrealloc(alloc, ptr, old_size, new_size); if (result == NULL) { alloc->free(alloc, ptr, old_size); return NULL; } else { return result; } } CORK_INLINE void cork_alloc_free(const struct cork_alloc *alloc, void *ptr, size_t size) { return alloc->free(alloc, ptr, size); } CORK_INLINE void cork_alloc_cfree(const struct cork_alloc *alloc, void *ptr, size_t count, size_t size) { assert(count < (SIZE_MAX / size)); return alloc->free(alloc, ptr, count * size); } #define cork_alloc_new(alloc, type) \ cork_alloc_malloc((alloc), sizeof(type)) #define cork_alloc_xnew(alloc, type) \ cork_alloc_xmalloc((alloc), sizeof(type)) #define cork_alloc_delete(alloc, type, ptr) \ cork_alloc_free((alloc), (ptr), sizeof(type)) /* string-related helper functions */ CORK_ATTR_MALLOC CORK_API const char * cork_alloc_strdup(const struct cork_alloc *alloc, const char *str); CORK_ATTR_MALLOC CORK_API const char * cork_alloc_strndup(const struct cork_alloc *alloc, const char *str, size_t size); CORK_ATTR_MALLOC CORK_API const char * cork_alloc_xstrdup(const struct cork_alloc *alloc, const char *str); CORK_ATTR_MALLOC CORK_API const char * cork_alloc_xstrndup(const struct cork_alloc *alloc, const char *str, size_t size); CORK_API void cork_alloc_strfree(const struct cork_alloc *alloc, const char *str); /*----------------------------------------------------------------------- * Using the allocator interface */ /* All of the functions that you use to actually allocate memory assume that * cork_current_allocator() returns the allocator instance that should be used. * Your easiest approach is to do nothing special; in that case, all of the * libcork memory allocation functions will transparently use the standard * malloc/free family of functions. * * If you're writing a library, and want to allow your library clients to * provide a separate custom memory allocator then the one they can already * override for libcork itself, you should declare a pair of functions for * getting and setting your library's current allocator (like libcork itself * does), and (only when compiling the source of your library) define * `cork_current_allocator` as a macro that aliases the getter function. That * will cause the libcork memory allocation functions to use whichever allocator * your library user has provided. * * If you're writing an application, and want to provide a single allocator that * all libcork-using libraries will pick up, just call cork_set_allocator before * calling any other library functions. Other libraries will use this as a * default and everything that uses libcork's memory allocation functions will * use your custom allocator. */ /* libcork's current allocator */ extern const struct cork_alloc *cork_allocator; /* We take control and will free when the process exits. This is *NOT* * thread-safe; it's only safe to call before you've called *ANY* other libcork * function (or any function from any other library that uses libcork). You can * only call this at most once. */ CORK_API void cork_set_allocator(const struct cork_alloc *alloc); /* The current allocator of whichever library is being compiled. */ #if !defined(cork_current_allocator) #define cork_current_allocator() (cork_allocator) #endif /* using an allocator */ CORK_ATTR_MALLOC CORK_INLINE void * cork_calloc(size_t count, size_t size) { const struct cork_alloc *alloc = cork_current_allocator(); return cork_alloc_calloc(alloc, count, size); } CORK_ATTR_MALLOC CORK_INLINE void * cork_malloc(size_t size) { const struct cork_alloc *alloc = cork_current_allocator(); return cork_alloc_malloc(alloc, size); } CORK_ATTR_MALLOC CORK_INLINE void * cork_realloc(void *ptr, size_t old_size, size_t new_size) { const struct cork_alloc *alloc = cork_current_allocator(); return cork_alloc_realloc(alloc, ptr, old_size, new_size); } CORK_ATTR_MALLOC CORK_INLINE void * cork_xcalloc(size_t count, size_t size) { const struct cork_alloc *alloc = cork_current_allocator(); return cork_alloc_xcalloc(alloc, count, size); } CORK_ATTR_MALLOC CORK_INLINE void * cork_xmalloc(size_t size) { const struct cork_alloc *alloc = cork_current_allocator(); return cork_alloc_xmalloc(alloc, size); } CORK_ATTR_MALLOC CORK_INLINE void * cork_xrealloc(void *ptr, size_t old_size, size_t new_size) { const struct cork_alloc *alloc = cork_current_allocator(); return cork_alloc_xrealloc(alloc, ptr, old_size, new_size); } CORK_ATTR_MALLOC CORK_INLINE void * cork_xreallocf(void *ptr, size_t old_size, size_t new_size) { const struct cork_alloc *alloc = cork_current_allocator(); return cork_alloc_xreallocf(alloc, ptr, old_size, new_size); } CORK_INLINE void cork_free(void *ptr, size_t size) { const struct cork_alloc *alloc = cork_current_allocator(); cork_alloc_free(alloc, ptr, size); } CORK_INLINE void cork_cfree(void *ptr, size_t count, size_t size) { const struct cork_alloc *alloc = cork_current_allocator(); cork_alloc_cfree(alloc, ptr, count, size); } #define cork_new(type) cork_malloc(sizeof(type)) #define cork_xnew(type) cork_xmalloc(sizeof(type)) #define cork_delete(type, ptr) cork_free((ptr), sizeof(type)) /* string-related helper functions */ CORK_ATTR_MALLOC CORK_INLINE const char * cork_strdup(const char *str) { const struct cork_alloc *alloc = cork_current_allocator(); return cork_alloc_strdup(alloc, str); } CORK_ATTR_MALLOC CORK_INLINE const char * cork_strndup(const char *str, size_t size) { const struct cork_alloc *alloc = cork_current_allocator(); return cork_alloc_strndup(alloc, str, size); } CORK_ATTR_MALLOC CORK_INLINE const char * cork_xstrdup(const char *str) { const struct cork_alloc *alloc = cork_current_allocator(); return cork_alloc_xstrdup(alloc, str); } CORK_ATTR_MALLOC CORK_INLINE const char * cork_xstrndup(const char *str, size_t size) { const struct cork_alloc *alloc = cork_current_allocator(); return cork_alloc_xstrndup(alloc, str, size); } CORK_INLINE void cork_strfree(const char *str) { const struct cork_alloc *alloc = cork_current_allocator(); return cork_alloc_strfree(alloc, str); } CORK_INLINE size_t cork_strlen(const char *str) { size_t* base = ((size_t*) str) - 1; return *base; } /*----------------------------------------------------------------------- * Debugging allocator */ /* An allocator that adds some additional debugging checks: * * - We verify that every "free" call (cork_free, cork_cfree, cork_delete, * cork_realloc) is passed the "correct" size — i.e., the same size that was * passed in to the correspond "new" call (cork_malloc, cork_calloc, * cork_realloc, cork_new). */ struct cork_alloc * cork_debug_alloc_new(const struct cork_alloc *parent); #endif /* LIBCORK_CORE_ALLOCATOR_H */ libcork-1.0.0-rc3/include/libcork/core/api.h000066400000000000000000000030611367645320400205560ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2012, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #ifndef LIBCORK_CORE_API_H #define LIBCORK_CORE_API_H #include #include /*----------------------------------------------------------------------- * Calling conventions */ /* If you're using libcork as a shared library, you don't need to do anything * special; the following will automatically set things up so that libcork's * public symbols are imported from the library. When we build the shared * library, we define this ourselves to export the symbols. */ #if !defined(CORK_API) #define CORK_API CORK_IMPORT #endif /*----------------------------------------------------------------------- * Library version */ #define CORK_VERSION_MAJOR CORK_CONFIG_VERSION_MAJOR #define CORK_VERSION_MINOR CORK_CONFIG_VERSION_MINOR #define CORK_VERSION_PATCH CORK_CONFIG_VERSION_PATCH #define CORK_MAKE_VERSION(major, minor, patch) \ ((major * 1000000) + (minor * 1000) + patch) #define CORK_VERSION \ CORK_MAKE_VERSION(CORK_VERSION_MAJOR, \ CORK_VERSION_MINOR, \ CORK_VERSION_PATCH) CORK_API const char * cork_version_string(void) CORK_ATTR_CONST; CORK_API const char * cork_revision_string(void) CORK_ATTR_CONST; #endif /* LIBCORK_CORE_API_H */ libcork-1.0.0-rc3/include/libcork/core/attributes.h000066400000000000000000000113111367645320400221700ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2011, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #ifndef LIBCORK_CORE_ATTRIBUTES_H #define LIBCORK_CORE_ATTRIBUTES_H #include /* * Declare a “const” function. * * A const function is one whose return value depends only on its * parameters. This is slightly more strict than a “pure” function; a * const function is not allowed to read from global variables, whereas * a pure function is. * * int square(int x) CORK_ATTR_CONST; */ #if CORK_CONFIG_HAVE_GCC_ATTRIBUTES #define CORK_ATTR_CONST __attribute__((const)) #else #define CORK_ATTR_CONST #endif /* * Declare a “pure” function. * * A pure function is one whose return value depends only on its * parameters, and global variables. * * int square(int x) CORK_ATTR_PURE; */ #if CORK_CONFIG_HAVE_GCC_ATTRIBUTES #define CORK_ATTR_PURE __attribute__((pure)) #else #define CORK_ATTR_PURE #endif /* * Declare that a function returns a newly allocated pointer. * * The compiler can use this information to generate more accurate * aliasing information, since it can infer that the result of the * function cannot alias any other existing pointer. */ #if CORK_CONFIG_HAVE_GCC_ATTRIBUTES #define CORK_ATTR_MALLOC __attribute__((malloc)) #else #define CORK_ATTR_MALLOC #endif /* * Declare that a function shouldn't be inlined. */ #if CORK_CONFIG_HAVE_GCC_ATTRIBUTES #define CORK_ATTR_NOINLINE __attribute__((noinline)) #else #define CORK_ATTR_NOINLINE #endif /* * Declare an entity that isn't used. * * This lets you keep -Wall activated in several cases where you're * obligated to define something that you don't intend to use. */ #if CORK_CONFIG_HAVE_GCC_ATTRIBUTES #define CORK_ATTR_UNUSED __attribute__((unused)) #else #define CORK_ATTR_UNUSED #endif /* * Declare a function that takes in printf-like parameters. * * When the compiler supports this attribute, it will check the format * string, and the following arguments, to make sure that they match. * format_index and args_index are 1-based. */ #if CORK_CONFIG_HAVE_GCC_ATTRIBUTES #define CORK_ATTR_PRINTF(format_index, args_index) \ __attribute__((format(printf, format_index, args_index))) #else #define CORK_ATTR_PRINTF(format_index, args_index) #endif /* * Declare a var-arg function whose last parameter must be a NULL * sentinel value. * * When the compiler supports this attribute, it will check the actual * parameters whenever this function is called, and ensure that the last * parameter is a @c NULL. */ #if CORK_CONFIG_HAVE_GCC_ATTRIBUTES #define CORK_ATTR_SENTINEL __attribute__((sentinel)) #else #define CORK_ATTR_SENTINEL #endif /* * Declare that a boolean expression is likely to be true or false. */ #if CORK_CONFIG_HAVE_GCC_ATTRIBUTES #define CORK_LIKELY(expr) __builtin_expect((expr), 1) #define CORK_UNLIKELY(expr) __builtin_expect((expr), 0) #else #define CORK_LIKELY(expr) (expr) #define CORK_UNLIKELY(expr) (expr) #endif /* * Declare that a function is part of the current library's public API, or that * it's internal to the current library. */ #if CORK_CONFIG_HAVE_GCC_ATTRIBUTES #define CORK_EXPORT __attribute__((visibility("default"))) #define CORK_IMPORT __attribute__((visibility("default"))) #define CORK_LOCAL __attribute__((visibility("hidden"))) #else #define CORK_EXPORT #define CORK_IMPORT #define CORK_LOCAL #endif /* * Define an inline function in a header file. This allows the compiler to * inline the definition if it makes sense. You should also provide a * _declaration_ in some source file, with exactly the same signature, which * will ensure that a single copy of the function is available to link against * if the compiler decides not to inline the function. */ #if CORK_CONFIG_HAVE_C99_INLINE #define CORK_INLINE inline #else #define CORK_INLINE CORK_ATTR_UNUSED static #endif /* * Declare a static function that should automatically be called at program * startup. */ /* TODO: When we implement a full Windows port, [1] describes how best to * implement an initialization function under Visual Studio. * * [1] http://stackoverflow.com/questions/1113409/attribute-constructor-equivalent-in-vc */ #if CORK_CONFIG_HAVE_GCC_ATTRIBUTES #define CORK_INITIALIZER(name) \ __attribute__((constructor)) \ static void \ name(void) #define CORK_FINALIZER(name) \ __attribute__((destructor)) \ static void \ name(void) #else #error "Don't know how to implement initialization functions of this platform" #endif #endif /* LIBCORK_CORE_ATTRIBUTES_H */ libcork-1.0.0-rc3/include/libcork/core/byte-order.h000066400000000000000000000162761367645320400220750ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2011, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #ifndef LIBCORK_CORE_BYTE_ORDER_H #define LIBCORK_CORE_BYTE_ORDER_H #include #include /* Constants to represent big endianness and little endianness */ #define CORK_BIG_ENDIAN 4321 #define CORK_LITTLE_ENDIAN 1234 /* Whether the current host is big- or little-endian. HOST gives us the * current system's endianness; OTHER gives the opposite endianness. * The _NAME macros can be used in debugging messages and other * human-readable output. * * Note that we actually detect the endianness in the various header * files in the libcork/config directory, since we want to keep * everything detection-related separated out from what we define based * on that detection. */ #if CORK_CONFIG_IS_BIG_ENDIAN #define CORK_HOST_ENDIANNESS CORK_BIG_ENDIAN #define CORK_OTHER_ENDIANNESS CORK_LITTLE_ENDIAN #define CORK_HOST_ENDIANNESS_NAME "big" #define CORK_OTHER_ENDIANNESS_NAME "little" #elif CORK_CONFIG_IS_LITTLE_ENDIAN #define CORK_HOST_ENDIANNESS CORK_LITTLE_ENDIAN #define CORK_OTHER_ENDIANNESS CORK_BIG_ENDIAN #define CORK_HOST_ENDIANNESS_NAME "little" #define CORK_OTHER_ENDIANNESS_NAME "big" #else #error "Unknown endianness" #endif /* Returns the byte-swapped version an integer, regardless of the * underlying endianness. * * These macros only require an rvalue as their parameter (which can * therefore be any arbitrary expression), and they don't modify the * original contents if it happens to be a variable. */ #define CORK_SWAP_UINT16(__u16) \ (((((uint16_t) __u16) & 0xff00u) >> 8) | \ ((((uint16_t) __u16) & 0x00ffu) << 8)) #define CORK_SWAP_UINT32(__u32) \ (((((uint32_t) __u32) & 0xff000000u) >> 24) | \ ((((uint32_t) __u32) & 0x00ff0000u) >> 8) | \ ((((uint32_t) __u32) & 0x0000ff00u) << 8) | \ ((((uint32_t) __u32) & 0x000000ffu) << 24)) #define CORK_SWAP_UINT64(__u64) \ (((((uint64_t) __u64) & UINT64_C(0xff00000000000000)) >> 56) | \ ((((uint64_t) __u64) & UINT64_C(0x00ff000000000000)) >> 40) | \ ((((uint64_t) __u64) & UINT64_C(0x0000ff0000000000)) >> 24) | \ ((((uint64_t) __u64) & UINT64_C(0x000000ff00000000)) >> 8) | \ ((((uint64_t) __u64) & UINT64_C(0x00000000ff000000)) << 8) | \ ((((uint64_t) __u64) & UINT64_C(0x0000000000ff0000)) << 24) | \ ((((uint64_t) __u64) & UINT64_C(0x000000000000ff00)) << 40) | \ ((((uint64_t) __u64) & UINT64_C(0x00000000000000ff)) << 56)) /* Bytes-swaps an integer variable in place. * * These macros require an lvalue as their parameter; the contents of * this variable will be modified by the macro. */ #define CORK_SWAP_IN_PLACE_UINT16(__u16) \ do { \ (__u16) = CORK_SWAP_UINT16(__u16); \ } while (0) #define CORK_SWAP_IN_PLACE_UINT32(__u32) \ do { \ (__u32) = CORK_SWAP_UINT32(__u32); \ } while (0) #define CORK_SWAP_IN_PLACE_UINT64(__u64) \ do { \ (__u64) = CORK_SWAP_UINT64(__u64); \ } while (0) /* * A slew of swapping macros whose operation depends on the endianness * of the current system: * * uint16_t CORK_UINT16_BIG_TO_HOST(u16) * uint32_t CORK_UINT32_BIG_TO_HOST(u32) * uint64_t CORK_UINT64_BIG_TO_HOST(u64) * uint16_t CORK_UINT16_LITTLE_TO_HOST(u16) * uint32_t CORK_UINT32_LITTLE_TO_HOST(u32) * uint64_t CORK_UINT64_LITTLE_TO_HOST(u64) * void CORK_UINT16_BIG_TO_HOST_IN_PLACE(&u16) * void CORK_UINT32_BIG_TO_HOST_IN_PLACE(&u32) * void CORK_UINT64_BIG_TO_HOST_IN_PLACE(&u64) * void CORK_UINT16_LITTLE_TO_HOST_IN_PLACE(&u16) * void CORK_UINT32_LITTLE_TO_HOST_IN_PLACE(&u32) * void CORK_UINT64_LITTLE_TO_HOST_IN_PLACE(&u64) * * uint16_t CORK_UINT16_HOST_TO_BIG(u16) * uint32_t CORK_UINT32_HOST_TO_BIG(u32) * uint64_t CORK_UINT64_HOST_TO_BIG(u64) * uint16_t CORK_UINT16_HOST_TO_LITTLE(u16) * uint32_t CORK_UINT32_HOST_TO_LITTLE(u32) * uint64_t CORK_UINT64_HOST_TO_LITTLE(u64) * void CORK_UINT16_HOST_TO_BIG_IN_PLACE(&u16) * void CORK_UINT32_HOST_TO_BIG_IN_PLACE(&u32) * void CORK_UINT64_HOST_TO_BIG_IN_PLACE(&u64) * void CORK_UINT16_HOST_TO_LITTLE_IN_PLACE(&u16) * void CORK_UINT32_HOST_TO_LITTLE_IN_PLACE(&u32) * void CORK_UINT64_HOST_TO_LITTLE_IN_PLACE(&u64) */ #if CORK_HOST_ENDIANNESS == CORK_BIG_ENDIAN #define CORK_UINT16_BIG_TO_HOST(__u16) (__u16) /* nothing to do */ #define CORK_UINT16_LITTLE_TO_HOST(__u16) CORK_SWAP_UINT16(__u16) #define CORK_UINT32_BIG_TO_HOST(__u32) (__u32) /* nothing to do */ #define CORK_UINT32_LITTLE_TO_HOST(__u32) CORK_SWAP_UINT32(__u32) #define CORK_UINT64_BIG_TO_HOST(__u64) (__u64) /* nothing to do */ #define CORK_UINT64_LITTLE_TO_HOST(__u64) CORK_SWAP_UINT64(__u64) #define CORK_UINT16_BIG_TO_HOST_IN_PLACE(__u16) /* nothing to do */ #define CORK_UINT16_LITTLE_TO_HOST_IN_PLACE(__u16) CORK_SWAP_IN_PLACE_UINT16(__u16) #define CORK_UINT32_BIG_TO_HOST_IN_PLACE(__u32) /* nothing to do */ #define CORK_UINT32_LITTLE_TO_HOST_IN_PLACE(__u32) CORK_SWAP_IN_PLACE_UINT32(__u32) #define CORK_UINT64_BIG_TO_HOST_IN_PLACE(__u64) /* nothing to do */ #define CORK_UINT64_LITTLE_TO_HOST_IN_PLACE(__u64) CORK_SWAP_IN_PLACE_UINT64(__u64) #elif CORK_HOST_ENDIANNESS == CORK_LITTLE_ENDIAN #define CORK_UINT16_BIG_TO_HOST(__u16) CORK_SWAP_UINT16(__u16) #define CORK_UINT16_LITTLE_TO_HOST(__u16) (__u16) /* nothing to do */ #define CORK_UINT32_BIG_TO_HOST(__u32) CORK_SWAP_UINT32(__u32) #define CORK_UINT32_LITTLE_TO_HOST(__u32) (__u32) /* nothing to do */ #define CORK_UINT64_BIG_TO_HOST(__u64) CORK_SWAP_UINT64(__u64) #define CORK_UINT64_LITTLE_TO_HOST(__u64) (__u64) /* nothing to do */ #define CORK_UINT16_BIG_TO_HOST_IN_PLACE(__u16) CORK_SWAP_IN_PLACE_UINT16(__u16) #define CORK_UINT16_LITTLE_TO_HOST_IN_PLACE(__u16) /* nothing to do */ #define CORK_UINT32_BIG_TO_HOST_IN_PLACE(__u32) CORK_SWAP_IN_PLACE_UINT32(__u32) #define CORK_UINT32_LITTLE_TO_HOST_IN_PLACE(__u32) /* nothing to do */ #define CORK_UINT64_BIG_TO_HOST_IN_PLACE(__u64) CORK_SWAP_IN_PLACE_UINT64(__u64) #define CORK_UINT64_LITTLE_TO_HOST_IN_PLACE(__u64) /* nothing to do */ #endif #define CORK_UINT16_HOST_TO_BIG(__u16) CORK_UINT16_BIG_TO_HOST(__u16) #define CORK_UINT32_HOST_TO_BIG(__u32) CORK_UINT32_BIG_TO_HOST(__u32) #define CORK_UINT64_HOST_TO_BIG(__u64) CORK_UINT64_BIG_TO_HOST(__u64) #define CORK_UINT16_HOST_TO_LITTLE(__u16) CORK_UINT16_LITTLE_TO_HOST(__u16) #define CORK_UINT32_HOST_TO_LITTLE(__u32) CORK_UINT32_LITTLE_TO_HOST(__u32) #define CORK_UINT64_HOST_TO_LITTLE(__u64) CORK_UINT64_LITTLE_TO_HOST(__u64) #define CORK_UINT16_HOST_TO_BIG_IN_PLACE(__u16) CORK_UINT16_BIG_TO_HOST_IN_PLACE(__u16) #define CORK_UINT32_HOST_TO_BIG_IN_PLACE(__u32) CORK_UINT32_BIG_TO_HOST_IN_PLACE(__u32) #define CORK_UINT64_HOST_TO_BIG_IN_PLACE(__u64) CORK_UINT64_BIG_TO_HOST_IN_PLACE(__u64) #define CORK_UINT16_HOST_TO_LITTLE_IN_PLACE(__u16) CORK_UINT16_LITTLE_TO_HOST_IN_PLACE(__u16) #define CORK_UINT32_HOST_TO_LITTLE_IN_PLACE(__u32) CORK_UINT32_LITTLE_TO_HOST_IN_PLACE(__u32) #define CORK_UINT64_HOST_TO_LITTLE_IN_PLACE(__u64) CORK_UINT64_LITTLE_TO_HOST_IN_PLACE(__u64) #endif /* LIBCORK_CORE_BYTE_ORDER_H */ libcork-1.0.0-rc3/include/libcork/core/callbacks.h000066400000000000000000000021011367645320400217160ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2013, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #ifndef LIBCORK_CORE_CALLBACKS_H #define LIBCORK_CORE_CALLBACKS_H #include typedef int (*cork_copy_f)(void *user_data, void *dest, const void *src); typedef void (*cork_done_f)(void *user_data, void *value); typedef void (*cork_free_f)(void *value); typedef cork_hash (*cork_hash_f)(void *user_data, const void *value); typedef bool (*cork_equals_f)(void *user_data, const void *value1, const void *value2); typedef void (*cork_init_f)(void *user_data, void *value); #define cork_free_user_data(parent) \ ((parent)->free_user_data == NULL? (void) 0: \ (parent)->free_user_data((parent)->user_data)) typedef void * (*cork_new_f)(void *user_data); typedef int (*cork_run_f)(void *user_data); #endif /* LIBCORK_CORE_CALLBACKS_H */ libcork-1.0.0-rc3/include/libcork/core/error.h000066400000000000000000000065541367645320400211500ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2011, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #ifndef LIBCORK_CORE_ERROR_H #define LIBCORK_CORE_ERROR_H #include #include #include #include #include #include #include /* Should be a hash of a string representing the error code. */ typedef uint32_t cork_error; /* An error code that represents “no error”. */ #define CORK_ERROR_NONE ((cork_error) 0) CORK_API bool cork_error_occurred(void); CORK_API cork_error cork_error_code(void); CORK_API const char * cork_error_message(void); CORK_API void cork_error_clear(void); CORK_API void cork_error_set_printf(cork_error code, const char *format, ...) CORK_ATTR_PRINTF(2,3); CORK_API void cork_error_set_string(cork_error code, const char *str); CORK_API void cork_error_set_vprintf(cork_error code, const char *format, va_list args) CORK_ATTR_PRINTF(2,0); CORK_API void cork_error_prefix_printf(const char *format, ...) CORK_ATTR_PRINTF(1,2); CORK_API void cork_error_prefix_string(const char *str); CORK_API void cork_error_prefix_vprintf(const char *format, va_list arg) CORK_ATTR_PRINTF(1,0); /* deprecated */ CORK_API void cork_error_set(uint32_t error_class, unsigned int error_code, const char *format, ...) CORK_ATTR_PRINTF(3,4); /* deprecated */ CORK_API void cork_error_prefix(const char *format, ...) CORK_ATTR_PRINTF(1,2); /*----------------------------------------------------------------------- * Built-in errors */ #define CORK_PARSE_ERROR 0x95dfd3c8 #define CORK_REDEFINED 0x171629cb #define CORK_UNDEFINED 0xedc3d7d9 #define CORK_UNKNOWN_ERROR 0x8cb0880d #define cork_parse_error(...) \ cork_error_set_printf(CORK_PARSE_ERROR, __VA_ARGS__) #define cork_redefined(...) \ cork_error_set_printf(CORK_REDEFINED, __VA_ARGS__) #define cork_undefined(...) \ cork_error_set_printf(CORK_UNDEFINED, __VA_ARGS__) CORK_API void cork_system_error_set(void); CORK_API void cork_system_error_set_explicit(int err); CORK_API void cork_unknown_error_set_(const char *location); #define cork_unknown_error() \ cork_unknown_error_set_(__func__) /*----------------------------------------------------------------------- * Abort on failure */ #define cork_abort_(func, file, line, fmt, ...) \ do { \ fprintf(stderr, fmt "\n in %s (%s:%u)\n", \ __VA_ARGS__, (func), (file), (unsigned int) (line)); \ abort(); \ } while (0) #define cork_abort(fmt, ...) \ cork_abort_(__func__, __FILE__, __LINE__, fmt, __VA_ARGS__) CORK_INLINE void * cork_abort_if_null_(void *ptr, const char *msg, const char *func, const char *file, unsigned int line) { if (CORK_UNLIKELY(ptr == NULL)) { cork_abort_(func, file, line, "%s", msg); } else { return ptr; } } #define cork_abort_if_null(ptr, msg) \ (cork_abort_if_null_(ptr, msg, __func__, __FILE__, __LINE__)) #define cork_unreachable() \ cork_abort("%s", "Code should not be reachable") #endif /* LIBCORK_CORE_ERROR_H */ libcork-1.0.0-rc3/include/libcork/core/gc.h000066400000000000000000000030641367645320400204010ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2011, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #ifndef LIBCORK_GC_REFCOUNT_H #define LIBCORK_GC_REFCOUNT_H #include #include struct cork_gc; /* A callback for recursing through the children of a garbage-collected * object. */ typedef void (*cork_gc_recurser)(struct cork_gc *gc, void *obj, void *ud); typedef void (*cork_gc_free_func)(void *obj); typedef void (*cork_gc_recurse_func)(struct cork_gc *gc, void *self, cork_gc_recurser recurser, void *ud); /* An interface that each garbage-collected object must implement. */ struct cork_gc_obj_iface { /* Perform additional cleanup; does *NOT* need to deallocate the * object itself, or release any child references */ cork_gc_free_func free; cork_gc_recurse_func recurse; }; CORK_API void cork_gc_init(void); CORK_API void cork_gc_done(void); CORK_API void * cork_gc_alloc(size_t instance_size, struct cork_gc_obj_iface *iface); #define cork_gc_new_iface(obj_type, iface) \ ((obj_type *) \ (cork_gc_alloc(sizeof(obj_type), (iface)))) #define cork_gc_new(struct_name) \ (cork_gc_new_iface(struct struct_name, &struct_name##__gc)) CORK_API void * cork_gc_incref(void *obj); CORK_API void cork_gc_decref(void *obj); #endif /* LIBCORK_GC_REFCOUNT_H */ libcork-1.0.0-rc3/include/libcork/core/hash.h000066400000000000000000000257121367645320400207370ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2011, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #ifndef LIBCORK_CORE_HASH_H #define LIBCORK_CORE_HASH_H #include #include #include #include #include /* Needed for memcpy */ #include typedef uint32_t cork_hash; typedef struct { cork_u128 u128; } cork_big_hash; CORK_INLINE bool cork_big_hash_equal(const cork_big_hash h1, const cork_big_hash h2) { return cork_u128_eq(h1.u128, h2.u128); } #define CORK_BIG_HASH_INIT() {{{{0}}}} /* We currently use MurmurHash3 [1], which is public domain, as our hash * implementation. * * [1] http://code.google.com/p/smhasher/ */ #define CORK_ROTL32(a,b) (((a) << ((b) & 0x1f)) | ((a) >> (32 - ((b) & 0x1f)))) #define CORK_ROTL64(a,b) (((a) << ((b) & 0x3f)) | ((a) >> (64 - ((b) & 0x3f)))) CORK_INLINE uint32_t cork_getblock32(const uint32_t *p, int i) { uint32_t u; memcpy(&u, p + i, sizeof(u)); return u; } CORK_INLINE uint64_t cork_getblock64(const uint64_t *p, int i) { uint64_t u; memcpy(&u, p + i, sizeof(u)); return u; } CORK_INLINE uint32_t cork_fmix32(uint32_t h) { h ^= h >> 16; h *= 0x85ebca6b; h ^= h >> 13; h *= 0xc2b2ae35; h ^= h >> 16; return h; } CORK_INLINE uint64_t cork_fmix64(uint64_t k) { k ^= k >> 33; k *= UINT64_C(0xff51afd7ed558ccd); k ^= k >> 33; k *= UINT64_C(0xc4ceb9fe1a85ec53); k ^= k >> 33; return k; } CORK_INLINE cork_hash cork_stable_hash_buffer(cork_hash seed, const void *src, size_t len) { typedef uint32_t __attribute__((__may_alias__)) cork_aliased_uint32_t; /* This is exactly the same as cork_murmur_hash_x86_32, but with a byte swap * to make sure that we always process the uint32s little-endian. */ const unsigned int nblocks = (unsigned int)(len / 4); const cork_aliased_uint32_t *blocks = (const cork_aliased_uint32_t *) src; const cork_aliased_uint32_t *end = blocks + nblocks; const cork_aliased_uint32_t *curr; const uint8_t *tail = (const uint8_t *) end; uint32_t h1 = seed; uint32_t c1 = 0xcc9e2d51; uint32_t c2 = 0x1b873593; uint32_t k1 = 0; /* body */ for (curr = blocks; curr != end; curr++) { uint32_t k1 = CORK_UINT32_HOST_TO_LITTLE(cork_getblock32((const uint32_t *) curr, 0)); k1 *= c1; k1 = CORK_ROTL32(k1,15); k1 *= c2; h1 ^= k1; h1 = CORK_ROTL32(h1,13); h1 = h1*5+0xe6546b64; } /* tail */ switch (len & 3) { case 3: k1 ^= tail[2] << 16; case 2: k1 ^= tail[1] << 8; case 1: k1 ^= tail[0]; k1 *= c1; k1 = CORK_ROTL32(k1,15); k1 *= c2; h1 ^= k1; }; /* finalization */ h1 ^= len; h1 = cork_fmix32(h1); return h1; } #define cork_murmur_hash_x86_32(seed, src, len, dest) \ do { \ typedef uint32_t __attribute__((__may_alias__)) cork_aliased_uint32_t; \ \ const unsigned int nblocks = len / 4; \ const cork_aliased_uint32_t *blocks = (const cork_aliased_uint32_t *) src; \ const cork_aliased_uint32_t *end = blocks + nblocks; \ const cork_aliased_uint32_t *curr; \ const uint8_t *tail = (const uint8_t *) end; \ \ uint32_t h1 = seed; \ uint32_t c1 = 0xcc9e2d51; \ uint32_t c2 = 0x1b873593; \ uint32_t k1 = 0; \ \ /* body */ \ for (curr = blocks; curr != end; curr++) { \ uint32_t k1 = cork_getblock32((const uint32_t *) curr, 0); \ \ k1 *= c1; \ k1 = CORK_ROTL32(k1,15); \ k1 *= c2; \ \ h1 ^= k1; \ h1 = CORK_ROTL32(h1,13); \ h1 = h1*5+0xe6546b64; \ } \ \ /* tail */ \ switch (len & 3) { \ case 3: k1 ^= tail[2] << 16; \ case 2: k1 ^= tail[1] << 8; \ case 1: k1 ^= tail[0]; \ k1 *= c1; k1 = CORK_ROTL32(k1,15); k1 *= c2; h1 ^= k1; \ }; \ \ /* finalization */ \ h1 ^= len; \ h1 = cork_fmix32(h1); \ *(dest) = h1; \ } while (0) #define cork_murmur_hash_x86_128(seed, src, len, dest) \ do { \ typedef uint32_t __attribute__((__may_alias__)) cork_aliased_uint32_t; \ \ const unsigned int nblocks = len / 16; \ const cork_aliased_uint32_t *blocks = (const cork_aliased_uint32_t *) src; \ const cork_aliased_uint32_t *end = blocks + (nblocks * 4); \ const cork_aliased_uint32_t *curr; \ const uint8_t *tail = (const uint8_t *) end; \ \ uint32_t h1 = cork_u128_be32(seed.u128, 0); \ uint32_t h2 = cork_u128_be32(seed.u128, 1); \ uint32_t h3 = cork_u128_be32(seed.u128, 2); \ uint32_t h4 = cork_u128_be32(seed.u128, 3); \ \ uint32_t c1 = 0x239b961b; \ uint32_t c2 = 0xab0e9789; \ uint32_t c3 = 0x38b34ae5; \ uint32_t c4 = 0xa1e38b93; \ \ uint32_t k1 = 0; \ uint32_t k2 = 0; \ uint32_t k3 = 0; \ uint32_t k4 = 0; \ \ /* body */ \ for (curr = blocks; curr != end; curr += 4) { \ uint32_t k1 = cork_getblock32((const uint32_t *) curr, 0); \ uint32_t k2 = cork_getblock32((const uint32_t *) curr, 1); \ uint32_t k3 = cork_getblock32((const uint32_t *) curr, 2); \ uint32_t k4 = cork_getblock32((const uint32_t *) curr, 3); \ \ k1 *= c1; k1 = CORK_ROTL32(k1,15); k1 *= c2; h1 ^= k1; \ h1 = CORK_ROTL32(h1,19); h1 += h2; h1 = h1*5+0x561ccd1b; \ \ k2 *= c2; k2 = CORK_ROTL32(k2,16); k2 *= c3; h2 ^= k2; \ h2 = CORK_ROTL32(h2,17); h2 += h3; h2 = h2*5+0x0bcaa747; \ \ k3 *= c3; k3 = CORK_ROTL32(k3,17); k3 *= c4; h3 ^= k3; \ h3 = CORK_ROTL32(h3,15); h3 += h4; h3 = h3*5+0x96cd1c35; \ \ k4 *= c4; k4 = CORK_ROTL32(k4,18); k4 *= c1; h4 ^= k4; \ h4 = CORK_ROTL32(h4,13); h4 += h1; h4 = h4*5+0x32ac3b17; \ } \ \ /* tail */ \ switch (len & 15) { \ case 15: k4 ^= tail[14] << 16; \ case 14: k4 ^= tail[13] << 8; \ case 13: k4 ^= tail[12] << 0; \ k4 *= c4; k4 = CORK_ROTL32(k4,18); k4 *= c1; h4 ^= k4; \ \ case 12: k3 ^= tail[11] << 24; \ case 11: k3 ^= tail[10] << 16; \ case 10: k3 ^= tail[ 9] << 8; \ case 9: k3 ^= tail[ 8] << 0; \ k3 *= c3; k3 = CORK_ROTL32(k3,17); k3 *= c4; h3 ^= k3; \ \ case 8: k2 ^= tail[ 7] << 24; \ case 7: k2 ^= tail[ 6] << 16; \ case 6: k2 ^= tail[ 5] << 8; \ case 5: k2 ^= tail[ 4] << 0; \ k2 *= c2; k2 = CORK_ROTL32(k2,16); k2 *= c3; h2 ^= k2; \ \ case 4: k1 ^= tail[ 3] << 24; \ case 3: k1 ^= tail[ 2] << 16; \ case 2: k1 ^= tail[ 1] << 8; \ case 1: k1 ^= tail[ 0] << 0; \ k1 *= c1; k1 = CORK_ROTL32(k1,15); k1 *= c2; h1 ^= k1; \ }; \ \ /* finalization */ \ \ h1 ^= len; h2 ^= len; h3 ^= len; h4 ^= len; \ \ h1 += h2; h1 += h3; h1 += h4; \ h2 += h1; h3 += h1; h4 += h1; \ \ h1 = cork_fmix32(h1); \ h2 = cork_fmix32(h2); \ h3 = cork_fmix32(h3); \ h4 = cork_fmix32(h4); \ \ h1 += h2; h1 += h3; h1 += h4; \ h2 += h1; h3 += h1; h4 += h1; \ \ (dest)->u128 = cork_u128_from_32(h1, h2, h3, h4); \ } while (0) #define cork_murmur_hash_x64_128(seed, src, len, dest) \ do { \ typedef uint64_t __attribute__((__may_alias__)) cork_aliased_uint64_t; \ \ const unsigned int nblocks = len / 16; \ const cork_aliased_uint64_t *blocks = (const cork_aliased_uint64_t *) src; \ const cork_aliased_uint64_t *end = blocks + (nblocks * 2); \ const cork_aliased_uint64_t *curr; \ const uint8_t *tail = (const uint8_t *) end; \ \ uint64_t h1 = cork_u128_be64(seed.u128, 0); \ uint64_t h2 = cork_u128_be64(seed.u128, 1); \ \ uint64_t c1 = UINT64_C(0x87c37b91114253d5); \ uint64_t c2 = UINT64_C(0x4cf5ad432745937f); \ \ uint64_t k1 = 0; \ uint64_t k2 = 0; \ \ /* body */ \ for (curr = blocks; curr != end; curr += 2) { \ uint64_t k1 = cork_getblock64((const uint64_t *) curr, 0); \ uint64_t k2 = cork_getblock64((const uint64_t *) curr, 1); \ \ k1 *= c1; k1 = CORK_ROTL64(k1,31); k1 *= c2; h1 ^= k1; \ h1 = CORK_ROTL64(h1,27); h1 += h2; h1 = h1*5+0x52dce729; \ \ k2 *= c2; k2 = CORK_ROTL64(k2,33); k2 *= c1; h2 ^= k2; \ h2 = CORK_ROTL64(h2,31); h2 += h1; h2 = h2*5+0x38495ab5; \ } \ \ /* tail */ \ switch (len & 15) { \ case 15: k2 ^= (uint64_t) (tail[14]) << 48; \ case 14: k2 ^= (uint64_t) (tail[13]) << 40; \ case 13: k2 ^= (uint64_t) (tail[12]) << 32; \ case 12: k2 ^= (uint64_t) (tail[11]) << 24; \ case 11: k2 ^= (uint64_t) (tail[10]) << 16; \ case 10: k2 ^= (uint64_t) (tail[ 9]) << 8; \ case 9: k2 ^= (uint64_t) (tail[ 8]) << 0; \ k2 *= c2; k2 = CORK_ROTL64(k2,33); k2 *= c1; h2 ^= k2; \ \ case 8: k1 ^= (uint64_t) (tail[ 7]) << 56; \ case 7: k1 ^= (uint64_t) (tail[ 6]) << 48; \ case 6: k1 ^= (uint64_t) (tail[ 5]) << 40; \ case 5: k1 ^= (uint64_t) (tail[ 4]) << 32; \ case 4: k1 ^= (uint64_t) (tail[ 3]) << 24; \ case 3: k1 ^= (uint64_t) (tail[ 2]) << 16; \ case 2: k1 ^= (uint64_t) (tail[ 1]) << 8; \ case 1: k1 ^= (uint64_t) (tail[ 0]) << 0; \ k1 *= c1; k1 = CORK_ROTL64(k1,31); k1 *= c2; h1 ^= k1; \ }; \ \ /* finalization */ \ \ h1 ^= len; h2 ^= len; \ \ h1 += h2; \ h2 += h1; \ \ h1 = cork_fmix64(h1); \ h2 = cork_fmix64(h2); \ \ h1 += h2; \ h2 += h1; \ \ (dest)->u128 = cork_u128_from_64(h1, h2); \ } while (0) CORK_INLINE cork_hash cork_hash_buffer(cork_hash seed, const void *src, size_t len) { #if CORK_SIZEOF_POINTER == 8 cork_big_hash big_seed = {cork_u128_from_32(seed, seed, seed, seed)}; cork_big_hash hash; cork_murmur_hash_x64_128(big_seed, src, (unsigned int)len, &hash); return cork_u128_be32(hash.u128, 0); #else cork_hash hash = 0; cork_murmur_hash_x86_32(seed, src, (unsigned int)len, &hash); return hash; #endif } CORK_INLINE cork_big_hash cork_big_hash_buffer(cork_big_hash seed, const void *src, size_t len) { cork_big_hash result; #if CORK_SIZEOF_POINTER == 8 cork_murmur_hash_x64_128(seed, src, (unsigned int)len, &result); #else cork_murmur_hash_x86_128(seed, src, (unsigned int)len, &result); #endif return result; } #define cork_hash_variable(seed, val) \ (cork_hash_buffer((seed), &(val), sizeof((val)))) #define cork_stable_hash_variable(seed, val) \ (cork_stable_hash_buffer((seed), &(val), sizeof((val)))) #define cork_big_hash_variable(seed, val) \ (cork_big_hash_buffer((seed), &(val), sizeof((val)))) #endif /* LIBCORK_CORE_HASH_H */ libcork-1.0.0-rc3/include/libcork/core/id.h000066400000000000000000000021311367645320400203760ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2013, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #ifndef LIBCORK_CORE_ID_H #define LIBCORK_CORE_ID_H #include #include struct cork_uid { const char *name; }; typedef const struct cork_uid *cork_uid; #define CORK_UID_NONE ((cork_uid) NULL) #define cork_uid_define_named(c_name, name) \ static const struct cork_uid c_name##__id = { name }; \ static cork_uid c_name = &c_name##__id; #define cork_uid_define(c_name) \ cork_uid_define_named(c_name, #c_name) CORK_INLINE bool cork_uid_equal(const cork_uid id1, const cork_uid id2) { return id1 == id2; } CORK_INLINE cork_hash cork_uid_hash(const cork_uid id) { return (cork_hash) (uintptr_t) id; } CORK_INLINE const char* cork_uid_name(const cork_uid id) { return id->name; } #endif /* LIBCORK_CORE_ID_H */ libcork-1.0.0-rc3/include/libcork/core/mempool.h000066400000000000000000000037731367645320400214670ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2012, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #ifndef LIBCORK_CORK_MEMPOOL_H #define LIBCORK_CORK_MEMPOOL_H #include #include #include #include #include #define CORK_MEMPOOL_DEFAULT_BLOCK_SIZE 4096 struct cork_mempool; CORK_API struct cork_mempool * cork_mempool_new_size_ex(size_t element_size, size_t block_size); CORK_INLINE struct cork_mempool* cork_mempool_new_size(size_t element_size) { return cork_mempool_new_size_ex( element_size, CORK_MEMPOOL_DEFAULT_BLOCK_SIZE); } #define cork_mempool_new_ex(type, block_size) \ (cork_mempool_new_size_ex(sizeof(type), (block_size))) #define cork_mempool_new(type) \ (cork_mempool_new_size(sizeof(type))) CORK_API void cork_mempool_free(struct cork_mempool *mp); CORK_API void cork_mempool_set_user_data(struct cork_mempool *mp, void *user_data, cork_free_f free_user_data); CORK_API void cork_mempool_set_init_object(struct cork_mempool *mp, cork_init_f init_object); CORK_API void cork_mempool_set_done_object(struct cork_mempool *mp, cork_done_f done_object); /* Deprecated; you should now use separate calls to cork_mempool_set_user_data, * cork_mempool_set_init_object, and cork_mempool_set_done_object. */ CORK_API void cork_mempool_set_callbacks(struct cork_mempool *mp, void *user_data, cork_free_f free_user_data, cork_init_f init_object, cork_done_f done_object); CORK_API void * cork_mempool_new_object(struct cork_mempool *mp); CORK_API void cork_mempool_free_object(struct cork_mempool *mp, void *ptr); #endif /* LIBCORK_CORK_MEMPOOL_H */ libcork-1.0.0-rc3/include/libcork/core/net-addresses.h000066400000000000000000000073231367645320400225530ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2011, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #ifndef LIBCORK_CORE_NET_ADDRESSES_H #define LIBCORK_CORE_NET_ADDRESSES_H #include #include #include #include #include /*----------------------------------------------------------------------- * IP addresses */ struct cork_ipv4 { union { uint8_t u8[4]; uint16_t u16[2]; uint32_t u32; } _; }; struct cork_ipv6 { union { uint8_t u8[16]; uint16_t u16[8]; uint32_t u32[4]; uint64_t u64[2]; } _; }; struct cork_ip { /* Which version of IP address this is. */ unsigned int version; union { struct cork_ipv4 v4; struct cork_ipv6 v6; } ip; }; #define CORK_IPV4_STRING_LENGTH (sizeof "xxx.xxx.xxx.xxx") #define CORK_IPV6_STRING_LENGTH \ (sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255") #define CORK_IP_STRING_LENGTH CORK_IPV6_STRING_LENGTH /*** IPv4 ***/ /* src must be well-formed: 4 bytes, big-endian */ CORK_INLINE void cork_ipv4_copy(struct cork_ipv4* addr, const void* src) { memcpy(addr, src, sizeof(struct cork_ipv4)); } CORK_INLINE bool cork_ipv4_equal(const struct cork_ipv4* addr1, const struct cork_ipv4* addr2) { return addr1->_.u32 == addr2->_.u32; } CORK_API int cork_ipv4_init(struct cork_ipv4 *addr, const char *str); CORK_API bool cork_ipv4_equal_(const struct cork_ipv4 *addr1, const struct cork_ipv4 *addr2); CORK_API void cork_ipv4_to_raw_string(const struct cork_ipv4 *addr, char *dest); CORK_API bool cork_ipv4_is_valid_network(const struct cork_ipv4 *addr, unsigned int cidr_prefix); /*** IPv6 ***/ /* src must be well-formed: 16 bytes, big-endian */ CORK_INLINE void cork_ipv6_copy(struct cork_ipv6* addr, const void* src) { memcpy(addr, src, sizeof(struct cork_ipv6)); } CORK_INLINE bool cork_ipv6_equal(const struct cork_ipv6* addr1, const struct cork_ipv6* addr2) { return addr1->_.u64[0] == addr2->_.u64[0] && addr1->_.u64[1] == addr2->_.u64[1]; } CORK_API int cork_ipv6_init(struct cork_ipv6 *addr, const char *str); CORK_API bool cork_ipv6_equal_(const struct cork_ipv6 *addr1, const struct cork_ipv6 *addr2); CORK_API void cork_ipv6_to_raw_string(const struct cork_ipv6 *addr, char *dest); CORK_API bool cork_ipv6_is_valid_network(const struct cork_ipv6 *addr, unsigned int cidr_prefix); /*** Generic IP ***/ CORK_INLINE bool cork_ip_equal(const struct cork_ip* addr1, const struct cork_ip* addr2) { if (addr1->version != addr2->version) return false; else if (addr1->version == 4) { return cork_ipv4_equal(&addr1->ip.v4, &addr2->ip.v4); } else { return cork_ipv6_equal(&addr1->ip.v6, &addr2->ip.v6); } } /* src must be well-formed: 4 bytes, big-endian */ CORK_INLINE void cork_ip_from_ipv4(struct cork_ip* addr, const void* src) { addr->version = 4; cork_ipv4_copy(&addr->ip.v4, src); } /* src must be well-formed: 16 bytes, big-endian */ CORK_INLINE void cork_ip_from_ipv6(struct cork_ip* addr, const void* src) { addr->version = 6; cork_ipv6_copy(&addr->ip.v6, src); } CORK_API int cork_ip_init(struct cork_ip *addr, const char *str); CORK_API void cork_ip_to_raw_string(const struct cork_ip *addr, char *dest); CORK_API bool cork_ip_is_valid_network(const struct cork_ip *addr, unsigned int cidr_prefix); #endif /* LIBCORK_CORE_NET_ADDRESSES_H */ libcork-1.0.0-rc3/include/libcork/core/timestamp.h000066400000000000000000000046401367645320400220140ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2011, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #ifndef LIBCORK_CORE_TIMESTAMP_H #define LIBCORK_CORE_TIMESTAMP_H #include #include #include #include typedef uint64_t cork_timestamp; CORK_INLINE void cork_timestamp_init_sec(cork_timestamp* ts, uint64_t sec) { *ts = sec << 32; } CORK_INLINE void cork_timestamp_init_gsec(cork_timestamp* ts, uint64_t sec, uint64_t gsec) { *ts = (sec << 32) | (gsec & 0xffffffff); } CORK_INLINE void cork_timestamp_init_msec(cork_timestamp* ts, uint64_t sec, uint64_t msec) { *ts = (sec << 32) | ((msec << 32) / 1000); } CORK_INLINE void cork_timestamp_init_usec(cork_timestamp* ts, uint64_t sec, uint64_t usec) { *ts = (sec << 32) | ((usec << 32) / 1000000); } CORK_INLINE void cork_timestamp_init_nsec(cork_timestamp* ts, uint64_t sec, uint64_t nsec) { *ts = (sec << 32) | ((nsec << 32) / 1000000000); } CORK_API void cork_timestamp_init_now(cork_timestamp *ts); CORK_INLINE uint32_t cork_timestamp_sec(const cork_timestamp ts) { return (uint32_t) (ts >> 32); } CORK_INLINE uint32_t cork_timestamp_gsec(const cork_timestamp ts) { return (uint32_t) (ts & 0xffffffff); } CORK_INLINE uint64_t cork_timestamp_gsec_to_units(const cork_timestamp ts, uint64_t denom) { uint64_t half = ((uint64_t) 1 << 31) / denom; uint64_t gsec = cork_timestamp_gsec(ts); gsec += half; gsec *= denom; gsec >>= 32; return gsec; } CORK_INLINE uint64_t cork_timestamp_msec(const cork_timestamp ts) { return cork_timestamp_gsec_to_units(ts, 1000); } CORK_INLINE uint64_t cork_timestamp_usec(const cork_timestamp ts) { return cork_timestamp_gsec_to_units(ts, 1000000); } CORK_INLINE uint64_t cork_timestamp_nsec(const cork_timestamp ts) { return cork_timestamp_gsec_to_units(ts, 1000000000); } CORK_API int cork_timestamp_format_utc(const cork_timestamp ts, const char *format, struct cork_buffer *dest); CORK_API int cork_timestamp_format_local(const cork_timestamp ts, const char *format, struct cork_buffer *dest); #endif /* LIBCORK_CORE_TIMESTAMP_H */ libcork-1.0.0-rc3/include/libcork/core/types.h000066400000000000000000000041071367645320400211530ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2011, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #ifndef LIBCORK_CORE_TYPES_H #define LIBCORK_CORE_TYPES_H /* For now, we assume that the C99 integer types are available using the * standard headers. */ #include #include #include #include #include /* Define preprocessor macros that contain the size of several built-in * types. Again, we assume that we have the C99 definitions available. */ #if SHRT_MAX == INT8_MAX #define CORK_SIZEOF_SHORT 1 #elif SHRT_MAX == INT16_MAX #define CORK_SIZEOF_SHORT 2 #elif SHRT_MAX == INT32_MAX #define CORK_SIZEOF_SHORT 4 #elif SHRT_MAX == INT64_MAX #define CORK_SIZEOF_SHORT 8 #else #error "Cannot determine size of short" #endif #if INT_MAX == INT8_MAX #define CORK_SIZEOF_INT 1 #elif INT_MAX == INT16_MAX #define CORK_SIZEOF_INT 2 #elif INT_MAX == INT32_MAX #define CORK_SIZEOF_INT 4 #elif INT_MAX == INT64_MAX #define CORK_SIZEOF_INT 8 #else #error "Cannot determine size of int" #endif #if LONG_MAX == INT8_MAX #define CORK_SIZEOF_LONG 1 #elif LONG_MAX == INT16_MAX #define CORK_SIZEOF_LONG 2 #elif LONG_MAX == INT32_MAX #define CORK_SIZEOF_LONG 4 #elif LONG_MAX == INT64_MAX #define CORK_SIZEOF_LONG 8 #else #error "Cannot determine size of long" #endif #if INTPTR_MAX == INT8_MAX #define CORK_SIZEOF_POINTER 1 #elif INTPTR_MAX == INT16_MAX #define CORK_SIZEOF_POINTER 2 #elif INTPTR_MAX == INT32_MAX #define CORK_SIZEOF_POINTER 4 #elif INTPTR_MAX == INT64_MAX #define CORK_SIZEOF_POINTER 8 #else #error "Cannot determine size of void *" #endif /* Return a pointer to a @c struct, given a pointer to one of its * fields. */ #define cork_container_of(field, struct_type, field_name) \ ((struct_type *) (- offsetof(struct_type, field_name) + \ (void *) (field))) #endif /* LIBCORK_CORE_TYPES_H */ libcork-1.0.0-rc3/include/libcork/core/u128.h000066400000000000000000000172171367645320400205140ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2013, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #ifndef LIBCORK_CORE_U128_H #define LIBCORK_CORE_U128_H #include #include #include #include #include typedef struct { union { uint8_t u8[16]; uint16_t u16[8]; uint32_t u32[4]; uint64_t u64[2]; #if CORK_HOST_ENDIANNESS == CORK_BIG_ENDIAN struct { uint64_t hi; uint64_t lo; } be64; #else struct { uint64_t lo; uint64_t hi; } be64; #endif #if CORK_CONFIG_HAVE_GCC_INT128 #define CORK_U128_HAVE_U128 1 unsigned __int128 u128; #elif CORK_CONFIG_HAVE_GCC_MODE_ATTRIBUTE #define CORK_U128_HAVE_U128 1 unsigned int u128 __attribute__((mode(TI))); #else #define CORK_U128_HAVE_U128 0 #endif } _; } cork_u128; /* i0-3 are given in big-endian order, regardless of host endianness */ CORK_INLINE cork_u128 cork_u128_from_32(uint32_t i0, uint32_t i1, uint32_t i2, uint32_t i3) { cork_u128 value; #if CORK_HOST_ENDIANNESS == CORK_BIG_ENDIAN value._.u32[0] = i0; value._.u32[1] = i1; value._.u32[2] = i2; value._.u32[3] = i3; #else value._.u32[3] = i0; value._.u32[2] = i1; value._.u32[1] = i2; value._.u32[0] = i3; #endif return value; } /* i0-1 are given in big-endian order, regardless of host endianness */ CORK_INLINE cork_u128 cork_u128_from_64(uint64_t i0, uint64_t i1) { cork_u128 value; #if CORK_HOST_ENDIANNESS == CORK_BIG_ENDIAN value._.u64[0] = i0; value._.u64[1] = i1; #else value._.u64[1] = i0; value._.u64[0] = i1; #endif return value; } CORK_INLINE cork_u128 cork_u128_zero(void) { return cork_u128_from_64(0, 0); } #if CORK_HOST_ENDIANNESS == CORK_BIG_ENDIAN #define cork_u128_be8(val, idx) ((val)._.u8[(idx)]) #define cork_u128_be16(val, idx) ((val)._.u16[(idx)]) #define cork_u128_be32(val, idx) ((val)._.u32[(idx)]) #define cork_u128_be64(val, idx) ((val)._.u64[(idx)]) #else #define cork_u128_be8(val, idx) ((val)._.u8[15 - (idx)]) #define cork_u128_be16(val, idx) ((val)._.u16[7 - (idx)]) #define cork_u128_be32(val, idx) ((val)._.u32[3 - (idx)]) #define cork_u128_be64(val, idx) ((val)._.u64[1 - (idx)]) #endif #define CORK_SWAP_UINT128(__u128) \ (cork_u128_from_64(CORK_SWAP_UINT64(cork_u128_be64((__u128), 1)), \ CORK_SWAP_UINT64(cork_u128_be64((__u128), 0)))) #define CORK_SWAP_IN_PLACE_UINT128(__u128) \ do { \ (__u128) = CORK_SWAP_UINT128(__u128); \ } while (0) #if CORK_HOST_ENDIANNESS == CORK_BIG_ENDIAN #define CORK_UINT128_BIG_TO_HOST(__u128) (__u128) /* nothing to do */ #define CORK_UINT128_LITTLE_TO_HOST(__u128) CORK_SWAP_UINT128(__u128) #define CORK_UINT128_BIG_TO_HOST_IN_PLACE(__u128) /* nothing to do */ #define CORK_UINT128_LITTLE_TO_HOST_IN_PLACE(__u128) \ CORK_SWAP_IN_PLACE_UINT128(__u128) #elif CORK_HOST_ENDIANNESS == CORK_LITTLE_ENDIAN #define CORK_UINT128_BIG_TO_HOST(__u128) CORK_SWAP_UINT128(__u128) #define CORK_UINT128_LITTLE_TO_HOST(__u128) (__u128) /* nothing to do */ #define CORK_UINT128_BIG_TO_HOST_IN_PLACE(__u128) \ CORK_SWAP_IN_PLACE_UINT128(__u128) #define CORK_UINT128_LITTLE_TO_HOST_IN_PLACE(__u128) /* nothing to do */ #endif #define CORK_UINT128_HOST_TO_BIG(__u128) CORK_UINT128_BIG_TO_HOST(__u128) #define CORK_UINT128_HOST_TO_LITTLE(__u128) CORK_UINT128_LITTLE_TO_HOST(__u128) #define CORK_UINT128_HOST_TO_BIG_IN_PLACE(__u128) \ CORK_UINT128_BIG_TO_HOST_IN_PLACE(__u128) #define CORK_UINT128_HOST_TO_LITTLE_IN_PLACE(__u128) \ CORK_UINT128_LITTLE_TO_HOST_IN_PLACE(__u128) CORK_INLINE bool cork_u128_eq(cork_u128 a, cork_u128 b) { #if CORK_U128_HAVE_U128 return (a._.u128 == b._.u128); #else return (a._.be64.hi == b._.be64.hi) && (a._.be64.lo == b._.be64.lo); #endif } CORK_INLINE bool cork_u128_ne(cork_u128 a, cork_u128 b) { #if CORK_U128_HAVE_U128 return (a._.u128 != b._.u128); #else return (a._.be64.hi != b._.be64.hi) || (a._.be64.lo != b._.be64.lo); #endif } CORK_INLINE bool cork_u128_lt(cork_u128 a, cork_u128 b) { #if CORK_U128_HAVE_U128 return (a._.u128 < b._.u128); #else if (a._.be64.hi == b._.be64.hi) { return a._.be64.lo < b._.be64.lo; } else { return a._.be64.hi < b._.be64.hi; } #endif } CORK_INLINE bool cork_u128_le(cork_u128 a, cork_u128 b) { #if CORK_U128_HAVE_U128 return (a._.u128 <= b._.u128); #else if (a._.be64.hi == b._.be64.hi) { return a._.be64.lo <= b._.be64.lo; } else { return a._.be64.hi <= b._.be64.hi; } #endif } CORK_INLINE bool cork_u128_gt(cork_u128 a, cork_u128 b) { #if CORK_U128_HAVE_U128 return (a._.u128 > b._.u128); #else if (a._.be64.hi == b._.be64.hi) { return a._.be64.lo > b._.be64.lo; } else { return a._.be64.hi > b._.be64.hi; } #endif } CORK_INLINE bool cork_u128_ge(cork_u128 a, cork_u128 b) { #if CORK_U128_HAVE_U128 return (a._.u128 >= b._.u128); #else if (a._.be64.hi == b._.be64.hi) { return a._.be64.lo >= b._.be64.lo; } else { return a._.be64.hi >= b._.be64.hi; } #endif } CORK_INLINE cork_u128 cork_u128_shl(cork_u128 a, unsigned int b) { #if CORK_U128_HAVE_U128 cork_u128 result; result._.u128 = a._.u128 << b; return result; #else if (b == 0) { return a; } if (b == 64) { return cork_u128_from_64(a._.be64.lo, 0); } if (b >= 128) { /* This is undefined behavior */ return cork_u128_zero(); } if (b >= 64) { return cork_u128_from_64(a._.be64.lo << (b - 64), 0); } return cork_u128_from_64( (a._.be64.hi << b) + (a._.be64.lo >> (64 - b)), a._.be64.lo << b); #endif } CORK_INLINE cork_u128 cork_u128_shr(cork_u128 a, unsigned int b) { #if CORK_U128_HAVE_U128 cork_u128 result; result._.u128 = a._.u128 >> b; return result; #else if (b == 0) { return a; } if (b == 64) { return cork_u128_from_64(0, a._.be64.hi); } if (b >= 128) { /* This is undefined behavior */ return cork_u128_zero(); } if (b >= 64) { return cork_u128_from_64(0, a._.be64.hi >> (b - 64)); } return cork_u128_from_64( a._.be64.hi >> b, (a._.be64.lo >> b) + (a._.be64.hi << (64 - b))); #endif } CORK_INLINE cork_u128 cork_u128_add(cork_u128 a, cork_u128 b) { cork_u128 result; #if CORK_U128_HAVE_U128 result._.u128 = a._.u128 + b._.u128; #else result._.be64.lo = a._.be64.lo + b._.be64.lo; result._.be64.hi = a._.be64.hi + b._.be64.hi + (result._.be64.lo < a._.be64.lo); #endif return result; } CORK_INLINE cork_u128 cork_u128_sub(cork_u128 a, cork_u128 b) { cork_u128 result; #if CORK_U128_HAVE_U128 result._.u128 = a._.u128 - b._.u128; #else result._.be64.lo = a._.be64.lo - b._.be64.lo; result._.be64.hi = a._.be64.hi - b._.be64.hi - (result._.be64.lo > a._.be64.lo); #endif return result; } /* log10(x) = log2(x) / log2(10) ~= log2(x) / 3.322 */ #define CORK_U128_DECIMAL_LENGTH 44 /* ~= 128 / 3 + 1 + 1 */ CORK_API const char * cork_u128_to_decimal(char *buf, cork_u128 val); #define CORK_U128_HEX_LENGTH 33 CORK_API const char * cork_u128_to_hex(char *buf, cork_u128 val); CORK_API const char * cork_u128_to_padded_hex(char *buf, cork_u128 val); #endif /* LIBCORK_CORE_U128_H */ libcork-1.0.0-rc3/include/libcork/ds.h000066400000000000000000000013141367645320400174620ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2011, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #ifndef LIBCORK_DS_H #define LIBCORK_DS_H /*** include all of the parts ***/ #include #include #include #include #include #include #include #include #include #endif /* LIBCORK_DS_H */ libcork-1.0.0-rc3/include/libcork/ds/000077500000000000000000000000001367645320400173125ustar00rootroot00000000000000libcork-1.0.0-rc3/include/libcork/ds/array.h000066400000000000000000000123771367645320400206130ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2011, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #ifndef LIBCORK_DS_ARRAY_H #define LIBCORK_DS_ARRAY_H #include #include #include /*----------------------------------------------------------------------- * Resizable arrays */ struct cork_array_priv; struct cork_raw_array { void *items; size_t size; struct cork_array_priv *priv; }; CORK_API void cork_raw_array_init(struct cork_raw_array *array, size_t element_size); CORK_API void cork_raw_array_done(struct cork_raw_array *array); CORK_API void cork_raw_array_set_callback_data(struct cork_raw_array *array, void *user_data, cork_free_f free_user_data); CORK_API void cork_raw_array_set_init(struct cork_raw_array *array, cork_init_f init); CORK_API void cork_raw_array_set_done(struct cork_raw_array *array, cork_done_f done); CORK_API void cork_raw_array_set_reuse(struct cork_raw_array *array, cork_init_f reuse); CORK_API void cork_raw_array_set_remove(struct cork_raw_array *array, cork_done_f remove); CORK_API size_t cork_raw_array_element_size(const struct cork_raw_array *array); CORK_API void cork_raw_array_clear(struct cork_raw_array *array); CORK_API void * cork_raw_array_elements(const struct cork_raw_array *array); CORK_API void * cork_raw_array_at(const struct cork_raw_array *array, size_t index); CORK_API size_t cork_raw_array_size(const struct cork_raw_array *array); CORK_API bool cork_raw_array_is_empty(const struct cork_raw_array *array); CORK_API void cork_raw_array_ensure_size(struct cork_raw_array *array, size_t count); CORK_API void * cork_raw_array_append(struct cork_raw_array *array); CORK_API void cork_raw_array_remove_range(struct cork_raw_array* array, size_t index, size_t count); CORK_INLINE void cork_raw_array_remove(struct cork_raw_array *array, size_t index) { cork_raw_array_remove_range(array, index, 1); } CORK_API int cork_raw_array_copy(struct cork_raw_array *dest, const struct cork_raw_array *src, cork_copy_f copy, void *user_data); /*----------------------------------------------------------------------- * Type-checked resizable arrays */ #define cork_array(T) \ struct { \ T *items; \ size_t size; \ struct cork_array_priv *priv; \ } #define cork_array_element_size(arr) (sizeof((arr)->items[0])) #define cork_array_elements(arr) ((arr)->items) #define cork_array_at(arr, i) ((arr)->items[(i)]) #define cork_array_size(arr) ((arr)->size) #define cork_array_is_empty(arr) ((arr)->size == 0) #define cork_array_to_raw(arr) ((struct cork_raw_array *) (void *) (arr)) #define cork_array_init(arr) \ (cork_raw_array_init(cork_array_to_raw(arr), cork_array_element_size(arr))) #define cork_array_done(arr) \ (cork_raw_array_done(cork_array_to_raw(arr))) #define cork_array_set_callback_data(arr, ud, fud) \ (cork_raw_array_set_callback_data(cork_array_to_raw(arr), (ud), (fud))) #define cork_array_set_init(arr, i) \ (cork_raw_array_set_init(cork_array_to_raw(arr), (i))) #define cork_array_set_done(arr, d) \ (cork_raw_array_set_done(cork_array_to_raw(arr), (d))) #define cork_array_set_reuse(arr, r) \ (cork_raw_array_set_reuse(cork_array_to_raw(arr), (r))) #define cork_array_set_remove(arr, r) \ (cork_raw_array_set_remove(cork_array_to_raw(arr), (r))) #define cork_array_clear(arr) \ (cork_raw_array_clear(cork_array_to_raw(arr))) #define cork_array_copy(d, s, c, ud) \ (cork_raw_array_copy(cork_array_to_raw(d), cork_array_to_raw(s), (c), (ud))) #define cork_array_ensure_size(arr, count) \ (cork_raw_array_ensure_size(cork_array_to_raw(arr), (count))) #define cork_array_append(arr, element) \ (cork_raw_array_append(cork_array_to_raw(arr)), \ ((arr)->items[(arr)->size - 1] = (element), (void) 0)) #define cork_array_append_get(arr) \ (cork_raw_array_append(cork_array_to_raw(arr)), \ &(arr)->items[(arr)->size - 1]) #define cork_array_remove(arr, element) \ (cork_raw_array_remove(cork_array_to_raw(arr), (element))) #define cork_array_remove_range(arr, element, count) \ (cork_raw_array_remove_range(cork_array_to_raw(arr), (element), (count))) /*----------------------------------------------------------------------- * Builtin array types */ CORK_API void cork_raw_pointer_array_init(struct cork_raw_array *array, cork_free_f free); #define cork_pointer_array_init(arr, f) \ (cork_raw_pointer_array_init(cork_array_to_raw(arr), (f))) struct cork_string_array { const char **items; size_t size; struct cork_array_priv *priv; }; CORK_API void cork_string_array_init(struct cork_string_array *array); CORK_API void cork_string_array_append(struct cork_string_array *array, const char *str); CORK_API void cork_string_array_copy(struct cork_string_array *dest, const struct cork_string_array *src); #endif /* LIBCORK_DS_ARRAY_H */ libcork-1.0.0-rc3/include/libcork/ds/bitset.h000066400000000000000000000037321367645320400207620ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2013, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #ifndef LIBCORK_DS_BITS_H #define LIBCORK_DS_BITS_H #include #include /*----------------------------------------------------------------------- * Bit sets */ struct cork_bitset { uint8_t *bits; size_t bit_count; size_t byte_count; }; CORK_API struct cork_bitset * cork_bitset_new(size_t bit_count); CORK_API void cork_bitset_init(struct cork_bitset *set, size_t bit_count); CORK_API void cork_bitset_free(struct cork_bitset *set); CORK_API void cork_bitset_done(struct cork_bitset *set); CORK_API void cork_bitset_clear(struct cork_bitset *set); /* Extract the byte that contains a particular bit in an array. */ #define cork_bitset_byte_for_bit(set, i) \ ((set)->bits[(i) / 8]) /* Create a bit mask that extracts a particular bit from the byte that it lives * in. */ #define cork_bitset_pos_mask_for_bit(i) \ (0x80 >> ((i) % 8)) /* Create a bit mask that extracts everything except for a particular bit from * the byte that it lives in. */ #define cork_bitset_neg_mask_for_bit(i) \ (~cork_bitset_pos_mask_for_bit(i)) /* Return whether a particular bit is set in a byte array. Bits are numbered * from 0, in a big-endian order. */ #define cork_bitset_get(set, i) \ ((cork_bitset_byte_for_bit(set, i) & cork_bitset_pos_mask_for_bit(i)) != 0) /* Set (or unset) a particular bit is set in a byte array. Bits are numbered * from 0, in a big-endian order. */ #define cork_bitset_set(set, i, val) \ (cork_bitset_byte_for_bit(set, i) = \ (cork_bitset_byte_for_bit(set, i) & cork_bitset_neg_mask_for_bit(i)) \ | ((val)? cork_bitset_pos_mask_for_bit(i): 0)) #endif /* LIBCORK_DS_BITS_H */ libcork-1.0.0-rc3/include/libcork/ds/buffer.h000066400000000000000000000127061367645320400207420ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2011, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #ifndef LIBCORK_DS_BUFFER_H #define LIBCORK_DS_BUFFER_H #include #include #include #include #include struct cork_buffer { /* The current contents of the buffer. */ void *buf; /* The current size of the buffer. */ size_t size; /* The amount of space allocated for buf. */ size_t allocated_size; }; CORK_INLINE void cork_buffer_init(struct cork_buffer* buffer) { buffer->buf = NULL; buffer->size = 0; buffer->allocated_size = 0; } #define CORK_BUFFER_INIT() { NULL, 0, 0 } CORK_API struct cork_buffer * cork_buffer_new(void); CORK_API void cork_buffer_done(struct cork_buffer *buffer); CORK_API void cork_buffer_free(struct cork_buffer *buffer); CORK_API bool cork_buffer_equal(const struct cork_buffer *buffer1, const struct cork_buffer *buffer2); CORK_API void cork_buffer_ensure_size_(struct cork_buffer* buffer, size_t desired_size); CORK_INLINE void cork_buffer_ensure_size(struct cork_buffer* buffer, size_t desired_size) { if (CORK_UNLIKELY(buffer->allocated_size < desired_size)) { cork_buffer_ensure_size_(buffer, desired_size); } } CORK_INLINE void cork_buffer_clear(struct cork_buffer* buffer) { buffer->size = 0; if (buffer->buf != NULL) { ((char*) buffer->buf)[0] = '\0'; } } CORK_INLINE void cork_buffer_truncate(struct cork_buffer* buffer, size_t length) { if (buffer->size > length) { buffer->size = length; if (buffer->buf != NULL) { ((char*) buffer->buf)[length] = '\0'; } } } #define cork_buffer_byte(buffer, i) (((const uint8_t *) (buffer)->buf)[(i)]) #define cork_buffer_char(buffer, i) (((const char *) (buffer)->buf)[(i)]) /*----------------------------------------------------------------------- * A whole bunch of methods for adding data */ CORK_INLINE void cork_buffer_set(struct cork_buffer* buffer, const void* src, size_t length) { cork_buffer_ensure_size(buffer, length + 1); memcpy(buffer->buf, src, length); ((char*) buffer->buf)[length] = '\0'; buffer->size = length; } CORK_INLINE void cork_buffer_copy(struct cork_buffer* dest, const struct cork_buffer* src) { cork_buffer_set(dest, src->buf, src->size); } CORK_INLINE void cork_buffer_append(struct cork_buffer* buffer, const void* src, size_t length) { cork_buffer_ensure_size(buffer, buffer->size + length + 1); memcpy(buffer->buf + buffer->size, src, length); buffer->size += length; ((char*) buffer->buf)[buffer->size] = '\0'; } CORK_INLINE void cork_buffer_append_copy(struct cork_buffer* dest, const struct cork_buffer* src) { cork_buffer_append(dest, src->buf, src->size); } CORK_INLINE void cork_buffer_set_string(struct cork_buffer* buffer, const char* str) { cork_buffer_set(buffer, str, strlen(str)); } CORK_INLINE void cork_buffer_append_string(struct cork_buffer* buffer, const char* str) { cork_buffer_append(buffer, str, strlen(str)); } #define cork_buffer_set_literal(buffer, str) \ (cork_buffer_set((buffer), (str), sizeof((str)) - 1)) #define cork_buffer_append_literal(buffer, str) \ (cork_buffer_append((buffer), (str), sizeof((str)) - 1)) CORK_API void cork_buffer_printf(struct cork_buffer *buffer, const char *format, ...) CORK_ATTR_PRINTF(2,3); CORK_API void cork_buffer_append_printf(struct cork_buffer *buffer, const char *format, ...) CORK_ATTR_PRINTF(2,3); CORK_API void cork_buffer_vprintf(struct cork_buffer *buffer, const char *format, va_list args) CORK_ATTR_PRINTF(2,0); CORK_API void cork_buffer_append_vprintf(struct cork_buffer *buffer, const char *format, va_list args) CORK_ATTR_PRINTF(2,0); /*----------------------------------------------------------------------- * Some helpers for pretty-printing data */ CORK_API void cork_buffer_append_indent(struct cork_buffer *buffer, size_t indent); CORK_API void cork_buffer_append_c_string(struct cork_buffer *buffer, const char *src, size_t length); CORK_API void cork_buffer_append_hex_dump(struct cork_buffer *buffer, size_t indent, const char *src, size_t length); CORK_API void cork_buffer_append_multiline(struct cork_buffer *buffer, size_t indent, const char *src, size_t length); CORK_API void cork_buffer_append_binary(struct cork_buffer *buffer, size_t indent, const char *src, size_t length); /*----------------------------------------------------------------------- * Buffer's managed buffer/slice implementation */ #include #include CORK_API struct cork_managed_buffer * cork_buffer_to_managed_buffer(struct cork_buffer *buffer); CORK_API int cork_buffer_to_slice(struct cork_buffer *buffer, struct cork_slice *slice); /*----------------------------------------------------------------------- * Buffer's stream consumer implementation */ #include CORK_API struct cork_stream_consumer * cork_buffer_to_stream_consumer(struct cork_buffer *buffer); #endif /* LIBCORK_DS_BUFFER_H */ libcork-1.0.0-rc3/include/libcork/ds/dllist.h000066400000000000000000000107101367645320400207550ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2011, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #ifndef LIBCORK_DS_DLLIST_H #define LIBCORK_DS_DLLIST_H #include #include struct cork_dllist_item { /* A pointer to the next element in the list. */ struct cork_dllist_item *next; /* A pointer to the previous element in the list. */ struct cork_dllist_item *prev; }; struct cork_dllist { /* The sentinel element for this list. */ struct cork_dllist_item head; }; #define CORK_DLLIST_INIT(list) { { &(list).head, &(list).head } } #define cork_dllist_init(list) \ do { \ (list)->head.next = &(list)->head; \ (list)->head.prev = &(list)->head; \ } while (0) /* DEPRECATED! Use cork_dllist_foreach or cork_dllist_visit instead. */ typedef void (*cork_dllist_map_func)(struct cork_dllist_item *element, void *user_data); CORK_API void cork_dllist_map(struct cork_dllist *list, cork_dllist_map_func func, void *user_data); typedef int cork_dllist_visit_f(void *ud, struct cork_dllist_item *element); CORK_API int cork_dllist_visit(struct cork_dllist *list, void *ud, cork_dllist_visit_f *visit); #define cork_dllist_foreach_void(list, curr, _next) \ for ((curr) = cork_dllist_start((list)), (_next) = (curr)->next; \ !cork_dllist_is_end((list), (curr)); \ (curr) = (_next), (_next) = (curr)->next) #define cork_dllist_foreach(list, curr, _next, etype, element, item_field) \ for ((curr) = cork_dllist_start((list)), (_next) = (curr)->next, \ (element) = cork_container_of((curr), etype, item_field); \ !cork_dllist_is_end((list), (curr)); \ (curr) = (_next), (_next) = (curr)->next, \ (element) = cork_container_of((curr), etype, item_field)) CORK_API size_t cork_dllist_size(const struct cork_dllist *list); #define cork_dllist_add_after(pred, element) \ do { \ (element)->prev = (pred); \ (element)->next = (pred)->next; \ (pred)->next->prev = (element); \ (pred)->next = (element); \ } while (0) #define cork_dllist_add_before(succ, element) \ do { \ (element)->next = (succ); \ (element)->prev = (succ)->prev; \ (succ)->prev->next = (element); \ (succ)->prev = (element); \ } while (0) #define cork_dllist_add_to_head(list, element) \ cork_dllist_add_after(&(list)->head, (element)) #define cork_dllist_add_to_tail(list, element) \ cork_dllist_add_before(&(list)->head, (element)) #define cork_dllist_add cork_dllist_add_to_tail #define cork_dllist_add_list_to_head(dest, src) \ do { \ struct cork_dllist_item *dest_start = cork_dllist_start(dest); \ struct cork_dllist_item *src_start = cork_dllist_start(src); \ dest_start->prev = &(src)->head; \ src_start->prev = &(dest)->head; \ (src)->head.next = dest_start; \ (dest)->head.next = src_start; \ cork_dllist_remove(&(src)->head); \ cork_dllist_init(src); \ } while (0) #define cork_dllist_add_list_to_tail(dest, src) \ do { \ struct cork_dllist_item *dest_end = cork_dllist_end(dest); \ struct cork_dllist_item *src_end = cork_dllist_end(src); \ dest_end->next = &(src)->head; \ src_end->next = &(dest)->head; \ (src)->head.prev = dest_end; \ (dest)->head.prev = src_end; \ cork_dllist_remove(&(src)->head); \ cork_dllist_init(src); \ } while (0) #define cork_dllist_remove(element) \ do { \ (element)->prev->next = (element)->next; \ (element)->next->prev = (element)->prev; \ } while (0) #define cork_dllist_is_empty(list) \ (cork_dllist_is_end((list), cork_dllist_start((list)))) #define cork_dllist_head(list) \ (((list)->head.next == &(list)->head)? NULL: (list)->head.next) #define cork_dllist_tail(list) \ (((list)->head.prev == &(list)->head)? NULL: (list)->head.prev) #define cork_dllist_start(list) \ ((list)->head.next) #define cork_dllist_end(list) \ ((list)->head.prev) #define cork_dllist_is_start(list, element) \ ((element) == &(list)->head) #define cork_dllist_is_end(list, element) \ ((element) == &(list)->head) #endif /* LIBCORK_DS_DLLIST_H */ libcork-1.0.0-rc3/include/libcork/ds/hash-table.h000066400000000000000000000114251367645320400214760ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2011, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #ifndef LIBCORK_DS_HASH_TABLE_H #define LIBCORK_DS_HASH_TABLE_H #include #include #include #include #include #include /*----------------------------------------------------------------------- * Hash tables */ struct cork_hash_table_entry { cork_hash hash; void *key; void *value; }; struct cork_hash_table; CORK_API struct cork_hash_table * cork_hash_table_new(size_t initial_size, unsigned int flags); CORK_API void cork_hash_table_free(struct cork_hash_table *table); CORK_API void cork_hash_table_set_user_data(struct cork_hash_table *table, void *user_data, cork_free_f free_user_data); CORK_API void cork_hash_table_set_equals(struct cork_hash_table *table, cork_equals_f equals); CORK_API void cork_hash_table_set_free_key(struct cork_hash_table *table, cork_free_f free); CORK_API void cork_hash_table_set_free_value(struct cork_hash_table *table, cork_free_f free); CORK_API void cork_hash_table_set_hash(struct cork_hash_table *table, cork_hash_f hash); CORK_API void cork_hash_table_clear(struct cork_hash_table *table); CORK_API void cork_hash_table_ensure_size(struct cork_hash_table *table, size_t desired_count); CORK_API size_t cork_hash_table_size(const struct cork_hash_table *table); CORK_API void * cork_hash_table_get(const struct cork_hash_table *table, const void *key); CORK_API void * cork_hash_table_get_hash(const struct cork_hash_table *table, cork_hash hash, const void *key); CORK_API struct cork_hash_table_entry * cork_hash_table_get_entry(const struct cork_hash_table *table, const void *key); CORK_API struct cork_hash_table_entry * cork_hash_table_get_entry_hash(const struct cork_hash_table *table, cork_hash hash, const void *key); CORK_API struct cork_hash_table_entry * cork_hash_table_get_or_create(struct cork_hash_table *table, void *key, bool *is_new); CORK_API struct cork_hash_table_entry * cork_hash_table_get_or_create_hash(struct cork_hash_table *table, cork_hash hash, void *key, bool *is_new); CORK_API void cork_hash_table_put(struct cork_hash_table *table, void *key, void *value, bool *is_new, void **old_key, void **old_value); CORK_API void cork_hash_table_put_hash(struct cork_hash_table *table, cork_hash hash, void *key, void *value, bool *is_new, void **old_key, void **old_value); CORK_API void cork_hash_table_delete_entry(struct cork_hash_table *table, struct cork_hash_table_entry *entry); CORK_API bool cork_hash_table_delete(struct cork_hash_table *table, const void *key, void **deleted_key, void **deleted_value); CORK_API bool cork_hash_table_delete_hash(struct cork_hash_table *table, cork_hash hash, const void *key, void **deleted_key, void **deleted_value); enum cork_hash_table_map_result { /* Abort the current @ref cork_hash_table_map operation. */ CORK_HASH_TABLE_MAP_ABORT = 0, /* Continue on to the next entry in the hash table. */ CORK_HASH_TABLE_MAP_CONTINUE = 1, /* Delete the entry that was just processed, and then continue on to * the next entry in the hash table. */ CORK_HASH_TABLE_MAP_DELETE = 2 }; typedef enum cork_hash_table_map_result (*cork_hash_table_map_f)(void *user_data, struct cork_hash_table_entry *entry); CORK_API void cork_hash_table_map(struct cork_hash_table *table, void *user_data, cork_hash_table_map_f mapper); struct cork_hash_table_iterator { struct cork_hash_table *table; void *priv; }; CORK_API void cork_hash_table_iterator_init(struct cork_hash_table *table, struct cork_hash_table_iterator *iterator); CORK_API struct cork_hash_table_entry * cork_hash_table_iterator_next(struct cork_hash_table_iterator *iterator); /*----------------------------------------------------------------------- * Built-in key types */ CORK_API struct cork_hash_table * cork_string_hash_table_new(size_t initial_size, unsigned int flags); CORK_API struct cork_hash_table * cork_pointer_hash_table_new(size_t initial_size, unsigned int flags); #endif /* LIBCORK_DS_HASH_TABLE_H */ libcork-1.0.0-rc3/include/libcork/ds/managed-buffer.h000066400000000000000000000040571367645320400223340ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2011, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #ifndef LIBCORK_DS_MANAGED_BUFFER_H #define LIBCORK_DS_MANAGED_BUFFER_H #include #include #include /*----------------------------------------------------------------------- * Managed buffers */ struct cork_managed_buffer; struct cork_managed_buffer_iface { /* Free the contents of a managed buffer, and the managed buffer * object itself. */ void (*free)(struct cork_managed_buffer *buf); }; struct cork_managed_buffer { /* The buffer that this instance manages */ const void *buf; /* The size of buf */ size_t size; /* A reference count for the buffer. If this drops to 0, the buffer * will be finalized. */ volatile int ref_count; /* The managed buffer implementation for this instance. */ struct cork_managed_buffer_iface *iface; }; CORK_API struct cork_managed_buffer * cork_managed_buffer_new_copy(const void *buf, size_t size); typedef void (*cork_managed_buffer_freer)(void *buf, size_t size); CORK_API struct cork_managed_buffer * cork_managed_buffer_new(const void *buf, size_t size, cork_managed_buffer_freer free); CORK_API struct cork_managed_buffer * cork_managed_buffer_ref(struct cork_managed_buffer *buf); CORK_API void cork_managed_buffer_unref(struct cork_managed_buffer *buf); CORK_API int cork_managed_buffer_slice(struct cork_slice *dest, struct cork_managed_buffer *buffer, size_t offset, size_t length); CORK_API int cork_managed_buffer_slice_offset(struct cork_slice *dest, struct cork_managed_buffer *buffer, size_t offset); #endif /* LIBCORK_DS_MANAGED_BUFFER_H */ libcork-1.0.0-rc3/include/libcork/ds/ring-buffer.h000066400000000000000000000031001367645320400216630ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2011, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #ifndef LIBCORK_DS_RING_BUFFER_H #define LIBCORK_DS_RING_BUFFER_H #include #include struct cork_ring_buffer { /* The elements of the ring buffer */ void **elements; /* The number of elements that can be stored in this ring * buffer. */ size_t allocated_size; /* The actual number of elements currently in the ring buffer. */ size_t size; /* The index of the next element to read from the buffer */ size_t read_index; /* The index of the next element to write into the buffer */ size_t write_index; }; CORK_API int cork_ring_buffer_init(struct cork_ring_buffer *buf, size_t size); CORK_API struct cork_ring_buffer * cork_ring_buffer_new(size_t size); CORK_API void cork_ring_buffer_done(struct cork_ring_buffer *buf); CORK_API void cork_ring_buffer_free(struct cork_ring_buffer *buf); #define cork_ring_buffer_is_empty(buf) ((buf)->size == 0) #define cork_ring_buffer_is_full(buf) ((buf)->size == (buf)->allocated_size) CORK_API int cork_ring_buffer_add(struct cork_ring_buffer *buf, void *element); CORK_API void * cork_ring_buffer_pop(struct cork_ring_buffer *buf); CORK_API void * cork_ring_buffer_peek(struct cork_ring_buffer *buf); #endif /* LIBCORK_DS_RING_BUFFER_H */ libcork-1.0.0-rc3/include/libcork/ds/slice.h000066400000000000000000000135571367645320400205750ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2011, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #ifndef LIBCORK_DS_SLICE_H #define LIBCORK_DS_SLICE_H #include #include #include /*----------------------------------------------------------------------- * Error handling */ /* hash of "libcork/ds/slice.h" */ #define CORK_SLICE_ERROR 0x960ca750 enum cork_slice_error { /* Trying to slice a nonexistent subset of a buffer */ CORK_SLICE_INVALID_SLICE }; /*----------------------------------------------------------------------- * Slices */ struct cork_slice; struct cork_slice_iface { /* Free the slice. Can be NULL if you don't need to free any * underlying buffer. */ void (*free)(struct cork_slice *self); /* Create a copy of a slice. You can assume that offset and length * refer to a valid subset of the buffer. */ int (*copy)(struct cork_slice *dest, const struct cork_slice *self, size_t offset, size_t length); /* Create a “light” copy of a slice. A light copy is not allowed to exist * longer than the slice that it was copied from, which can sometimes let * you perform less work to produce the copy. You can assume that offset * and length refer to a valid subset of the buffer. */ int (*light_copy)(struct cork_slice *dest, const struct cork_slice *self, size_t offset, size_t length); /* Update the current slice to point at a different subset. You can * assume that offset and length refer to a valid subset of the * buffer. Can be NULL if you don't need to do anything special to * the underlying buffer; in this case, we'll update the slice's buf * and size fields for you. */ int (*slice)(struct cork_slice *self, size_t offset, size_t length); }; struct cork_slice { /* The beginning of the sliced portion of the buffer. */ const void *buf; /* The length of the sliced portion of the buffer. */ size_t size; /* The slice implementation of the underlying buffer. */ struct cork_slice_iface *iface; /* An opaque pointer used by the slice implementation to refer to * the underlying buffer. */ void *user_data; }; CORK_API void cork_slice_clear(struct cork_slice *slice); CORK_INLINE bool cork_slice_is_empty(const struct cork_slice* slice) { return slice->buf == NULL; } CORK_API int cork_slice_copy(struct cork_slice *dest, const struct cork_slice *slice, size_t offset, size_t length); CORK_INLINE int cork_slice_copy_fast(struct cork_slice* dest, const struct cork_slice* slice, size_t offset, size_t length) { return slice->iface->copy(dest, slice, offset, length); } CORK_API int cork_slice_copy_offset(struct cork_slice *dest, const struct cork_slice *slice, size_t offset); CORK_INLINE int cork_slice_copy_offset_fast(struct cork_slice *dest, const struct cork_slice *slice, size_t offset) { return slice->iface->copy(dest, slice, offset, slice->size - offset); } CORK_API int cork_slice_light_copy(struct cork_slice *dest, const struct cork_slice *slice, size_t offset, size_t length); CORK_INLINE int cork_slice_light_copy_fast(struct cork_slice *dest, const struct cork_slice *slice, size_t offset, size_t length) { return slice->iface->light_copy(dest, slice, offset, length); } CORK_API int cork_slice_light_copy_offset(struct cork_slice *dest, const struct cork_slice *slice, size_t offset); CORK_INLINE int cork_slice_light_copy_offset_fast(struct cork_slice *dest, const struct cork_slice *slice, size_t offset) { return slice->iface->light_copy(dest, slice, offset, slice->size - offset); } CORK_API int cork_slice_slice(struct cork_slice *slice, size_t offset, size_t length); CORK_INLINE int cork_slice_slice_fast(struct cork_slice *slice, size_t offset, size_t length) { if (CORK_LIKELY(slice->iface->slice == NULL)) { slice->buf += offset; slice->size = length; return 0; } else { return slice->iface->slice(slice, offset, length); } } CORK_API int cork_slice_slice_offset(struct cork_slice *slice, size_t offset); CORK_INLINE int cork_slice_slice_offset_fast(struct cork_slice *slice, size_t offset) { if (CORK_LIKELY(slice->iface->slice == NULL)) { slice->buf += offset; slice->size -= offset; return 0; } else { return slice->iface->slice(slice, offset, slice->size - offset); } } CORK_API const void* cork_slice_advance_checked(struct cork_slice* slice, size_t offset); CORK_INLINE const void* cork_slice_advance(struct cork_slice* slice, size_t offset) { if (CORK_LIKELY(slice->iface->slice == NULL)) { const void* buf = slice->buf; slice->buf += offset; slice->size -= offset; return buf; } else { const void* buf = slice->buf; int rc = slice->iface->slice(slice, offset, slice->size - offset); if (CORK_UNLIKELY(rc != 0)) { return NULL; } return buf; } } CORK_API void cork_slice_finish(struct cork_slice *slice); CORK_API bool cork_slice_equal(const struct cork_slice *slice1, const struct cork_slice *slice2); CORK_API void cork_slice_init_static(struct cork_slice *dest, const void *buf, size_t size); CORK_API void cork_slice_init_copy_once(struct cork_slice *dest, const void *buf, size_t size); #endif /* LIBCORK_DS_SLICE_H */ libcork-1.0.0-rc3/include/libcork/ds/stream.h000066400000000000000000000034351367645320400207630ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2011, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #ifndef LIBCORK_DS_STREAM_H #define LIBCORK_DS_STREAM_H #include #include #include #include struct cork_stream_consumer { int (*data)(struct cork_stream_consumer *consumer, const void *buf, size_t size, bool is_first_chunk); int (*eof)(struct cork_stream_consumer *consumer); void (*free)(struct cork_stream_consumer *consumer); }; CORK_INLINE int cork_stream_consumer_data(struct cork_stream_consumer* consumer, const void *buf, size_t size, bool is_first_chunk) { return consumer->data(consumer, buf, size, is_first_chunk); } CORK_INLINE int cork_stream_consumer_eof(struct cork_stream_consumer *consumer) { return consumer->eof(consumer); } CORK_INLINE void cork_stream_consumer_free(struct cork_stream_consumer *consumer) { consumer->free(consumer); } CORK_API int cork_consume_fd(struct cork_stream_consumer *consumer, int fd); CORK_API int cork_consume_file(struct cork_stream_consumer *consumer, FILE *fp); CORK_API int cork_consume_file_from_path(struct cork_stream_consumer *consumer, const char *path, int flags); CORK_API struct cork_stream_consumer * cork_fd_consumer_new(int fd); CORK_API struct cork_stream_consumer * cork_file_consumer_new(FILE *fp); CORK_API struct cork_stream_consumer * cork_file_from_path_consumer_new(const char *path, int flags); #endif /* LIBCORK_DS_STREAM_H */ libcork-1.0.0-rc3/include/libcork/helpers/000077500000000000000000000000001367645320400203465ustar00rootroot00000000000000libcork-1.0.0-rc3/include/libcork/helpers/errors.h000066400000000000000000000071741367645320400220440ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2011, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #ifndef LIBCORK_HELPERS_ERRORS_H #define LIBCORK_HELPERS_ERRORS_H /* This header is *not* automatically included when you include * libcork/core.h, since we define some macros that don't include a * cork_ or CORK_ prefix. Don't want to pollute your namespace unless * you ask for it! */ #include #include #include #if !defined(CORK_PRINT_ERRORS) #define CORK_PRINT_ERRORS 0 #endif #if !defined(CORK_PRINT_ERROR) #if CORK_PRINT_ERRORS #include #define CORK_PRINT_ERROR_(func, file, line) \ fprintf(stderr, "---\nError in %s (%s:%u)\n %s\n", \ (func), (file), (unsigned int) (line), \ cork_error_message()); #define CORK_PRINT_ERROR() CORK_PRINT_ERROR_(__func__, __FILE__, __LINE__) #else #define CORK_PRINT_ERROR() /* do nothing */ #endif #endif /* A bunch of macros for calling a function that returns an error. If * an error occurs, it will automatically be propagated out as the * result of your own function. With these macros, you won't have a * check to check or modify the error condition; it's returned as-is. * * XZ_check * * where: * * X = what happens if an error occurs * "e" = jump to the "error" label * "rY" = return a default error result (Y defined below) * "x" = return an error result that you specify * * Y = your return type * "i" = int * "p" = some pointer type * * Z = the return type of the function you're calling * "e" = use cork_error_occurred() to check * "i" = int * "p" = some pointer type * * In all cases, we assume that your function has a cork_error parameter * called "err". */ /* jump to "error" label */ #define ee_check(call) \ do { \ (call); \ if (CORK_UNLIKELY(cork_error_occurred())) { \ CORK_PRINT_ERROR(); \ goto error; \ } \ } while (0) #define ei_check(call) \ do { \ int __rc = (call); \ if (CORK_UNLIKELY(__rc != 0)) { \ CORK_PRINT_ERROR(); \ goto error; \ } \ } while (0) #define ep_check(call) \ do { \ const void *__result = (call); \ if (CORK_UNLIKELY(__result == NULL)) { \ CORK_PRINT_ERROR(); \ goto error; \ } \ } while (0) /* return specific error code */ #define xe_check(result, call) \ do { \ (call); \ if (CORK_UNLIKELY(cork_error_occurred())) { \ CORK_PRINT_ERROR(); \ return result; \ } \ } while (0) #define xi_check(result, call) \ do { \ int __rc = (call); \ if (CORK_UNLIKELY(__rc != 0)) { \ CORK_PRINT_ERROR(); \ return result; \ } \ } while (0) #define xp_check(result, call) \ do { \ const void *__result = (call); \ if (CORK_UNLIKELY(__result == NULL)) { \ CORK_PRINT_ERROR(); \ return result; \ } \ } while (0) /* return default error code */ #define rie_check(call) xe_check(-1, call) #define rii_check(call) xi_check(__rc, call) #define rip_check(call) xp_check(-1, call) #define rpe_check(call) xe_check(NULL, call) #define rpi_check(call) xi_check(NULL, call) #define rpp_check(call) xp_check(NULL, call) #endif /* LIBCORK_HELPERS_ERRORS_H */ libcork-1.0.0-rc3/include/libcork/helpers/gc.h000066400000000000000000000021501367645320400211060ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2011, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #ifndef LIBCORK_HELPERS_REFCOUNT_H #define LIBCORK_HELPERS_REFCOUNT_H #include #include #define _free_(name) \ static void \ name##__free(void *obj) #define _recurse_(name) \ static void \ name##__recurse(struct cork_gc *gc, void *obj, \ cork_gc_recurser recurse, void *ud) #define _gc_(name) \ static struct cork_gc_obj_iface name##__gc = { \ name##__free, name##__recurse \ }; #define _gc_no_free_(name) \ static struct cork_gc_obj_iface name##__gc = { \ NULL, name##__recurse \ }; #define _gc_no_recurse_(name) \ static struct cork_gc_obj_iface name##__gc = { \ name##__free, NULL \ }; #define _gc_leaf_(name) \ static struct cork_gc_obj_iface name##__gc = { \ NULL, NULL \ }; #endif /* LIBCORK_HELPERS_REFCOUNT_H */ libcork-1.0.0-rc3/include/libcork/helpers/posix.h000066400000000000000000000047261367645320400216720ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2013, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #ifndef LIBCORK_HELPERS_POSIX_H #define LIBCORK_HELPERS_POSIX_H /* This header is *not* automatically included when you include * libcork/core.h, since we define some macros that don't include a * cork_ or CORK_ prefix. Don't want to pollute your namespace unless * you ask for it! */ #include #include #include #include #if !defined(CORK_PRINT_ERRORS) #define CORK_PRINT_ERRORS 0 #endif #if !defined(CORK_PRINT_ERROR) #if CORK_PRINT_ERRORS #include #define CORK_PRINT_ERROR_(func, file, line) \ fprintf(stderr, "---\nError in %s (%s:%u)\n %s\n", \ (func), (file), (unsigned int) (line), \ cork_error_message()); #define CORK_PRINT_ERROR() CORK_PRINT_ERROR_(__func__, __FILE__, __LINE__) #else #define CORK_PRINT_ERROR() /* do nothing */ #endif #endif #define xi_check_posix(call, on_error) \ do { \ while (true) { \ if ((call) == -1) { \ if (errno == EINTR) { \ continue; \ } else { \ cork_system_error_set(); \ CORK_PRINT_ERROR(); \ on_error; \ } \ } else { \ break; \ } \ } \ } while (0) #define xp_check_posix(call, on_error) \ do { \ while (true) { \ if ((call) == NULL) { \ if (errno == EINTR) { \ continue; \ } else { \ cork_system_error_set(); \ CORK_PRINT_ERROR(); \ on_error; \ } \ } else { \ break; \ } \ } \ } while (0) #define ei_check_posix(call) xi_check_posix(call, goto error) #define rii_check_posix(call) xi_check_posix(call, return -1) #define rpi_check_posix(call) xi_check_posix(call, return NULL) #define ep_check_posix(call) xp_check_posix(call, goto error) #define rip_check_posix(call) xp_check_posix(call, return -1) #define rpp_check_posix(call) xp_check_posix(call, return NULL) #endif /* LIBCORK_HELPERS_POSIX_H */ libcork-1.0.0-rc3/include/libcork/os.h000066400000000000000000000010071367645320400174740ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2012, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #ifndef LIBCORK_OS_H #define LIBCORK_OS_H /*** include all of the parts ***/ #include #include #include #endif /* LIBCORK_OS_H */ libcork-1.0.0-rc3/include/libcork/os/000077500000000000000000000000001367645320400173255ustar00rootroot00000000000000libcork-1.0.0-rc3/include/libcork/os/files.h000066400000000000000000000155421367645320400206070ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2012, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #ifndef LIBCORK_CORE_FILES_H #define LIBCORK_CORE_FILES_H #include #include /*----------------------------------------------------------------------- * Paths */ struct cork_path; /* path can be relative or absolute */ CORK_API struct cork_path * cork_path_new(const char *path); CORK_API struct cork_path * cork_path_clone(const struct cork_path *other); CORK_API void cork_path_free(struct cork_path *path); CORK_API void cork_path_set(struct cork_path *path, const char *content); CORK_API const char * cork_path_get(const struct cork_path *path); CORK_API int cork_path_set_cwd(struct cork_path *path); CORK_API struct cork_path * cork_path_cwd(void); CORK_API int cork_path_set_absolute(struct cork_path *path); CORK_API struct cork_path * cork_path_absolute(const struct cork_path *other); CORK_API void cork_path_append(struct cork_path *path, const char *more); CORK_API void cork_path_append_path(struct cork_path *path, const struct cork_path *more); CORK_API struct cork_path * cork_path_join(const struct cork_path *other, const char *more); CORK_API struct cork_path * cork_path_join_path(const struct cork_path *other, const struct cork_path *more); CORK_API void cork_path_set_basename(struct cork_path *path); CORK_API struct cork_path * cork_path_basename(const struct cork_path *other); CORK_API void cork_path_set_dirname(struct cork_path *path); CORK_API struct cork_path * cork_path_dirname(const struct cork_path *other); /*----------------------------------------------------------------------- * Lists of paths */ struct cork_path_list; CORK_API struct cork_path_list * cork_path_list_new_empty(void); /* list must be a colon-separated list of paths */ CORK_API struct cork_path_list * cork_path_list_new(const char *list); CORK_API void cork_path_list_free(struct cork_path_list *list); CORK_API const char * cork_path_list_to_string(const struct cork_path_list *list); /* Takes control of path. path must not already be in the list. */ CORK_API void cork_path_list_add(struct cork_path_list *list, struct cork_path *path); CORK_API size_t cork_path_list_size(const struct cork_path_list *list); /* The list still owns path; you must not free it or modify it. */ CORK_API const struct cork_path * cork_path_list_get(const struct cork_path_list *list, size_t index); /*----------------------------------------------------------------------- * Files */ #define CORK_FILE_RECURSIVE 0x0001 #define CORK_FILE_PERMISSIVE 0x0002 typedef unsigned int cork_file_mode; enum cork_file_type { CORK_FILE_MISSING = 0, CORK_FILE_REGULAR = 1, CORK_FILE_DIRECTORY = 2, CORK_FILE_SYMLINK = 3, CORK_FILE_UNKNOWN = 4 }; struct cork_file; CORK_API struct cork_file * cork_file_new(const char *path); /* Takes control of path */ CORK_API struct cork_file * cork_file_new_from_path(struct cork_path *path); CORK_API void cork_file_free(struct cork_file *file); /* File owns the result; you should not free it */ CORK_API const struct cork_path * cork_file_path(struct cork_file *file); CORK_API int cork_file_exists(struct cork_file *file, bool *exists); CORK_API int cork_file_type(struct cork_file *file, enum cork_file_type *type); typedef int (*cork_file_directory_iterator)(struct cork_file *child, const char *rel_name, void *user_data); CORK_API int cork_file_iterate_directory(struct cork_file *file, cork_file_directory_iterator iterator, void *user_data); /* If flags includes CORK_FILE_RECURSIVE, this creates parent directories, * if needed. If flags doesn't include CORK_FILE_PERMISSIVE, then it's an error * if the directory already exists. */ CORK_API int cork_file_mkdir(struct cork_file *file, cork_file_mode mode, unsigned int flags); /* Removes a file or directory. If file is a directory, and flags contains * CORK_FILE_RECURSIVE, then all of the directory's contents are removed, too. * Otherwise, the directory must already be empty. */ CORK_API int cork_file_remove(struct cork_file *file, unsigned int flags); CORK_API struct cork_file * cork_path_list_find_file(const struct cork_path_list *list, const char *rel_path); /*----------------------------------------------------------------------- * Lists of files */ struct cork_file_list; CORK_API struct cork_file_list * cork_file_list_new_empty(void); CORK_API struct cork_file_list * cork_file_list_new(struct cork_path_list *path_list); CORK_API void cork_file_list_free(struct cork_file_list *list); /* Takes control of file. file must not already be in the list. */ CORK_API void cork_file_list_add(struct cork_file_list *list, struct cork_file *file); CORK_API size_t cork_file_list_size(struct cork_file_list *list); /* The list still owns file; you must not free it. Editing the file updates the * entry in the list. */ CORK_API struct cork_file * cork_file_list_get(struct cork_file_list *list, size_t index); CORK_API struct cork_file_list * cork_path_list_find_files(const struct cork_path_list *list, const char *rel_path); /*----------------------------------------------------------------------- * Walking a directory tree */ #define CORK_SKIP_DIRECTORY 1 struct cork_dir_walker { int (*enter_directory)(struct cork_dir_walker *walker, const char *full_path, const char *rel_path, const char *base_name); int (*file)(struct cork_dir_walker *walker, const char *full_path, const char *rel_path, const char *base_name); int (*leave_directory)(struct cork_dir_walker *walker, const char *full_path, const char *rel_path, const char *base_name); }; #define cork_dir_walker_enter_directory(w, fp, rp, bn) \ ((w)->enter_directory((w), (fp), (rp), (bn))) #define cork_dir_walker_file(w, fp, rp, bn) \ ((w)->file((w), (fp), (rp), (bn))) #define cork_dir_walker_leave_directory(w, fp, rp, bn) \ ((w)->leave_directory((w), (fp), (rp), (bn))) CORK_API int cork_walk_directory(const char *path, struct cork_dir_walker *walker); /*----------------------------------------------------------------------- * Standard paths and path lists */ CORK_API struct cork_path * cork_path_home(void); CORK_API struct cork_path_list * cork_path_config_paths(void); CORK_API struct cork_path_list * cork_path_data_paths(void); CORK_API struct cork_path * cork_path_user_cache_path(void); CORK_API struct cork_path * cork_path_user_runtime_path(void); #endif /* LIBCORK_CORE_FILES_H */ libcork-1.0.0-rc3/include/libcork/os/process.h000066400000000000000000000013461367645320400211600ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2013, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #ifndef LIBCORK_CORE_PROCESS_H #define LIBCORK_CORE_PROCESS_H #include typedef void (*cork_cleanup_function)(void); CORK_API void cork_cleanup_at_exit_named(const char *name, int priority, cork_cleanup_function function); #define cork_cleanup_at_exit(priority, function) \ cork_cleanup_at_exit_named(#function, priority, function) #endif /* LIBCORK_CORE_PROCESS_H */ libcork-1.0.0-rc3/include/libcork/os/subprocess.h000066400000000000000000000117341367645320400216740ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2012, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #ifndef LIBCORK_OS_SUBPROCESS_H #define LIBCORK_OS_SUBPROCESS_H #include #include #include #include #include #include /*----------------------------------------------------------------------- * Environments */ struct cork_env; CORK_API struct cork_env * cork_env_new(void); CORK_API struct cork_env * cork_env_clone_current(void); CORK_API void cork_env_free(struct cork_env *env); CORK_API void cork_env_replace_current(struct cork_env *env); /* For all of the following, if env is NULL, these functions access or update * the actual environment of the current process. Otherwise, they act on the * given environment instance. */ CORK_API const char * cork_env_get(struct cork_env *env, const char *name); CORK_API void cork_env_add(struct cork_env *env, const char *name, const char *value); CORK_API void cork_env_add_printf(struct cork_env *env, const char *name, const char *format, ...) CORK_ATTR_PRINTF(3,4); CORK_API void cork_env_add_vprintf(struct cork_env *env, const char *name, const char *format, va_list args) CORK_ATTR_PRINTF(3,0); CORK_API void cork_env_remove(struct cork_env *env, const char *name); /*----------------------------------------------------------------------- * Executing another process */ struct cork_exec; CORK_API struct cork_exec * cork_exec_new(const char *program); CORK_ATTR_SENTINEL CORK_API struct cork_exec * cork_exec_new_with_params(const char *program, ...); CORK_API struct cork_exec * cork_exec_new_with_param_array(const char *program, char * const *params); CORK_API void cork_exec_free(struct cork_exec *exec); CORK_API const char * cork_exec_description(struct cork_exec *exec); CORK_API const char * cork_exec_program(struct cork_exec *exec); CORK_API size_t cork_exec_param_count(struct cork_exec *exec); CORK_API const char * cork_exec_param(struct cork_exec *exec, size_t index); CORK_API void cork_exec_add_param(struct cork_exec *exec, const char *param); /* Can return NULL */ CORK_API struct cork_env * cork_exec_env(struct cork_exec *exec); /* Takes control of env */ CORK_API void cork_exec_set_env(struct cork_exec *exec, struct cork_env *env); /* Can return NULL */ CORK_API const char * cork_exec_cwd(struct cork_exec *exec); CORK_API void cork_exec_set_cwd(struct cork_exec *exec, const char *directory); CORK_API int cork_exec_run(struct cork_exec *exec); /*----------------------------------------------------------------------- * Subprocesses */ struct cork_subprocess; /* If env is NULL, we use the environment variables of the calling process. */ /* Takes control of body */ CORK_API struct cork_subprocess * cork_subprocess_new(void *user_data, cork_free_f free_user_data, cork_run_f run, struct cork_stream_consumer *stdout_consumer, struct cork_stream_consumer *stderr_consumer, int *exit_code); /* Takes control of exec */ CORK_API struct cork_subprocess * cork_subprocess_new_exec(struct cork_exec *exec, struct cork_stream_consumer *stdout_consumer, struct cork_stream_consumer *stderr_consumer, int *exit_code); CORK_API void cork_subprocess_free(struct cork_subprocess *sub); CORK_API struct cork_stream_consumer * cork_subprocess_stdin(struct cork_subprocess *sub); CORK_API int cork_subprocess_start(struct cork_subprocess *sub); CORK_API bool cork_subprocess_is_finished(struct cork_subprocess *sub); CORK_API int cork_subprocess_abort(struct cork_subprocess *sub); CORK_API bool cork_subprocess_drain(struct cork_subprocess *sub); CORK_API int cork_subprocess_wait(struct cork_subprocess *sub); /*----------------------------------------------------------------------- * Groups of subprocesses */ struct cork_subprocess_group; CORK_API struct cork_subprocess_group * cork_subprocess_group_new(void); CORK_API void cork_subprocess_group_free(struct cork_subprocess_group *group); /* Takes control of sub */ CORK_API void cork_subprocess_group_add(struct cork_subprocess_group *group, struct cork_subprocess *sub); CORK_API int cork_subprocess_group_start(struct cork_subprocess_group *group); CORK_API bool cork_subprocess_group_is_finished(struct cork_subprocess_group *group); CORK_API int cork_subprocess_group_abort(struct cork_subprocess_group *group); CORK_API bool cork_subprocess_group_drain(struct cork_subprocess_group *group); CORK_API int cork_subprocess_group_wait(struct cork_subprocess_group *group); #endif /* LIBCORK_OS_SUBPROCESS_H */ libcork-1.0.0-rc3/include/libcork/threads.h000066400000000000000000000007761367645320400205210ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2012, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #ifndef LIBCORK_THREADS_H #define LIBCORK_THREADS_H /*** include all of the parts ***/ #include #include #endif /* LIBCORK_THREADS_H */ libcork-1.0.0-rc3/include/libcork/threads/000077500000000000000000000000001367645320400203365ustar00rootroot00000000000000libcork-1.0.0-rc3/include/libcork/threads/atomics.h000066400000000000000000000033601367645320400221500ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2012, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #ifndef LIBCORK_THREADS_ATOMICS_H #define LIBCORK_THREADS_ATOMICS_H #include #include /*----------------------------------------------------------------------- * GCC intrinsics */ /* Ideally we can use GCC's intrinsics to define everything */ #if defined(CORK_CONFIG_HAVE_GCC_ATOMICS) #define cork_int_atomic_add __sync_add_and_fetch #define cork_uint_atomic_add __sync_add_and_fetch #define cork_size_atomic_add __sync_add_and_fetch #define cork_int_atomic_pre_add __sync_fetch_and_add #define cork_uint_atomic_pre_add __sync_fetch_and_add #define cork_size_atomic_pre_add __sync_fetch_and_add #define cork_int_atomic_sub __sync_sub_and_fetch #define cork_uint_atomic_sub __sync_sub_and_fetch #define cork_size_atomic_sub __sync_sub_and_fetch #define cork_int_atomic_pre_sub __sync_fetch_and_sub #define cork_uint_atomic_pre_sub __sync_fetch_and_sub #define cork_size_atomic_pre_sub __sync_fetch_and_sub #define cork_int_cas __sync_val_compare_and_swap #define cork_uint_cas __sync_val_compare_and_swap #define cork_size_cas __sync_val_compare_and_swap #define cork_ptr_cas __sync_val_compare_and_swap /*----------------------------------------------------------------------- * End of atomic implementations */ #else #error "No atomics implementation!" #endif #endif /* LIBCORK_THREADS_ATOMICS_H */ libcork-1.0.0-rc3/include/libcork/threads/basics.h000066400000000000000000000137471367645320400217670ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2012, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #ifndef LIBCORK_THREADS_BASICS_H #define LIBCORK_THREADS_BASICS_H #include #include #include #include #include /*----------------------------------------------------------------------- * Thread IDs */ typedef unsigned int cork_thread_id; #define CORK_THREAD_NONE ((cork_thread_id) 0) /* Returns a valid ID for any thread — even the main thread and threads that * aren't created by libcork. */ CORK_API cork_thread_id cork_current_thread_get_id(void); /*----------------------------------------------------------------------- * Threads */ struct cork_thread; /* Returns NULL for the main thread, and for any thread not created via * cork_thread_new/cork_thread_start. */ CORK_API struct cork_thread * cork_current_thread_get(void); CORK_API struct cork_thread * cork_thread_new(const char *name, void *user_data, cork_free_f free_user_data, cork_run_f run); /* Thread must not have been started yet. */ CORK_API void cork_thread_free(struct cork_thread *thread); CORK_API const char * cork_thread_get_name(struct cork_thread *thread); CORK_API cork_thread_id cork_thread_get_id(struct cork_thread *thread); /* Can only be called once per thread. Thread will automatically be freed when * its done. */ CORK_API int cork_thread_start(struct cork_thread *thread); /* Can only be called once per thread; must be called after cork_thread_start. */ CORK_API int cork_thread_join(struct cork_thread *thread); /*----------------------------------------------------------------------- * Executing something once */ #if CORK_CONFIG_HAVE_GCC_ASM && (CORK_CONFIG_ARCH_X86 || CORK_CONFIG_ARCH_X64) #define cork_pause() \ do { \ __asm__ __volatile__ ("pause"); \ } while (0) #else #define cork_pause() do { /* do nothing */ } while (0) #endif #define cork_once_barrier(name) \ static struct { \ volatile int barrier; \ cork_thread_id initializing_thread; \ } name##__once; #define cork_once(name, call) \ do { \ if (CORK_LIKELY(name##__once.barrier == 2)) { \ /* already initialized */ \ } else { \ /* Try to claim the ability to perform the initialization */ \ int prior_state = cork_int_cas(&name##__once.barrier, 0, 1); \ if (CORK_LIKELY(prior_state == 0)) { \ CORK_ATTR_UNUSED int result; \ /* we get to initialize */ \ call; \ result = cork_int_cas(&name##__once.barrier, 1, 2); \ assert(result == 1); \ } else { \ /* someone else is initializing, spin/wait until done */ \ while (name##__once.barrier != 2) { cork_pause(); } \ } \ } \ } while (0) #define cork_once_recursive(name, call) \ do { \ if (CORK_LIKELY(name##__once.barrier == 2)) { \ /* already initialized */ \ } else { \ /* Try to claim the ability to perform the initialization */ \ int prior_state = cork_int_cas(&name##__once.barrier, 0, 1); \ if (CORK_LIKELY(prior_state == 0)) { \ CORK_ATTR_UNUSED int result; \ /* we get to initialize */ \ name##__once.initializing_thread = \ cork_current_thread_get_id(); \ call; \ result = cork_int_cas(&name##__once.barrier, 1, 2); \ assert(result == 1); \ } else { \ /* someone else is initializing, is it us? */ \ if (name##__once.initializing_thread == \ cork_current_thread_get_id()) { \ /* yep, fall through to let our recursion continue */ \ } else { \ /* nope; wait for the initialization to finish */ \ while (name##__once.barrier != 2) { cork_pause(); } \ } \ } \ } \ } while (0) /*----------------------------------------------------------------------- * Thread-local storage */ /* Prefer, in order: * * 1) __thread storage class * 2) pthread_key_t */ #if CORK_CONFIG_HAVE_THREAD_STORAGE_CLASS #define cork_tls(TYPE, NAME) \ static __thread TYPE NAME##__tls; \ \ static TYPE * \ NAME##_get(void) \ { \ return &NAME##__tls; \ } #define cork_tls_with_alloc(TYPE, NAME, allocate, deallocate) \ cork_tls(TYPE, NAME) #elif CORK_HAVE_PTHREADS #include #include #include #define cork_tls_with_alloc(TYPE, NAME, allocate, deallocate) \ static pthread_key_t NAME##__tls_key; \ cork_once_barrier(NAME##__tls_barrier); \ \ static void \ NAME##__tls_destroy(void *self) \ { \ deallocate(self); \ } \ \ static void \ NAME##__create_key(void) \ { \ CORK_ATTR_UNUSED int rc; \ rc = pthread_key_create(&NAME##__tls_key, &NAME##__tls_destroy); \ assert(rc == 0); \ } \ \ static TYPE * \ NAME##_get(void) \ { \ TYPE *self; \ cork_once(NAME##__tls_barrier, NAME##__create_key()); \ self = pthread_getspecific(NAME##__tls_key); \ if (CORK_UNLIKELY(self == NULL)) { \ self = allocate(); \ pthread_setspecific(NAME##__tls_key, self); \ } \ return self; \ } #define cork_tls(TYPE, NAME) \ \ static TYPE * \ NAME##__tls_allocate(void) \ { \ return cork_calloc(1, sizeof(TYPE)); \ } \ \ static void \ NAME##__tls_deallocate(void *vself) \ { \ cork_cfree(vself, 1, sizeof(TYPE)); \ } \ \ cork_tls_with_alloc(TYPE, NAME, NAME##__tls_allocate, NAME##__tls_deallocate); #else #error "No thread-local storage implementation!" #endif #endif /* LIBCORK_THREADS_BASICS_H */ libcork-1.0.0-rc3/m4/000077500000000000000000000000001367645320400141545ustar00rootroot00000000000000libcork-1.0.0-rc3/m4/ax_pthread.m4000066400000000000000000000505221367645320400165410ustar00rootroot00000000000000# =========================================================================== # https://www.gnu.org/software/autoconf-archive/ax_pthread.html # =========================================================================== # # SYNOPSIS # # AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) # # DESCRIPTION # # This macro figures out how to build C programs using POSIX threads. It # sets the PTHREAD_LIBS output variable to the threads library and linker # flags, and the PTHREAD_CFLAGS output variable to any special C compiler # flags that are needed. (The user can also force certain compiler # flags/libs to be tested by setting these environment variables.) # # Also sets PTHREAD_CC to any special C compiler that is needed for # multi-threaded programs (defaults to the value of CC otherwise). (This # is necessary on AIX to use the special cc_r compiler alias.) # # NOTE: You are assumed to not only compile your program with these flags, # but also to link with them as well. For example, you might link with # $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS # # If you are only building threaded programs, you may wish to use these # variables in your default LIBS, CFLAGS, and CC: # # LIBS="$PTHREAD_LIBS $LIBS" # CFLAGS="$CFLAGS $PTHREAD_CFLAGS" # CC="$PTHREAD_CC" # # In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant # has a nonstandard name, this macro defines PTHREAD_CREATE_JOINABLE to # that name (e.g. PTHREAD_CREATE_UNDETACHED on AIX). # # Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the # PTHREAD_PRIO_INHERIT symbol is defined when compiling with # PTHREAD_CFLAGS. # # ACTION-IF-FOUND is a list of shell commands to run if a threads library # is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it # is not found. If ACTION-IF-FOUND is not specified, the default action # will define HAVE_PTHREAD. # # Please let the authors know if this macro fails on any platform, or if # you have any other suggestions or comments. This macro was based on work # by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help # from M. Frigo), as well as ac_pthread and hb_pthread macros posted by # Alejandro Forero Cuervo to the autoconf macro repository. We are also # grateful for the helpful feedback of numerous users. # # Updated for Autoconf 2.68 by Daniel Richard G. # # LICENSE # # Copyright (c) 2008 Steven G. Johnson # Copyright (c) 2011 Daniel Richard G. # # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation, either version 3 of the License, or (at your # option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . # # As a special exception, the respective Autoconf Macro's copyright owner # gives unlimited permission to copy, distribute and modify the configure # scripts that are the output of Autoconf when processing the Macro. You # need not follow the terms of the GNU General Public License when using # or distributing such scripts, even though portions of the text of the # Macro appear in them. The GNU General Public License (GPL) does govern # all other use of the material that constitutes the Autoconf Macro. # # This special exception to the GPL applies to versions of the Autoconf # Macro released by the Autoconf Archive. When you make and distribute a # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. #serial 24 AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD]) AC_DEFUN([AX_PTHREAD], [ AC_REQUIRE([AC_CANONICAL_HOST]) AC_REQUIRE([AC_PROG_CC]) AC_REQUIRE([AC_PROG_SED]) AC_LANG_PUSH([C]) ax_pthread_ok=no # We used to check for pthread.h first, but this fails if pthread.h # requires special compiler flags (e.g. on Tru64 or Sequent). # It gets checked for in the link test anyway. # First of all, check if the user has set any of the PTHREAD_LIBS, # etcetera environment variables, and if threads linking works using # them: if test "x$PTHREAD_CFLAGS$PTHREAD_LIBS" != "x"; then ax_pthread_save_CC="$CC" ax_pthread_save_CFLAGS="$CFLAGS" ax_pthread_save_LIBS="$LIBS" AS_IF([test "x$PTHREAD_CC" != "x"], [CC="$PTHREAD_CC"]) CFLAGS="$CFLAGS $PTHREAD_CFLAGS" LIBS="$PTHREAD_LIBS $LIBS" AC_MSG_CHECKING([for pthread_join using $CC $PTHREAD_CFLAGS $PTHREAD_LIBS]) AC_LINK_IFELSE([AC_LANG_CALL([], [pthread_join])], [ax_pthread_ok=yes]) AC_MSG_RESULT([$ax_pthread_ok]) if test "x$ax_pthread_ok" = "xno"; then PTHREAD_LIBS="" PTHREAD_CFLAGS="" fi CC="$ax_pthread_save_CC" CFLAGS="$ax_pthread_save_CFLAGS" LIBS="$ax_pthread_save_LIBS" fi # We must check for the threads library under a number of different # names; the ordering is very important because some systems # (e.g. DEC) have both -lpthread and -lpthreads, where one of the # libraries is broken (non-POSIX). # Create a list of thread flags to try. Items starting with a "-" are # C compiler flags, and other items are library names, except for "none" # which indicates that we try without any flags at all, and "pthread-config" # which is a program returning the flags for the Pth emulation library. ax_pthread_flags="pthreads none -Kthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" # The ordering *is* (sometimes) important. Some notes on the # individual items follow: # pthreads: AIX (must check this before -lpthread) # none: in case threads are in libc; should be tried before -Kthread and # other compiler flags to prevent continual compiler warnings # -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) # -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads), Tru64 # (Note: HP C rejects this with "bad form for `-t' option") # -pthreads: Solaris/gcc (Note: HP C also rejects) # -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it # doesn't hurt to check since this sometimes defines pthreads and # -D_REENTRANT too), HP C (must be checked before -lpthread, which # is present but should not be used directly; and before -mthreads, # because the compiler interprets this as "-mt" + "-hreads") # -mthreads: Mingw32/gcc, Lynx/gcc # pthread: Linux, etcetera # --thread-safe: KAI C++ # pthread-config: use pthread-config program (for GNU Pth library) case $host_os in freebsd*) # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) ax_pthread_flags="-kthread lthread $ax_pthread_flags" ;; hpux*) # From the cc(1) man page: "[-mt] Sets various -D flags to enable # multi-threading and also sets -lpthread." ax_pthread_flags="-mt -pthread pthread $ax_pthread_flags" ;; openedition*) # IBM z/OS requires a feature-test macro to be defined in order to # enable POSIX threads at all, so give the user a hint if this is # not set. (We don't define these ourselves, as they can affect # other portions of the system API in unpredictable ways.) AC_EGREP_CPP([AX_PTHREAD_ZOS_MISSING], [ # if !defined(_OPEN_THREADS) && !defined(_UNIX03_THREADS) AX_PTHREAD_ZOS_MISSING # endif ], [AC_MSG_WARN([IBM z/OS requires -D_OPEN_THREADS or -D_UNIX03_THREADS to enable pthreads support.])]) ;; solaris*) # On Solaris (at least, for some versions), libc contains stubbed # (non-functional) versions of the pthreads routines, so link-based # tests will erroneously succeed. (N.B.: The stubs are missing # pthread_cleanup_push, or rather a function called by this macro, # so we could check for that, but who knows whether they'll stub # that too in a future libc.) So we'll check first for the # standard Solaris way of linking pthreads (-mt -lpthread). ax_pthread_flags="-mt,pthread pthread $ax_pthread_flags" ;; esac # GCC generally uses -pthread, or -pthreads on some platforms (e.g. SPARC) AS_IF([test "x$GCC" = "xyes"], [ax_pthread_flags="-pthread -pthreads $ax_pthread_flags"]) # The presence of a feature test macro requesting re-entrant function # definitions is, on some systems, a strong hint that pthreads support is # correctly enabled case $host_os in darwin* | hpux* | linux* | osf* | solaris*) ax_pthread_check_macro="_REENTRANT" ;; aix*) ax_pthread_check_macro="_THREAD_SAFE" ;; *) ax_pthread_check_macro="--" ;; esac AS_IF([test "x$ax_pthread_check_macro" = "x--"], [ax_pthread_check_cond=0], [ax_pthread_check_cond="!defined($ax_pthread_check_macro)"]) # Are we compiling with Clang? AC_CACHE_CHECK([whether $CC is Clang], [ax_cv_PTHREAD_CLANG], [ax_cv_PTHREAD_CLANG=no # Note that Autoconf sets GCC=yes for Clang as well as GCC if test "x$GCC" = "xyes"; then AC_EGREP_CPP([AX_PTHREAD_CC_IS_CLANG], [/* Note: Clang 2.7 lacks __clang_[a-z]+__ */ # if defined(__clang__) && defined(__llvm__) AX_PTHREAD_CC_IS_CLANG # endif ], [ax_cv_PTHREAD_CLANG=yes]) fi ]) ax_pthread_clang="$ax_cv_PTHREAD_CLANG" ax_pthread_clang_warning=no # Clang needs special handling, because older versions handle the -pthread # option in a rather... idiosyncratic way if test "x$ax_pthread_clang" = "xyes"; then # Clang takes -pthread; it has never supported any other flag # (Note 1: This will need to be revisited if a system that Clang # supports has POSIX threads in a separate library. This tends not # to be the way of modern systems, but it's conceivable.) # (Note 2: On some systems, notably Darwin, -pthread is not needed # to get POSIX threads support; the API is always present and # active. We could reasonably leave PTHREAD_CFLAGS empty. But # -pthread does define _REENTRANT, and while the Darwin headers # ignore this macro, third-party headers might not.) PTHREAD_CFLAGS="-pthread" PTHREAD_LIBS= ax_pthread_ok=yes # However, older versions of Clang make a point of warning the user # that, in an invocation where only linking and no compilation is # taking place, the -pthread option has no effect ("argument unused # during compilation"). They expect -pthread to be passed in only # when source code is being compiled. # # Problem is, this is at odds with the way Automake and most other # C build frameworks function, which is that the same flags used in # compilation (CFLAGS) are also used in linking. Many systems # supported by AX_PTHREAD require exactly this for POSIX threads # support, and in fact it is often not straightforward to specify a # flag that is used only in the compilation phase and not in # linking. Such a scenario is extremely rare in practice. # # Even though use of the -pthread flag in linking would only print # a warning, this can be a nuisance for well-run software projects # that build with -Werror. So if the active version of Clang has # this misfeature, we search for an option to squash it. AC_CACHE_CHECK([whether Clang needs flag to prevent "argument unused" warning when linking with -pthread], [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG], [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG=unknown # Create an alternate version of $ac_link that compiles and # links in two steps (.c -> .o, .o -> exe) instead of one # (.c -> exe), because the warning occurs only in the second # step ax_pthread_save_ac_link="$ac_link" ax_pthread_sed='s/conftest\.\$ac_ext/conftest.$ac_objext/g' ax_pthread_link_step=`$as_echo "$ac_link" | sed "$ax_pthread_sed"` ax_pthread_2step_ac_link="($ac_compile) && (echo ==== >&5) && ($ax_pthread_link_step)" ax_pthread_save_CFLAGS="$CFLAGS" for ax_pthread_try in '' -Qunused-arguments -Wno-unused-command-line-argument unknown; do AS_IF([test "x$ax_pthread_try" = "xunknown"], [break]) CFLAGS="-Werror -Wunknown-warning-option $ax_pthread_try -pthread $ax_pthread_save_CFLAGS" ac_link="$ax_pthread_save_ac_link" AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])], [ac_link="$ax_pthread_2step_ac_link" AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])], [break]) ]) done ac_link="$ax_pthread_save_ac_link" CFLAGS="$ax_pthread_save_CFLAGS" AS_IF([test "x$ax_pthread_try" = "x"], [ax_pthread_try=no]) ax_cv_PTHREAD_CLANG_NO_WARN_FLAG="$ax_pthread_try" ]) case "$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG" in no | unknown) ;; *) PTHREAD_CFLAGS="$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG $PTHREAD_CFLAGS" ;; esac fi # $ax_pthread_clang = yes if test "x$ax_pthread_ok" = "xno"; then for ax_pthread_try_flag in $ax_pthread_flags; do case $ax_pthread_try_flag in none) AC_MSG_CHECKING([whether pthreads work without any flags]) ;; -mt,pthread) AC_MSG_CHECKING([whether pthreads work with -mt -lpthread]) PTHREAD_CFLAGS="-mt" PTHREAD_LIBS="-lpthread" ;; -*) AC_MSG_CHECKING([whether pthreads work with $ax_pthread_try_flag]) PTHREAD_CFLAGS="$ax_pthread_try_flag" ;; pthread-config) AC_CHECK_PROG([ax_pthread_config], [pthread-config], [yes], [no]) AS_IF([test "x$ax_pthread_config" = "xno"], [continue]) PTHREAD_CFLAGS="`pthread-config --cflags`" PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" ;; *) AC_MSG_CHECKING([for the pthreads library -l$ax_pthread_try_flag]) PTHREAD_LIBS="-l$ax_pthread_try_flag" ;; esac ax_pthread_save_CFLAGS="$CFLAGS" ax_pthread_save_LIBS="$LIBS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" LIBS="$PTHREAD_LIBS $LIBS" # Check for various functions. We must include pthread.h, # since some functions may be macros. (On the Sequent, we # need a special flag -Kthread to make this header compile.) # We check for pthread_join because it is in -lpthread on IRIX # while pthread_create is in libc. We check for pthread_attr_init # due to DEC craziness with -lpthreads. We check for # pthread_cleanup_push because it is one of the few pthread # functions on Solaris that doesn't have a non-functional libc stub. # We try pthread_create on general principles. AC_LINK_IFELSE([AC_LANG_PROGRAM([#include # if $ax_pthread_check_cond # error "$ax_pthread_check_macro must be defined" # endif static void routine(void *a) { a = 0; } static void *start_routine(void *a) { return a; }], [pthread_t th; pthread_attr_t attr; pthread_create(&th, 0, start_routine, 0); pthread_join(th, 0); pthread_attr_init(&attr); pthread_cleanup_push(routine, 0); pthread_cleanup_pop(0) /* ; */])], [ax_pthread_ok=yes], []) CFLAGS="$ax_pthread_save_CFLAGS" LIBS="$ax_pthread_save_LIBS" AC_MSG_RESULT([$ax_pthread_ok]) AS_IF([test "x$ax_pthread_ok" = "xyes"], [break]) PTHREAD_LIBS="" PTHREAD_CFLAGS="" done fi # Various other checks: if test "x$ax_pthread_ok" = "xyes"; then ax_pthread_save_CFLAGS="$CFLAGS" ax_pthread_save_LIBS="$LIBS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" LIBS="$PTHREAD_LIBS $LIBS" # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. AC_CACHE_CHECK([for joinable pthread attribute], [ax_cv_PTHREAD_JOINABLE_ATTR], [ax_cv_PTHREAD_JOINABLE_ATTR=unknown for ax_pthread_attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do AC_LINK_IFELSE([AC_LANG_PROGRAM([#include ], [int attr = $ax_pthread_attr; return attr /* ; */])], [ax_cv_PTHREAD_JOINABLE_ATTR=$ax_pthread_attr; break], []) done ]) AS_IF([test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xunknown" && \ test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xPTHREAD_CREATE_JOINABLE" && \ test "x$ax_pthread_joinable_attr_defined" != "xyes"], [AC_DEFINE_UNQUOTED([PTHREAD_CREATE_JOINABLE], [$ax_cv_PTHREAD_JOINABLE_ATTR], [Define to necessary symbol if this constant uses a non-standard name on your system.]) ax_pthread_joinable_attr_defined=yes ]) AC_CACHE_CHECK([whether more special flags are required for pthreads], [ax_cv_PTHREAD_SPECIAL_FLAGS], [ax_cv_PTHREAD_SPECIAL_FLAGS=no case $host_os in solaris*) ax_cv_PTHREAD_SPECIAL_FLAGS="-D_POSIX_PTHREAD_SEMANTICS" ;; esac ]) AS_IF([test "x$ax_cv_PTHREAD_SPECIAL_FLAGS" != "xno" && \ test "x$ax_pthread_special_flags_added" != "xyes"], [PTHREAD_CFLAGS="$ax_cv_PTHREAD_SPECIAL_FLAGS $PTHREAD_CFLAGS" ax_pthread_special_flags_added=yes]) AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT], [ax_cv_PTHREAD_PRIO_INHERIT], [AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], [[int i = PTHREAD_PRIO_INHERIT;]])], [ax_cv_PTHREAD_PRIO_INHERIT=yes], [ax_cv_PTHREAD_PRIO_INHERIT=no]) ]) AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes" && \ test "x$ax_pthread_prio_inherit_defined" != "xyes"], [AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], [1], [Have PTHREAD_PRIO_INHERIT.]) ax_pthread_prio_inherit_defined=yes ]) CFLAGS="$ax_pthread_save_CFLAGS" LIBS="$ax_pthread_save_LIBS" # More AIX lossage: compile with *_r variant if test "x$GCC" != "xyes"; then case $host_os in aix*) AS_CASE(["x/$CC"], [x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6], [#handle absolute path differently from PATH based program lookup AS_CASE(["x$CC"], [x/*], [AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"])], [AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC])])]) ;; esac fi fi test -n "$PTHREAD_CC" || PTHREAD_CC="$CC" AC_SUBST([PTHREAD_LIBS]) AC_SUBST([PTHREAD_CFLAGS]) AC_SUBST([PTHREAD_CC]) # Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: if test "x$ax_pthread_ok" = "xyes"; then ifelse([$1],,[AC_DEFINE([HAVE_PTHREAD],[1],[Define if you have POSIX threads libraries and header files.])],[$1]) : else ax_pthread_ok=no $2 fi AC_LANG_POP ])dnl AX_PTHREAD libcork-1.0.0-rc3/m4/ax_valgrind_check.m4000066400000000000000000000212311367645320400200500ustar00rootroot00000000000000# =========================================================================== # http://www.gnu.org/software/autoconf-archive/ax_valgrind_check.html # =========================================================================== # # SYNOPSIS # # AX_VALGRIND_DFLT(memcheck|helgrind|drd|sgcheck, on|off) # AX_VALGRIND_CHECK() # # DESCRIPTION # # AX_VALGRIND_CHECK checks whether Valgrind is present and, if so, allows # running `make check` under a variety of Valgrind tools to check for # memory and threading errors. # # Defines VALGRIND_CHECK_RULES which should be substituted in your # Makefile; and $enable_valgrind which can be used in subsequent configure # output. VALGRIND_ENABLED is defined and substituted, and corresponds to # the value of the --enable-valgrind option, which defaults to being # enabled if Valgrind is installed and disabled otherwise. Individual # Valgrind tools can be disabled via --disable-valgrind-, the # default is configurable via the AX_VALGRIND_DFLT command or is to use # all commands not disabled via AX_VALGRIND_DFLT. All AX_VALGRIND_DFLT # calls must be made before the call to AX_VALGRIND_CHECK. # # If unit tests are written using a shell script and automake's # LOG_COMPILER system, the $(VALGRIND) variable can be used within the # shell scripts to enable Valgrind, as described here: # # https://www.gnu.org/software/gnulib/manual/html_node/Running-self_002dtests-under-valgrind.html # # Usage example: # # configure.ac: # # AX_VALGRIND_DFLT([sgcheck], [off]) # AX_VALGRIND_CHECK # # Makefile.am: # # @VALGRIND_CHECK_RULES@ # VALGRIND_SUPPRESSIONS_FILES = my-project.supp # EXTRA_DIST = my-project.supp # # This results in a "check-valgrind" rule being added to any Makefile.am # which includes "@VALGRIND_CHECK_RULES@" (assuming the module has been # configured with --enable-valgrind). Running `make check-valgrind` in # that directory will run the module's test suite (`make check`) once for # each of the available Valgrind tools (out of memcheck, helgrind and drd) # while the sgcheck will be skipped unless enabled again on the # commandline with --enable-valgrind-sgcheck. The results for each check # will be output to test-suite-$toolname.log. The target will succeed if # there are zero errors and fail otherwise. # # Alternatively, a "check-valgrind-$TOOL" rule will be added, for $TOOL in # memcheck, helgrind, drd and sgcheck. These are useful because often only # some of those tools can be ran cleanly on a codebase. # # The macro supports running with and without libtool. # # LICENSE # # Copyright (c) 2014, 2015, 2016 Philip Withnall # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. This file is offered as-is, without any # warranty. #serial 13.1 dnl Configured tools m4_define([valgrind_tool_list], [[memcheck], [helgrind], [drd], [sgcheck]]) m4_set_add_all([valgrind_exp_tool_set], [sgcheck]) m4_foreach([vgtool], [valgrind_tool_list], [m4_define([en_dflt_valgrind_]vgtool, [on])]) AC_DEFUN([AX_VALGRIND_DFLT],[ m4_define([en_dflt_valgrind_$1], [$2]) ])dnl AC_DEFUN([AX_VALGRIND_CHECK],[ dnl Check for --enable-valgrind AC_ARG_ENABLE([valgrind], [AS_HELP_STRING([--enable-valgrind], [Whether to enable Valgrind on the unit tests])], [enable_valgrind=$enableval],[enable_valgrind=]) AS_IF([test "$enable_valgrind" != "no"],[ # Check for Valgrind. AC_CHECK_PROG([VALGRIND],[valgrind],[valgrind]) AS_IF([test "$VALGRIND" = ""],[ AS_IF([test "$enable_valgrind" = "yes"],[ AC_MSG_ERROR([Could not find valgrind; either install it or reconfigure with --disable-valgrind]) ],[ enable_valgrind=no ]) ],[ enable_valgrind=yes ]) ]) AM_CONDITIONAL([VALGRIND_ENABLED],[test "$enable_valgrind" = "yes"]) AC_SUBST([VALGRIND_ENABLED],[$enable_valgrind]) # Check for Valgrind tools we care about. [valgrind_enabled_tools=] m4_foreach([vgtool],[valgrind_tool_list],[ AC_ARG_ENABLE([valgrind-]vgtool, m4_if(m4_defn([en_dflt_valgrind_]vgtool),[off],dnl [AS_HELP_STRING([--enable-valgrind-]vgtool, [Whether to use ]vgtool[ during the Valgrind tests])],dnl [AS_HELP_STRING([--disable-valgrind-]vgtool, [Whether to skip ]vgtool[ during the Valgrind tests])]), [enable_valgrind_]vgtool[=$enableval], [enable_valgrind_]vgtool[=]) AS_IF([test "$enable_valgrind" = "no"],[ enable_valgrind_]vgtool[=no], [test "$enable_valgrind_]vgtool[" ]dnl m4_if(m4_defn([en_dflt_valgrind_]vgtool), [off], [= "yes"], [!= "no"]),[ AC_CACHE_CHECK([for Valgrind tool ]vgtool, [ax_cv_valgrind_tool_]vgtool,[ ax_cv_valgrind_tool_]vgtool[=no m4_set_contains([valgrind_exp_tool_set],vgtool, [m4_define([vgtoolx],[exp-]vgtool)], [m4_define([vgtoolx],vgtool)]) AS_IF([`$VALGRIND --tool=]vgtoolx[ --help >/dev/null 2>&1`],[ ax_cv_valgrind_tool_]vgtool[=yes ]) ]) AS_IF([test "$ax_cv_valgrind_tool_]vgtool[" = "no"],[ AS_IF([test "$enable_valgrind_]vgtool[" = "yes"],[ AC_MSG_ERROR([Valgrind does not support ]vgtool[; reconfigure with --disable-valgrind-]vgtool) ],[ enable_valgrind_]vgtool[=no ]) ],[ enable_valgrind_]vgtool[=yes ]) ]) AS_IF([test "$enable_valgrind_]vgtool[" = "yes"],[ valgrind_enabled_tools="$valgrind_enabled_tools ]m4_bpatsubst(vgtool,[^exp-])[" ]) AC_SUBST([ENABLE_VALGRIND_]vgtool,[$enable_valgrind_]vgtool) ]) AC_SUBST([valgrind_tools],["]m4_join([ ], valgrind_tool_list)["]) AC_SUBST([valgrind_enabled_tools],[$valgrind_enabled_tools]) [VALGRIND_CHECK_RULES=' # Valgrind check # # Optional: # - VALGRIND_SUPPRESSIONS_FILES: Space-separated list of Valgrind suppressions # files to load. (Default: empty) # - VALGRIND_FLAGS: General flags to pass to all Valgrind tools. # (Default: --num-callers=30) # - VALGRIND_$toolname_FLAGS: Flags to pass to Valgrind $toolname (one of: # memcheck, helgrind, drd, sgcheck). (Default: various) # Optional variables VALGRIND_SUPPRESSIONS ?= $(addprefix --suppressions=,$(VALGRIND_SUPPRESSIONS_FILES)) VALGRIND_FLAGS ?= --num-callers=30 VALGRIND_memcheck_FLAGS ?= --leak-check=full --show-reachable=no VALGRIND_helgrind_FLAGS ?= --history-level=approx VALGRIND_drd_FLAGS ?= VALGRIND_sgcheck_FLAGS ?= # Internal use valgrind_log_files = $(addprefix test-suite-,$(addsuffix .log,$(valgrind_tools))) valgrind_memcheck_flags = --tool=memcheck $(VALGRIND_memcheck_FLAGS) valgrind_helgrind_flags = --tool=helgrind $(VALGRIND_helgrind_FLAGS) valgrind_drd_flags = --tool=drd $(VALGRIND_drd_FLAGS) valgrind_sgcheck_flags = --tool=exp-sgcheck $(VALGRIND_sgcheck_FLAGS) valgrind_quiet = $(valgrind_quiet_$(V)) valgrind_quiet_ = $(valgrind_quiet_$(AM_DEFAULT_VERBOSITY)) valgrind_quiet_0 = --quiet valgrind_v_use = $(valgrind_v_use_$(V)) valgrind_v_use_ = $(valgrind_v_use_$(AM_DEFAULT_VERBOSITY)) valgrind_v_use_0 = @echo " USE " $(patsubst check-valgrind-%,%,$''@):; # Support running with and without libtool. ifneq ($(LIBTOOL),) valgrind_lt = $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=execute else valgrind_lt = endif # Use recursive makes in order to ignore errors during check check-valgrind: ifeq ($(VALGRIND_ENABLED),yes) -$(A''M_V_at)$(foreach tool,$(valgrind_enabled_tools), \ $(MAKE) $(AM_MAKEFLAGS) -k check-valgrind-$(tool); \ ) else @echo "Need to reconfigure with --enable-valgrind" endif # Valgrind running VALGRIND_TESTS_ENVIRONMENT = \ $(TESTS_ENVIRONMENT) \ env VALGRIND=$(VALGRIND) \ G_SLICE=always-malloc,debug-blocks \ G_DEBUG=fatal-warnings,fatal-criticals,gc-friendly VALGRIND_LOG_COMPILER = \ $(valgrind_lt) \ $(VALGRIND) $(VALGRIND_SUPPRESSIONS) --error-exitcode=1 $(VALGRIND_FLAGS) define valgrind_tool_rule check-valgrind-$(1): ifeq ($$(VALGRIND_ENABLED)-$$(ENABLE_VALGRIND_$(1)),yes-yes) $$(valgrind_v_use)$$(MAKE) check-TESTS \ TESTS_ENVIRONMENT="$$(VALGRIND_TESTS_ENVIRONMENT)" \ LOG_COMPILER="$$(VALGRIND_LOG_COMPILER)" \ LOG_FLAGS="$$(valgrind_$(1)_flags)" \ TEST_SUITE_LOG=test-suite-$(1).log else ifeq ($$(VALGRIND_ENABLED),yes) @echo "Need to reconfigure with --enable-valgrind-$(1)" else @echo "Need to reconfigure with --enable-valgrind" endif endef $(foreach tool,$(valgrind_tools),$(eval $(call valgrind_tool_rule,$(tool)))) A''M_DISTCHECK_CONFIGURE_FLAGS ?= A''M_DISTCHECK_CONFIGURE_FLAGS += --disable-valgrind MOSTLYCLEANFILES ?= MOSTLYCLEANFILES += $(valgrind_log_files) .PHONY: check-valgrind $(add-prefix check-valgrind-,$(valgrind_tools)) '] AC_SUBST([VALGRIND_CHECK_RULES]) m4_ifdef([_AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE([VALGRIND_CHECK_RULES])]) ]) libcork-1.0.0-rc3/make-dist.sh000077500000000000000000000003561367645320400160550ustar00rootroot00000000000000#!/bin/sh PROJECT=libcork COMMIT="$1" if [ -z "$COMMIT" ]; then COMMIT="HEAD" fi VERSION=$(git describe ${COMMIT}) git archive --prefix=${PROJECT}-${VERSION}/ --format=tar ${COMMIT} | \ bzip2 -c > ${PROJECT}-${VERSION}.tar.bz2 libcork-1.0.0-rc3/run.sh000077500000000000000000000017201367645320400147770ustar00rootroot00000000000000#!/bin/sh # Usage: run.sh [debug|release] program arguments # # Runs a program from one of the build directories, with # LD_LIBRARY_PATH set correctly so that it can find all of the shared # libraries before they're installed. # Check that there are enough command-line parameters. if [ $# -lt 1 ]; then echo "Usage: run.sh [debug|release] program arguments" exit 1 fi # Verify that the user chose a valid build type. BUILD="$1" shift case "$BUILD" in debug) ;; release) ;; *) echo "Unknown build type $BUILD" exit 1 ;; esac # Find all of the src subdirectories in the build directory, and use # those as the LD_LIBRARY_PATH. SRC_DIRS=$(find build/$BUILD -name src) JOINED=$(echo $SRC_DIRS | perl -ne 'print join(":", split)') # Run the desired program, and pass on any command-line arguments # as-is. LD_LIBRARY_PATH="$JOINED:$LD_LIBRARY_PATH" \ DYLD_LIBRARY_PATH="$JOINED:$DYLD_LIBRARY_PATH" \ "$@" libcork-1.0.0-rc3/scripts/000077500000000000000000000000001367645320400153235ustar00rootroot00000000000000libcork-1.0.0-rc3/scripts/install000077500000000000000000000004041367645320400167150ustar00rootroot00000000000000#!/bin/sh set -e if [ "$OS_NAME" = "ubuntu-latest" ]; then sudo dpkg --add-architecture i386 sudo apt-get update sudo apt-get install -y check check:i386 gcc-multilib valgrind elif [ "$OS_NAME" = osx ]; then brew install --universal check fi libcork-1.0.0-rc3/scripts/test000077500000000000000000000053271367645320400162370ustar00rootroot00000000000000#!/bin/bash set -e # Defaults ENABLE_SHARED_TESTS=NO SRCDIR="$PWD" : ${TMPDIR:=/tmp} if [ "$COMPILER" = clang ]; then export CC=/usr/bin/clang else export CC=/usr/bin/gcc fi if [ "$OS_NAME" = "ubuntu-latest" ]; then if [ "$ARCH" = i386 ]; then ARCH_FLAGS="-m32" export PKG_CONFIG_PATH=/usr/lib/i386-linux-gnu/pkgconfig else ARCH_FLAGS="" fi elif [ "$OS_NAME" = osx ]; then if [ "$ARCH" = i386 ]; then ARCH_FLAGS="-arch i386" else ARCH_FLAGS="-arch x86_64" fi fi build_autotools () { BUILDDIR=$(mktemp -d "$TMPDIR"/build-autotools-XXXXXX) echo "Using autotools to build in $BUILDDIR" echo "$ autogen.sh" "$SRCDIR"/autogen.sh cd "$BUILDDIR" echo "$ configure" CFLAGS="-g -O3 $ARCH_FLAGS" "$SRCDIR"/configure echo "$ make" make echo "$ make check" make check VERBOSE=1 echo "$ make distcheck" make distcheck } build_cmake () { BUILDDIR=$(mktemp -d "$TMPDIR"/build-cmake-XXXXXX) echo "Using cmake to build in $BUILDDIR" cd "$BUILDDIR" echo "$ cmake" cmake "$SRCDIR" \ -DCMAKE_BUILD_TYPE=RelWithDebInfo \ -DCMAKE_C_FLAGS_RELWITHDEBINFO="-g -O3 $ARCH_FLAGS $EXTRA_FLAGS" \ -DENABLE_SHARED_TESTS="$ENABLE_SHARED_TESTS" echo "$ make" make echo "$ make test" CTEST_OUTPUT_ON_FAILURE=1 make test } build_cmake_from_dist () { DISTDIR=$(mktemp -d "$TMPDIR"/build-dist-XXXXXX) echo "Using autotools to create dist tarball in $DISTDIR" "$SRCDIR"/autogen.sh cd "$DISTDIR" echo "$ configure" CFLAGS="-g -O3 $ARCH_FLAGS" "$SRCDIR"/configure echo "$ make dist" make dist # We should end up with exactly one *.tar.xz file TARBALL=$(ls *.tar.xz | head -n 1) UNPACKED=${TARBALL%.tar.xz} UNPACKEDDIR=$(mktemp -d "$TMPDIR"/build-unpacked-XXXXXX) echo "Unpacking dist tarball $UNPACKED into $UNPACKEDDIR" cd "$UNPACKEDDIR" tar xvf "$DISTDIR"/"$TARBALL" BUILDDIR=$(mktemp -d "$TMPDIR"/build-cmake-XXXXXX) echo "Using cmake to build from dist tarball in $BUILDDIR" cd "$BUILDDIR" echo "$ cmake" cmake "$UNPACKEDDIR"/"$UNPACKED" \ -DCMAKE_BUILD_TYPE=RelWithDebInfo \ -DCMAKE_C_FLAGS_RELWITHDEBINFO="-g -O3 $ARCH_FLAGS $EXTRA_FLAGS" echo "$ make" make echo "$ make test" CTEST_OUTPUT_ON_FAILURE=1 make test } # Flavors case $FLAVOR in autotools) BUILDER=autotools ;; cmake) BUILDER=cmake ;; shared-cmake) BUILDER=cmake ENABLE_SHARED_TESTS=yes ;; fallback-u128) BUILDER=cmake EXTRA_FLAGS="$EXTRA_FLAGS -DCORK_CONFIG_HAVE_GCC_INT128=0" EXTRA_FLAGS="$EXTRA_FLAGS -DCORK_CONFIG_HAVE_GCC_MODE_ATTRIBUTE=0" ;; cmake-from-dist) BUILDER=cmake_from_dist ;; *) echo "Unknown build flavor $FLAVOR!" >&2 exit 1 ;; esac build_$BUILDER libcork-1.0.0-rc3/share/000077500000000000000000000000001367645320400147365ustar00rootroot00000000000000libcork-1.0.0-rc3/share/CMakeLists.txt000066400000000000000000000006431367645320400175010ustar00rootroot00000000000000# -*- coding: utf-8 -*- # ---------------------------------------------------------------------- # Copyright © 2012, libcork authors # All rights reserved. # # Please see the COPYING file in this distribution for license details. # ---------------------------------------------------------------------- install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/valgrind DESTINATION share FILES_MATCHING PATTERN "*.supp") libcork-1.0.0-rc3/share/valgrind/000077500000000000000000000000001367645320400165445ustar00rootroot00000000000000libcork-1.0.0-rc3/share/valgrind/libcork.supp000066400000000000000000000001621367645320400211010ustar00rootroot00000000000000# Valgrind suppressions for libcork { libcork/cork_gc_get Memcheck:Leak fun:calloc fun:cork_gc_get } libcork-1.0.0-rc3/src/000077500000000000000000000000001367645320400144235ustar00rootroot00000000000000libcork-1.0.0-rc3/src/APPNAME.pc.in000066400000000000000000000003701367645320400164350ustar00rootroot00000000000000prefix=@prefix@ exec_prefix=${prefix} libdir=${exec_prefix}/lib includedir=${prefix}/include sharedir=${prefix}/share Name: APPNAME Description: FILL_THIS_IN Version: @VERSION@ URL: FILL_THIS_IN Libs: -L${libdir} -lAPPNAME Cflags: -I${includedir} libcork-1.0.0-rc3/src/CMakeLists.txt000066400000000000000000000073071367645320400171720ustar00rootroot00000000000000# -*- coding: utf-8 -*- # ---------------------------------------------------------------------- # Copyright © 2011, libcork authors # All rights reserved. # # Please see the COPYING file in this distribution for license details. # ---------------------------------------------------------------------- #----------------------------------------------------------------------- # libcork # Update the VERSION_INFO property below according to the following rules (taken # from [1]): # # VERSION_INFO = current:revision:age # # 1. Start with a VERSION of `0:0:0` for each shared library. # 2. Update VERSION_INFO only immediately before a public release of your # software. More frequent updates are unnecessary, and only guarantee that # the current interface number gets larger faster. # 3. If the library source code has changed at all since the last update, then # increment `revision` (`c:r:a` becomes `c:r+1:a`). # 4. If any interfaces have been added, removed, or changed since the last # update, increment `current`, and set `revision` to 0. # 5. If any interfaces have been added since the last public release, then # increment `age`. # 6. If any interfaces have been removed or changed since the last public # release, then set `age` to 0. # # Note that changing `current` and setting `age` to 0 means that you are # releasing a new backwards-incompatible version of the library. This has # implications on packaging, so once an API has stabilized, this should be a # rare occurrence. # # [1] http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html#Updating-version-info add_c_library( libcork OUTPUT_NAME cork PKGCONFIG_NAME libcork VERSION_INFO 17:0:1 SOURCES libcork/cli/commands.c libcork/core/allocator.c libcork/core/error.c libcork/core/gc.c libcork/core/hash.c libcork/core/id.c libcork/core/ip-address.c libcork/core/mempool.c libcork/core/timestamp.c libcork/core/u128.c libcork/core/version.c libcork/ds/array.c libcork/ds/bitset.c libcork/ds/buffer.c libcork/ds/dllist.c libcork/ds/file-stream.c libcork/ds/hash-table.c libcork/ds/managed-buffer.c libcork/ds/ring-buffer.c libcork/ds/slice.c libcork/ds/stream.c libcork/posix/directory-walker.c libcork/posix/env.c libcork/posix/exec.c libcork/posix/files.c libcork/posix/process.c libcork/posix/subprocess.c libcork/pthreads/thread.c LIBRARIES threads ) if (ENABLE_SHARED OR ENABLE_SHARED_EXECUTABLES OR ENABLE_SHARED_TESTS) set_target_properties(libcork-shared PROPERTIES COMPILE_DEFINITIONS CORK_API=CORK_EXPORT ) endif (ENABLE_SHARED OR ENABLE_SHARED_EXECUTABLES OR ENABLE_SHARED_TESTS) if (ENABLE_STATIC OR NOT ENABLE_SHARED_EXECUTABLES OR NOT ENABLE_SHARED_TESTS) set_target_properties(libcork-static PROPERTIES COMPILE_DEFINITIONS CORK_API=CORK_LOCAL ) endif (ENABLE_STATIC OR NOT ENABLE_SHARED_EXECUTABLES OR NOT ENABLE_SHARED_TESTS) #----------------------------------------------------------------------- # Utility commands add_c_executable( cork-hash OUTPUT_NAME cork-hash SOURCES cork-hash/cork-hash.c LOCAL_LIBRARIES libcork ) add_c_executable( cork-initializer SKIP_INSTALL OUTPUT_NAME cork-initializer SOURCES cork-initializer/init1.c cork-initializer/init2.c cork-initializer/main.c LOCAL_LIBRARIES libcork ) add_c_executable( cork-test SKIP_INSTALL OUTPUT_NAME cork-test SOURCES cork-test/cork-test.c LOCAL_LIBRARIES libcork ) libcork-1.0.0-rc3/src/cork-hash/000077500000000000000000000000001367645320400163025ustar00rootroot00000000000000libcork-1.0.0-rc3/src/cork-hash/cork-hash.c000066400000000000000000000056411367645320400203330ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2012, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #include #include #include #include #include enum cork_hash_type { CORK_HASH_BIG, CORK_HASH_FASTEST, CORK_HASH_STABLE }; static enum cork_hash_type type = CORK_HASH_STABLE; static const char *string = NULL; #define OPT_VERSION 1000 static struct option opts[] = { { "big", no_argument, NULL, 'b' }, { "fastest", no_argument, NULL, 'f' }, { "stable", no_argument, NULL, 's' }, { "version", no_argument, NULL, OPT_VERSION }, { NULL, 0, NULL, 0 } }; static void usage(void) { fprintf(stderr, "Usage: cork-hash [] \n" "\n" "Options:\n" " -b, --big\n" " -f, --fastest\n" " -s, --stable\n"); } static void print_version(void) { const char *version = cork_version_string(); const char *revision = cork_revision_string(); printf("cork-hash %s\n", version); if (strcmp(version, revision) != 0) { printf("Revision %s\n", revision); } } static void parse_options(int argc, char **argv) { int ch; while ((ch = getopt_long(argc, argv, "+bfs", opts, NULL)) != -1) { switch (ch) { case 'b': type = CORK_HASH_BIG; break; case 'f': type = CORK_HASH_FASTEST; break; case 's': type = CORK_HASH_STABLE; break; case OPT_VERSION: print_version(); exit(EXIT_SUCCESS); default: usage(); exit(EXIT_FAILURE); } } if (optind != argc-1) { usage(); exit(EXIT_FAILURE); } string = argv[optind]; } int main(int argc, char **argv) { parse_options(argc, argv); if (type == CORK_HASH_BIG) { cork_big_hash result = CORK_BIG_HASH_INIT(); result = cork_big_hash_buffer(result, string, strlen(string)); printf("%016" PRIx64 "%016" PRIx64 "\n", cork_u128_be64(result.u128, 0), cork_u128_be64(result.u128, 1)); } if (type == CORK_HASH_FASTEST) { /* don't include NUL terminator in hash */ cork_hash result = 0; result = cork_hash_buffer(result, string, strlen(string)); printf("0x%08" PRIx32 "\n", result); } if (type == CORK_HASH_STABLE) { /* don't include NUL terminator in hash */ cork_hash result = 0; result = cork_stable_hash_buffer(result, string, strlen(string)); printf("0x%08" PRIx32 "\n", result); } return EXIT_SUCCESS; } libcork-1.0.0-rc3/src/cork-initializer/000077500000000000000000000000001367645320400177025ustar00rootroot00000000000000libcork-1.0.0-rc3/src/cork-initializer/init1.c000066400000000000000000000007341367645320400210760ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2012, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #include #include CORK_INITIALIZER(init) { printf("Initializer 1\n"); } CORK_FINALIZER(done) { printf("Finalizer 1\n"); } libcork-1.0.0-rc3/src/cork-initializer/init2.c000066400000000000000000000006451367645320400211000ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2012, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #include #include CORK_INITIALIZER(init) { printf("Initializer 2\n"); } libcork-1.0.0-rc3/src/cork-initializer/main.c000066400000000000000000000005561367645320400210000ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2012, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ int main(int argc, char **argv) { return 0; } libcork-1.0.0-rc3/src/cork-test/000077500000000000000000000000001367645320400163365ustar00rootroot00000000000000libcork-1.0.0-rc3/src/cork-test/cork-test.c000066400000000000000000000417601367645320400204250ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2012, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #include #include #include #include #include "libcork/cli.h" #include "libcork/core.h" #include "libcork/ds.h" #include "libcork/os.h" #define streq(s1, s2) (strcmp((s1), (s2)) == 0) /* Note: We print out error messages to stdout, not stderr, to ensure that our * test case output is reproducible. (It's not defined how stdout and stderr * are interleaved together.) */ #define ri_check_exit(call) \ do { \ if ((call) != 0) { \ printf("%s\n", cork_error_message()); \ exit(EXIT_FAILURE); \ } \ } while (0) #define rp_check_exit(call) \ do { \ if ((call) == NULL) { \ printf("%s\n", cork_error_message()); \ exit(EXIT_FAILURE); \ } \ } while (0) /*----------------------------------------------------------------------- * Command list */ static bool test_option = false; static const char *file_option = NULL; /* cork-test c1 s1 */ static int c1_s1_options(int argc, char **argv); static void c1_s1_run(int argc, char **argv); static struct cork_command c1_s1 = cork_leaf_command("s1", "Subcommand 1", "[] ", "This is a pretty cool command.\n", c1_s1_options, c1_s1_run); static int c1_s1_options(int argc, char **argv) { if (argc >= 2 && (streq(argv[1], "-t") || streq(argv[1], "--test"))) { test_option = true; return 2; } else { return 1; } } static void c1_s1_run(int argc, char **argv) { printf("You chose command \"c1 s1\". Good for you!\n"); if (test_option) { printf("And you gave the --test option! Look at that.\n"); } if (file_option != NULL) { printf("And you want the file to be %s. Sure thing.\n", file_option); } exit(EXIT_SUCCESS); } /* cork-test c1 s2 */ static void c1_s2_run(int argc, char **argv) { printf("You chose command \"c1 s2\". Fantastico!\n"); if (file_option != NULL) { struct cork_stream_consumer *consumer; printf("And you want the file to be %s. Sure thing.\n", file_option); /* Print the contents of the file to stdout. */ rp_check_exit(consumer = cork_file_consumer_new(stdout)); ri_check_exit(cork_consume_file_from_path (consumer, file_option, O_RDONLY)); cork_stream_consumer_free(consumer); } exit(EXIT_SUCCESS); } static struct cork_command c1_s2 = cork_leaf_command("s2", "Subcommand 2", "[] ", "This is an excellent command.\n", NULL, c1_s2_run); /* cork-test c1 */ static int c1_options(int argc, char **argv); static struct cork_command *c1_subcommands[] = { &c1_s1, &c1_s2, NULL }; static struct cork_command c1 = cork_command_set("c1", "Command 1 (now with subcommands)", c1_options, c1_subcommands); static int c1_options(int argc, char **argv) { if (argc >= 3) { if (streq(argv[1], "-f") || streq(argv[1], "--file")) { file_option = argv[2]; return 3; } } if (argc >= 2) { if (memcmp(argv[1], "--file=", 7) == 0) { file_option = argv[1] + 7; return 2; } } return 1; } /* cork-test c2 */ static void c2_run(int argc, char **argv) { printf("You chose command \"c2\". That's pretty good.\n"); exit(EXIT_SUCCESS); } static struct cork_command c2 = cork_leaf_command("c2", "Command 2", "[] ", "This command is pretty decent.\n", NULL, c2_run); /*----------------------------------------------------------------------- * Forking subprocesses */ static const char *sub_cwd = NULL; static const char *sub_stdin = NULL; static int sub_options(int argc, char **argv); static void sub_run(int argc, char **argv); static struct cork_command sub = cork_leaf_command("sub", "Run a subcommand", " []", "Runs a subcommand.\n", sub_options, sub_run); static int sub_options(int argc, char **argv) { int processed = 1; for (argc--, argv++; argc >= 1; argc--, argv++, processed++) { if ((streq(argv[0], "-d") || streq(argv[0], "--cwd"))) { if (argc >= 2) { sub_cwd = argv[1]; argc--, argv++, processed++; } else { cork_command_show_help(&sub, "Missing directory for --cwd"); exit(EXIT_FAILURE); } } else if ((streq(argv[0], "-i") || streq(argv[0], "--stdin"))) { if (argc >= 2) { sub_stdin = argv[1]; argc--, argv++, processed++; } else { cork_command_show_help(&sub, "Missing content for --stdin"); exit(EXIT_FAILURE); } } else { return processed; } } return processed; } static void sub_run(int argc, char **argv) { struct cork_env *env; struct cork_exec *exec; struct cork_subprocess_group *group; struct cork_subprocess *sp; struct cork_stream_consumer *stdin_consumer; struct cork_stream_consumer *stdout_consumer; if (argc == 0) { cork_command_show_help(&sub, "Missing command"); exit(EXIT_FAILURE); } /* Note that we explicitly copy the child's stdout to our stdout using a * cork_stream_consumer, instead of letting the child process inherit our * stdout. This gives us more control over how our output messages * interleave with the child process's output messages, which is needed if * we want the expected test output to be deterministic. */ rp_check_exit(env = cork_env_clone_current()); rp_check_exit(exec = cork_exec_new_with_param_array(argv[0], argv)); cork_exec_set_env(exec, env); if (sub_cwd != NULL) { cork_exec_set_cwd(exec, sub_cwd); } printf("%s\n", cork_exec_description(exec)); rp_check_exit(stdout_consumer = cork_file_consumer_new(stdout)); rp_check_exit(group = cork_subprocess_group_new()); rp_check_exit(sp = cork_subprocess_new_exec( exec, stdout_consumer, stdout_consumer, NULL)); cork_subprocess_group_add(group, sp); ri_check_exit(cork_subprocess_group_start(group)); stdin_consumer = cork_subprocess_stdin(sp); if (sub_stdin != NULL) { size_t stdin_length = strlen(sub_stdin); cork_stream_consumer_data (stdin_consumer, sub_stdin, stdin_length, true); cork_stream_consumer_data(stdin_consumer, "\n", 1, false); } cork_stream_consumer_eof(stdin_consumer); ri_check_exit(cork_subprocess_group_wait(group)); cork_subprocess_group_free(group); cork_stream_consumer_free(stdout_consumer); } /*----------------------------------------------------------------------- * pwd */ /* cork-test pwd */ static void pwd_run(int argc, char **argv); static struct cork_command pwd = cork_leaf_command("pwd", "Print working directory", "", "Prints out the current working directory.\n", NULL, pwd_run); static void pwd_run(int argc, char **argv) { struct cork_path *path; rp_check_exit(path = cork_path_cwd()); printf("%s\n", cork_path_get(path)); cork_path_free(path); exit(EXIT_SUCCESS); } /*----------------------------------------------------------------------- * mkdir */ static unsigned int mkdir_flags = CORK_FILE_PERMISSIVE; /* cork-test mkdir */ static int mkdir_options(int argc, char **argv); static void mkdir_run(int argc, char **argv); static struct cork_command mkdir_cmd = cork_leaf_command("mkdir", "Create a directory", "[] ", "Create a new directory.\n", mkdir_options, mkdir_run); static int mkdir_options(int argc, char **argv) { int count = 1; while (count < argc) { if (streq(argv[count], "--recursive")) { mkdir_flags |= CORK_FILE_RECURSIVE; count++; } else if (streq(argv[count], "--require")) { mkdir_flags &= ~CORK_FILE_PERMISSIVE; count++; } else { return count; } } return count; } static void mkdir_run(int argc, char **argv) { struct cork_file *file; if (argc < 1) { cork_command_show_help(&mkdir_cmd, "Missing file"); exit(EXIT_FAILURE); } else if (argc > 1) { cork_command_show_help(&mkdir_cmd, "Too many directories"); exit(EXIT_FAILURE); } file = cork_file_new(argv[0]); ri_check_exit(cork_file_mkdir(file, 0755, mkdir_flags)); cork_file_free(file); exit(EXIT_SUCCESS); } /*----------------------------------------------------------------------- * rm */ static unsigned int rm_flags = CORK_FILE_PERMISSIVE; /* cork-test rm */ static int rm_options(int argc, char **argv); static void rm_run(int argc, char **argv); static struct cork_command rm_cmd = cork_leaf_command("rm", "Remove a file or directory", "[] ", "Remove a file or directory.\n", rm_options, rm_run); static int rm_options(int argc, char **argv) { int count = 1; while (count < argc) { if (streq(argv[count], "--recursive")) { rm_flags |= CORK_FILE_RECURSIVE; count++; } else if (streq(argv[count], "--require")) { rm_flags &= ~CORK_FILE_PERMISSIVE; count++; } else { return count; } } return count; } static void rm_run(int argc, char **argv) { struct cork_file *file; if (argc < 1) { cork_command_show_help(&rm_cmd, "Missing file"); exit(EXIT_FAILURE); } else if (argc > 1) { cork_command_show_help(&rm_cmd, "Too many directories"); exit(EXIT_FAILURE); } file = cork_file_new(argv[0]); ri_check_exit(cork_file_remove(file, rm_flags)); cork_file_free(file); exit(EXIT_SUCCESS); } /*----------------------------------------------------------------------- * find */ static bool find_all = false; /* cork-test find */ static int find_options(int argc, char **argv); static void find_run(int argc, char **argv); static struct cork_command find = cork_leaf_command("find", "Search for a file in a list of directories", " ", "Search for a file in a list of directories.\n", find_options, find_run); static int find_options(int argc, char **argv) { if (argc >= 2 && streq(argv[1], "--all")) { find_all = true; return 2; } return 1; } static void find_run(int argc, char **argv) { struct cork_path_list *list; if (argc < 1) { cork_command_show_help(&find, "Missing file"); exit(EXIT_FAILURE); } else if (argc < 2) { cork_command_show_help(&find, "Missing path"); exit(EXIT_FAILURE); } else if (argc < 2) { cork_command_show_help(&find, "Too many parameters"); exit(EXIT_FAILURE); } list = cork_path_list_new(argv[1]); if (find_all) { struct cork_file_list *file_list; size_t i; size_t count; rp_check_exit(file_list = cork_path_list_find_files(list, argv[0])); count = cork_file_list_size(file_list); for (i = 0; i < count; i++) { struct cork_file *file = cork_file_list_get(file_list, i); printf("%s\n", cork_path_get(cork_file_path(file))); } cork_file_list_free(file_list); } else { struct cork_file *file; rp_check_exit(file = cork_path_list_find_file(list, argv[0])); printf("%s\n", cork_path_get(cork_file_path(file))); cork_file_free(file); } cork_path_list_free(list); exit(EXIT_SUCCESS); } /*----------------------------------------------------------------------- * paths */ /* cork-test paths */ static void paths_run(int argc, char **argv); static struct cork_command paths = cork_leaf_command("paths", "Print out standard paths for the current user", "", "Print out standard paths for the current user.\n", NULL, paths_run); static void print_path(const char *prefix, struct cork_path *path) { rp_check_exit(path); printf("%s %s\n", prefix, cork_path_get(path)); cork_path_free(path); } static void print_path_list(const char *prefix, struct cork_path_list *list) { rp_check_exit(list); printf("%s %s\n", prefix, cork_path_list_to_string(list)); cork_path_list_free(list); } static void paths_run(int argc, char **argv) { print_path ("Home: ", cork_path_home()); print_path_list("Config: ", cork_path_config_paths()); print_path_list("Data: ", cork_path_data_paths()); print_path ("Cache: ", cork_path_user_cache_path()); print_path ("Runtime:", cork_path_user_runtime_path()); exit(EXIT_SUCCESS); } /*----------------------------------------------------------------------- * Directory walker */ static bool only_files = false; static bool shallow = false; static const char *dir_path = NULL; static int dir_options(int argc, char **argv) { if (argc == 3) { if (streq(argv[1], "--shallow")) { shallow = true; dir_path = argv[2]; return 3; } else if (streq(argv[1], "--only-files")) { only_files = true; dir_path = argv[2]; return 3; } } else if (argc == 2) { dir_path = argv[1]; return 2; } printf("Invalid usage.\n"); exit(EXIT_FAILURE); } static size_t indent = 0; static void print_indent(void) { size_t i; for (i = 0; i < indent; i++) { printf(" "); } } static int enter_directory(struct cork_dir_walker *walker, const char *full_path, const char *rel_path, const char *base_name) { print_indent(); if (shallow) { printf("Skipping %s\n", rel_path); return CORK_SKIP_DIRECTORY; } else if (only_files) { return 0; } else { printf("Entering %s (%s)\n", base_name, rel_path); indent++; return 0; } } static int print_file(struct cork_dir_walker *walker, const char *full_path, const char *rel_path, const char *base_name) { if (only_files) { printf("%s\n", rel_path); } else { print_indent(); printf("%s (%s) (%s)\n", base_name, rel_path, full_path); } return 0; } static int leave_directory(struct cork_dir_walker *walker, const char *full_path, const char *rel_path, const char *base_name) { if (!only_files) { indent--; print_indent(); printf("Leaving %s\n", rel_path); } return 0; } static struct cork_dir_walker walker = { enter_directory, print_file, leave_directory }; static void dir_run(int argc, char **argv) { ri_check_exit(cork_walk_directory(dir_path, &walker)); exit(EXIT_SUCCESS); } static struct cork_command dir = cork_leaf_command("dir", "Print the contents of a directory", "[--shallow] ", "Prints the contents of a directory.\n", dir_options, dir_run); /*----------------------------------------------------------------------- * Cleanup functions */ #define define_cleanup_function(id) \ static void \ cleanup_##id(void) \ { \ printf("Cleanup function " #id "\n"); \ } define_cleanup_function(0); define_cleanup_function(1); define_cleanup_function(2); define_cleanup_function(3); define_cleanup_function(4); define_cleanup_function(5); static void cleanup_run(int argc, char **argv) { cork_cleanup_at_exit(10, cleanup_1); cork_cleanup_at_exit( 0, cleanup_0); cork_cleanup_at_exit(50, cleanup_5); cork_cleanup_at_exit(20, cleanup_2); cork_cleanup_at_exit(40, cleanup_4); cork_cleanup_at_exit(30, cleanup_3); } static struct cork_command cleanup = cork_leaf_command("cleanup", "Test process cleanup functions", "", "Test process cleanup functions.\n", NULL, cleanup_run); /*----------------------------------------------------------------------- * Root command */ /* [root] cork-test */ static struct cork_command *root_subcommands[] = { &c1, &c2, &pwd, &mkdir_cmd, &rm_cmd, &find, &paths, &dir, &sub, &cleanup, NULL }; static struct cork_command root_command = cork_command_set("cork-test", NULL, NULL, root_subcommands); /*----------------------------------------------------------------------- * Entry point */ int main(int argc, char **argv) { return cork_command_main(&root_command, argc, argv); } libcork-1.0.0-rc3/src/libcork.pc.in000066400000000000000000000006231367645320400170020ustar00rootroot00000000000000PACKAGE_TARNAME=@PACKAGE_TARNAME@ prefix=@prefix@ exec_prefix=@exec_prefix@ datarootdir=@datarootdir@ docdir=@docdir@ includedir=@includedir@ libdir=@libdir@ sphinxdir=${docdir}/html Name: libcork Description: A simple, easily embeddable cross-platform C library Version: @VERSION@ URL: https://libcork.io/ Libs: -L${libdir} -lcork Libs.private: @PTHREAD_LIBS@ Cflags: -I${includedir} @PTHREAD_CFLAGS@ libcork-1.0.0-rc3/src/libcork/000077500000000000000000000000001367645320400160505ustar00rootroot00000000000000libcork-1.0.0-rc3/src/libcork/cli/000077500000000000000000000000001367645320400166175ustar00rootroot00000000000000libcork-1.0.0-rc3/src/libcork/cli/commands.c000066400000000000000000000135111367645320400205650ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2012, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #include #include #include #include "libcork/cli.h" #include "libcork/core.h" #include "libcork/ds.h" #define streq(a,b) (strcmp((a), (b)) == 0) static struct cork_buffer breadcrumbs_buf = CORK_BUFFER_INIT(); static void cork_command_add_breadcrumb(struct cork_command *command) { cork_buffer_append_printf(&breadcrumbs_buf, " %s", command->name); } #define cork_command_breadcrumbs() ((char *) breadcrumbs_buf.buf) static void cork_command_run(struct cork_command *command, int argc, char **argv); static struct cork_command * cork_command_set_get_subcommand(struct cork_command *command, const char *command_name) { struct cork_command **curr; for (curr = command->set; *curr != NULL; curr++) { if (streq(command_name, (*curr)->name)) { return *curr; } } return NULL; } static void cork_command_set_show_help(struct cork_command *command) { size_t max_length = 0; struct cork_command **curr; /* Calculate the length of the longest command name. */ for (curr = command->set; *curr != NULL; curr++) { size_t len = strlen((*curr)->name); if (len > max_length) { max_length = len; } } /* Then print out the available commands. */ printf("Usage:%s []\n" "\nAvailable commands:\n", cork_command_breadcrumbs()); for (curr = command->set; *curr != NULL; curr++) { printf(" %*s", (int) -max_length, (*curr)->name); if ((*curr)->short_desc != NULL) { printf(" %s\n", (*curr)->short_desc); } else { printf("\n"); } } } static void cork_command_leaf_show_help(struct cork_command *command) { printf("Usage:%s", cork_command_breadcrumbs()); if (command->usage_suffix != NULL) { printf(" %s", command->usage_suffix); } if (command->full_help != NULL) { printf("\n\n%s", command->full_help); } else { printf("\n"); } } void cork_command_show_help(struct cork_command *command, const char *message) { if (message != NULL) { printf("%s\n", message); } if (command->type == CORK_COMMAND_SET) { cork_command_set_show_help(command); } else if (command->type == CORK_LEAF_COMMAND) { cork_command_leaf_show_help(command); } } static void cork_command_set_run_help(struct cork_command *command, int argc, char **argv) { /* When we see the help command when processing a command set, we use any * remaining arguments to identifity which subcommand the user wants help * with. */ /* Skip over the name of the command set */ argc--; argv++; while (argc > 0 && command->type == CORK_COMMAND_SET) { struct cork_command *subcommand = cork_command_set_get_subcommand(command, argv[0]); if (subcommand == NULL) { printf("Unknown command \"%s\".\n" "Usage:%s []\n", argv[0], cork_command_breadcrumbs()); exit(EXIT_FAILURE); } cork_command_add_breadcrumb(subcommand); command = subcommand; argc--; argv++; } cork_command_show_help(command, NULL); } static void cork_command_set_run(struct cork_command *command, int argc, char **argv) { const char *command_name; struct cork_command *subcommand; if (argc == 0) { printf("No command given.\n"); cork_command_set_show_help(command); exit(EXIT_FAILURE); } command_name = argv[0]; /* The "help" command is special. */ if (streq(command_name, "help")) { cork_command_set_run_help(command, argc, argv); return; } /* Otherwise look for a real subcommand with this name. */ subcommand = cork_command_set_get_subcommand(command, command_name); if (subcommand == NULL) { printf("Unknown command \"%s\".\n" "Usage:%s []\n", command_name, cork_command_breadcrumbs()); exit(EXIT_FAILURE); } else { cork_command_run(subcommand, argc, argv); } } static void cork_command_leaf_run(struct cork_command *command, int argc, char **argv) { command->run(argc, argv); } static void cork_command_cleanup(void) { cork_buffer_done(&breadcrumbs_buf); } static void cork_command_run(struct cork_command *command, int argc, char **argv) { cork_command_add_breadcrumb(command); /* If the gives the --help option at this point, describe the current * command. */ if (argc >= 2 && (streq(argv[1], "--help") || streq(argv[1], "-h"))) { cork_command_show_help(command, NULL); return; } /* Otherwise let the command parse any options that occur here. */ if (command->parse_options != NULL) { int option_count = command->parse_options(argc, argv); argc -= option_count; argv += option_count; } else { argc--; argv++; } switch (command->type) { case CORK_COMMAND_SET: cork_command_set_run(command, argc, argv); return; case CORK_LEAF_COMMAND: cork_command_leaf_run(command, argc, argv); return; default: cork_unreachable(); } } int cork_command_main(struct cork_command *root, int argc, char **argv) { /* Clean up after ourselves when the command finishes. */ atexit(cork_command_cleanup); /* Run the root command. */ cork_command_run(root, argc, argv); return EXIT_SUCCESS; } libcork-1.0.0-rc3/src/libcork/core/000077500000000000000000000000001367645320400170005ustar00rootroot00000000000000libcork-1.0.0-rc3/src/libcork/core/allocator.c000066400000000000000000000306111367645320400211250ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2011, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #include #include #include #include #include "libcork/core/allocator.h" #include "libcork/core/attributes.h" #include "libcork/core/error.h" #include "libcork/core/types.h" #include "libcork/os/process.h" /*----------------------------------------------------------------------- * Allocator interface */ struct cork_alloc_priv { struct cork_alloc public; struct cork_alloc_priv *next; }; static void * cork_alloc__default_calloc(const struct cork_alloc *alloc, size_t count, size_t size) { void *result = cork_alloc_xcalloc(alloc, count, size); if (CORK_UNLIKELY(result == NULL)) { abort(); } return result; } static void * cork_alloc__default_malloc(const struct cork_alloc *alloc, size_t size) { void *result = cork_alloc_xmalloc(alloc, size); if (CORK_UNLIKELY(result == NULL)) { abort(); } return result; } static void * cork_alloc__default_realloc(const struct cork_alloc *alloc, void *ptr, size_t old_size, size_t new_size) { void *result = cork_alloc_xrealloc(alloc, ptr, old_size, new_size); if (CORK_UNLIKELY(result == NULL)) { abort(); } return result; } static void * cork_alloc__default_xcalloc(const struct cork_alloc *alloc, size_t count, size_t size) { void *result; assert(count < (SIZE_MAX / size)); result = cork_alloc_xmalloc(alloc, count * size); if (result != NULL) { memset(result, 0, count * size); } return result; } static void * cork_alloc__default_xmalloc(const struct cork_alloc *alloc, size_t size) { cork_abort("%s isn't defined", "cork_alloc:xmalloc"); } static void * cork_alloc__default_xrealloc(const struct cork_alloc *alloc, void *ptr, size_t old_size, size_t new_size) { void *result = cork_alloc_xmalloc(alloc, new_size); if (CORK_LIKELY(result != NULL) && ptr != NULL) { size_t min_size = (new_size < old_size)? new_size: old_size; memcpy(result, ptr, min_size); cork_alloc_free(alloc, ptr, old_size); } return result; } static void cork_alloc__default_free(const struct cork_alloc *alloc, void *ptr, size_t size) { cork_abort("%s isn't defined", "cork_alloc:free"); } static bool cleanup_registered = false; static struct cork_alloc_priv *all_allocs = NULL; static void cork_alloc_free_alloc(struct cork_alloc_priv *alloc) { cork_free_user_data(&alloc->public); cork_alloc_delete(alloc->public.parent, struct cork_alloc_priv, alloc); } static void cork_alloc_free_all(void) { struct cork_alloc_priv *curr; struct cork_alloc_priv *next; for (curr = all_allocs; curr != NULL; curr = next) { next = curr->next; cork_alloc_free_alloc(curr); } } static void cork_alloc_register_cleanup(void) { if (CORK_UNLIKELY(!cleanup_registered)) { /* We don't use cork_cleanup because that requires the allocators to * have already been set up! (atexit calls its functions in reverse * order, and this one will be registered before cork_cleanup's, which * makes it safe for cork_cleanup functions to still use the allocator, * since the allocator atexit function will be called last.) */ atexit(cork_alloc_free_all); cleanup_registered = true; } } struct cork_alloc * cork_alloc_new_alloc(const struct cork_alloc *parent) { struct cork_alloc_priv *alloc = cork_alloc_new(parent, struct cork_alloc_priv); alloc->public.parent = parent; alloc->public.user_data = NULL; alloc->public.free_user_data = NULL; alloc->public.calloc = cork_alloc__default_calloc; alloc->public.malloc = cork_alloc__default_malloc; alloc->public.realloc = cork_alloc__default_realloc; alloc->public.xcalloc = cork_alloc__default_xcalloc; alloc->public.xmalloc = cork_alloc__default_xmalloc; alloc->public.xrealloc = cork_alloc__default_xrealloc; alloc->public.free = cork_alloc__default_free; cork_alloc_register_cleanup(); alloc->next = all_allocs; all_allocs = alloc; return &alloc->public; } void cork_alloc_set_user_data(struct cork_alloc *alloc, void *user_data, cork_free_f free_user_data) { cork_free_user_data(alloc); alloc->user_data = user_data; alloc->free_user_data = free_user_data; } void cork_alloc_set_calloc(struct cork_alloc *alloc, cork_alloc_calloc_f calloc) { alloc->calloc = calloc; } void cork_alloc_set_malloc(struct cork_alloc *alloc, cork_alloc_malloc_f malloc) { alloc->malloc = malloc; } void cork_alloc_set_realloc(struct cork_alloc *alloc, cork_alloc_realloc_f realloc) { alloc->realloc = realloc; } void cork_alloc_set_xcalloc(struct cork_alloc *alloc, cork_alloc_calloc_f xcalloc) { alloc->xcalloc = xcalloc; } void cork_alloc_set_xmalloc(struct cork_alloc *alloc, cork_alloc_malloc_f xmalloc) { alloc->xmalloc = xmalloc; } void cork_alloc_set_xrealloc(struct cork_alloc *alloc, cork_alloc_realloc_f xrealloc) { alloc->xrealloc = xrealloc; } void cork_alloc_set_free(struct cork_alloc *alloc, cork_alloc_free_f free) { alloc->free = free; } /*----------------------------------------------------------------------- * Allocating strings */ static inline const char * strndup_internal(const struct cork_alloc *alloc, const char *str, size_t len) { char *dest; size_t allocated_size = len + sizeof(size_t) + 1; size_t *new_str = cork_alloc_malloc(alloc, allocated_size); *new_str = len; dest = (char *) (void *) (new_str + 1); memcpy(dest, str, len); dest[len] = '\0'; return dest; } const char * cork_alloc_strdup(const struct cork_alloc *alloc, const char *str) { return strndup_internal(alloc, str, strlen(str)); } const char * cork_alloc_strndup(const struct cork_alloc *alloc, const char *str, size_t size) { return strndup_internal(alloc, str, size); } static inline const char * xstrndup_internal(const struct cork_alloc *alloc, const char *str, size_t len) { size_t allocated_size = len + sizeof(size_t) + 1; size_t *new_str = cork_alloc_xmalloc(alloc, allocated_size); if (CORK_UNLIKELY(new_str == NULL)) { return NULL; } else { char *dest; *new_str = len; dest = (char *) (void *) (new_str + 1); memcpy(dest, str, len); dest[len] = '\0'; return dest; } } const char * cork_alloc_xstrdup(const struct cork_alloc *alloc, const char *str) { return xstrndup_internal(alloc, str, strlen(str)); } const char * cork_alloc_xstrndup(const struct cork_alloc *alloc, const char *str, size_t size) { return xstrndup_internal(alloc, str, size); } void cork_alloc_strfree(const struct cork_alloc *alloc, const char *str) { size_t* base = ((size_t*) str) - 1; size_t len = *base; size_t allocated_size = len + sizeof(size_t) + 1; cork_alloc_free(alloc, base, allocated_size); } /*----------------------------------------------------------------------- * stdlib allocator */ static void * cork_stdlib_alloc__calloc(const struct cork_alloc *alloc, size_t count, size_t size) { void *result = calloc(count, size); if (CORK_UNLIKELY(result == NULL)) { abort(); } return result; } static void * cork_stdlib_alloc__malloc(const struct cork_alloc *alloc, size_t size) { void *result = malloc(size); if (CORK_UNLIKELY(result == NULL)) { abort(); } return result; } static void * cork_stdlib_alloc__realloc(const struct cork_alloc *alloc, void *ptr, size_t old_size, size_t new_size) { /* Technically we don't really need to free `ptr` if the reallocation fails, * since we'll abort the process immediately after. But my sense of * cleanliness makes me do it anyway. */ #if CORK_HAVE_REALLOCF void *result = reallocf(ptr, new_size); if (result == NULL) { abort(); } return result; #else void *result = realloc(ptr, new_size); if (result == NULL) { free(ptr); abort(); } return result; #endif } static void * cork_stdlib_alloc__xcalloc(const struct cork_alloc *alloc, size_t count, size_t size) { return calloc(count, size); } static void * cork_stdlib_alloc__xmalloc(const struct cork_alloc *alloc, size_t size) { return malloc(size); } static void * cork_stdlib_alloc__xrealloc(const struct cork_alloc *alloc, void *ptr, size_t old_size, size_t new_size) { return realloc(ptr, new_size); } static void cork_stdlib_alloc__free(const struct cork_alloc *alloc, void *ptr, size_t size) { free(ptr); } static const struct cork_alloc default_allocator = { NULL, NULL, NULL, cork_stdlib_alloc__calloc, cork_stdlib_alloc__malloc, cork_stdlib_alloc__realloc, cork_stdlib_alloc__xcalloc, cork_stdlib_alloc__xmalloc, cork_stdlib_alloc__xrealloc, cork_stdlib_alloc__free }; /*----------------------------------------------------------------------- * Customizing libcork's allocator */ const struct cork_alloc *cork_allocator = &default_allocator; void cork_set_allocator(const struct cork_alloc *alloc) { cork_allocator = alloc; } /*----------------------------------------------------------------------- * Debugging allocator */ static void * cork_debug_alloc__xmalloc(const struct cork_alloc *alloc, size_t size) { size_t real_size = size + sizeof(size_t); size_t *base = cork_alloc_xmalloc(alloc->parent, real_size); *base = size; return base + 1; } static void cork_debug_alloc__free(const struct cork_alloc *alloc, void *ptr, size_t expected_size) { size_t *base = ((size_t *) ptr) - 1; size_t actual_size = *base; size_t real_size = actual_size + sizeof(size_t); if (CORK_UNLIKELY(actual_size != expected_size)) { cork_abort ("Incorrect size when freeing pointer (got %zu, expected %zu)", expected_size, actual_size); } cork_alloc_free(alloc->parent, base, real_size); } struct cork_alloc * cork_debug_alloc_new(const struct cork_alloc *parent) { struct cork_alloc *debug = cork_alloc_new_alloc(parent); cork_alloc_set_xmalloc(debug, cork_debug_alloc__xmalloc); cork_alloc_set_free(debug, cork_debug_alloc__free); return debug; } /*----------------------------------------------------------------------- * Inline declarations */ void * cork_alloc_calloc(const struct cork_alloc *alloc, size_t count, size_t size); void * cork_alloc_malloc(const struct cork_alloc *alloc, size_t size); void * cork_alloc_realloc(const struct cork_alloc *alloc, void *ptr, size_t old_size, size_t new_size); void * cork_alloc_xcalloc(const struct cork_alloc *alloc, size_t count, size_t size); void * cork_alloc_xmalloc(const struct cork_alloc *alloc, size_t size); void * cork_alloc_xrealloc(const struct cork_alloc *alloc, void *ptr, size_t old_size, size_t new_size); void * cork_alloc_xreallocf(const struct cork_alloc *alloc, void *ptr, size_t old_size, size_t new_size); void cork_alloc_free(const struct cork_alloc *alloc, void *ptr, size_t size); void cork_alloc_cfree(const struct cork_alloc *alloc, void *ptr, size_t count, size_t size); void * cork_calloc(size_t count, size_t size); void * cork_malloc(size_t size); void * cork_realloc(void *ptr, size_t old_size, size_t new_size); void * cork_xcalloc(size_t count, size_t size); void * cork_xmalloc(size_t size); void * cork_xrealloc(void *ptr, size_t old_size, size_t new_size); void * cork_xreallocf(void *ptr, size_t old_size, size_t new_size); void cork_free(void *ptr, size_t size); void cork_cfree(void *ptr, size_t count, size_t size); const char * cork_strdup(const char *str); const char * cork_strndup(const char *str, size_t size); const char * cork_xstrdup(const char *str); const char * cork_xstrndup(const char *str, size_t size); void cork_strfree(const char *str); size_t cork_strlen(const char *str); libcork-1.0.0-rc3/src/libcork/core/error.c000066400000000000000000000135241367645320400203020ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2011, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #include #include #include #include #include "libcork/config.h" #include "libcork/core/allocator.h" #include "libcork/core/error.h" #include "libcork/ds/buffer.h" #include "libcork/os/process.h" #include "libcork/threads/basics.h" /*----------------------------------------------------------------------- * Life cycle */ struct cork_error { cork_error code; struct cork_buffer *message; struct cork_buffer *other; struct cork_buffer buf1; struct cork_buffer buf2; struct cork_error *next; }; static struct cork_error * cork_error_new(void) { struct cork_error *error = cork_new(struct cork_error); error->code = CORK_ERROR_NONE; cork_buffer_init(&error->buf1); cork_buffer_init(&error->buf2); error->message = &error->buf1; error->other = &error->buf2; return error; } static void cork_error_free(struct cork_error *error) { cork_buffer_done(&error->buf1); cork_buffer_done(&error->buf2); cork_delete(struct cork_error, error); } static struct cork_error * volatile errors; cork_once_barrier(cork_error_list); static void cork_error_list_done(void) { struct cork_error *curr; struct cork_error *next; for (curr = errors; curr != NULL; curr = next) { next = curr->next; cork_error_free(curr); } } static void cork_error_list_init(void) { cork_cleanup_at_exit(0, cork_error_list_done); } cork_tls(struct cork_error *, cork_error_); static struct cork_error * cork_error_get(void) { struct cork_error **error_ptr = cork_error__get(); if (CORK_UNLIKELY(*error_ptr == NULL)) { struct cork_error *old_head; struct cork_error *error = cork_error_new(); cork_once(cork_error_list, cork_error_list_init()); do { old_head = errors; error->next = old_head; } while (cork_ptr_cas(&errors, old_head, error) != old_head); *error_ptr = error; return error; } else { return *error_ptr; } } /*----------------------------------------------------------------------- * Public error API */ bool cork_error_occurred(void) { struct cork_error *error = cork_error_get(); return error->code != CORK_ERROR_NONE; } cork_error cork_error_code(void) { struct cork_error *error = cork_error_get(); return error->code; } const char * cork_error_message(void) { struct cork_error *error = cork_error_get(); return error->message->buf; } void cork_error_clear(void) { struct cork_error *error = cork_error_get(); error->code = CORK_ERROR_NONE; cork_buffer_clear(error->message); } void cork_error_set_printf(cork_error code, const char *format, ...) { va_list args; struct cork_error *error = cork_error_get(); error->code = code; va_start(args, format); cork_buffer_vprintf(error->message, format, args); va_end(args); } void cork_error_set_string(cork_error code, const char *str) { struct cork_error *error = cork_error_get(); error->code = code; cork_buffer_set_string(error->message, str); } void cork_error_set_vprintf(cork_error code, const char *format, va_list args) { struct cork_error *error = cork_error_get(); error->code = code; cork_buffer_vprintf(error->message, format, args); } void cork_error_prefix_printf(const char *format, ...) { va_list args; struct cork_error *error = cork_error_get(); struct cork_buffer *temp; va_start(args, format); cork_buffer_vprintf(error->other, format, args); va_end(args); cork_buffer_append_copy(error->other, error->message); temp = error->other; error->other = error->message; error->message = temp; } void cork_error_prefix_string(const char *str) { struct cork_error *error = cork_error_get(); struct cork_buffer *temp; cork_buffer_set_string(error->other, str); cork_buffer_append_copy(error->other, error->message); temp = error->other; error->other = error->message; error->message = temp; } void cork_error_prefix_vprintf(const char *format, va_list args) { struct cork_error *error = cork_error_get(); struct cork_buffer *temp; cork_buffer_vprintf(error->other, format, args); cork_buffer_append_copy(error->other, error->message); temp = error->other; error->other = error->message; error->message = temp; } /*----------------------------------------------------------------------- * Deprecated */ void cork_error_set(uint32_t error_class, unsigned int error_code, const char *format, ...) { /* Create a fallback error code that's most likely not very useful. */ va_list args; va_start(args, format); cork_error_set_vprintf(error_class + error_code, format, args); va_end(args); } void cork_error_prefix(const char *format, ...) { va_list args; va_start(args, format); cork_error_prefix_vprintf(format, args); va_end(args); } /*----------------------------------------------------------------------- * Built-in errors */ void cork_system_error_set_explicit(int err) { cork_error_set_string(err, strerror(err)); } void cork_system_error_set(void) { cork_error_set_string(errno, strerror(errno)); } void cork_unknown_error_set_(const char *location) { cork_error_set_printf(CORK_UNKNOWN_ERROR, "Unknown error in %s", location); } /*----------------------------------------------------------------------- * Inline declarations */ void * cork_abort_if_null_(void *ptr, const char *msg, const char *func, const char *file, unsigned int line); libcork-1.0.0-rc3/src/libcork/core/gc.c000066400000000000000000000262011367645320400175360ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2011, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #include #include "libcork/config/config.h" #include "libcork/core/allocator.h" #include "libcork/core/gc.h" #include "libcork/core/types.h" #include "libcork/ds/dllist.h" #include "libcork/threads/basics.h" #if !defined(CORK_DEBUG_GC) #define CORK_DEBUG_GC 0 #endif #if CORK_DEBUG_GC #include #define DEBUG(...) fprintf(stderr, __VA_ARGS__) #else #define DEBUG(...) /* no debug messages */ #endif /*----------------------------------------------------------------------- * GC context life cycle */ #define ROOTS_SIZE 1024 /* An internal structure allocated with every garbage-collected object. */ struct cork_gc_header; /* A garbage collector context. */ struct cork_gc { /* The number of used entries in roots. */ size_t root_count; /* The possible roots of garbage cycles */ struct cork_gc_header *roots[ROOTS_SIZE]; }; cork_tls(struct cork_gc, cork_gc); static void cork_gc_collect_cycles(struct cork_gc *gc); /*----------------------------------------------------------------------- * Garbage collection functions */ struct cork_gc_header { /* The current reference count for this object, along with its color * during the mark/sweep process. */ volatile int ref_count_color; /* The allocated size of this garbage-collected object (including * the header). */ size_t allocated_size; /* The garbage collection interface for this object. */ struct cork_gc_obj_iface *iface; }; /* * Structure of ref_count_color: * * +-----+---+---+---+---+---+ * | ... | 4 | 3 | 2 | 1 | 0 | * +-----+---+---+---+---+---+ * ref_count | color * | * buffered --/ */ #define cork_gc_ref_count_color(count, buffered, color) \ (((count) << 3) | ((buffered) << 2) | (color)) #define cork_gc_get_ref_count(hdr) \ ((hdr)->ref_count_color >> 3) #define cork_gc_inc_ref_count(hdr) \ do { \ (hdr)->ref_count_color += (1 << 3); \ } while (0) #define cork_gc_dec_ref_count(hdr) \ do { \ (hdr)->ref_count_color -= (1 << 3); \ } while (0) #define cork_gc_get_color(hdr) \ ((hdr)->ref_count_color & 0x3) #define cork_gc_set_color(hdr, color) \ do { \ (hdr)->ref_count_color = \ ((hdr)->ref_count_color & ~0x3) | (color & 0x3); \ } while (0) #define cork_gc_get_buffered(hdr) \ (((hdr)->ref_count_color & 0x4) != 0) #define cork_gc_set_buffered(hdr, buffered) \ do { \ (hdr)->ref_count_color = \ ((hdr)->ref_count_color & ~0x4) | (((buffered) & 1) << 2); \ } while (0) #define cork_gc_free(hdr) \ do { \ if ((hdr)->iface->free != NULL) { \ (hdr)->iface->free(cork_gc_get_object((hdr))); \ } \ cork_free((hdr), (hdr)->allocated_size); \ } while (0) #define cork_gc_recurse(gc, hdr, recurser) \ do { \ if ((hdr)->iface->recurse != NULL) { \ (hdr)->iface->recurse \ ((gc), cork_gc_get_object((hdr)), (recurser), NULL); \ } \ } while (0) enum cork_gc_color { /* In use or free */ GC_BLACK = 0, /* Possible member of garbage cycle */ GC_GRAY = 1, /* Member of garbage cycle */ GC_WHITE = 2, /* Possible root of garbage cycle */ GC_PURPLE = 3 }; #define cork_gc_get_header(obj) \ (((struct cork_gc_header *) (obj)) - 1) #define cork_gc_get_object(hdr) \ ((void *) (((struct cork_gc_header *) (hdr)) + 1)) void cork_gc_init(void) { cork_gc_get(); } void cork_gc_done(void) { cork_gc_collect_cycles(cork_gc_get()); } void * cork_gc_alloc(size_t instance_size, struct cork_gc_obj_iface *iface) { size_t full_size = instance_size + sizeof(struct cork_gc_header); DEBUG("Allocating %zu (%zu) bytes\n", instance_size, full_size); struct cork_gc_header *header = cork_malloc(full_size); DEBUG(" Result is %p[%p]\n", cork_gc_get_object(header), header); header->ref_count_color = cork_gc_ref_count_color(1, false, GC_BLACK); header->allocated_size = full_size; header->iface = iface; return cork_gc_get_object(header); } void * cork_gc_incref(void *obj) { if (obj != NULL) { struct cork_gc_header *header = cork_gc_get_header(obj); cork_gc_inc_ref_count(header); DEBUG("Incrementing %p -> %d\n", obj, cork_gc_get_ref_count(header)); cork_gc_set_color(header, GC_BLACK); } return obj; } static void cork_gc_decref_step(struct cork_gc *gc, void *obj, void *ud); static void cork_gc_release(struct cork_gc *gc, struct cork_gc_header *header) { cork_gc_recurse(gc, header, cork_gc_decref_step); cork_gc_set_color(header, GC_BLACK); if (!cork_gc_get_buffered(header)) { cork_gc_free(header); } } static void cork_gc_possible_root(struct cork_gc *gc, struct cork_gc_header *header) { if (cork_gc_get_color(header) != GC_PURPLE) { DEBUG(" Possible garbage cycle root\n"); cork_gc_set_color(header, GC_PURPLE); if (!cork_gc_get_buffered(header)) { cork_gc_set_buffered(header, true); if (gc->root_count >= ROOTS_SIZE) { cork_gc_collect_cycles(gc); } gc->roots[gc->root_count++] = header; } } else { DEBUG(" Already marked as possible garbage cycle root\n"); } } static void cork_gc_decref_step(struct cork_gc *gc, void *obj, void *ud) { if (obj != NULL) { struct cork_gc_header *header = cork_gc_get_header(obj); cork_gc_dec_ref_count(header); DEBUG("Decrementing %p -> %d\n", obj, cork_gc_get_ref_count(header)); if (cork_gc_get_ref_count(header) == 0) { DEBUG(" Releasing %p\n", header); cork_gc_release(gc, header); } else { cork_gc_possible_root(gc, header); } } } void cork_gc_decref(void *obj) { if (obj != NULL) { struct cork_gc *gc = cork_gc_get(); struct cork_gc_header *header = cork_gc_get_header(obj); cork_gc_dec_ref_count(header); DEBUG("Decrementing %p -> %d\n", obj, cork_gc_get_ref_count(header)); if (cork_gc_get_ref_count(header) == 0) { DEBUG(" Releasing %p\n", header); cork_gc_release(gc, header); } else { cork_gc_possible_root(gc, header); } } } static void cork_gc_mark_gray_step(struct cork_gc *gc, void *obj, void *ud); static void cork_gc_mark_gray(struct cork_gc *gc, struct cork_gc_header *header) { if (cork_gc_get_color(header) != GC_GRAY) { DEBUG(" Setting color to gray\n"); cork_gc_set_color(header, GC_GRAY); cork_gc_recurse(gc, header, cork_gc_mark_gray_step); } } static void cork_gc_mark_gray_step(struct cork_gc *gc, void *obj, void *ud) { if (obj != NULL) { DEBUG(" cork_gc_mark_gray(%p)\n", obj); struct cork_gc_header *header = cork_gc_get_header(obj); cork_gc_dec_ref_count(header); DEBUG(" Reference count now %d\n", cork_gc_get_ref_count(header)); cork_gc_mark_gray(gc, header); } } static void cork_gc_mark_roots(struct cork_gc *gc) { size_t i; for (i = 0; i < gc->root_count; i++) { struct cork_gc_header *header = gc->roots[i]; if (cork_gc_get_color(header) == GC_PURPLE) { DEBUG(" Checking possible garbage cycle root %p\n", cork_gc_get_object(header)); DEBUG(" cork_gc_mark_gray(%p)\n", cork_gc_get_object(header)); cork_gc_mark_gray(gc, header); } else { DEBUG(" Possible garbage cycle root %p already checked\n", cork_gc_get_object(header)); cork_gc_set_buffered(header, false); gc->roots[i] = NULL; if (cork_gc_get_color(header) == GC_BLACK && cork_gc_get_ref_count(header) == 0) { DEBUG(" Freeing %p\n", header); cork_gc_free(header); } } } } static void cork_gc_scan_black_step(struct cork_gc *gc, void *obj, void *ud); static void cork_gc_scan_black(struct cork_gc *gc, struct cork_gc_header *header) { DEBUG(" Setting color of %p to BLACK\n", cork_gc_get_object(header)); cork_gc_set_color(header, GC_BLACK); cork_gc_recurse(gc, header, cork_gc_scan_black_step); } static void cork_gc_scan_black_step(struct cork_gc *gc, void *obj, void *ud) { if (obj != NULL) { struct cork_gc_header *header = cork_gc_get_header(obj); cork_gc_inc_ref_count(header); DEBUG(" Increasing reference count %p -> %d\n", obj, cork_gc_get_ref_count(header)); if (cork_gc_get_color(header) != GC_BLACK) { cork_gc_scan_black(gc, header); } } } static void cork_gc_scan(struct cork_gc *gc, void *obj, void *ud) { if (obj != NULL) { DEBUG(" Scanning possible garbage cycle entry %p\n", obj); struct cork_gc_header *header = cork_gc_get_header(obj); if (cork_gc_get_color(header) == GC_GRAY) { if (cork_gc_get_ref_count(header) > 0) { DEBUG(" Remaining references; can't be a cycle\n"); cork_gc_scan_black(gc, header); } else { DEBUG(" Definitely a garbage cycle\n"); cork_gc_set_color(header, GC_WHITE); cork_gc_recurse(gc, header, cork_gc_scan); } } else { DEBUG(" Already checked\n"); } } } static void cork_gc_scan_roots(struct cork_gc *gc) { size_t i; for (i = 0; i < gc->root_count; i++) { if (gc->roots[i] != NULL) { void *obj = cork_gc_get_object(gc->roots[i]); cork_gc_scan(gc, obj, NULL); } } } static void cork_gc_collect_white(struct cork_gc *gc, void *obj, void *ud) { if (obj != NULL) { struct cork_gc_header *header = cork_gc_get_header(obj); if (cork_gc_get_color(header) == GC_WHITE && !cork_gc_get_buffered(header)) { DEBUG(" Releasing %p\n", obj); cork_gc_set_color(header, GC_BLACK); cork_gc_recurse(gc, header, cork_gc_collect_white); DEBUG(" Freeing %p\n", header); cork_gc_free(header); } } } static void cork_gc_collect_roots(struct cork_gc *gc) { size_t i; for (i = 0; i < gc->root_count; i++) { if (gc->roots[i] != NULL) { struct cork_gc_header *header = gc->roots[i]; void *obj = cork_gc_get_object(header); cork_gc_set_buffered(header, false); DEBUG("Collecting cycles from garbage root %p\n", obj); cork_gc_collect_white(gc, obj, NULL); gc->roots[i] = NULL; } } gc->root_count = 0; } static void cork_gc_collect_cycles(struct cork_gc *gc) { DEBUG("Collecting garbage cycles\n"); cork_gc_mark_roots(gc); cork_gc_scan_roots(gc); cork_gc_collect_roots(gc); } libcork-1.0.0-rc3/src/libcork/core/hash.c000066400000000000000000000012671367645320400200750ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2011, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #include "libcork/core/hash.h" #include "libcork/core/types.h" bool cork_big_hash_equal(const cork_big_hash h1, const cork_big_hash h2); cork_hash cork_stable_hash_buffer(cork_hash seed, const void *src, size_t len); cork_hash cork_hash_buffer(cork_hash seed, const void *src, size_t len); cork_big_hash cork_big_hash_buffer(cork_big_hash seed, const void *src, size_t len); libcork-1.0.0-rc3/src/libcork/core/id.c000066400000000000000000000011311367645320400175340ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2020, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #include "libcork/core/id.h" /*----------------------------------------------------------------------- * Inline declarations */ bool cork_uid_equal(const cork_uid id1, const cork_uid id2); cork_hash cork_uid_hash(const cork_uid id); const char* cork_uid_name(const cork_uid id); libcork-1.0.0-rc3/src/libcork/core/ip-address.c000066400000000000000000000412041367645320400212000ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2011, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #include #include #include "libcork/core/byte-order.h" #include "libcork/core/error.h" #include "libcork/core/net-addresses.h" #include "libcork/core/types.h" #ifndef CORK_IP_ADDRESS_DEBUG #define CORK_IP_ADDRESS_DEBUG 0 #endif #if CORK_IP_ADDRESS_DEBUG #include #define DEBUG(...) \ do { \ fprintf(stderr, __VA_ARGS__); \ } while (0) #else #define DEBUG(...) /* nothing */ #endif /*----------------------------------------------------------------------- * IP addresses */ /*** IPv4 ***/ static inline const char * cork_ipv4_parse(struct cork_ipv4 *addr, const char *str) { const char *ch; bool seen_digit_in_octet = false; unsigned int octets = 0; unsigned int digit = 0; uint8_t result[4]; for (ch = str; *ch != '\0'; ch++) { DEBUG("%2u: %c\t", (unsigned int) (ch-str), *ch); switch (*ch) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': seen_digit_in_octet = true; digit *= 10; digit += (*ch - '0'); DEBUG("digit = %u\n", digit); if (CORK_UNLIKELY(digit > 255)) { DEBUG("\t"); goto parse_error; } break; case '.': /* If this would be the fourth octet, it can't have a trailing * period. */ if (CORK_UNLIKELY(octets == 3)) { goto parse_error; } DEBUG("octet %u = %u\n", octets, digit); result[octets] = digit; digit = 0; octets++; seen_digit_in_octet = false; break; default: /* Any other character is a parse error. */ goto parse_error; } } /* If we have a valid octet at the end, and that would be the fourth octet, * then we've got a valid final parse. */ DEBUG("%2u:\t", (unsigned int) (ch-str)); if (CORK_LIKELY(seen_digit_in_octet && octets == 3)) { #if CORK_IP_ADDRESS_DEBUG char parsed_ipv4[CORK_IPV4_STRING_LENGTH]; #endif DEBUG("octet %u = %u\n", octets, digit); result[octets] = digit; cork_ipv4_copy(addr, result); #if CORK_IP_ADDRESS_DEBUG cork_ipv4_to_raw_string(addr, parsed_ipv4); DEBUG("\tParsed address: %s\n", parsed_ipv4); #endif return ch; } parse_error: DEBUG("parse error\n"); cork_parse_error("Invalid IPv4 address: \"%s\"", str); return NULL; } int cork_ipv4_init(struct cork_ipv4 *addr, const char *str) { return cork_ipv4_parse(addr, str) == NULL? -1: 0; } bool cork_ipv4_equal_(const struct cork_ipv4 *addr1, const struct cork_ipv4 *addr2) { return cork_ipv4_equal(addr1, addr2); } void cork_ipv4_to_raw_string(const struct cork_ipv4 *addr, char *dest) { snprintf(dest, CORK_IPV4_STRING_LENGTH, "%u.%u.%u.%u", addr->_.u8[0], addr->_.u8[1], addr->_.u8[2], addr->_.u8[3]); } bool cork_ipv4_is_valid_network(const struct cork_ipv4 *addr, unsigned int cidr_prefix) { uint32_t cidr_mask; if (cidr_prefix > 32) { return false; } else if (cidr_prefix == 32) { /* This handles undefined behavior for overflow bit shifts. */ cidr_mask = 0; } else { cidr_mask = 0xffffffff >> cidr_prefix; } return (CORK_UINT32_BIG_TO_HOST(addr->_.u32) & cidr_mask) == 0; } /*** IPv6 ***/ int cork_ipv6_init(struct cork_ipv6 *addr, const char *str) { const char *ch; uint16_t digit = 0; unsigned int before_count = 0; uint16_t before_double_colon[8]; uint16_t after_double_colon[8]; uint16_t *dest = before_double_colon; unsigned int digits_seen = 0; unsigned int hextets_seen = 0; bool another_required = true; bool digit_allowed = true; bool colon_allowed = true; bool double_colon_allowed = true; bool just_saw_colon = false; for (ch = str; *ch != '\0'; ch++) { DEBUG("%2u: %c\t", (unsigned int) (ch-str), *ch); switch (*ch) { #define process_digit(base) \ /* Make sure a digit is allowed here. */ \ if (CORK_UNLIKELY(!digit_allowed)) { \ goto parse_error; \ } \ /* If we've already seen 4 digits, it's a parse error. */ \ if (CORK_UNLIKELY(digits_seen == 4)) { \ goto parse_error; \ } \ \ digits_seen++; \ colon_allowed = true; \ just_saw_colon = false; \ digit <<= 4; \ digit |= (*ch - (base)); \ DEBUG("digit = %04x\n", digit); case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': process_digit('0'); break; case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': process_digit('a'-10); break; case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': process_digit('A'-10); break; #undef process_digit case ':': /* We can only see a colon immediately after a hextet or as part * of a double-colon. */ if (CORK_UNLIKELY(!colon_allowed)) { goto parse_error; } /* If this is a double-colon, start parsing hextets into our * second array. */ if (just_saw_colon) { DEBUG("double-colon\n"); colon_allowed = false; digit_allowed = true; another_required = false; double_colon_allowed = false; before_count = hextets_seen; dest = after_double_colon; continue; } /* If this would end the eighth hextet (regardless of the * placement of a double-colon), then there can't be a trailing * colon. */ if (CORK_UNLIKELY(hextets_seen == 8)) { goto parse_error; } /* If this is the very beginning of the string, then we can only * have a double-colon, not a single colon. */ if (digits_seen == 0 && hextets_seen == 0) { DEBUG("initial colon\n"); colon_allowed = true; digit_allowed = false; just_saw_colon = true; another_required = true; continue; } /* Otherwise this ends the current hextet. */ DEBUG("hextet %u = %04x\n", hextets_seen, digit); *(dest++) = CORK_UINT16_HOST_TO_BIG(digit); digit = 0; hextets_seen++; digits_seen = 0; colon_allowed = double_colon_allowed; just_saw_colon = true; another_required = true; break; case '.': { /* If we see a period, then we must be in the middle of an IPv4 * address at the end of the IPv6 address. */ struct cork_ipv4 *ipv4 = (struct cork_ipv4 *) dest; DEBUG("Detected IPv4 address %s\n", ch-digits_seen); /* Ensure that we have space for the two hextets that the IPv4 * address will take up. */ if (CORK_UNLIKELY(hextets_seen >= 7)) { goto parse_error; } /* Parse the IPv4 address directly into our current hextet * buffer. */ ch = cork_ipv4_parse(ipv4, ch - digits_seen); if (CORK_LIKELY(ch != NULL)) { hextets_seen += 2; digits_seen = 0; another_required = false; /* ch now points at the NUL terminator, but we're about to * increment ch. */ ch--; break; } /* The IPv4 parse failed, so we have an IPv6 parse error. */ goto parse_error; } default: /* Any other character is a parse error. */ goto parse_error; } } /* If we have a valid hextet at the end, and we've either seen a * double-colon, or we have eight hextets in total, then we've got a valid * final parse. */ DEBUG("%2u:\t", (unsigned int) (ch-str)); if (CORK_LIKELY(digits_seen > 0)) { /* If there are trailing digits that would form a ninth hextet * (regardless of the placement of a double-colon), then we have a parse * error. */ if (CORK_UNLIKELY(hextets_seen == 8)) { goto parse_error; } DEBUG("hextet %u = %04x\n\t", hextets_seen, digit); *(dest++) = CORK_UINT16_HOST_TO_BIG(digit); hextets_seen++; } else if (CORK_UNLIKELY(another_required)) { goto parse_error; } if (!double_colon_allowed) { /* We've seen a double-colon, so use 0000 for any hextets that weren't * present. */ #if CORK_IP_ADDRESS_DEBUG char parsed_result[CORK_IPV6_STRING_LENGTH]; #endif unsigned int after_count = hextets_seen - before_count; DEBUG("Saw double-colon; %u hextets before, %u after\n", before_count, after_count); memset(addr, 0, sizeof(struct cork_ipv6)); memcpy(addr, before_double_colon, sizeof(uint16_t) * before_count); memcpy(&addr->_.u16[8-after_count], after_double_colon, sizeof(uint16_t) * after_count); #if CORK_IP_ADDRESS_DEBUG cork_ipv6_to_raw_string(addr, parsed_result); DEBUG("\tParsed address: %s\n", parsed_result); #endif return 0; } else if (hextets_seen == 8) { /* No double-colon, so we must have exactly eight hextets. */ #if CORK_IP_ADDRESS_DEBUG char parsed_result[CORK_IPV6_STRING_LENGTH]; #endif DEBUG("No double-colon\n"); cork_ipv6_copy(addr, before_double_colon); #if CORK_IP_ADDRESS_DEBUG cork_ipv6_to_raw_string(addr, parsed_result); DEBUG("\tParsed address: %s\n", parsed_result); #endif return 0; } parse_error: DEBUG("parse error\n"); cork_parse_error("Invalid IPv6 address: \"%s\"", str); return -1; } bool cork_ipv6_equal_(const struct cork_ipv6 *addr1, const struct cork_ipv6 *addr2) { return cork_ipv6_equal(addr1, addr2); } #define NS_IN6ADDRSZ 16 #define NS_INT16SZ 2 void cork_ipv6_to_raw_string(const struct cork_ipv6 *addr, char *dest) { const uint8_t *src = addr->_.u8; /* * Note that int32_t and int16_t need only be "at least" large enough * to contain a value of the specified size. On some systems, like * Crays, there is no such thing as an integer variable with 16 bits. * Keep this in mind if you think this function should have been coded * to use pointer overlays. All the world's not a VAX. */ char *tp; struct { int base, len; } best, cur; unsigned int words[NS_IN6ADDRSZ / NS_INT16SZ]; int i; /* * Preprocess: * Copy the input (bytewise) array into a wordwise array. * Find the longest run of 0x00's in src[] for :: shorthanding. */ memset(words, '\0', sizeof words); for (i = 0; i < NS_IN6ADDRSZ; i++) words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3)); best.base = -1; best.len = 0; cur.base = -1; cur.len = 0; for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) { if (words[i] == 0) { if (cur.base == -1) cur.base = i, cur.len = 1; else cur.len++; } else { if (cur.base != -1) { if (best.base == -1 || cur.len > best.len) best = cur; cur.base = -1; } } } if (cur.base != -1) { if (best.base == -1 || cur.len > best.len) best = cur; } if (best.base != -1 && best.len < 2) best.base = -1; /* * Format the result. */ tp = dest; for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) { /* Are we inside the best run of 0x00's? */ if (best.base != -1 && i >= best.base && i < (best.base + best.len)) { if (i == best.base) *tp++ = ':'; continue; } /* Are we following an initial run of 0x00s or any real hex? */ if (i != 0) *tp++ = ':'; /* Is this address an encapsulated IPv4? */ if (i == 6 && best.base == 0 && (best.len == 6 || (best.len == 5 && words[5] == 0xffff))) { tp += sprintf(tp, "%u.%u.%u.%u", src[12], src[13], src[14], src[15]); break; } tp += sprintf(tp, "%x", words[i]); } /* Was it a trailing run of 0x00's? */ if (best.base != -1 && (best.base + best.len) == (NS_IN6ADDRSZ / NS_INT16SZ)) *tp++ = ':'; *tp++ = '\0'; } bool cork_ipv6_is_valid_network(const struct cork_ipv6 *addr, unsigned int cidr_prefix) { uint64_t cidr_mask[2]; if (cidr_prefix > 128) { return false; } else if (cidr_prefix == 128) { /* This handles undefined behavior for overflow bit shifts. */ cidr_mask[0] = cidr_mask[1] = 0; } else if (cidr_prefix == 64) { /* This handles undefined behavior for overflow bit shifts. */ cidr_mask[0] = 0; cidr_mask[1] = UINT64_C(0xffffffffffffffff); } else if (cidr_prefix > 64) { cidr_mask[0] = 0; cidr_mask[1] = UINT64_C(0xffffffffffffffff) >> (cidr_prefix-64); } else { cidr_mask[0] = UINT64_C(0xffffffffffffffff) >> cidr_prefix; cidr_mask[1] = UINT64_C(0xffffffffffffffff); } return (CORK_UINT64_BIG_TO_HOST(addr->_.u64[0] & cidr_mask[0]) == 0) && (CORK_UINT64_BIG_TO_HOST(addr->_.u64[1] & cidr_mask[1]) == 0); } /*** IP ***/ void cork_ip_from_ipv4_(struct cork_ip *addr, const void *src) { cork_ip_from_ipv4(addr, src); } void cork_ip_from_ipv6_(struct cork_ip *addr, const void *src) { cork_ip_from_ipv6(addr, src); } int cork_ip_init(struct cork_ip *addr, const char *str) { int rc; /* Try IPv4 first */ rc = cork_ipv4_init(&addr->ip.v4, str); if (rc == 0) { /* successful parse */ addr->version = 4; return 0; } /* Then try IPv6 */ cork_error_clear(); rc = cork_ipv6_init(&addr->ip.v6, str); if (rc == 0) { /* successful parse */ addr->version = 6; return 0; } /* Parse error for both address types */ cork_parse_error("Invalid IP address: \"%s\"", str); return -1; } bool cork_ip_equal_(const struct cork_ip *addr1, const struct cork_ip *addr2) { return cork_ip_equal(addr1, addr2); } void cork_ip_to_raw_string(const struct cork_ip *addr, char *dest) { switch (addr->version) { case 4: cork_ipv4_to_raw_string(&addr->ip.v4, dest); return; case 6: cork_ipv6_to_raw_string(&addr->ip.v6, dest); return; default: strncpy(dest, "", CORK_IP_STRING_LENGTH); return; } } bool cork_ip_is_valid_network(const struct cork_ip *addr, unsigned int cidr_prefix) { switch (addr->version) { case 4: return cork_ipv4_is_valid_network(&addr->ip.v4, cidr_prefix); case 6: return cork_ipv6_is_valid_network(&addr->ip.v6, cidr_prefix); default: return false; } } /*----------------------------------------------------------------------- * Inline declarations */ void cork_ipv4_copy(struct cork_ipv4* addr, const void* src); bool cork_ipv4_equal(const struct cork_ipv4* addr1, const struct cork_ipv4* addr2); void cork_ipv6_copy(struct cork_ipv6* addr, const void* src); bool cork_ipv6_equal(const struct cork_ipv6* addr1, const struct cork_ipv6* addr2); bool cork_ip_equal(const struct cork_ip* addr1, const struct cork_ip* addr2); void cork_ip_from_ipv4(struct cork_ip* addr, const void* src); void cork_ip_from_ipv6(struct cork_ip* addr, const void* src); libcork-1.0.0-rc3/src/libcork/core/mempool.c000066400000000000000000000131221367645320400206130ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2012, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #include #include #include "libcork/core/callbacks.h" #include "libcork/core/mempool.h" #include "libcork/core/types.h" #include "libcork/helpers/errors.h" #if !defined(CORK_DEBUG_MEMPOOL) #define CORK_DEBUG_MEMPOOL 0 #endif #if CORK_DEBUG_MEMPOOL #include #define DEBUG(...) fprintf(stderr, __VA_ARGS__) #else #define DEBUG(...) /* no debug messages */ #endif struct cork_mempool { size_t element_size; size_t block_size; struct cork_mempool_object *free_list; /* The number of objects that have been given out by * cork_mempool_new but not returned via cork_mempool_free. */ size_t allocated_count; struct cork_mempool_block *blocks; void *user_data; cork_free_f free_user_data; cork_init_f init_object; cork_done_f done_object; }; struct cork_mempool_object { /* When this object is unclaimed, it will be in the cork_mempool * object's free_list using this pointer. */ struct cork_mempool_object *next_free; }; struct cork_mempool_block { struct cork_mempool_block *next_block; }; #define cork_mempool_object_size(mp) \ (sizeof(struct cork_mempool_object) + (mp)->element_size) #define cork_mempool_get_header(obj) \ (((struct cork_mempool_object *) (obj)) - 1) #define cork_mempool_get_object(hdr) \ ((void *) (((struct cork_mempool_object *) (hdr)) + 1)) struct cork_mempool * cork_mempool_new_size_ex(size_t element_size, size_t block_size) { struct cork_mempool *mp = cork_new(struct cork_mempool); mp->element_size = element_size; mp->block_size = block_size; mp->free_list = NULL; mp->allocated_count = 0; mp->blocks = NULL; mp->user_data = NULL; mp->free_user_data = NULL; mp->init_object = NULL; mp->done_object = NULL; return mp; } void cork_mempool_free(struct cork_mempool *mp) { struct cork_mempool_block *curr; assert(mp->allocated_count == 0); if (mp->done_object != NULL) { struct cork_mempool_object *obj; for (obj = mp->free_list; obj != NULL; obj = obj->next_free) { mp->done_object (mp->user_data, cork_mempool_get_object(obj)); } } for (curr = mp->blocks; curr != NULL; ) { struct cork_mempool_block *next = curr->next_block; cork_free(curr, mp->block_size); /* Do this here instead of in the for statement to avoid * accessing the just-freed block. */ curr = next; } cork_free_user_data(mp); cork_delete(struct cork_mempool, mp); } void cork_mempool_set_user_data(struct cork_mempool *mp, void *user_data, cork_free_f free_user_data) { cork_free_user_data(mp); mp->user_data = user_data; mp->free_user_data = free_user_data; } void cork_mempool_set_init_object(struct cork_mempool *mp, cork_init_f init_object) { mp->init_object = init_object; } void cork_mempool_set_done_object(struct cork_mempool *mp, cork_done_f done_object) { mp->done_object = done_object; } void cork_mempool_set_callbacks(struct cork_mempool *mp, void *user_data, cork_free_f free_user_data, cork_init_f init_object, cork_done_f done_object) { cork_mempool_set_user_data(mp, user_data, free_user_data); cork_mempool_set_init_object(mp, init_object); cork_mempool_set_done_object(mp, done_object); } /* If this function succeeds, then we guarantee that there will be at * least one object in mp->free_list. */ static void cork_mempool_new_block(struct cork_mempool *mp) { /* Allocate the new block and add it to mp's block list. */ struct cork_mempool_block *block; void *vblock; DEBUG("Allocating new %zu-byte block\n", mp->block_size); block = cork_malloc(mp->block_size); block->next_block = mp->blocks; mp->blocks = block; vblock = block; /* Divide the block's memory region into a bunch of objects. */ size_t index = sizeof(struct cork_mempool_block); for (index = sizeof(struct cork_mempool_block); (index + cork_mempool_object_size(mp)) <= mp->block_size; index += cork_mempool_object_size(mp)) { struct cork_mempool_object *obj = vblock + index; DEBUG(" New object at %p[%p]\n", cork_mempool_get_object(obj), obj); if (mp->init_object != NULL) { mp->init_object (mp->user_data, cork_mempool_get_object(obj)); } obj->next_free = mp->free_list; mp->free_list = obj; } } void * cork_mempool_new_object(struct cork_mempool *mp) { struct cork_mempool_object *obj; void *ptr; if (CORK_UNLIKELY(mp->free_list == NULL)) { cork_mempool_new_block(mp); } obj = mp->free_list; mp->free_list = obj ? obj->next_free : NULL; mp->allocated_count++; ptr = cork_mempool_get_object(obj); return ptr; } void cork_mempool_free_object(struct cork_mempool *mp, void *ptr) { struct cork_mempool_object *obj = cork_mempool_get_header(ptr); DEBUG("Returning %p[%p] to memory pool\n", ptr, obj); obj->next_free = mp->free_list; mp->free_list = obj; mp->allocated_count--; } /*----------------------------------------------------------------------- * Inline declarations */ struct cork_mempool* cork_mempool_new_size(size_t element_size); libcork-1.0.0-rc3/src/libcork/core/timestamp.c000066400000000000000000000123171367645320400211530ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2011, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #include #include #include #include "libcork/core/timestamp.h" #include "libcork/core/types.h" #include "libcork/helpers/errors.h" void cork_timestamp_init_now(cork_timestamp *ts) { struct timeval tp; gettimeofday(&tp, NULL); cork_timestamp_init_usec(ts, tp.tv_sec, tp.tv_usec); } #define is_digit(ch) ((ch) >= '0' && (ch) <= '9') static uint64_t power_of_10(unsigned int width) { uint64_t accumulator = 10; uint64_t result = 1; while (width != 0) { if ((width % 2) == 1) { result *= accumulator; width--; } accumulator *= accumulator; width /= 2; } return result; } static int append_fractional(const cork_timestamp ts, unsigned int width, struct cork_buffer *dest) { if (CORK_UNLIKELY(width == 0 || width > 9)) { cork_error_set_printf (EINVAL, "Invalid width %u for fractional cork_timestamp", width); return -1; } else { uint64_t denom = power_of_10(width); uint64_t frac = cork_timestamp_gsec_to_units(ts, denom); cork_buffer_append_printf(dest, "%0*" PRIu64, width, frac); return 0; } } static int cork_timestamp_format_parts(const cork_timestamp ts, struct tm *tm, const char *format, struct cork_buffer *dest) { const char *next_percent; while ((next_percent = strchr(format, '%')) != NULL) { const char *spec = next_percent + 1; unsigned int width = 0; /* First append any text in between the previous format specifier and * this one. */ cork_buffer_append(dest, format, next_percent - format); /* Then parse the format specifier */ while (is_digit(*spec)) { width *= 10; width += (*spec++ - '0'); } switch (*spec) { case '\0': cork_error_set_string (EINVAL, "Trailing %% at end of cork_timestamp format string"); return -1; case '%': cork_buffer_append(dest, "%", 1); break; case 'Y': cork_buffer_append_printf(dest, "%04d", tm->tm_year + 1900); break; case 'm': cork_buffer_append_printf(dest, "%02d", tm->tm_mon + 1); break; case 'd': cork_buffer_append_printf(dest, "%02d", tm->tm_mday); break; case 'H': cork_buffer_append_printf(dest, "%02d", tm->tm_hour); break; case 'M': cork_buffer_append_printf(dest, "%02d", tm->tm_min); break; case 'S': cork_buffer_append_printf(dest, "%02d", tm->tm_sec); break; case 's': cork_buffer_append_printf (dest, "%" PRIu32, cork_timestamp_sec(ts)); break; case 'f': rii_check(append_fractional(ts, width, dest)); break; default: cork_error_set_printf (EINVAL, "Unknown cork_timestamp format specifier %%%c", *spec); return -1; } format = spec + 1; } /* When we fall through, there is some additional content after the final * format specifier. */ cork_buffer_append_string(dest, format); return 0; } int cork_timestamp_format_utc(const cork_timestamp ts, const char *format, struct cork_buffer *dest) { time_t clock; struct tm tm; clock = cork_timestamp_sec(ts); gmtime_r(&clock, &tm); return cork_timestamp_format_parts(ts, &tm, format, dest); } int cork_timestamp_format_local(const cork_timestamp ts, const char *format, struct cork_buffer *dest) { time_t clock; struct tm tm; clock = cork_timestamp_sec(ts); localtime_r(&clock, &tm); return cork_timestamp_format_parts(ts, &tm, format, dest); } /*----------------------------------------------------------------------- * Inline declarations */ void cork_timestamp_init_sec(cork_timestamp* ts, uint64_t sec); void cork_timestamp_init_gsec(cork_timestamp* ts, uint64_t sec, uint64_t gsec); void cork_timestamp_init_msec(cork_timestamp* ts, uint64_t sec, uint64_t msec); void cork_timestamp_init_usec(cork_timestamp* ts, uint64_t sec, uint64_t usec); void cork_timestamp_init_nsec(cork_timestamp* ts, uint64_t sec, uint64_t nsec); uint32_t cork_timestamp_sec(const cork_timestamp ts); uint32_t cork_timestamp_gsec(const cork_timestamp ts); uint64_t cork_timestamp_gsec_to_units(const cork_timestamp ts, uint64_t denom); uint64_t cork_timestamp_msec(const cork_timestamp ts); uint64_t cork_timestamp_usec(const cork_timestamp ts); uint64_t cork_timestamp_nsec(const cork_timestamp ts); libcork-1.0.0-rc3/src/libcork/core/u128.c000066400000000000000000000060131367645320400176430ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2013, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #include #include #include "libcork/core/types.h" #include "libcork/core/u128.h" /* From http://stackoverflow.com/questions/8023414/how-to-convert-a-128-bit-integer-to-a-decimal-ascii-string-in-c */ const char * cork_u128_to_decimal(char *dest, cork_u128 val) { uint32_t n[4]; char *s = dest; char *p = dest; unsigned int i; /* This algorithm assumes that n[3] is the MSW. */ n[3] = cork_u128_be32(val, 0); n[2] = cork_u128_be32(val, 1); n[1] = cork_u128_be32(val, 2); n[0] = cork_u128_be32(val, 3); memset(s, '0', CORK_U128_DECIMAL_LENGTH - 1); s[CORK_U128_DECIMAL_LENGTH - 1] = '\0'; for (i = 0; i < 128; i++) { unsigned int j; unsigned int carry; carry = (n[3] >= 0x80000000); /* Shift n[] left, doubling it */ n[3] = ((n[3] << 1) & 0xFFFFFFFF) + (n[2] >= 0x80000000); n[2] = ((n[2] << 1) & 0xFFFFFFFF) + (n[1] >= 0x80000000); n[1] = ((n[1] << 1) & 0xFFFFFFFF) + (n[0] >= 0x80000000); n[0] = ((n[0] << 1) & 0xFFFFFFFF); /* Add s[] to itself in decimal, doubling it */ for (j = CORK_U128_DECIMAL_LENGTH - 1; j-- > 0; ) { s[j] += s[j] - '0' + carry; carry = (s[j] > '9'); if (carry) { s[j] -= 10; } } } while ((p[0] == '0') && (p < &s[CORK_U128_DECIMAL_LENGTH - 2])) { p++; } return p; } const char * cork_u128_to_hex(char *buf, cork_u128 val) { uint64_t hi = val._.be64.hi; uint64_t lo = val._.be64.lo; if (hi == 0) { snprintf(buf, CORK_U128_HEX_LENGTH, "%" PRIx64, lo); } else { snprintf(buf, CORK_U128_HEX_LENGTH, "%" PRIx64 "%016" PRIx64, hi, lo); } return buf; } const char * cork_u128_to_padded_hex(char *buf, cork_u128 val) { uint64_t hi = val._.be64.hi; uint64_t lo = val._.be64.lo; snprintf(buf, CORK_U128_HEX_LENGTH, "%016" PRIx64 "%016" PRIx64, hi, lo); return buf; } /*----------------------------------------------------------------------- * Inline declarations */ cork_u128 cork_u128_from_32(uint32_t i0, uint32_t i1, uint32_t i2, uint32_t i3); cork_u128 cork_u128_from_64(uint64_t i0, uint64_t i1); cork_u128 cork_u128_zero(void); bool cork_u128_eq(cork_u128 a, cork_u128 b); bool cork_u128_ne(cork_u128 a, cork_u128 b); bool cork_u128_lt(cork_u128 a, cork_u128 b); bool cork_u128_le(cork_u128 a, cork_u128 b); bool cork_u128_gt(cork_u128 a, cork_u128 b); bool cork_u128_ge(cork_u128 a, cork_u128 b); cork_u128 cork_u128_shl(cork_u128 a, unsigned int b); cork_u128 cork_u128_shr(cork_u128 a, unsigned int b); cork_u128 cork_u128_add(cork_u128 a, cork_u128 b); cork_u128 cork_u128_sub(cork_u128 a, cork_u128 b); libcork-1.0.0-rc3/src/libcork/core/version.c000066400000000000000000000011721367645320400206320ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2015, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #include "libcork/config.h" #include "libcork/core/api.h" /*----------------------------------------------------------------------- * Library version */ const char * cork_version_string(void) { return CORK_CONFIG_VERSION_STRING; } const char * cork_revision_string(void) { return CORK_CONFIG_REVISION; } libcork-1.0.0-rc3/src/libcork/ds/000077500000000000000000000000001367645320400164565ustar00rootroot00000000000000libcork-1.0.0-rc3/src/libcork/ds/array.c000066400000000000000000000272021367645320400177430ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2011, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #include #include #include #include "libcork/core/types.h" #include "libcork/ds/array.h" #include "libcork/helpers/errors.h" #ifndef CORK_ARRAY_DEBUG #define CORK_ARRAY_DEBUG 0 #endif #if CORK_ARRAY_DEBUG #include #define DEBUG(...) \ do { \ fprintf(stderr, __VA_ARGS__); \ fprintf(stderr, "\n"); \ } while (0) #else #define DEBUG(...) /* nothing */ #endif /*----------------------------------------------------------------------- * Resizable arrays */ struct cork_array_priv { size_t allocated_count; size_t allocated_size; size_t element_size; size_t initialized_count; void *user_data; cork_free_f free_user_data; cork_init_f init; cork_done_f done; cork_init_f reuse; cork_done_f remove; }; void cork_raw_array_init(struct cork_raw_array *array, size_t element_size) { array->items = NULL; array->size = 0; array->priv = cork_new(struct cork_array_priv); array->priv->allocated_count = 0; array->priv->allocated_size = 0; array->priv->element_size = element_size; array->priv->initialized_count = 0; array->priv->user_data = NULL; array->priv->free_user_data = NULL; array->priv->init = NULL; array->priv->done = NULL; array->priv->reuse = NULL; array->priv->remove = NULL; } void cork_raw_array_done(struct cork_raw_array *array) { if (array->priv->done != NULL) { size_t i; char *element = array->items; for (i = 0; i < array->priv->initialized_count; i++) { array->priv->done(array->priv->user_data, element); element += array->priv->element_size; } } if (array->items != NULL) { cork_free(array->items, array->priv->allocated_size); } cork_free_user_data(array->priv); cork_delete(struct cork_array_priv, array->priv); } void cork_raw_array_set_callback_data(struct cork_raw_array *array, void *user_data, cork_free_f free_user_data) { array->priv->user_data = user_data; array->priv->free_user_data = free_user_data; } void cork_raw_array_set_init(struct cork_raw_array *array, cork_init_f init) { array->priv->init = init; } void cork_raw_array_set_done(struct cork_raw_array *array, cork_done_f done) { array->priv->done = done; } void cork_raw_array_set_reuse(struct cork_raw_array *array, cork_init_f reuse) { array->priv->reuse = reuse; } void cork_raw_array_set_remove(struct cork_raw_array *array, cork_done_f remove) { array->priv->remove = remove; } size_t cork_raw_array_element_size(const struct cork_raw_array *array) { return array->priv->element_size; } void cork_raw_array_clear(struct cork_raw_array *array) { if (array->priv->remove != NULL) { size_t i; char *element = array->items; for (i = 0; i < array->priv->initialized_count; i++) { array->priv->remove(array->priv->user_data, element); element += array->priv->element_size; } } array->size = 0; } void * cork_raw_array_elements(const struct cork_raw_array *array) { return array->items; } void * cork_raw_array_at(const struct cork_raw_array *array, size_t index) { return ((char *) array->items) + (array->priv->element_size * index); } size_t cork_raw_array_size(const struct cork_raw_array *array) { return array->size; } bool cork_raw_array_is_empty(const struct cork_raw_array *array) { return (array->size == 0); } void cork_raw_array_ensure_size(struct cork_raw_array *array, size_t desired_count) { size_t desired_size; DEBUG("--- Array %p: Ensure %zu %zu-byte elements", array, desired_count, array->priv->element_size); desired_size = desired_count * array->priv->element_size; if (desired_size > array->priv->allocated_size) { size_t new_count = array->priv->allocated_count * 2; size_t new_size = array->priv->allocated_size * 2; if (desired_size > new_size) { new_count = desired_count; new_size = desired_size; } DEBUG("--- Array %p: Reallocating %zu->%zu bytes", array, array->priv->allocated_size, new_size); array->items = cork_realloc(array->items, array->priv->allocated_size, new_size); array->priv->allocated_count = new_count; array->priv->allocated_size = new_size; } } void * cork_raw_array_append(struct cork_raw_array *array) { size_t index; void *element; index = array->size++; cork_raw_array_ensure_size(array, array->size); element = cork_raw_array_at(array, index); /* Call the init or reset callback, depending on whether this entry has been * initialized before. */ /* Since we can currently only add elements by appending them one at a time, * then this entry is either already initialized, or is the first * uninitialized entry. */ assert(index <= array->priv->initialized_count); if (index == array->priv->initialized_count) { /* This element has not been initialized yet. */ array->priv->initialized_count++; if (array->priv->init != NULL) { array->priv->init(array->priv->user_data, element); } } else { /* This element has already been initialized. */ if (array->priv->reuse != NULL) { array->priv->reuse(array->priv->user_data, element); } } return element; } void cork_raw_array_remove(struct cork_raw_array *array, size_t index); void cork_raw_array_remove_range(struct cork_raw_array* array, size_t index, size_t count) { size_t element_size = array->priv->element_size; size_t remaining = array->size - index - count; char* start_of_removed = array->items + (index * element_size); char* end_of_removed = start_of_removed + (count * element_size); char* end_of_array = array->items + (array->size * element_size); array->size -= count; if (array->priv->remove == NULL) { /* If there isn't a cleanup callback, just move the array contents * directly. */ if (end_of_removed < end_of_array) { memmove(start_of_removed, end_of_removed, remaining * element_size); } } else { /* If there are any elements _after_ the elements to be removed, we * first swap them into the beginning of the elements to be removed. */ char temp[element_size]; while (end_of_removed < end_of_array) { memcpy(temp, start_of_removed, element_size); memcpy(start_of_removed, end_of_removed, element_size); memcpy(end_of_removed, temp, element_size); start_of_removed += element_size; end_of_removed += element_size; } /* Then call the cleanup callback on all of the elements to be removed, * which are now guaranteed to be at the end of the array. */ while (start_of_removed < end_of_removed) { array->priv->remove(array->priv->user_data, start_of_removed); start_of_removed += element_size; } } } int cork_raw_array_copy(struct cork_raw_array *dest, const struct cork_raw_array *src, cork_copy_f copy, void *user_data) { size_t i; size_t reuse_count; char *dest_element; DEBUG("--- Copying %zu elements (%zu bytes) from %p to %p", src->size, src->size * dest->priv->element_size, src, dest); assert(dest->priv->element_size == src->priv->element_size); cork_array_clear(dest); cork_array_ensure_size(dest, src->size); /* Initialize enough elements to hold the contents of src */ reuse_count = dest->priv->initialized_count; if (src->size < reuse_count) { reuse_count = src->size; } dest_element = dest->items; if (dest->priv->reuse != NULL) { DEBUG(" Calling reuse on elements 0-%zu", reuse_count); for (i = 0; i < reuse_count; i++) { dest->priv->reuse(dest->priv->user_data, dest_element); dest_element += dest->priv->element_size; } } else { dest_element += reuse_count * dest->priv->element_size; } if (dest->priv->init != NULL) { DEBUG(" Calling init on elements %zu-%zu", reuse_count, src->size); for (i = reuse_count; i < src->size; i++) { dest->priv->init(dest->priv->user_data, dest_element); dest_element += dest->priv->element_size; } } if (src->size > dest->priv->initialized_count) { dest->priv->initialized_count = src->size; } /* If the caller provided a copy function, let it copy each element in turn. * Otherwise, bulk copy everything using memcpy. */ if (copy == NULL) { memcpy(dest->items, src->items, src->size * dest->priv->element_size); } else { const char *src_element = src->items; dest_element = dest->items; for (i = 0; i < src->size; i++) { rii_check(copy(user_data, dest_element, src_element)); dest_element += dest->priv->element_size; src_element += dest->priv->element_size; } } dest->size = src->size; return 0; } /*----------------------------------------------------------------------- * Pointer arrays */ struct cork_pointer_array { cork_free_f free; }; static void pointer__init(void *user_data, void *vvalue) { void **value = vvalue; *value = NULL; } static void pointer__done(void *user_data, void *vvalue) { struct cork_pointer_array *ptr_array = user_data; void **value = vvalue; if (*value != NULL) { ptr_array->free(*value); } } static void pointer__remove(void *user_data, void *vvalue) { struct cork_pointer_array *ptr_array = user_data; void **value = vvalue; if (*value != NULL) { ptr_array->free(*value); } *value = NULL; } static void pointer__free(void *user_data) { struct cork_pointer_array *ptr_array = user_data; cork_delete(struct cork_pointer_array, ptr_array); } void cork_raw_pointer_array_init(struct cork_raw_array *array, cork_free_f free_ptr) { struct cork_pointer_array *ptr_array = cork_new(struct cork_pointer_array); ptr_array->free = free_ptr; cork_raw_array_init(array, sizeof(void *)); cork_array_set_callback_data(array, ptr_array, pointer__free); cork_array_set_init(array, pointer__init); cork_array_set_done(array, pointer__done); cork_array_set_remove(array, pointer__remove); } /*----------------------------------------------------------------------- * String arrays */ void cork_string_array_init(struct cork_string_array *array) { cork_raw_pointer_array_init ((struct cork_raw_array *) array, (cork_free_f) cork_strfree); } void cork_string_array_append(struct cork_string_array *array, const char *str) { const char *copy = cork_strdup(str); cork_array_append(array, copy); } static int string__copy(void *user_data, void *vdest, const void *vsrc) { const char **dest = vdest; const char **src = (const char **) vsrc; *dest = cork_strdup(*src); return 0; } void cork_string_array_copy(struct cork_string_array *dest, const struct cork_string_array *src) { CORK_ATTR_UNUSED int rc; rc = cork_array_copy(dest, src, string__copy, NULL); /* cork_array_copy can only fail if the copy callback fails, and ours * doesn't! */ assert(rc == 0); } libcork-1.0.0-rc3/src/libcork/ds/bitset.c000066400000000000000000000027011367645320400201140ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2013, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #include #include "libcork/core/allocator.h" #include "libcork/core/api.h" #include "libcork/core/types.h" #include "libcork/ds/bitset.h" static size_t bytes_needed(size_t bit_count) { /* We need one byte for every bit... */ size_t bytes_needed = bit_count / 8; /* Plus one extra for the leftovers that don't fit into a whole byte. */ bytes_needed += ((bit_count % 8) > 0); return bytes_needed; } void cork_bitset_init(struct cork_bitset *set, size_t bit_count) { set->bit_count = bit_count; set->byte_count = bytes_needed(bit_count); set->bits = cork_malloc(set->byte_count); memset(set->bits, 0, set->byte_count); } struct cork_bitset * cork_bitset_new(size_t bit_count) { struct cork_bitset *set = cork_new(struct cork_bitset); cork_bitset_init(set, bit_count); return set; } void cork_bitset_done(struct cork_bitset *set) { cork_free(set->bits, set->byte_count); } void cork_bitset_free(struct cork_bitset *set) { cork_bitset_done(set); cork_delete(struct cork_bitset, set); } void cork_bitset_clear(struct cork_bitset *set) { memset(set->bits, 0, set->byte_count); } libcork-1.0.0-rc3/src/libcork/ds/buffer.c000066400000000000000000000271001367645320400200730ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2011, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #include #include #include #include "libcork/core/allocator.h" #include "libcork/core/types.h" #include "libcork/ds/buffer.h" #include "libcork/ds/managed-buffer.h" #include "libcork/ds/stream.h" #include "libcork/helpers/errors.h" void cork_buffer_init(struct cork_buffer* buffer); struct cork_buffer * cork_buffer_new(void) { struct cork_buffer *buffer = cork_new(struct cork_buffer); cork_buffer_init(buffer); return buffer; } void cork_buffer_done(struct cork_buffer *buffer) { if (buffer->buf != NULL) { cork_free(buffer->buf, buffer->allocated_size); buffer->buf = NULL; } buffer->size = 0; buffer->allocated_size = 0; } void cork_buffer_free(struct cork_buffer *buffer) { cork_buffer_done(buffer); cork_delete(struct cork_buffer, buffer); } bool cork_buffer_equal(const struct cork_buffer *buffer1, const struct cork_buffer *buffer2) { if (buffer1 == buffer2) { return true; } if (buffer1->size != buffer2->size) { return false; } return (memcmp(buffer1->buf, buffer2->buf, buffer1->size) == 0); } void cork_buffer_ensure_size_(struct cork_buffer* buffer, size_t desired_size) { size_t new_size; /* Make sure we at least double the old size when reallocating. */ new_size = buffer->allocated_size * 2; if (desired_size > new_size) { new_size = desired_size; } buffer->buf = cork_realloc(buffer->buf, buffer->allocated_size, new_size); buffer->allocated_size = new_size; } void cork_buffer_ensure_size(struct cork_buffer* buffer, size_t desired_size); void cork_buffer_clear(struct cork_buffer* buffer); void cork_buffer_truncate(struct cork_buffer* buffer, size_t length); void cork_buffer_set(struct cork_buffer* buffer, const void* src, size_t length); void cork_buffer_append(struct cork_buffer* buffer, const void* src, size_t length); void cork_buffer_set_string(struct cork_buffer* buffer, const char* str); void cork_buffer_append_string(struct cork_buffer* buffer, const char* str); void cork_buffer_append_vprintf(struct cork_buffer *buffer, const char *format, va_list args) { size_t format_size; va_list args1; va_copy(args1, args); format_size = vsnprintf(buffer->buf + buffer->size, buffer->allocated_size - buffer->size, format, args1); va_end(args1); /* If the first call works, then set buffer->size and return. */ if (format_size < (buffer->allocated_size - buffer->size)) { buffer->size += format_size; return; } /* If the first call fails, resize buffer and try again. */ cork_buffer_ensure_size(buffer, buffer->allocated_size + format_size + 1); format_size = vsnprintf(buffer->buf + buffer->size, buffer->allocated_size - buffer->size, format, args); buffer->size += format_size; } void cork_buffer_vprintf(struct cork_buffer *buffer, const char *format, va_list args) { cork_buffer_clear(buffer); cork_buffer_append_vprintf(buffer, format, args); } void cork_buffer_append_printf(struct cork_buffer *buffer, const char *format, ...) { va_list args; va_start(args, format); cork_buffer_append_vprintf(buffer, format, args); va_end(args); } void cork_buffer_printf(struct cork_buffer *buffer, const char *format, ...) { va_list args; va_start(args, format); cork_buffer_vprintf(buffer, format, args); va_end(args); } void cork_buffer_append_indent(struct cork_buffer *buffer, size_t indent) { cork_buffer_ensure_size(buffer, buffer->size + indent + 1); memset(buffer->buf + buffer->size, ' ', indent); buffer->size += indent; ((char *) buffer->buf)[buffer->size] = '\0'; } /* including space */ #define is_sprint(ch) ((ch) >= 0x20 && (ch) <= 0x7e) /* not including space */ #define is_print(ch) ((ch) > 0x20 && (ch) <= 0x7e) #define is_space(ch) \ ((ch) == ' ' || \ (ch) == '\f' || \ (ch) == '\n' || \ (ch) == '\r' || \ (ch) == '\t' || \ (ch) == '\v') #define to_hex(nybble) \ ((nybble) < 10? '0' + (nybble): 'a' - 10 + (nybble)) void cork_buffer_append_c_string(struct cork_buffer *dest, const char *chars, size_t length) { size_t i; cork_buffer_append(dest, "\"", 1); for (i = 0; i < length; i++) { char ch = chars[i]; switch (ch) { case '\"': cork_buffer_append_literal(dest, "\\\""); break; case '\\': cork_buffer_append_literal(dest, "\\\\"); break; case '\f': cork_buffer_append_literal(dest, "\\f"); break; case '\n': cork_buffer_append_literal(dest, "\\n"); break; case '\r': cork_buffer_append_literal(dest, "\\r"); break; case '\t': cork_buffer_append_literal(dest, "\\t"); break; case '\v': cork_buffer_append_literal(dest, "\\v"); break; default: if (is_sprint(ch)) { cork_buffer_append(dest, &chars[i], 1); } else { uint8_t byte = ch; cork_buffer_append_printf(dest, "\\x%02" PRIx8, byte); } break; } } cork_buffer_append(dest, "\"", 1); } void cork_buffer_append_hex_dump(struct cork_buffer *dest, size_t indent, const char *chars, size_t length) { char hex[3 * 16]; char print[16]; char *curr_hex = hex; char *curr_print = print; size_t i; size_t column = 0; for (i = 0; i < length; i++) { char ch = chars[i]; uint8_t u8 = ch; *curr_hex++ = to_hex(u8 >> 4); *curr_hex++ = to_hex(u8 & 0x0f); *curr_hex++ = ' '; *curr_print++ = is_sprint(ch)? ch: '.'; if (column == 0 && i != 0) { cork_buffer_append_literal(dest, "\n"); cork_buffer_append_indent(dest, indent); column++; } else if (column == 15) { cork_buffer_append_printf (dest, "%-48.*s", (int) (curr_hex - hex), hex); cork_buffer_append_literal(dest, " |"); cork_buffer_append(dest, print, curr_print - print); cork_buffer_append_literal(dest, "|"); curr_hex = hex; curr_print = print; column = 0; } else { column++; } } if (column > 0) { cork_buffer_append_printf(dest, "%-48.*s", (int) (curr_hex - hex), hex); cork_buffer_append_literal(dest, " |"); cork_buffer_append(dest, print, curr_print - print); cork_buffer_append_literal(dest, "|"); } } void cork_buffer_append_multiline(struct cork_buffer *dest, size_t indent, const char *chars, size_t length) { size_t i; for (i = 0; i < length; i++) { char ch = chars[i]; if (ch == '\n') { cork_buffer_append_literal(dest, "\n"); cork_buffer_append_indent(dest, indent); } else { cork_buffer_append(dest, &chars[i], 1); } } } void cork_buffer_append_binary(struct cork_buffer *dest, size_t indent, const char *chars, size_t length) { size_t i; bool newline = false; /* If there are any non-printable characters, print out a hex dump */ for (i = 0; i < length; i++) { if (!is_print(chars[i]) && !is_space(chars[i])) { cork_buffer_append_literal(dest, "(hex)\n"); cork_buffer_append_indent(dest, indent); cork_buffer_append_hex_dump(dest, indent, chars, length); return; } else if (chars[i] == '\n') { newline = true; /* Don't immediately use the multiline format, since there might be * a non-printable character later on that kicks us over to the hex * dump format. */ } } if (newline) { cork_buffer_append_literal(dest, "(multiline)\n"); cork_buffer_append_indent(dest, indent); cork_buffer_append_multiline(dest, indent, chars, length); } else { cork_buffer_append(dest, chars, length); } } struct cork_buffer__managed_buffer { struct cork_managed_buffer parent; struct cork_buffer *buffer; }; static void cork_buffer__managed_free(struct cork_managed_buffer *vself) { struct cork_buffer__managed_buffer *self = cork_container_of(vself, struct cork_buffer__managed_buffer, parent); cork_buffer_free(self->buffer); cork_delete(struct cork_buffer__managed_buffer, self); } static struct cork_managed_buffer_iface CORK_BUFFER__MANAGED_BUFFER = { cork_buffer__managed_free }; struct cork_managed_buffer * cork_buffer_to_managed_buffer(struct cork_buffer *buffer) { struct cork_buffer__managed_buffer *self = cork_new(struct cork_buffer__managed_buffer); self->parent.buf = buffer->buf; self->parent.size = buffer->size; self->parent.ref_count = 1; self->parent.iface = &CORK_BUFFER__MANAGED_BUFFER; self->buffer = buffer; return &self->parent; } int cork_buffer_to_slice(struct cork_buffer *buffer, struct cork_slice *slice) { struct cork_managed_buffer *managed = cork_buffer_to_managed_buffer(buffer); /* We don't have to check for NULL; cork_managed_buffer_slice_offset * will do that for us. */ int rc = cork_managed_buffer_slice_offset(slice, managed, 0); /* Before returning, drop our reference to the managed buffer. If * the slicing succeeded, then there will be one remaining reference * in the slice. If it didn't succeed, this will free the managed * buffer for us. */ cork_managed_buffer_unref(managed); return rc; } struct cork_buffer__stream_consumer { struct cork_stream_consumer consumer; struct cork_buffer *buffer; }; static int cork_buffer_stream_consumer_data(struct cork_stream_consumer *consumer, const void *buf, size_t size, bool is_first_chunk) { struct cork_buffer__stream_consumer *bconsumer = cork_container_of (consumer, struct cork_buffer__stream_consumer, consumer); cork_buffer_append(bconsumer->buffer, buf, size); return 0; } static int cork_buffer_stream_consumer_eof(struct cork_stream_consumer *consumer) { return 0; } static void cork_buffer_stream_consumer_free(struct cork_stream_consumer *consumer) { struct cork_buffer__stream_consumer *bconsumer = cork_container_of (consumer, struct cork_buffer__stream_consumer, consumer); cork_delete(struct cork_buffer__stream_consumer, bconsumer); } struct cork_stream_consumer * cork_buffer_to_stream_consumer(struct cork_buffer *buffer) { struct cork_buffer__stream_consumer *bconsumer = cork_new(struct cork_buffer__stream_consumer); bconsumer->consumer.data = cork_buffer_stream_consumer_data; bconsumer->consumer.eof = cork_buffer_stream_consumer_eof; bconsumer->consumer.free = cork_buffer_stream_consumer_free; bconsumer->buffer = buffer; return &bconsumer->consumer; } libcork-1.0.0-rc3/src/libcork/ds/dllist.c000066400000000000000000000027521367645320400201230ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2011, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #include "libcork/core/api.h" #include "libcork/core/types.h" #include "libcork/ds/dllist.h" /* Include a linkable (but deprecated) version of this to maintain ABI * compatibility. */ #undef cork_dllist_init CORK_API void cork_dllist_init(struct cork_dllist *list) { list->head.next = &list->head; list->head.prev = &list->head; } void cork_dllist_map(struct cork_dllist *list, cork_dllist_map_func func, void *user_data) { struct cork_dllist_item *curr; struct cork_dllist_item *next; cork_dllist_foreach_void(list, curr, next) { func(curr, user_data); } } int cork_dllist_visit(struct cork_dllist *list, void *ud, cork_dllist_visit_f *visit) { struct cork_dllist_item *curr; struct cork_dllist_item *next; cork_dllist_foreach_void(list, curr, next) { int rc = visit(ud, curr); if (rc != 0) { return rc; } } return 0; } size_t cork_dllist_size(const struct cork_dllist *list) { size_t size = 0; struct cork_dllist_item *curr; struct cork_dllist_item *next; cork_dllist_foreach_void(list, curr, next) { size++; } return size; } libcork-1.0.0-rc3/src/libcork/ds/file-stream.c000066400000000000000000000127361367645320400210430ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2012, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #include #include #include #include #include #include "libcork/ds/stream.h" #include "libcork/helpers/errors.h" #include "libcork/helpers/posix.h" #define BUFFER_SIZE 4096 /*----------------------------------------------------------------------- * Producers */ int cork_consume_fd(struct cork_stream_consumer *consumer, int fd) { char buf[BUFFER_SIZE]; ssize_t bytes_read; bool first = true; while (true) { while ((bytes_read = read(fd, buf, BUFFER_SIZE)) > 0) { rii_check(cork_stream_consumer_data (consumer, buf, bytes_read, first)); first = false; } if (bytes_read == 0) { return cork_stream_consumer_eof(consumer); } else if (errno != EINTR) { cork_system_error_set(); return -1; } } } int cork_consume_file(struct cork_stream_consumer *consumer, FILE *fp) { char buf[BUFFER_SIZE]; size_t bytes_read; bool first = true; while (true) { while ((bytes_read = fread(buf, 1, BUFFER_SIZE, fp)) > 0) { rii_check(cork_stream_consumer_data (consumer, buf, bytes_read, first)); first = false; } if (feof(fp)) { return cork_stream_consumer_eof(consumer); } else if (errno != EINTR) { cork_system_error_set(); return -1; } } } int cork_consume_file_from_path(struct cork_stream_consumer *consumer, const char *path, int flags) { int fd; rii_check_posix(fd = open(path, flags)); ei_check(cork_consume_fd(consumer, fd)); rii_check_posix(close(fd)); return 0; error: rii_check_posix(close(fd)); return -1; } /*----------------------------------------------------------------------- * Consumers */ struct cork_file_consumer { struct cork_stream_consumer parent; FILE *fp; }; static int cork_file_consumer__data(struct cork_stream_consumer *vself, const void *buf, size_t size, bool is_first) { struct cork_file_consumer *self = cork_container_of(vself, struct cork_file_consumer, parent); size_t bytes_written = fwrite(buf, 1, size, self->fp); /* If there was an error writing to the file, then signal this to * the producer */ if (bytes_written == size) { return 0; } else { cork_system_error_set(); return -1; } } static int cork_file_consumer__eof(struct cork_stream_consumer *vself) { /* We never close the file with this version of the consumer, so there's * nothing special to do at end-of-stream. */ return 0; } static void cork_file_consumer__free(struct cork_stream_consumer *vself) { struct cork_file_consumer *self = cork_container_of(vself, struct cork_file_consumer, parent); cork_delete(struct cork_file_consumer, self); } struct cork_stream_consumer * cork_file_consumer_new(FILE *fp) { struct cork_file_consumer *self = cork_new(struct cork_file_consumer); self->parent.data = cork_file_consumer__data; self->parent.eof = cork_file_consumer__eof; self->parent.free = cork_file_consumer__free; self->fp = fp; return &self->parent; } struct cork_fd_consumer { struct cork_stream_consumer parent; int fd; }; static int cork_fd_consumer__data(struct cork_stream_consumer *vself, const void *buf, size_t size, bool is_first) { struct cork_fd_consumer *self = cork_container_of(vself, struct cork_fd_consumer, parent); size_t bytes_left = size; while (bytes_left > 0) { ssize_t rc = write(self->fd, buf, bytes_left); if (rc == -1 && errno != EINTR) { cork_system_error_set(); return -1; } else { bytes_left -= rc; buf += rc; } } return 0; } static int cork_fd_consumer__eof_close(struct cork_stream_consumer *vself) { int rc; struct cork_fd_consumer *self = cork_container_of(vself, struct cork_fd_consumer, parent); rii_check_posix(rc = close(self->fd)); return 0; } static void cork_fd_consumer__free(struct cork_stream_consumer *vself) { struct cork_fd_consumer *self = cork_container_of(vself, struct cork_fd_consumer, parent); cork_delete(struct cork_fd_consumer, self); } struct cork_stream_consumer * cork_fd_consumer_new(int fd) { struct cork_fd_consumer *self = cork_new(struct cork_fd_consumer); self->parent.data = cork_fd_consumer__data; /* We don't want to close fd, so we reuse file_consumer's eof method */ self->parent.eof = cork_file_consumer__eof; self->parent.free = cork_fd_consumer__free; self->fd = fd; return &self->parent; } struct cork_stream_consumer * cork_file_from_path_consumer_new(const char *path, int flags) { int fd; struct cork_fd_consumer *self; rpi_check_posix(fd = open(path, flags)); self = cork_new(struct cork_fd_consumer); self->parent.data = cork_fd_consumer__data; self->parent.eof = cork_fd_consumer__eof_close; self->parent.free = cork_fd_consumer__free; self->fd = fd; return &self->parent; } libcork-1.0.0-rc3/src/libcork/ds/hash-table.c000066400000000000000000000474731367645320400206510ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2011, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #include #include #include "libcork/core/callbacks.h" #include "libcork/core/hash.h" #include "libcork/core/types.h" #include "libcork/ds/dllist.h" #include "libcork/ds/hash-table.h" #include "libcork/helpers/errors.h" #ifndef CORK_HASH_TABLE_DEBUG #define CORK_HASH_TABLE_DEBUG 0 #endif #if CORK_HASH_TABLE_DEBUG #include #define DEBUG(...) \ do { \ fprintf(stderr, __VA_ARGS__); \ fprintf(stderr, "\n"); \ } while (0) #else #define DEBUG(...) /* nothing */ #endif /*----------------------------------------------------------------------- * Hash tables */ struct cork_hash_table_entry_priv { struct cork_hash_table_entry public; struct cork_dllist_item in_bucket; struct cork_dllist_item insertion_order; }; struct cork_hash_table { struct cork_dllist *bins; struct cork_dllist insertion_order; size_t bin_count; size_t bin_mask; size_t entry_count; void *user_data; cork_free_f free_user_data; cork_hash_f hash; cork_equals_f equals; cork_free_f free_key; cork_free_f free_value; }; static cork_hash cork_hash_table__default_hash(void *user_data, const void *key) { return (cork_hash) (uintptr_t) key; } static bool cork_hash_table__default_equals(void *user_data, const void *key1, const void *key2) { return key1 == key2; } /* The default initial number of bins to allocate in a new table. */ #define CORK_HASH_TABLE_DEFAULT_INITIAL_SIZE 8 /* The default number of entries per bin to allow before increasing the * number of bins. */ #define CORK_HASH_TABLE_MAX_DENSITY 5 /* Return a power-of-2 bin count that's at least as big as the given requested * size. */ static inline size_t cork_hash_table_new_size(size_t desired_count) { size_t v = desired_count; size_t r = 1; while (v >>= 1) { r <<= 1; } if (r != desired_count) { r <<= 1; } return r; } #define bin_index(table, hash) ((hash) & (table)->bin_mask) /* Allocates a new bins array in a hash table. We overwrite the old * array, so make sure to stash it away somewhere safe first. */ static void cork_hash_table_allocate_bins(struct cork_hash_table *table, size_t desired_count) { size_t i; table->bin_count = cork_hash_table_new_size(desired_count); table->bin_mask = table->bin_count - 1; DEBUG("Allocate %zu bins", table->bin_count); table->bins = cork_calloc(table->bin_count, sizeof(struct cork_dllist)); for (i = 0; i < table->bin_count; i++) { cork_dllist_init(&table->bins[i]); } } static struct cork_hash_table_entry_priv * cork_hash_table_new_entry(struct cork_hash_table *table, cork_hash hash, void *key, void *value) { struct cork_hash_table_entry_priv *entry = cork_new(struct cork_hash_table_entry_priv); cork_dllist_add(&table->insertion_order, &entry->insertion_order); entry->public.hash = hash; entry->public.key = key; entry->public.value = value; return entry; } static void cork_hash_table_free_entry(struct cork_hash_table *table, struct cork_hash_table_entry_priv *entry) { if (table->free_key != NULL) { table->free_key(entry->public.key); } if (table->free_value != NULL) { table->free_value(entry->public.value); } cork_dllist_remove(&entry->insertion_order); cork_delete(struct cork_hash_table_entry_priv, entry); } struct cork_hash_table * cork_hash_table_new(size_t initial_size, unsigned int flags) { struct cork_hash_table *table = cork_new(struct cork_hash_table); table->entry_count = 0; table->user_data = NULL; table->free_user_data = NULL; table->hash = cork_hash_table__default_hash; table->equals = cork_hash_table__default_equals; table->free_key = NULL; table->free_value = NULL; cork_dllist_init(&table->insertion_order); if (initial_size < CORK_HASH_TABLE_DEFAULT_INITIAL_SIZE) { initial_size = CORK_HASH_TABLE_DEFAULT_INITIAL_SIZE; } cork_hash_table_allocate_bins(table, initial_size); return table; } void cork_hash_table_clear(struct cork_hash_table *table) { size_t i; struct cork_dllist_item *curr; struct cork_dllist_item *next; DEBUG("(clear) Remove all entries"); for (curr = cork_dllist_start(&table->insertion_order); !cork_dllist_is_end(&table->insertion_order, curr); curr = next) { struct cork_hash_table_entry_priv *entry = cork_container_of (curr, struct cork_hash_table_entry_priv, insertion_order); next = curr->next; cork_hash_table_free_entry(table, entry); } cork_dllist_init(&table->insertion_order); DEBUG("(clear) Clear bins"); for (i = 0; i < table->bin_count; i++) { DEBUG(" Bin %zu", i); cork_dllist_init(&table->bins[i]); } table->entry_count = 0; } void cork_hash_table_free(struct cork_hash_table *table) { cork_hash_table_clear(table); cork_cfree(table->bins, table->bin_count, sizeof(struct cork_dllist)); cork_delete(struct cork_hash_table, table); } size_t cork_hash_table_size(const struct cork_hash_table *table) { return table->entry_count; } void cork_hash_table_set_user_data(struct cork_hash_table *table, void *user_data, cork_free_f free_user_data) { table->user_data = user_data; table->free_user_data = free_user_data; } void cork_hash_table_set_hash(struct cork_hash_table *table, cork_hash_f hash) { table->hash = hash; } void cork_hash_table_set_equals(struct cork_hash_table *table, cork_equals_f equals) { table->equals = equals; } void cork_hash_table_set_free_key(struct cork_hash_table *table, cork_free_f free) { table->free_key = free; } void cork_hash_table_set_free_value(struct cork_hash_table *table, cork_free_f free) { table->free_value = free; } void cork_hash_table_ensure_size(struct cork_hash_table *table, size_t desired_count) { if (desired_count > table->bin_count) { struct cork_dllist *old_bins = table->bins; size_t old_bin_count = table->bin_count; cork_hash_table_allocate_bins(table, desired_count); if (old_bins != NULL) { size_t i; for (i = 0; i < old_bin_count; i++) { struct cork_dllist *bin = &old_bins[i]; struct cork_dllist_item *curr = cork_dllist_start(bin); while (!cork_dllist_is_end(bin, curr)) { struct cork_hash_table_entry_priv *entry = cork_container_of (curr, struct cork_hash_table_entry_priv, in_bucket); struct cork_dllist_item *next = curr->next; size_t bin_index = bin_index(table, entry->public.hash); DEBUG(" Rehash %p from bin %zu to bin %zu", entry, i, bin_index); cork_dllist_add(&table->bins[bin_index], curr); curr = next; } } cork_cfree(old_bins, old_bin_count, sizeof(struct cork_dllist)); } } } static void cork_hash_table_rehash(struct cork_hash_table *table) { DEBUG(" Reached maximum density; rehash"); cork_hash_table_ensure_size(table, table->bin_count + 1); } struct cork_hash_table_entry * cork_hash_table_get_entry_hash(const struct cork_hash_table *table, cork_hash hash, const void *key) { size_t bin_index; struct cork_dllist *bin; struct cork_dllist_item *curr; if (table->bin_count == 0) { DEBUG("(get) Empty table when searching for key %p " "(hash 0x%08" PRIx32 ")", key, hash); return NULL; } bin_index = bin_index(table, hash); DEBUG("(get) Search for key %p (hash 0x%08" PRIx32 ", bin %zu)", key, hash, bin_index); bin = &table->bins[bin_index]; curr = cork_dllist_start(bin); while (!cork_dllist_is_end(bin, curr)) { struct cork_hash_table_entry_priv *entry = cork_container_of (curr, struct cork_hash_table_entry_priv, in_bucket); DEBUG(" Check entry %p", entry); if (table->equals(table->user_data, key, entry->public.key)) { DEBUG(" Match"); return &entry->public; } curr = curr->next; } DEBUG(" Entry not found"); return NULL; } struct cork_hash_table_entry * cork_hash_table_get_entry(const struct cork_hash_table *table, const void *key) { cork_hash hash = table->hash(table->user_data, key); return cork_hash_table_get_entry_hash(table, hash, key); } void * cork_hash_table_get_hash(const struct cork_hash_table *table, cork_hash hash, const void *key) { struct cork_hash_table_entry *entry = cork_hash_table_get_entry_hash(table, hash, key); if (entry == NULL) { return NULL; } else { DEBUG(" Extract value pointer %p", entry->value); return entry->value; } } void * cork_hash_table_get(const struct cork_hash_table *table, const void *key) { struct cork_hash_table_entry *entry = cork_hash_table_get_entry(table, key); if (entry == NULL) { return NULL; } else { DEBUG(" Extract value pointer %p", entry->value); return entry->value; } } struct cork_hash_table_entry * cork_hash_table_get_or_create_hash(struct cork_hash_table *table, cork_hash hash, void *key, bool *is_new) { struct cork_hash_table_entry_priv *entry; size_t bin_index; if (table->bin_count > 0) { struct cork_dllist *bin; struct cork_dllist_item *curr; bin_index = bin_index(table, hash); DEBUG("(get_or_create) Search for key %p " "(hash 0x%08" PRIx32 ", bin %zu)", key, hash, bin_index); bin = &table->bins[bin_index]; curr = cork_dllist_start(bin); while (!cork_dllist_is_end(bin, curr)) { struct cork_hash_table_entry_priv *entry = cork_container_of (curr, struct cork_hash_table_entry_priv, in_bucket); DEBUG(" Check entry %p", entry); if (table->equals(table->user_data, key, entry->public.key)) { DEBUG(" Match"); DEBUG(" Return value pointer %p", entry->public.value); *is_new = false; return &entry->public; } curr = curr->next; } /* create a new entry */ DEBUG(" Entry not found"); if ((table->entry_count / table->bin_count) > CORK_HASH_TABLE_MAX_DENSITY) { cork_hash_table_rehash(table); bin_index = bin_index(table, hash); } } else { DEBUG("(get_or_create) Search for key %p (hash 0x%08" PRIx32 ")", key, hash); DEBUG(" Empty table"); cork_hash_table_rehash(table); bin_index = bin_index(table, hash); } DEBUG(" Allocate new entry"); entry = cork_hash_table_new_entry(table, hash, key, NULL); DEBUG(" Created new entry %p", entry); DEBUG(" Add entry into bin %zu", bin_index); cork_dllist_add(&table->bins[bin_index], &entry->in_bucket); table->entry_count++; *is_new = true; return &entry->public; } struct cork_hash_table_entry * cork_hash_table_get_or_create(struct cork_hash_table *table, void *key, bool *is_new) { cork_hash hash = table->hash(table->user_data, key); return cork_hash_table_get_or_create_hash(table, hash, key, is_new); } void cork_hash_table_put_hash(struct cork_hash_table *table, cork_hash hash, void *key, void *value, bool *is_new, void **old_key, void **old_value) { struct cork_hash_table_entry_priv *entry; size_t bin_index; if (table->bin_count > 0) { struct cork_dllist *bin; struct cork_dllist_item *curr; bin_index = bin_index(table, hash); DEBUG("(put) Search for key %p (hash 0x%08" PRIx32 ", bin %zu)", key, hash, bin_index); bin = &table->bins[bin_index]; curr = cork_dllist_start(bin); while (!cork_dllist_is_end(bin, curr)) { struct cork_hash_table_entry_priv *entry = cork_container_of (curr, struct cork_hash_table_entry_priv, in_bucket); DEBUG(" Check entry %p", entry); if (table->equals(table->user_data, key, entry->public.key)) { DEBUG(" Found existing entry; overwriting"); DEBUG(" Return old key %p", entry->public.key); if (old_key != NULL) { *old_key = entry->public.key; } DEBUG(" Return old value %p", entry->public.value); if (old_value != NULL) { *old_value = entry->public.value; } DEBUG(" Copy key %p into entry", key); entry->public.key = key; DEBUG(" Copy value %p into entry", value); entry->public.value = value; if (is_new != NULL) { *is_new = false; } return; } curr = curr->next; } /* create a new entry */ DEBUG(" Entry not found"); if ((table->entry_count / table->bin_count) > CORK_HASH_TABLE_MAX_DENSITY) { cork_hash_table_rehash(table); bin_index = bin_index(table, hash); } } else { DEBUG("(put) Search for key %p (hash 0x%08" PRIx32 ")", key, hash); DEBUG(" Empty table"); cork_hash_table_rehash(table); bin_index = bin_index(table, hash); } DEBUG(" Allocate new entry"); entry = cork_hash_table_new_entry(table, hash, key, value); DEBUG(" Created new entry %p", entry); DEBUG(" Add entry into bin %zu", bin_index); cork_dllist_add(&table->bins[bin_index], &entry->in_bucket); table->entry_count++; if (old_key != NULL) { *old_key = NULL; } if (old_value != NULL) { *old_value = NULL; } if (is_new != NULL) { *is_new = true; } } void cork_hash_table_put(struct cork_hash_table *table, void *key, void *value, bool *is_new, void **old_key, void **old_value) { cork_hash hash = table->hash(table->user_data, key); cork_hash_table_put_hash (table, hash, key, value, is_new, old_key, old_value); } void cork_hash_table_delete_entry(struct cork_hash_table *table, struct cork_hash_table_entry *ventry) { struct cork_hash_table_entry_priv *entry = cork_container_of(ventry, struct cork_hash_table_entry_priv, public); cork_dllist_remove(&entry->in_bucket); table->entry_count--; cork_hash_table_free_entry(table, entry); } bool cork_hash_table_delete_hash(struct cork_hash_table *table, cork_hash hash, const void *key, void **deleted_key, void **deleted_value) { size_t bin_index; struct cork_dllist *bin; struct cork_dllist_item *curr; if (table->bin_count == 0) { DEBUG("(delete) Empty table when searching for key %p " "(hash 0x%08" PRIx32 ")", key, hash); return false; } bin_index = bin_index(table, hash); DEBUG("(delete) Search for key %p (hash 0x%08" PRIx32 ", bin %zu)", key, hash, bin_index); bin = &table->bins[bin_index]; curr = cork_dllist_start(bin); while (!cork_dllist_is_end(bin, curr)) { struct cork_hash_table_entry_priv *entry = cork_container_of (curr, struct cork_hash_table_entry_priv, in_bucket); DEBUG(" Check entry %p", entry); if (table->equals(table->user_data, key, entry->public.key)) { DEBUG(" Match"); if (deleted_key != NULL) { *deleted_key = entry->public.key; } if (deleted_value != NULL) { *deleted_value = entry->public.value; } DEBUG(" Remove entry from hash bin %zu", bin_index); cork_dllist_remove(curr); table->entry_count--; DEBUG(" Free entry %p", entry); cork_hash_table_free_entry(table, entry); return true; } curr = curr->next; } DEBUG(" Entry not found"); return false; } bool cork_hash_table_delete(struct cork_hash_table *table, const void *key, void **deleted_key, void **deleted_value) { cork_hash hash = table->hash(table->user_data, key); return cork_hash_table_delete_hash (table, hash, key, deleted_key, deleted_value); } void cork_hash_table_map(struct cork_hash_table *table, void *user_data, cork_hash_table_map_f map) { struct cork_dllist_item *curr; DEBUG("Map across hash table"); curr = cork_dllist_start(&table->insertion_order); while (!cork_dllist_is_end(&table->insertion_order, curr)) { struct cork_hash_table_entry_priv *entry = cork_container_of (curr, struct cork_hash_table_entry_priv, insertion_order); struct cork_dllist_item *next = curr->next; enum cork_hash_table_map_result result; DEBUG(" Apply function to entry %p", entry); result = map(user_data, &entry->public); if (result == CORK_HASH_TABLE_MAP_ABORT) { return; } else if (result == CORK_HASH_TABLE_MAP_DELETE) { DEBUG(" Delete requested"); cork_dllist_remove(curr); cork_dllist_remove(&entry->in_bucket); table->entry_count--; cork_hash_table_free_entry(table, entry); } curr = next; } } void cork_hash_table_iterator_init(struct cork_hash_table *table, struct cork_hash_table_iterator *iterator) { DEBUG("Iterate through hash table"); iterator->table = table; iterator->priv = cork_dllist_start(&table->insertion_order); } struct cork_hash_table_entry * cork_hash_table_iterator_next(struct cork_hash_table_iterator *iterator) { struct cork_hash_table *table = iterator->table; struct cork_dllist_item *curr = iterator->priv; struct cork_hash_table_entry_priv *entry; if (cork_dllist_is_end(&table->insertion_order, curr)) { return NULL; } entry = cork_container_of (curr, struct cork_hash_table_entry_priv, insertion_order); DEBUG(" Return entry %p", entry); iterator->priv = curr->next; return &entry->public; } /*----------------------------------------------------------------------- * Built-in key types */ static cork_hash string_hash(void *user_data, const void *vk) { const char *k = vk; size_t len = strlen(k); return cork_hash_buffer(0, k, len); } static bool string_equals(void *user_data, const void *vk1, const void *vk2) { const char *k1 = vk1; const char *k2 = vk2; return strcmp(k1, k2) == 0; } struct cork_hash_table * cork_string_hash_table_new(size_t initial_size, unsigned int flags) { struct cork_hash_table *table = cork_hash_table_new(initial_size, flags); cork_hash_table_set_hash(table, string_hash); cork_hash_table_set_equals(table, string_equals); return table; } struct cork_hash_table * cork_pointer_hash_table_new(size_t initial_size, unsigned int flags) { return cork_hash_table_new(initial_size, flags); } libcork-1.0.0-rc3/src/libcork/ds/managed-buffer.c000066400000000000000000000147271367645320400215000ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2011, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #include #include #include "libcork/core/error.h" #include "libcork/core/types.h" #include "libcork/ds/managed-buffer.h" #include "libcork/ds/slice.h" #include "libcork/helpers/errors.h" /*----------------------------------------------------------------------- * Error handling */ static void cork_slice_invalid_slice_set(size_t buf_size, size_t requested_offset, size_t requested_length) { cork_error_set (CORK_SLICE_ERROR, CORK_SLICE_INVALID_SLICE, "Cannot slice %zu-byte buffer at %zu:%zu", buf_size, requested_offset, requested_length); } /*----------------------------------------------------------------------- * Managed buffers */ struct cork_managed_buffer_wrapped { struct cork_managed_buffer parent; void *buf; size_t size; cork_managed_buffer_freer free; }; static void cork_managed_buffer_wrapped__free(struct cork_managed_buffer *vself) { struct cork_managed_buffer_wrapped *self = cork_container_of(vself, struct cork_managed_buffer_wrapped, parent); self->free(self->buf, self->size); cork_delete(struct cork_managed_buffer_wrapped, self); } static struct cork_managed_buffer_iface CORK_MANAGED_BUFFER_WRAPPED = { cork_managed_buffer_wrapped__free }; struct cork_managed_buffer * cork_managed_buffer_new(const void *buf, size_t size, cork_managed_buffer_freer free) { /* DEBUG("Creating new struct cork_managed_buffer [%p:%zu], refcount now 1", buf, size); */ struct cork_managed_buffer_wrapped *self = cork_new(struct cork_managed_buffer_wrapped); self->parent.buf = buf; self->parent.size = size; self->parent.ref_count = 1; self->parent.iface = &CORK_MANAGED_BUFFER_WRAPPED; self->buf = (void *) buf; self->size = size; self->free = free; return &self->parent; } struct cork_managed_buffer_copied { struct cork_managed_buffer parent; }; #define cork_managed_buffer_copied_data(self) \ (((void *) (self)) + sizeof(struct cork_managed_buffer_copied)) #define cork_managed_buffer_copied_sizeof(sz) \ ((sz) + sizeof(struct cork_managed_buffer_copied)) static void cork_managed_buffer_copied__free(struct cork_managed_buffer *vself) { struct cork_managed_buffer_copied *self = cork_container_of(vself, struct cork_managed_buffer_copied, parent); size_t allocated_size = cork_managed_buffer_copied_sizeof(self->parent.size); cork_free(self, allocated_size); } static struct cork_managed_buffer_iface CORK_MANAGED_BUFFER_COPIED = { cork_managed_buffer_copied__free }; struct cork_managed_buffer * cork_managed_buffer_new_copy(const void *buf, size_t size) { size_t allocated_size = cork_managed_buffer_copied_sizeof(size); struct cork_managed_buffer_copied *self = cork_malloc(allocated_size); if (self == NULL) { return NULL; } self->parent.buf = cork_managed_buffer_copied_data(self); self->parent.size = size; self->parent.ref_count = 1; self->parent.iface = &CORK_MANAGED_BUFFER_COPIED; memcpy((void *) self->parent.buf, buf, size); return &self->parent; } static void cork_managed_buffer_free(struct cork_managed_buffer *self) { /* DEBUG("Freeing struct cork_managed_buffer [%p:%zu]", self->buf, self->size); */ self->iface->free(self); } struct cork_managed_buffer * cork_managed_buffer_ref(struct cork_managed_buffer *self) { /* int old_count = self->ref_count++; DEBUG("Referencing struct cork_managed_buffer [%p:%zu], refcount now %d", self->buf, self->size, old_count + 1); */ self->ref_count++; return self; } void cork_managed_buffer_unref(struct cork_managed_buffer *self) { /* int old_count = self->ref_count--; DEBUG("Dereferencing struct cork_managed_buffer [%p:%zu], refcount now %d", self->buf, self->size, old_count - 1); */ if (--self->ref_count == 0) { cork_managed_buffer_free(self); } } static struct cork_slice_iface CORK_MANAGED_BUFFER__SLICE; static void cork_managed_buffer__slice_free(struct cork_slice *self) { struct cork_managed_buffer *mbuf = self->user_data; cork_managed_buffer_unref(mbuf); } static int cork_managed_buffer__slice_copy(struct cork_slice *dest, const struct cork_slice *src, size_t offset, size_t length) { struct cork_managed_buffer *mbuf = src->user_data; dest->buf = src->buf + offset; dest->size = length; dest->iface = &CORK_MANAGED_BUFFER__SLICE; dest->user_data = cork_managed_buffer_ref(mbuf); return 0; } static struct cork_slice_iface CORK_MANAGED_BUFFER__SLICE = { cork_managed_buffer__slice_free, cork_managed_buffer__slice_copy, cork_managed_buffer__slice_copy, NULL }; int cork_managed_buffer_slice(struct cork_slice *dest, struct cork_managed_buffer *buffer, size_t offset, size_t length) { if ((buffer != NULL) && (offset <= buffer->size) && ((offset + length) <= buffer->size)) { /* DEBUG("Slicing [%p:%zu] at %zu:%zu, gives <%p:%zu>", buffer->buf, buffer->size, offset, length, buffer->buf + offset, length); */ dest->buf = buffer->buf + offset; dest->size = length; dest->iface = &CORK_MANAGED_BUFFER__SLICE; dest->user_data = cork_managed_buffer_ref(buffer); return 0; } else { /* DEBUG("Cannot slice [%p:%zu] at %zu:%zu", buffer->buf, buffer->size, offset, length); */ cork_slice_clear(dest); cork_slice_invalid_slice_set(0, offset, 0); return -1; } } int cork_managed_buffer_slice_offset(struct cork_slice *dest, struct cork_managed_buffer *buffer, size_t offset) { if (buffer == NULL) { cork_slice_clear(dest); cork_slice_invalid_slice_set(0, offset, 0); return -1; } else { return cork_managed_buffer_slice (dest, buffer, offset, buffer->size - offset); } } libcork-1.0.0-rc3/src/libcork/ds/ring-buffer.c000066400000000000000000000037471367645320400210430ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2011, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #include #include "libcork/core/allocator.h" #include "libcork/core/types.h" #include "libcork/ds/ring-buffer.h" int cork_ring_buffer_init(struct cork_ring_buffer *self, size_t size) { self->elements = cork_calloc(size, sizeof(void *)); self->allocated_size = size; self->size = 0; self->read_index = 0; self->write_index = 0; return 0; } struct cork_ring_buffer * cork_ring_buffer_new(size_t size) { struct cork_ring_buffer *buf = cork_new(struct cork_ring_buffer); cork_ring_buffer_init(buf, size); return buf; } void cork_ring_buffer_done(struct cork_ring_buffer *self) { cork_cfree(self->elements, self->allocated_size, sizeof(void *)); } void cork_ring_buffer_free(struct cork_ring_buffer *buf) { cork_ring_buffer_done(buf); cork_delete(struct cork_ring_buffer, buf); } int cork_ring_buffer_add(struct cork_ring_buffer *self, void *element) { if (cork_ring_buffer_is_full(self)) { return -1; } self->elements[self->write_index++] = element; self->size++; if (self->write_index == self->allocated_size) { self->write_index = 0; } return 0; } void * cork_ring_buffer_pop(struct cork_ring_buffer *self) { if (cork_ring_buffer_is_empty(self)) { return NULL; } else { void *result = self->elements[self->read_index++]; self->size--; if (self->read_index == self->allocated_size) { self->read_index = 0; } return result; } } void * cork_ring_buffer_peek(struct cork_ring_buffer *self) { if (cork_ring_buffer_is_empty(self)) { return NULL; } else { return self->elements[self->read_index]; } } libcork-1.0.0-rc3/src/libcork/ds/slice.c000066400000000000000000000211061367645320400177210ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2011, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #include #include "libcork/core/error.h" #include "libcork/core/types.h" #include "libcork/ds/managed-buffer.h" #include "libcork/ds/slice.h" #include "libcork/helpers/errors.h" /*----------------------------------------------------------------------- * Error handling */ static void cork_slice_invalid_slice_set(size_t buf_size, size_t requested_offset, size_t requested_length) { cork_error_set (CORK_SLICE_ERROR, CORK_SLICE_INVALID_SLICE, "Cannot slice %zu-byte buffer at %zu:%zu", buf_size, requested_offset, requested_length); } /*----------------------------------------------------------------------- * Slices */ void cork_slice_clear(struct cork_slice *slice) { slice->buf = NULL; slice->size = 0; slice->iface = NULL; slice->user_data = NULL; } int cork_slice_copy(struct cork_slice *dest, const struct cork_slice *slice, size_t offset, size_t length) { if ((slice != NULL) && (offset <= slice->size) && ((offset + length) <= slice->size)) { /* DEBUG("Slicing <%p:%zu> at %zu:%zu, gives <%p:%zu>", slice->buf, slice->size, offset, length, slice->buf + offset, length); */ return slice->iface->copy(dest, slice, offset, length); } else { /* DEBUG("Cannot slice <%p:%zu> at %zu:%zu", slice->buf, slice->size, offset, length); */ cork_slice_clear(dest); cork_slice_invalid_slice_set ((slice == NULL)? 0: slice->size, offset, length); return -1; } } int cork_slice_copy_offset(struct cork_slice *dest, const struct cork_slice *slice, size_t offset) { if (slice == NULL) { cork_slice_clear(dest); cork_slice_invalid_slice_set(0, offset, 0); return -1; } else { return cork_slice_copy (dest, slice, offset, slice->size - offset); } } int cork_slice_light_copy(struct cork_slice *dest, const struct cork_slice *slice, size_t offset, size_t length) { if ((slice != NULL) && (offset <= slice->size) && ((offset + length) <= slice->size)) { /* DEBUG("Slicing <%p:%zu> at %zu:%zu, gives <%p:%zu>", slice->buf, slice->size, offset, length, slice->buf + offset, length); */ return slice->iface->light_copy(dest, slice, offset, length); } else { /* DEBUG("Cannot slice <%p:%zu> at %zu:%zu", slice->buf, slice->size, offset, length); */ cork_slice_clear(dest); cork_slice_invalid_slice_set ((slice == NULL)? 0: slice->size, offset, length); return -1; } } int cork_slice_light_copy_offset(struct cork_slice *dest, const struct cork_slice *slice, size_t offset) { if (slice == NULL) { cork_slice_clear(dest); cork_slice_invalid_slice_set(0, offset, 0); return -1; } else { return cork_slice_light_copy (dest, slice, offset, slice->size - offset); } } int cork_slice_slice(struct cork_slice *slice, size_t offset, size_t length) { if ((slice != NULL) && (offset <= slice->size) && ((offset + length) <= slice->size)) { /* DEBUG("Slicing <%p:%zu> at %zu:%zu, gives <%p:%zu>", slice->buf, slice->size, offset, length, slice->buf + offset, length); */ if (slice->iface->slice == NULL) { slice->buf += offset; slice->size = length; return 0; } else { return slice->iface->slice(slice, offset, length); } } else { /* DEBUG("Cannot slice <%p:%zu> at %zu:%zu", slice->buf, slice->size, offset, length); */ if (slice != NULL) cork_slice_invalid_slice_set(slice->size, offset, length); return -1; } } int cork_slice_slice_offset(struct cork_slice *slice, size_t offset) { if (slice == NULL) { cork_slice_invalid_slice_set(0, offset, 0); return -1; } else { return cork_slice_slice (slice, offset, slice->size - offset); } } const void* cork_slice_advance_checked(struct cork_slice* slice, size_t offset) { if (slice == NULL) { cork_slice_invalid_slice_set(0, offset, 0); return NULL; } else { const void* buf = slice->buf; rpi_check(cork_slice_slice(slice, offset, slice->size - offset)); return buf; } } void cork_slice_finish(struct cork_slice *slice) { /* DEBUG("Finalizing <%p:%zu>", dest->buf, dest->size); */ if (slice->iface != NULL && slice->iface->free != NULL) { slice->iface->free(slice); } cork_slice_clear(slice); } bool cork_slice_equal(const struct cork_slice *slice1, const struct cork_slice *slice2) { if (slice1 == slice2) { return true; } if (slice1->size != slice2->size) { return false; } return (memcmp(slice1->buf, slice2->buf, slice1->size) == 0); } /*----------------------------------------------------------------------- * Slices of static content */ static struct cork_slice_iface cork_static_slice; static int cork_static_slice_copy(struct cork_slice *dest, const struct cork_slice *src, size_t offset, size_t length) { dest->buf = src->buf + offset; dest->size = length; dest->iface = &cork_static_slice; dest->user_data = NULL; return 0; } static struct cork_slice_iface cork_static_slice = { NULL, cork_static_slice_copy, cork_static_slice_copy, NULL }; void cork_slice_init_static(struct cork_slice *dest, const void *buf, size_t size) { dest->buf = buf; dest->size = size; dest->iface = &cork_static_slice; dest->user_data = NULL; } /*----------------------------------------------------------------------- * Copy-once slices */ static struct cork_slice_iface cork_copy_once_slice; static int cork_copy_once_slice__copy(struct cork_slice *dest, const struct cork_slice *src, size_t offset, size_t length) { struct cork_managed_buffer *mbuf = cork_managed_buffer_new_copy(src->buf, src->size); rii_check(cork_managed_buffer_slice(dest, mbuf, offset, length)); rii_check(cork_managed_buffer_slice ((struct cork_slice *) src, mbuf, 0, src->size)); cork_managed_buffer_unref(mbuf); return 0; } static int cork_copy_once_slice__light_copy(struct cork_slice *dest, const struct cork_slice *src, size_t offset, size_t length) { dest->buf = src->buf + offset; dest->size = length; dest->iface = &cork_copy_once_slice; dest->user_data = NULL; return 0; } static struct cork_slice_iface cork_copy_once_slice = { NULL, cork_copy_once_slice__copy, cork_copy_once_slice__light_copy, NULL }; void cork_slice_init_copy_once(struct cork_slice *dest, const void *buf, size_t size) { dest->buf = buf; dest->size = size; dest->iface = &cork_copy_once_slice; dest->user_data = NULL; } /*----------------------------------------------------------------------- * Inline declarations */ bool cork_slice_is_empty(const struct cork_slice* slice); int cork_slice_copy_fast(struct cork_slice* dest, const struct cork_slice* slice, size_t offset, size_t length); int cork_slice_copy_offset_fast(struct cork_slice *dest, const struct cork_slice *slice, size_t offset); int cork_slice_light_copy_fast(struct cork_slice *dest, const struct cork_slice *slice, size_t offset, size_t length); int cork_slice_light_copy_offset_fast(struct cork_slice *dest, const struct cork_slice *slice, size_t offset); int cork_slice_slice_fast(struct cork_slice *slice, size_t offset, size_t length); int cork_slice_slice_offset_fast(struct cork_slice *slice, size_t offset); const void* cork_slice_advance(struct cork_slice *slice, size_t offset); libcork-1.0.0-rc3/src/libcork/ds/stream.c000066400000000000000000000013431367645320400201160ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2020, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #include "libcork/ds/stream.h" /*----------------------------------------------------------------------- * Inline declarations */ int cork_stream_consumer_data(struct cork_stream_consumer* consumer, const void *buf, size_t size, bool is_first_chunk); int cork_stream_consumer_eof(struct cork_stream_consumer *consumer); void cork_stream_consumer_free(struct cork_stream_consumer *consumer); libcork-1.0.0-rc3/src/libcork/posix/000077500000000000000000000000001367645320400172125ustar00rootroot00000000000000libcork-1.0.0-rc3/src/libcork/posix/directory-walker.c000066400000000000000000000074061367645320400226540ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2012, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #include #include #include #include #include #include #include #include "libcork/core/attributes.h" #include "libcork/core/error.h" #include "libcork/core/types.h" #include "libcork/ds/buffer.h" #include "libcork/helpers/errors.h" #include "libcork/helpers/posix.h" #include "libcork/os/files.h" static int cork_walk_one_directory(struct cork_dir_walker *w, struct cork_buffer *path, size_t root_path_size) { DIR *dir = NULL; struct dirent *entry; size_t dir_path_size; rip_check_posix(dir = opendir(path->buf)); cork_buffer_append(path, "/", 1); dir_path_size = path->size; errno = 0; while ((entry = readdir(dir)) != NULL) { struct stat info; /* Skip the "." and ".." entries */ if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { continue; } /* Stat the directory entry */ cork_buffer_append_string(path, entry->d_name); ei_check_posix(stat(path->buf, &info)); /* If the entry is a subdirectory, recurse into it. */ if (S_ISDIR(info.st_mode)) { int rc = cork_dir_walker_enter_directory (w, path->buf, path->buf + root_path_size, path->buf + dir_path_size); if (rc != CORK_SKIP_DIRECTORY) { ei_check(cork_walk_one_directory(w, path, root_path_size)); ei_check(cork_dir_walker_leave_directory (w, path->buf, path->buf + root_path_size, path->buf + dir_path_size)); } } else if (S_ISREG(info.st_mode)) { ei_check(cork_dir_walker_file (w, path->buf, path->buf + root_path_size, path->buf + dir_path_size)); } /* Remove this entry name from the path buffer. */ cork_buffer_truncate(path, dir_path_size); /* We have to reset errno to 0 because of the ambiguous way * readdir uses a return value of NULL. Other functions may * return normally yet set errno to a non-zero value. dlopen * on Mac OS X is an ogreish example. Since an error readdir * is indicated by returning NULL and setting errno to indicate * the error, then we need to reset it to zero before each call. * We shall assume, perhaps to our great misery, that functions * within this loop do proper error checking and act accordingly. */ errno = 0; } /* Check errno immediately after the while loop terminates */ if (CORK_UNLIKELY(errno != 0)) { cork_system_error_set(); goto error; } /* Remove the trailing '/' from the path buffer. */ cork_buffer_truncate(path, dir_path_size - 1); rii_check_posix(closedir(dir)); return 0; error: if (dir != NULL) { rii_check_posix(closedir(dir)); } return -1; } int cork_walk_directory(const char *path, struct cork_dir_walker *w) { int rc; char *p; struct cork_buffer buf = CORK_BUFFER_INIT(); /* Seed the buffer with the directory's path, ensuring that there's no * trailing '/' */ cork_buffer_append_string(&buf, path); p = buf.buf; while (p[buf.size-1] == '/') { buf.size--; p[buf.size] = '\0'; } rc = cork_walk_one_directory(w, &buf, buf.size + 1); cork_buffer_done(&buf); return rc; } libcork-1.0.0-rc3/src/libcork/posix/env.c000066400000000000000000000122141367645320400201460ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2013, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #include #include #include #include "libcork/core.h" #include "libcork/ds.h" #include "libcork/os/subprocess.h" #include "libcork/helpers/errors.h" #ifdef __CYGWIN__ #include #endif #if defined(__APPLE__) /* Apple doesn't provide access to the "environ" variable from a shared library. * There's a workaround function to grab the environ pointer described at [1]. * * [1] http://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man7/environ.7.html */ #include #define environ (*_NSGetEnviron()) #else /* On all other POSIX platforms, we assume that environ is available in shared * libraries. */ extern char **environ; #endif struct cork_env_var { const char *name; const char *value; }; static struct cork_env_var * cork_env_var_new(const char *name, const char *value) { struct cork_env_var *var = cork_new(struct cork_env_var); var->name = cork_strdup(name); var->value = cork_strdup(value); return var; } static void cork_env_var_free(void *vvar) { struct cork_env_var *var = vvar; cork_strfree(var->name); cork_strfree(var->value); cork_delete(struct cork_env_var, var); } struct cork_env { struct cork_hash_table *variables; struct cork_buffer buffer; }; struct cork_env * cork_env_new(void) { struct cork_env *env = cork_new(struct cork_env); env->variables = cork_string_hash_table_new(0, 0); cork_hash_table_set_free_value(env->variables, cork_env_var_free); cork_buffer_init(&env->buffer); return env; } static void cork_env_add_internal(struct cork_env *env, const char *name, const char *value) { if (env == NULL) { setenv(name, value, true); } else { struct cork_env_var *var = cork_env_var_new(name, value); void *old_var; cork_hash_table_put (env->variables, (void *) var->name, var, NULL, NULL, &old_var); if (old_var != NULL) { cork_env_var_free(old_var); } } } struct cork_env * cork_env_clone_current(void) { char **curr; struct cork_env *env = cork_env_new(); for (curr = environ; *curr != NULL; curr++) { const char *entry = *curr; const char *equal; equal = strchr(entry, '='); if (CORK_UNLIKELY(equal == NULL)) { /* This environment entry is malformed; skip it. */ continue; } /* Make a copy of the name so that it's NUL-terminated rather than * equal-terminated. */ cork_buffer_set(&env->buffer, entry, equal - entry); cork_env_add_internal(env, env->buffer.buf, equal + 1); } return env; } void cork_env_free(struct cork_env *env) { cork_hash_table_free(env->variables); cork_buffer_done(&env->buffer); cork_delete(struct cork_env, env); } const char * cork_env_get(struct cork_env *env, const char *name) { if (env == NULL) { return getenv(name); } else { struct cork_env_var *var = cork_hash_table_get(env->variables, (void *) name); return (var == NULL)? NULL: var->value; } } void cork_env_add(struct cork_env *env, const char *name, const char *value) { cork_env_add_internal(env, name, value); } void cork_env_add_vprintf(struct cork_env *env, const char *name, const char *format, va_list args) { cork_buffer_vprintf(&env->buffer, format, args); cork_env_add_internal(env, name, env->buffer.buf); } void cork_env_add_printf(struct cork_env *env, const char *name, const char *format, ...) { va_list args; va_start(args, format); cork_env_add_vprintf(env, name, format, args); va_end(args); } void cork_env_remove(struct cork_env *env, const char *name) { if (env == NULL) { unsetenv(name); } else { void *old_var; cork_hash_table_delete(env->variables, (void *) name, NULL, &old_var); if (old_var != NULL) { cork_env_var_free(old_var); } } } static enum cork_hash_table_map_result cork_env_set_vars(void *user_data, struct cork_hash_table_entry *entry) { struct cork_env_var *var = entry->value; setenv(var->name, var->value, false); return CORK_HASH_TABLE_MAP_CONTINUE; } #if (defined(__APPLE__) || (defined(BSD) && (BSD >= 199103)) || (defined(__CYGWIN__) && CYGWIN_VERSION_API_MINOR < 326)) && !defined(__GNU__) /* A handful of platforms [1] don't provide clearenv(), so we must implement our * own version that clears the environ array directly. * * [1] http://www.gnu.org/software/gnulib/manual/html_node/clearenv.html */ static void clearenv(void) { *environ = NULL; } #else /* Otherwise assume that we have clearenv available. */ #endif void cork_env_replace_current(struct cork_env *env) { clearenv(); cork_hash_table_map(env->variables, NULL, cork_env_set_vars); } libcork-1.0.0-rc3/src/libcork/posix/exec.c000066400000000000000000000105151367645320400203040ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2013, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #include #include #include "libcork/core.h" #include "libcork/ds.h" #include "libcork/os/subprocess.h" #include "libcork/helpers/errors.h" #define ri_check_posix(call) \ do { \ while (true) { \ if ((call) == -1) { \ if (errno == EINTR) { \ continue; \ } else { \ cork_system_error_set(); \ CORK_PRINT_ERROR(); \ return -1; \ } \ } else { \ break; \ } \ } \ } while (0) struct cork_exec { const char *program; struct cork_string_array params; struct cork_env *env; const char *cwd; struct cork_buffer description; }; struct cork_exec * cork_exec_new(const char *program) { struct cork_exec *exec = cork_new(struct cork_exec); exec->program = cork_strdup(program); cork_string_array_init(&exec->params); exec->env = NULL; exec->cwd = NULL; cork_buffer_init(&exec->description); cork_buffer_set_string(&exec->description, program); return exec; } struct cork_exec * cork_exec_new_with_params(const char *program, ...) { struct cork_exec *exec; va_list args; const char *param; exec = cork_exec_new(program); cork_exec_add_param(exec, program); va_start(args, program); while ((param = va_arg(args, const char *)) != NULL) { cork_exec_add_param(exec, param); } return exec; } struct cork_exec * cork_exec_new_with_param_array(const char *program, char * const *params) { char * const *curr; struct cork_exec *exec = cork_exec_new(program); for (curr = params; *curr != NULL; curr++) { cork_exec_add_param(exec, *curr); } return exec; } void cork_exec_free(struct cork_exec *exec) { cork_strfree(exec->program); cork_array_done(&exec->params); if (exec->env != NULL) { cork_env_free(exec->env); } if (exec->cwd != NULL) { cork_strfree(exec->cwd); } cork_buffer_done(&exec->description); cork_delete(struct cork_exec, exec); } const char * cork_exec_description(struct cork_exec *exec) { return exec->description.buf; } const char * cork_exec_program(struct cork_exec *exec) { return exec->program; } size_t cork_exec_param_count(struct cork_exec *exec) { return cork_array_size(&exec->params); } const char * cork_exec_param(struct cork_exec *exec, size_t index) { return cork_array_at(&exec->params, index); } void cork_exec_add_param(struct cork_exec *exec, const char *param) { /* Don't add the first parameter to the description; that's a copy of the * program name, which we've already added. */ if (!cork_array_is_empty(&exec->params)) { cork_buffer_append(&exec->description, " ", 1); cork_buffer_append_string(&exec->description, param); } cork_array_append(&exec->params, cork_strdup(param)); } struct cork_env * cork_exec_env(struct cork_exec *exec) { return exec->env; } void cork_exec_set_env(struct cork_exec *exec, struct cork_env *env) { if (exec->env != NULL) { cork_env_free(exec->env); } exec->env = env; } const char * cork_exec_cwd(struct cork_exec *exec) { return exec->cwd; } void cork_exec_set_cwd(struct cork_exec *exec, const char *directory) { if (exec->cwd != NULL) { cork_strfree(exec->cwd); } exec->cwd = cork_strdup(directory); } int cork_exec_run(struct cork_exec *exec) { const char **params; /* Make sure the parameter array is NULL-terminated. */ cork_array_append(&exec->params, NULL); params = cork_array_elements(&exec->params); /* Fill in the requested environment */ if (exec->env != NULL) { cork_env_replace_current(exec->env); } /* Change the working directory, if requested */ if (exec->cwd != NULL) { ri_check_posix(chdir(exec->cwd)); } /* Execute the new program */ ri_check_posix(execvp(exec->program, (char * const *) params)); /* This is unreachable */ return 0; } libcork-1.0.0-rc3/src/libcork/posix/files.c000066400000000000000000000547271367645320400204770ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2013, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #ifdef __GNU__ #define _GNU_SOURCE #endif #include #include #include #include #include #include #include #include #include "libcork/core/attributes.h" #include "libcork/core/error.h" #include "libcork/core/types.h" #include "libcork/ds/array.h" #include "libcork/ds/buffer.h" #include "libcork/helpers/errors.h" #include "libcork/helpers/posix.h" #include "libcork/os/files.h" #include "libcork/os/subprocess.h" #if !defined(CORK_DEBUG_FILES) #define CORK_DEBUG_FILES 0 #endif #if CORK_DEBUG_FILES #include #define DEBUG(...) fprintf(stderr, __VA_ARGS__) #else #define DEBUG(...) /* no debug messages */ #endif /*----------------------------------------------------------------------- * Paths */ struct cork_path { struct cork_buffer given; }; static struct cork_path * cork_path_new_internal(const char *str, size_t length) { struct cork_path *path = cork_new(struct cork_path); cork_buffer_init(&path->given); if (length == 0) { cork_buffer_ensure_size(&path->given, 16); cork_buffer_set(&path->given, "", 0); } else { cork_buffer_set(&path->given, str, length); } return path; } struct cork_path * cork_path_new(const char *source) { return cork_path_new_internal(source, source == NULL? 0: strlen(source)); } struct cork_path * cork_path_clone(const struct cork_path *other) { return cork_path_new_internal(other->given.buf, other->given.size); } void cork_path_free(struct cork_path *path) { cork_buffer_done(&path->given); cork_delete(struct cork_path, path); } void cork_path_set(struct cork_path *path, const char *content) { if (content == NULL) { cork_buffer_clear(&path->given); } else { cork_buffer_set_string(&path->given, content); } } const char * cork_path_get(const struct cork_path *path) { return path->given.buf; } #define cork_path_get(path) ((const char *) (path)->given.buf) #define cork_path_size(path) ((path)->given.size) #define cork_path_truncate(path, size) \ (cork_buffer_truncate(&(path)->given, (size))) int cork_path_set_cwd(struct cork_path *path) { #ifdef __GNU__ char *dirname = get_current_dir_name(); rip_check_posix(dirname); cork_buffer_set(&path->given, dirname, strlen(dirname)); free(dirname); #else cork_buffer_ensure_size(&path->given, PATH_MAX); rip_check_posix(getcwd(path->given.buf, PATH_MAX)); path->given.size = strlen(path->given.buf); #endif return 0; } struct cork_path * cork_path_cwd(void) { struct cork_path *path = cork_path_new(NULL); ei_check(cork_path_set_cwd(path)); return path; error: cork_path_free(path); return NULL; } int cork_path_set_absolute(struct cork_path *path) { struct cork_buffer buf; if (path->given.size > 0 && cork_buffer_char(&path->given, 0) == '/') { /* The path is already absolute. */ return 0; } #ifdef __GNU__ char *dirname; dirname = get_current_dir_name(); ep_check_posix(dirname); cork_buffer_init(&buf); cork_buffer_set(&buf, dirname, strlen(dirname)); free(dirname); #else cork_buffer_init(&buf); cork_buffer_ensure_size(&buf, PATH_MAX); ep_check_posix(getcwd(buf.buf, PATH_MAX)); buf.size = strlen(buf.buf); #endif cork_buffer_append(&buf, "/", 1); cork_buffer_append_copy(&buf, &path->given); cork_buffer_done(&path->given); path->given = buf; return 0; error: cork_buffer_done(&buf); return -1; } struct cork_path * cork_path_absolute(const struct cork_path *other) { struct cork_path *path = cork_path_clone(other); ei_check(cork_path_set_absolute(path)); return path; error: cork_path_free(path); return NULL; } void cork_path_append(struct cork_path *path, const char *more) { if (more == NULL || more[0] == '\0') { return; } if (more[0] == '/') { /* If more starts with a "/", then it's absolute, and should replace * the contents of the current path. */ cork_buffer_set_string(&path->given, more); } else { /* Otherwise, more is relative, and should be appended to the current * path. If the current given path doesn't end in a "/", then we need * to add one to keep the path well-formed. */ if (path->given.size > 0 && cork_buffer_char(&path->given, path->given.size - 1) != '/') { cork_buffer_append(&path->given, "/", 1); } cork_buffer_append_string(&path->given, more); } } struct cork_path * cork_path_join(const struct cork_path *other, const char *more) { struct cork_path *path = cork_path_clone(other); cork_path_append(path, more); return path; } void cork_path_append_path(struct cork_path *path, const struct cork_path *more) { cork_path_append(path, more->given.buf); } struct cork_path * cork_path_join_path(const struct cork_path *other, const struct cork_path *more) { struct cork_path *path = cork_path_clone(other); cork_path_append_path(path, more); return path; } void cork_path_set_basename(struct cork_path *path) { char *given = path->given.buf; const char *last_slash = strrchr(given, '/'); if (last_slash != NULL) { size_t offset = last_slash - given; size_t basename_length = path->given.size - offset - 1; memmove(given, last_slash + 1, basename_length); given[basename_length] = '\0'; path->given.size = basename_length; } } struct cork_path * cork_path_basename(const struct cork_path *other) { struct cork_path *path = cork_path_clone(other); cork_path_set_basename(path); return path; } void cork_path_set_dirname(struct cork_path *path) { const char *given = path->given.buf; const char *last_slash = strrchr(given, '/'); if (last_slash == NULL) { cork_buffer_clear(&path->given); } else { size_t offset = last_slash - given; if (offset == 0) { /* A special case for the immediate subdirectories of "/" */ cork_buffer_truncate(&path->given, 1); } else { cork_buffer_truncate(&path->given, offset); } } } struct cork_path * cork_path_dirname(const struct cork_path *other) { struct cork_path *path = cork_path_clone(other); cork_path_set_dirname(path); return path; } /*----------------------------------------------------------------------- * Lists of paths */ struct cork_path_list { cork_array(struct cork_path *) array; struct cork_buffer string; }; struct cork_path_list * cork_path_list_new_empty(void) { struct cork_path_list *list = cork_new(struct cork_path_list); cork_array_init(&list->array); cork_buffer_init(&list->string); return list; } void cork_path_list_free(struct cork_path_list *list) { size_t i; for (i = 0; i < cork_array_size(&list->array); i++) { struct cork_path *path = cork_array_at(&list->array, i); cork_path_free(path); } cork_array_done(&list->array); cork_buffer_done(&list->string); cork_delete(struct cork_path_list, list); } const char * cork_path_list_to_string(const struct cork_path_list *list) { return list->string.buf; } void cork_path_list_add(struct cork_path_list *list, struct cork_path *path) { cork_array_append(&list->array, path); if (cork_array_size(&list->array) > 1) { cork_buffer_append(&list->string, ":", 1); } cork_buffer_append_string(&list->string, cork_path_get(path)); } size_t cork_path_list_size(const struct cork_path_list *list) { return cork_array_size(&list->array); } const struct cork_path * cork_path_list_get(const struct cork_path_list *list, size_t index) { return cork_array_at(&list->array, index); } static void cork_path_list_append_string(struct cork_path_list *list, const char *str) { struct cork_path *path; const char *curr = str; const char *next; while ((next = strchr(curr, ':')) != NULL) { size_t size = next - curr; path = cork_path_new_internal(curr, size); cork_path_list_add(list, path); curr = next + 1; } path = cork_path_new(curr); cork_path_list_add(list, path); } struct cork_path_list * cork_path_list_new(const char *str) { struct cork_path_list *list = cork_path_list_new_empty(); cork_path_list_append_string(list, str); return list; } /*----------------------------------------------------------------------- * Files */ struct cork_file { struct cork_path *path; struct stat stat; enum cork_file_type type; bool has_stat; }; static void cork_file_init(struct cork_file *file, struct cork_path *path) { file->path = path; file->has_stat = false; } struct cork_file * cork_file_new(const char *path) { return cork_file_new_from_path(cork_path_new(path)); } struct cork_file * cork_file_new_from_path(struct cork_path *path) { struct cork_file *file = cork_new(struct cork_file); cork_file_init(file, path); return file; } static void cork_file_reset(struct cork_file *file) { file->has_stat = false; } static void cork_file_done(struct cork_file *file) { cork_path_free(file->path); } void cork_file_free(struct cork_file *file) { cork_file_done(file); cork_delete(struct cork_file, file); } const struct cork_path * cork_file_path(struct cork_file *file) { return file->path; } static int cork_file_stat(struct cork_file *file) { if (file->has_stat) { return 0; } else { int rc; rc = stat(cork_path_get(file->path), &file->stat); if (rc == -1) { if (errno == ENOENT || errno == ENOTDIR) { file->type = CORK_FILE_MISSING; file->has_stat = true; return 0; } else { cork_system_error_set(); return -1; } } if (S_ISREG(file->stat.st_mode)) { file->type = CORK_FILE_REGULAR; } else if (S_ISDIR(file->stat.st_mode)) { file->type = CORK_FILE_DIRECTORY; } else if (S_ISLNK(file->stat.st_mode)) { file->type = CORK_FILE_SYMLINK; } else { file->type = CORK_FILE_UNKNOWN; } file->has_stat = true; return 0; } } int cork_file_exists(struct cork_file *file, bool *exists) { rii_check(cork_file_stat(file)); *exists = (file->type != CORK_FILE_MISSING); return 0; } int cork_file_type(struct cork_file *file, enum cork_file_type *type) { rii_check(cork_file_stat(file)); *type = file->type; return 0; } struct cork_file * cork_path_list_find_file(const struct cork_path_list *list, const char *rel_path) { size_t i; size_t count = cork_path_list_size(list); struct cork_file *file; for (i = 0; i < count; i++) { const struct cork_path *path = cork_path_list_get(list, i); struct cork_path *joined = cork_path_join(path, rel_path); bool exists; file = cork_file_new_from_path(joined); ei_check(cork_file_exists(file, &exists)); if (exists) { return file; } else { cork_file_free(file); } } cork_error_set_printf (ENOENT, "%s not found in %s", rel_path, cork_path_list_to_string(list)); return NULL; error: cork_file_free(file); return NULL; } /*----------------------------------------------------------------------- * Directories */ int cork_file_iterate_directory(struct cork_file *file, cork_file_directory_iterator iterator, void *user_data) { DIR *dir = NULL; struct dirent *entry; size_t dir_path_size; struct cork_path *child_path; struct cork_file child_file; rip_check_posix(dir = opendir(cork_path_get(file->path))); child_path = cork_path_clone(file->path); cork_file_init(&child_file, child_path); dir_path_size = cork_path_size(child_path); errno = 0; while ((entry = readdir(dir)) != NULL) { /* Skip the "." and ".." entries */ if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { continue; } cork_path_append(child_path, entry->d_name); ei_check(cork_file_stat(&child_file)); /* If the entry is a subdirectory, recurse into it. */ ei_check(iterator(&child_file, entry->d_name, user_data)); /* Remove this entry name from the path buffer. */ cork_path_truncate(child_path, dir_path_size); cork_file_reset(&child_file); /* We have to reset errno to 0 because of the ambiguous way readdir uses * a return value of NULL. Other functions may return normally yet set * errno to a non-zero value. dlopen on Mac OS X is an ogreish example. * Since an error readdir is indicated by returning NULL and setting * errno to indicate the error, then we need to reset it to zero before * each call. We shall assume, perhaps to our great misery, that * functions within this loop do proper error checking and act * accordingly. */ errno = 0; } /* Check errno immediately after the while loop terminates */ if (CORK_UNLIKELY(errno != 0)) { cork_system_error_set(); goto error; } cork_file_done(&child_file); rii_check_posix(closedir(dir)); return 0; error: cork_file_done(&child_file); rii_check_posix(closedir(dir)); return -1; } static int cork_file_mkdir_one(struct cork_file *file, cork_file_mode mode, unsigned int flags) { DEBUG("mkdir %s\n", cork_path_get(file->path)); /* First check if the directory already exists. */ rii_check(cork_file_stat(file)); if (file->type == CORK_FILE_DIRECTORY) { DEBUG(" Already exists!\n"); if (!(flags & CORK_FILE_PERMISSIVE)) { cork_system_error_set_explicit(EEXIST); return -1; } else { return 0; } } else if (file->type != CORK_FILE_MISSING) { DEBUG(" Exists and not a directory!\n"); cork_system_error_set_explicit(EEXIST); return -1; } /* If the caller asked for a recursive mkdir, then make sure the parent * directory exists. */ if (flags & CORK_FILE_RECURSIVE) { struct cork_path *parent = cork_path_dirname(file->path); DEBUG(" Checking parent %s\n", cork_path_get(parent)); if (parent->given.size == 0) { /* There is no parent; we're either at the filesystem root (for an * absolute path) or the current directory (for a relative one). * Either way, we can assume it already exists. */ cork_path_free(parent); } else { int rc; struct cork_file parent_file; cork_file_init(&parent_file, parent); rc = cork_file_mkdir_one (&parent_file, mode, flags | CORK_FILE_PERMISSIVE); cork_file_done(&parent_file); rii_check(rc); } } /* Create the directory already! */ DEBUG(" Creating %s\n", cork_path_get(file->path)); rii_check_posix(mkdir(cork_path_get(file->path), mode)); return 0; } int cork_file_mkdir(struct cork_file *file, cork_file_mode mode, unsigned int flags) { return cork_file_mkdir_one(file, mode, flags); } static int cork_file_remove_iterator(struct cork_file *file, const char *rel_name, void *user_data) { unsigned int *flags = user_data; return cork_file_remove(file, *flags); } int cork_file_remove(struct cork_file *file, unsigned int flags) { DEBUG("rm %s\n", cork_path_get(file->path)); rii_check(cork_file_stat(file)); if (file->type == CORK_FILE_MISSING) { if (flags & CORK_FILE_PERMISSIVE) { return 0; } else { cork_system_error_set_explicit(ENOENT); return -1; } } else if (file->type == CORK_FILE_DIRECTORY) { if (flags & CORK_FILE_RECURSIVE) { /* The user asked that we delete the contents of the directory * first. */ rii_check(cork_file_iterate_directory (file, cork_file_remove_iterator, &flags)); } rii_check_posix(rmdir(cork_path_get(file->path))); return 0; } else { rii_check(unlink(cork_path_get(file->path))); return 0; } } /*----------------------------------------------------------------------- * Lists of files */ struct cork_file_list { cork_array(struct cork_file *) array; }; struct cork_file_list * cork_file_list_new_empty(void) { struct cork_file_list *list = cork_new(struct cork_file_list); cork_array_init(&list->array); return list; } void cork_file_list_free(struct cork_file_list *list) { size_t i; for (i = 0; i < cork_array_size(&list->array); i++) { struct cork_file *file = cork_array_at(&list->array, i); cork_file_free(file); } cork_array_done(&list->array); cork_delete(struct cork_file_list, list); } void cork_file_list_add(struct cork_file_list *list, struct cork_file *file) { cork_array_append(&list->array, file); } size_t cork_file_list_size(struct cork_file_list *list) { return cork_array_size(&list->array); } struct cork_file * cork_file_list_get(struct cork_file_list *list, size_t index) { return cork_array_at(&list->array, index); } struct cork_file_list * cork_file_list_new(struct cork_path_list *path_list) { struct cork_file_list *list = cork_file_list_new_empty(); size_t count = cork_path_list_size(path_list); size_t i; for (i = 0; i < count; i++) { const struct cork_path *path = cork_path_list_get(path_list, i); struct cork_file *file = cork_file_new(cork_path_get(path)); cork_array_append(&list->array, file); } return list; } struct cork_file_list * cork_path_list_find_files(const struct cork_path_list *path_list, const char *rel_path) { size_t i; size_t count = cork_path_list_size(path_list); struct cork_file_list *list = cork_file_list_new_empty(); struct cork_file *file; for (i = 0; i < count; i++) { const struct cork_path *path = cork_path_list_get(path_list, i); struct cork_path *joined = cork_path_join(path, rel_path); bool exists; file = cork_file_new_from_path(joined); ei_check(cork_file_exists(file, &exists)); if (exists) { cork_file_list_add(list, file); } else { cork_file_free(file); } } return list; error: cork_file_list_free(list); cork_file_free(file); return NULL; } /*----------------------------------------------------------------------- * Standard paths and path lists */ #define empty_string(str) ((str) == NULL || (str)[0] == '\0') struct cork_path * cork_path_home(void) { const char *path = cork_env_get(NULL, "HOME"); if (empty_string(path)) { cork_undefined("Cannot determine home directory"); return NULL; } else { return cork_path_new(path); } } struct cork_path_list * cork_path_config_paths(void) { struct cork_path_list *list = cork_path_list_new_empty(); const char *var; struct cork_path *path; /* The first entry should be the user's configuration directory. This is * specified by $XDG_CONFIG_HOME, with $HOME/.config as the default. */ var = cork_env_get(NULL, "XDG_CONFIG_HOME"); if (empty_string(var)) { ep_check(path = cork_path_home()); cork_path_append(path, ".config"); cork_path_list_add(list, path); } else { path = cork_path_new(var); cork_path_list_add(list, path); } /* The remaining entries should be the system-wide configuration * directories. These are specified by $XDG_CONFIG_DIRS, with /etc/xdg as * the default. */ var = cork_env_get(NULL, "XDG_CONFIG_DIRS"); if (empty_string(var)) { path = cork_path_new("/etc/xdg"); cork_path_list_add(list, path); } else { cork_path_list_append_string(list, var); } return list; error: cork_path_list_free(list); return NULL; } struct cork_path_list * cork_path_data_paths(void) { struct cork_path_list *list = cork_path_list_new_empty(); const char *var; struct cork_path *path; /* The first entry should be the user's data directory. This is specified * by $XDG_DATA_HOME, with $HOME/.local/share as the default. */ var = cork_env_get(NULL, "XDG_DATA_HOME"); if (empty_string(var)) { ep_check(path = cork_path_home()); cork_path_append(path, ".local/share"); cork_path_list_add(list, path); } else { path = cork_path_new(var); cork_path_list_add(list, path); } /* The remaining entries should be the system-wide configuration * directories. These are specified by $XDG_DATA_DIRS, with * /usr/local/share:/usr/share as the the default. */ var = cork_env_get(NULL, "XDG_DATA_DIRS"); if (empty_string(var)) { path = cork_path_new("/usr/local/share"); cork_path_list_add(list, path); path = cork_path_new("/usr/share"); cork_path_list_add(list, path); } else { cork_path_list_append_string(list, var); } return list; error: cork_path_list_free(list); return NULL; } struct cork_path * cork_path_user_cache_path(void) { const char *var; struct cork_path *path; /* The user's cache directory is specified by $XDG_CACHE_HOME, with * $HOME/.cache as the default. */ var = cork_env_get(NULL, "XDG_CACHE_HOME"); if (empty_string(var)) { rpp_check(path = cork_path_home()); cork_path_append(path, ".cache"); return path; } else { return cork_path_new(var); } } struct cork_path * cork_path_user_runtime_path(void) { const char *var; /* The user's cache directory is specified by $XDG_RUNTIME_DIR, with * no default given by the spec. */ var = cork_env_get(NULL, "XDG_RUNTIME_DIR"); if (empty_string(var)) { cork_undefined("Cannot determine user-specific runtime directory"); return NULL; } else { return cork_path_new(var); } } libcork-1.0.0-rc3/src/libcork/posix/process.c000066400000000000000000000067701367645320400210460ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2013, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #include #include "libcork/core.h" #include "libcork/ds.h" #include "libcork/os/process.h" #include "libcork/helpers/errors.h" #if !defined(CORK_DEBUG_PROCESS) #define CORK_DEBUG_PROCESS 0 #endif #if CORK_DEBUG_PROCESS #include #define DEBUG(...) fprintf(stderr, __VA_ARGS__) #else #define DEBUG(...) /* no debug messages */ #endif struct cork_cleanup_entry { struct cork_dllist_item item; int priority; const char *name; cork_cleanup_function function; }; static struct cork_cleanup_entry * cork_cleanup_entry_new(const char *name, int priority, cork_cleanup_function function) { struct cork_cleanup_entry *self = cork_new(struct cork_cleanup_entry); self->priority = priority; self->name = cork_strdup(name); self->function = function; return self; } static void cork_cleanup_entry_free(struct cork_cleanup_entry *self) { cork_strfree(self->name); cork_delete(struct cork_cleanup_entry, self); } static struct cork_dllist cleanup_entries = CORK_DLLIST_INIT(cleanup_entries); static bool cleanup_registered = false; static void cork_cleanup_call_one(struct cork_dllist_item *item, void *user_data) { struct cork_cleanup_entry *entry = cork_container_of(item, struct cork_cleanup_entry, item); cork_cleanup_function function = entry->function; DEBUG("Call cleanup function [%d] %s\n", entry->priority, entry->name); /* We need to free the entry before calling the entry's function, since one * of the functions that libcork registers frees the allocator instance that * we'd use to free the entry. If we called the function first, the * allocator would be freed before we could use it to free the entry. */ cork_cleanup_entry_free(entry); function(); } static void cork_cleanup_call_all(void) { cork_dllist_map(&cleanup_entries, cork_cleanup_call_one, NULL); } static void cork_cleanup_entry_add(struct cork_cleanup_entry *entry) { struct cork_dllist_item *curr; if (CORK_UNLIKELY(!cleanup_registered)) { atexit(cork_cleanup_call_all); cleanup_registered = true; } /* Linear search through the list of existing cleanup functions. When we * find the first existing function with a higher priority, we've found * where to insert the new function. */ for (curr = cork_dllist_start(&cleanup_entries); !cork_dllist_is_end(&cleanup_entries, curr); curr = curr->next) { struct cork_cleanup_entry *existing = cork_container_of(curr, struct cork_cleanup_entry, item); if (existing->priority > entry->priority) { cork_dllist_add_before(&existing->item, &entry->item); return; } } /* If we fall through the loop, then the new function should be appended to * the end of the list. */ cork_dllist_add(&cleanup_entries, &entry->item); } CORK_API void cork_cleanup_at_exit_named(const char *name, int priority, cork_cleanup_function function) { struct cork_cleanup_entry *entry = cork_cleanup_entry_new(name, priority, function); DEBUG("Register cleanup function [%d] %s\n", priority, name); cork_cleanup_entry_add(entry); } libcork-1.0.0-rc3/src/libcork/posix/subprocess.c000066400000000000000000000407501367645320400215540ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2012, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #include #include #include #include #include #include #include #include "libcork/core.h" #include "libcork/ds.h" #include "libcork/os/subprocess.h" #include "libcork/threads/basics.h" #include "libcork/helpers/errors.h" #include "libcork/helpers/posix.h" #if !defined(CORK_DEBUG_SUBPROCESS) #define CORK_DEBUG_SUBPROCESS 0 #endif #if CORK_DEBUG_SUBPROCESS #include #define DEBUG(...) fprintf(stderr, __VA_ARGS__) #else #define DEBUG(...) /* no debug messages */ #endif /*----------------------------------------------------------------------- * Subprocess groups */ #define BUF_SIZE 4096 struct cork_subprocess_group { cork_array(struct cork_subprocess *) subprocesses; }; struct cork_subprocess_group * cork_subprocess_group_new(void) { struct cork_subprocess_group *group = cork_new(struct cork_subprocess_group); cork_pointer_array_init (&group->subprocesses, (cork_free_f) cork_subprocess_free); return group; } void cork_subprocess_group_free(struct cork_subprocess_group *group) { cork_array_done(&group->subprocesses); cork_delete(struct cork_subprocess_group, group); } void cork_subprocess_group_add(struct cork_subprocess_group *group, struct cork_subprocess *sub) { cork_array_append(&group->subprocesses, sub); } /*----------------------------------------------------------------------- * Pipes (parent reads) */ struct cork_read_pipe { struct cork_stream_consumer *consumer; int fds[2]; bool first; }; static void cork_read_pipe_init(struct cork_read_pipe *p, struct cork_stream_consumer *consumer) { p->consumer = consumer; p->fds[0] = -1; p->fds[1] = -1; } static int cork_read_pipe_close_read(struct cork_read_pipe *p) { if (p->fds[0] != -1) { DEBUG("Closing read pipe %d\n", p->fds[0]); rii_check_posix(close(p->fds[0])); p->fds[0] = -1; } return 0; } static int cork_read_pipe_close_write(struct cork_read_pipe *p) { if (p->fds[1] != -1) { DEBUG("Closing write pipe %d\n", p->fds[1]); rii_check_posix(close(p->fds[1])); p->fds[1] = -1; } return 0; } static void cork_read_pipe_close(struct cork_read_pipe *p) { cork_read_pipe_close_read(p); cork_read_pipe_close_write(p); } static void cork_read_pipe_done(struct cork_read_pipe *p) { cork_read_pipe_close(p); } static int cork_read_pipe_open(struct cork_read_pipe *p) { if (p->consumer != NULL) { int flags; /* We want the read end of the pipe to be non-blocking. */ DEBUG("[read] Opening pipe\n"); rii_check_posix(pipe(p->fds)); DEBUG("[read] Got read=%d write=%d\n", p->fds[0], p->fds[1]); DEBUG("[read] Setting non-blocking flag on read pipe\n"); ei_check_posix(flags = fcntl(p->fds[0], F_GETFD)); flags |= O_NONBLOCK; ei_check_posix(fcntl(p->fds[0], F_SETFD, flags)); } p->first = true; return 0; error: cork_read_pipe_close(p); return -1; } static int cork_read_pipe_dup(struct cork_read_pipe *p, int fd) { if (p->fds[1] != -1) { rii_check_posix(dup2(p->fds[1], fd)); } return 0; } static int cork_read_pipe_read(struct cork_read_pipe *p, char *buf, bool *progress) { if (p->fds[0] == -1) { return 0; } do { DEBUG("[read] Reading from pipe %d\n", p->fds[0]); ssize_t bytes_read = read(p->fds[0], buf, BUF_SIZE); if (bytes_read == -1) { if (errno == EAGAIN) { /* We've exhausted all of the data currently available. */ DEBUG("[read] No more bytes without blocking\n"); return 0; } else if (errno == EINTR) { /* Interrupted by a signal; return so that our wait loop can * catch that. */ DEBUG("[read] Interrupted by signal\n"); return 0; } else { /* An actual error */ cork_system_error_set(); DEBUG("[read] Error: %s\n", cork_error_message()); return -1; } } else if (bytes_read == 0) { DEBUG("[read] End of stream\n"); *progress = true; rii_check(cork_stream_consumer_eof(p->consumer)); rii_check_posix(close(p->fds[0])); p->fds[0] = -1; return 0; } else { DEBUG("[read] Got %zd bytes\n", bytes_read); *progress = true; rii_check(cork_stream_consumer_data (p->consumer, buf, bytes_read, p->first)); p->first = false; } } while (true); } static bool cork_read_pipe_is_finished(struct cork_read_pipe *p) { return p->fds[0] == -1; } /*----------------------------------------------------------------------- * Pipes (parent writes) */ struct cork_write_pipe { struct cork_stream_consumer consumer; int fds[2]; }; static int cork_write_pipe_close_read(struct cork_write_pipe *p) { if (p->fds[0] != -1) { DEBUG("[write] Closing read pipe %d\n", p->fds[0]); rii_check_posix(close(p->fds[0])); p->fds[0] = -1; } return 0; } static int cork_write_pipe_close_write(struct cork_write_pipe *p) { if (p->fds[1] != -1) { DEBUG("[write] Closing write pipe %d\n", p->fds[1]); rii_check_posix(close(p->fds[1])); p->fds[1] = -1; } return 0; } static int cork_write_pipe__data(struct cork_stream_consumer *consumer, const void *buf, size_t size, bool is_first_chunk) { struct cork_write_pipe *p = cork_container_of(consumer, struct cork_write_pipe, consumer); rii_check_posix(write(p->fds[1], buf, size)); return 0; } static int cork_write_pipe__eof(struct cork_stream_consumer *consumer) { struct cork_write_pipe *p = cork_container_of(consumer, struct cork_write_pipe, consumer); return cork_write_pipe_close_write(p); } static void cork_write_pipe__free(struct cork_stream_consumer *consumer) { } static void cork_write_pipe_init(struct cork_write_pipe *p) { p->consumer.data = cork_write_pipe__data; p->consumer.eof = cork_write_pipe__eof; p->consumer.free = cork_write_pipe__free; p->fds[0] = -1; p->fds[1] = -1; } static void cork_write_pipe_close(struct cork_write_pipe *p) { cork_write_pipe_close_read(p); cork_write_pipe_close_write(p); } static void cork_write_pipe_done(struct cork_write_pipe *p) { cork_write_pipe_close(p); } static int cork_write_pipe_open(struct cork_write_pipe *p) { DEBUG("[write] Opening writer pipe\n"); rii_check_posix(pipe(p->fds)); DEBUG("[write] Got read=%d write=%d\n", p->fds[0], p->fds[1]); return 0; } static int cork_write_pipe_dup(struct cork_write_pipe *p, int fd) { if (p->fds[0] != -1) { rii_check_posix(dup2(p->fds[0], fd)); } return 0; } /*----------------------------------------------------------------------- * Subprocesses */ struct cork_subprocess { pid_t pid; struct cork_write_pipe stdin_pipe; struct cork_read_pipe stdout_pipe; struct cork_read_pipe stderr_pipe; void *user_data; cork_free_f free_user_data; cork_run_f run; int *exit_code; char buf[BUF_SIZE]; }; struct cork_subprocess * cork_subprocess_new(void *user_data, cork_free_f free_user_data, cork_run_f run, struct cork_stream_consumer *stdout_consumer, struct cork_stream_consumer *stderr_consumer, int *exit_code) { struct cork_subprocess *self = cork_new(struct cork_subprocess); cork_write_pipe_init(&self->stdin_pipe); cork_read_pipe_init(&self->stdout_pipe, stdout_consumer); cork_read_pipe_init(&self->stderr_pipe, stderr_consumer); self->pid = 0; self->user_data = user_data; self->free_user_data = free_user_data; self->run = run; self->exit_code = exit_code; return self; } void cork_subprocess_free(struct cork_subprocess *self) { cork_free_user_data(self); cork_write_pipe_done(&self->stdin_pipe); cork_read_pipe_done(&self->stdout_pipe); cork_read_pipe_done(&self->stderr_pipe); cork_delete(struct cork_subprocess, self); } struct cork_stream_consumer * cork_subprocess_stdin(struct cork_subprocess *self) { return &self->stdin_pipe.consumer; } /*----------------------------------------------------------------------- * Executing another program */ static int cork_exec__run(void *vself) { struct cork_exec *exec = vself; return cork_exec_run(exec); } static void cork_exec__free(void *vself) { struct cork_exec *exec = vself; cork_exec_free(exec); } struct cork_subprocess * cork_subprocess_new_exec(struct cork_exec *exec, struct cork_stream_consumer *out, struct cork_stream_consumer *err, int *exit_code) { return cork_subprocess_new (exec, cork_exec__free, cork_exec__run, out, err, exit_code); } /*----------------------------------------------------------------------- * Running subprocesses */ int cork_subprocess_start(struct cork_subprocess *self) { pid_t pid; /* Create the stdout and stderr pipes. */ if (cork_write_pipe_open(&self->stdin_pipe) == -1) { return -1; } if (cork_read_pipe_open(&self->stdout_pipe) == -1) { cork_write_pipe_close(&self->stdin_pipe); return -1; } if (cork_read_pipe_open(&self->stderr_pipe) == -1) { cork_write_pipe_close(&self->stdin_pipe); cork_read_pipe_close(&self->stdout_pipe); return -1; } /* Fork the child process. */ DEBUG("Forking child process\n"); pid = fork(); if (pid == 0) { /* Child process */ int rc; /* Close the parent's end of the pipes */ DEBUG("[child] "); cork_write_pipe_close_write(&self->stdin_pipe); DEBUG("[child] "); cork_read_pipe_close_read(&self->stdout_pipe); DEBUG("[child] "); cork_read_pipe_close_read(&self->stderr_pipe); /* Bind the stdout and stderr pipes */ if (cork_write_pipe_dup(&self->stdin_pipe, STDIN_FILENO) == -1) { _exit(EXIT_FAILURE); } if (cork_read_pipe_dup(&self->stdout_pipe, STDOUT_FILENO) == -1) { _exit(EXIT_FAILURE); } if (cork_read_pipe_dup(&self->stderr_pipe, STDERR_FILENO) == -1) { _exit(EXIT_FAILURE); } /* Run the subprocess */ rc = self->run(self->user_data); if (CORK_LIKELY(rc == 0)) { _exit(EXIT_SUCCESS); } else { fprintf(stderr, "%s\n", cork_error_message()); _exit(EXIT_FAILURE); } } else if (pid < 0) { /* Error forking */ cork_system_error_set(); return -1; } else { /* Parent process */ DEBUG(" Child PID=%d\n", (int) pid); self->pid = pid; cork_write_pipe_close_read(&self->stdin_pipe); cork_read_pipe_close_write(&self->stdout_pipe); cork_read_pipe_close_write(&self->stderr_pipe); return 0; } } static int cork_subprocess_reap(struct cork_subprocess *self, int flags, bool *progress) { int pid; int status; rii_check_posix(pid = waitpid(self->pid, &status, flags)); if (pid == self->pid) { *progress = true; self->pid = 0; if (self->exit_code != NULL) { *self->exit_code = WEXITSTATUS(status); } } return 0; } int cork_subprocess_abort(struct cork_subprocess *self) { if (self->pid > 0) { CORK_ATTR_UNUSED bool progress; DEBUG("Terminating child process %d\n", (int) self->pid); kill(self->pid, SIGTERM); return cork_subprocess_reap(self, 0, &progress); } else { return 0; } } bool cork_subprocess_is_finished(struct cork_subprocess *self) { return (self->pid == 0) && cork_read_pipe_is_finished(&self->stdout_pipe) && cork_read_pipe_is_finished(&self->stderr_pipe); } #if defined(__APPLE__) #include #define THREAD_YIELD pthread_yield_np #elif defined(__linux__) || defined(BSD) || defined(__FreeBSD_kernel__) || defined(__GNU__) #include #define THREAD_YIELD sched_yield #else #error "Unknown thread yield implementation" #endif static void cork_subprocess_yield(unsigned int *spin_count) { /* Adapted from * http://www.1024cores.net/home/lock-free-algorithms/tricks/spinning */ if (*spin_count < 10) { /* Spin-wait */ cork_pause(); } else if (*spin_count < 20) { /* A more intense spin-wait */ int i; for (i = 0; i < 50; i++) { cork_pause(); } } else if (*spin_count < 22) { THREAD_YIELD(); } else if (*spin_count < 24) { usleep(0); } else if (*spin_count < 50) { usleep(1); } else if (*spin_count < 75) { usleep((*spin_count - 49) * 1000); } else { usleep(25000); } (*spin_count)++; } static int cork_subprocess_drain_(struct cork_subprocess *self, bool *progress) { rii_check(cork_read_pipe_read(&self->stdout_pipe, self->buf, progress)); rii_check(cork_read_pipe_read(&self->stderr_pipe, self->buf, progress)); if (self->pid > 0) { return cork_subprocess_reap(self, WNOHANG, progress); } else { return 0; } } bool cork_subprocess_drain(struct cork_subprocess *self) { bool progress; cork_subprocess_drain_(self, &progress); return progress; } int cork_subprocess_wait(struct cork_subprocess *self) { unsigned int spin_count = 0; bool progress; while (!cork_subprocess_is_finished(self)) { progress = false; rii_check(cork_subprocess_drain_(self, &progress)); if (!progress) { cork_subprocess_yield(&spin_count); } } return 0; } /*----------------------------------------------------------------------- * Running subprocess groups */ static int cork_subprocess_group_terminate(struct cork_subprocess_group *group) { size_t i; for (i = 0; i < cork_array_size(&group->subprocesses); i++) { struct cork_subprocess *sub = cork_array_at(&group->subprocesses, i); rii_check(cork_subprocess_abort(sub)); } return 0; } int cork_subprocess_group_start(struct cork_subprocess_group *group) { size_t i; DEBUG("Starting subprocess group\n"); /* Start each subprocess. */ for (i = 0; i < cork_array_size(&group->subprocesses); i++) { struct cork_subprocess *sub = cork_array_at(&group->subprocesses, i); ei_check(cork_subprocess_start(sub)); } return 0; error: cork_subprocess_group_terminate(group); return -1; } int cork_subprocess_group_abort(struct cork_subprocess_group *group) { DEBUG("Aborting subprocess group\n"); return cork_subprocess_group_terminate(group); } bool cork_subprocess_group_is_finished(struct cork_subprocess_group *group) { size_t i; for (i = 0; i < cork_array_size(&group->subprocesses); i++) { struct cork_subprocess *sub = cork_array_at(&group->subprocesses, i); bool sub_finished = cork_subprocess_is_finished(sub); if (!sub_finished) { return false; } } return true; } static int cork_subprocess_group_drain_(struct cork_subprocess_group *group, bool *progress) { size_t i; for (i = 0; i < cork_array_size(&group->subprocesses); i++) { struct cork_subprocess *sub = cork_array_at(&group->subprocesses, i); rii_check(cork_subprocess_drain_(sub, progress)); } return 0; } bool cork_subprocess_group_drain(struct cork_subprocess_group *group) { bool progress = false; cork_subprocess_group_drain_(group, &progress); return progress; } int cork_subprocess_group_wait(struct cork_subprocess_group *group) { unsigned int spin_count = 0; bool progress; DEBUG("Waiting for subprocess group to finish\n"); while (!cork_subprocess_group_is_finished(group)) { progress = false; rii_check(cork_subprocess_group_drain_(group, &progress)); if (!progress) { cork_subprocess_yield(&spin_count); } } return 0; } libcork-1.0.0-rc3/src/libcork/pthreads/000077500000000000000000000000001367645320400176625ustar00rootroot00000000000000libcork-1.0.0-rc3/src/libcork/pthreads/thread.c000066400000000000000000000135461367645320400213060ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2013, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #if defined(__linux) /* This is needed on Linux to get the pthread_setname_np function. */ #if !defined(_GNU_SOURCE) #define _GNU_SOURCE 1 #endif #endif #include #include #include #include "libcork/core/allocator.h" #include "libcork/core/error.h" #include "libcork/core/types.h" #include "libcork/ds/buffer.h" #include "libcork/threads/basics.h" /*----------------------------------------------------------------------- * Current thread */ static volatile cork_thread_id last_thread_descriptor = 0; struct cork_thread { const char *name; cork_thread_id id; pthread_t thread_id; void *user_data; cork_free_f free_user_data; cork_run_f run; cork_error error_code; struct cork_buffer error_message; bool started; bool joined; }; struct cork_thread_descriptor { struct cork_thread *current_thread; cork_thread_id id; }; cork_tls(struct cork_thread_descriptor, cork_thread_descriptor); struct cork_thread * cork_current_thread_get(void) { struct cork_thread_descriptor *desc = cork_thread_descriptor_get(); return desc->current_thread; } cork_thread_id cork_current_thread_get_id(void) { struct cork_thread_descriptor *desc = cork_thread_descriptor_get(); if (CORK_UNLIKELY(desc->id == 0)) { if (desc->current_thread == NULL) { desc->id = cork_uint_atomic_add(&last_thread_descriptor, 1); } else { desc->id = desc->current_thread->id; } } return desc->id; } /*----------------------------------------------------------------------- * Threads */ struct cork_thread * cork_thread_new(const char *name, void *user_data, cork_free_f free_user_data, cork_run_f run) { struct cork_thread *self = cork_new(struct cork_thread); self->name = cork_strdup(name); self->id = cork_uint_atomic_add(&last_thread_descriptor, 1); self->user_data = user_data; self->free_user_data = free_user_data; self->run = run; self->error_code = CORK_ERROR_NONE; cork_buffer_init(&self->error_message); self->started = false; self->joined = false; return self; } static void cork_thread_free_private(struct cork_thread *self) { cork_strfree(self->name); cork_free_user_data(self); cork_buffer_done(&self->error_message); cork_delete(struct cork_thread, self); } void cork_thread_free(struct cork_thread *self) { assert(!self->started); cork_thread_free_private(self); } const char * cork_thread_get_name(struct cork_thread *self) { return self->name; } cork_thread_id cork_thread_get_id(struct cork_thread *self) { return self->id; } #define PTHREADS_MAX_THREAD_NAME_LENGTH 16 static void * cork_thread_pthread_run(void *vself) { int rc; struct cork_thread *self = vself; struct cork_thread_descriptor *desc = cork_thread_descriptor_get(); #if defined(__APPLE__) && defined(__MACH__) char thread_name[PTHREADS_MAX_THREAD_NAME_LENGTH]; #endif desc->current_thread = self; desc->id = self->id; rc = self->run(self->user_data); #if defined(__APPLE__) && defined(__MACH__) /* On Mac OS X, we set the name of the current thread, not of an arbitrary * thread of our choosing. */ strncpy(thread_name, self->name, PTHREADS_MAX_THREAD_NAME_LENGTH); thread_name[PTHREADS_MAX_THREAD_NAME_LENGTH - 1] = '\0'; pthread_setname_np(thread_name); #endif /* If an error occurred in the body of the thread, save the error into the * cork_thread object so that we can propagate that error when some calls * cork_thread_join. */ if (CORK_UNLIKELY(rc != 0)) { if (CORK_LIKELY(cork_error_occurred())) { self->error_code = cork_error_code(); cork_buffer_set_string(&self->error_message, cork_error_message()); } else { self->error_code = CORK_UNKNOWN_ERROR; cork_buffer_set_string(&self->error_message, "Unknown error"); } } return NULL; } int cork_thread_start(struct cork_thread *self) { int rc; pthread_t thread_id; #if defined(__linux) && ((__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 12)) char thread_name[PTHREADS_MAX_THREAD_NAME_LENGTH]; #endif assert(!self->started); rc = pthread_create(&thread_id, NULL, cork_thread_pthread_run, self); if (CORK_UNLIKELY(rc != 0)) { cork_system_error_set_explicit(rc); return -1; } #if defined(__linux) && ((__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 12)) /* On Linux we choose which thread to name via an explicit thread ID. * However, pthread_setname_np() isn't supported on versions of glibc * earlier than 2.12. So we need to check for a MINOR version of 12 or * higher. */ strncpy(thread_name, self->name, PTHREADS_MAX_THREAD_NAME_LENGTH); thread_name[PTHREADS_MAX_THREAD_NAME_LENGTH - 1] = '\0'; pthread_setname_np(thread_id, thread_name); #endif self->thread_id = thread_id; self->started = true; return 0; } int cork_thread_join(struct cork_thread *self) { int rc; assert(self->started && !self->joined); rc = pthread_join(self->thread_id, NULL); if (CORK_UNLIKELY(rc != 0)) { cork_system_error_set_explicit(rc); cork_thread_free_private(self); return -1; } if (CORK_UNLIKELY(self->error_code != CORK_ERROR_NONE)) { cork_error_set_printf (self->error_code, "Error from thread %s: %s", self->name, (char *) self->error_message.buf); cork_thread_free_private(self); return -1; } cork_thread_free_private(self); return 0; } libcork-1.0.0-rc3/tests/000077500000000000000000000000001367645320400147765ustar00rootroot00000000000000libcork-1.0.0-rc3/tests/.gitattributes000066400000000000000000000000461367645320400176710ustar00rootroot00000000000000*.t -whitespace cram.py -diff libcork-1.0.0-rc3/tests/.gitignore000066400000000000000000000000101367645320400167550ustar00rootroot00000000000000*.t.err libcork-1.0.0-rc3/tests/CMakeLists.txt000066400000000000000000000064601367645320400175440ustar00rootroot00000000000000# -*- coding: utf-8 -*- # ---------------------------------------------------------------------- # Copyright © 2011, libcork authors # All rights reserved. # # Please see the COPYING file in this distribution for license details. # ---------------------------------------------------------------------- include_directories( ${CMAKE_SOURCE_DIR}/include ${CMAKE_BINARY_DIR}/include ${CMAKE_CURRENT_BINARY_DIR} ) find_package(PythonInterp) #----------------------------------------------------------------------- # Build the test cases macro(make_test test_name) add_executable(${test_name} ${test_name}.c) add_test(${test_name} ${test_name}) if (ENABLE_SHARED_TESTS) target_link_libraries(${test_name} ${CHECK_LIBRARIES} libcork-shared) else (ENABLE_SHARED_TESTS) set_target_properties(${test_name} PROPERTIES COMPILE_DEFINITIONS CORK_EMBEDDED_TEST=1) target_link_libraries(${test_name} ${CHECK_LIBRARIES} libcork-static) endif (ENABLE_SHARED_TESTS) endmacro(make_test) make_test(test-array) make_test(test-bitset) make_test(test-buffer) make_test(test-core) make_test(test-dllist) make_test(test-files) make_test(test-gc) make_test(test-hash-table) make_test(test-managed-buffer) make_test(test-mempool) make_test(test-ring-buffer) make_test(test-slice) make_test(test-subprocess) make_test(test-threads) make_test(test-u128) #----------------------------------------------------------------------- # Test suite for 128-bit integers # We use Python to generate a LARGE pile of test cases for our 128-bit integer # arithmetic functions. add_custom_target(u128-test-suite) add_dependencies(test-u128 u128-test-suite) macro(make_u128_suite op) set(generator "${CMAKE_CURRENT_SOURCE_DIR}/create-u128-test-cases.py") set(test_suite_file "${CMAKE_CURRENT_BINARY_DIR}/u128-tests-${op}.c.in") add_custom_command( OUTPUT "${test_suite_file}" COMMAND "${PYTHON_EXECUTABLE}" "${generator}" "${op}" "${test_suite_file}" COMMENT "Generating u128 ${op} test cases" DEPENDS "${generator}" ) add_custom_target(u128-test-${op} DEPENDS "${test_suite_file}") add_dependencies(u128-test-suite u128-test-${op}) endmacro(make_u128_suite) make_u128_suite(eq) make_u128_suite(ne) make_u128_suite(lt) make_u128_suite(le) make_u128_suite(gt) make_u128_suite(ge) make_u128_suite(shl) make_u128_suite(shr) make_u128_suite(add) make_u128_suite(sub) #----------------------------------------------------------------------- # Command-line tests if (NOT CRAM_EXECUTABLE) set(CRAM_EXECUTABLE "${CMAKE_CURRENT_SOURCE_DIR}/cram.py") endif (NOT CRAM_EXECUTABLE) if (PYTHON_EXECUTABLE) configure_file(ccram ${CMAKE_BINARY_DIR}/ccram COPYONLY) file(GLOB_RECURSE TESTS "${CMAKE_CURRENT_SOURCE_DIR}/*.t") foreach(TEST ${TESTS}) get_filename_component(TEST_NAME "${TEST}" NAME_WE) add_test( ${TEST_NAME} ${CMAKE_COMMAND} -E chdir ${CMAKE_BINARY_DIR} ${CMAKE_BINARY_DIR}/ccram --python ${PYTHON_EXECUTABLE} --root ${CMAKE_SOURCE_DIR} --build ${CMAKE_BINARY_DIR}/src --cram ${CRAM_EXECUTABLE} --tests ${TEST} ) endforeach(TEST) else (PYTHON_EXECUTABLE) message(WARNING "Unable to find Python; skipping cram tests.") endif (PYTHON_EXECUTABLE) libcork-1.0.0-rc3/tests/COPYING.cram.txt000066400000000000000000000431101367645320400175670ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. libcork-1.0.0-rc3/tests/ccram000077500000000000000000000010651367645320400160130ustar00rootroot00000000000000#!/bin/sh set -e if [ "$1" = "--python" ]; then shift PYTHON="$1" shift else PYTHON=python fi if [ "$1" = "--root" ]; then shift ROOT="$1" shift else ROOT=$(dirname $PWD) fi if [ "$1" = "--build" ]; then shift BUILD="$1" shift else BUILD=$PWD fi if [ "$1" = "--cram" ]; then shift CRAM="$1" shift else CRAM="$ROOT/tests/cram.py" fi if [ "$1" = "--tests" ]; then shift TESTS="$1" shift else TESTS=../tests fi export ROOT PATH="$BUILD:$PATH" exec "$PYTHON" "$CRAM" "$@" "$TESTS" libcork-1.0.0-rc3/tests/cork-hash.t000066400000000000000000000003001367645320400170330ustar00rootroot00000000000000Make sure that our stable hash is really stable. $ cork-hash foo 0xf6a5c420 $ cork-hash bar 0x450e998d $ cork-hash tests.h 0x0b0628ee $ cork-hash "A longer string" 0x53a2c885 libcork-1.0.0-rc3/tests/cork-initializer.t000066400000000000000000000002731367645320400204440ustar00rootroot00000000000000We need to sort the output, since there's no guarantee about which order our initializer functions will run in. $ cork-initializer | sort Finalizer 1 Initializer 1 Initializer 2 libcork-1.0.0-rc3/tests/cork-test/000077500000000000000000000000001367645320400167115ustar00rootroot00000000000000libcork-1.0.0-rc3/tests/cork-test/cleanup.t000066400000000000000000000002241367645320400205230ustar00rootroot00000000000000 $ cork-test cleanup Cleanup function 0 Cleanup function 1 Cleanup function 2 Cleanup function 3 Cleanup function 4 Cleanup function 5 libcork-1.0.0-rc3/tests/cork-test/directory-watcher.t000066400000000000000000000025411367645320400225370ustar00rootroot00000000000000This first test makes sure to only create one file or subdirectory in each directory. That means that we don't have to sort the output to get a reproducible result, which lets us check the ordering of the different events in the callback stucture. $ mkdir test1 $ mkdir test1/a $ mkdir test1/a/b $ touch test1/a/b/c $ cork-test dir test1 Entering a (a) Entering b (a/b) c (a/b/c) (test1/a/b/c) Leaving a/b Leaving a $ cork-test dir --shallow test1 Skipping a $ mkdir test2 $ touch test2/a $ cork-test dir test2 a (a) (test2/a) $ cork-test dir --shallow test2 a (a) (test2/a) A more complex directory structure. We have to sort the output, since there's no guarantee in what order the directory walker will encounter the files. $ mkdir test3 $ mkdir test3/d1 $ mkdir test3/d2 $ touch test3/d2/a $ touch test3/d2/b $ mkdir test3/d3 $ touch test3/d3/a $ touch test3/d3/b $ touch test3/d3/c $ mkdir test3/d3/s1 $ mkdir test3/d3/s1/s2 $ touch test3/d3/s1/s2/a $ cork-test dir --only-files test3 | sort d2/a d2/b d3/a d3/b d3/c d3/s1/s2/a Test what happens when the directory doesn't exit. $ cork-test dir missing No such file or directory [1] $ cork-test dir --only-files missing No such file or directory [1] $ cork-test dir --shallow missing No such file or directory [1] libcork-1.0.0-rc3/tests/cork-test/help1-c1-s1.t000066400000000000000000000001571367645320400207340ustar00rootroot00000000000000 $ cork-test c1 s1 --help Usage: cork-test c1 s1 [] This is a pretty cool command. libcork-1.0.0-rc3/tests/cork-test/help1-c1-s2.t000066400000000000000000000001561367645320400207340ustar00rootroot00000000000000 $ cork-test c1 s2 --help Usage: cork-test c1 s2 [] This is an excellent command. libcork-1.0.0-rc3/tests/cork-test/help1-c1.t000066400000000000000000000002071367645320400204070ustar00rootroot00000000000000 $ cork-test c1 --help Usage: cork-test c1 [] Available commands: s1 Subcommand 1 s2 Subcommand 2 libcork-1.0.0-rc3/tests/cork-test/help1-c2.t000066400000000000000000000001511367645320400204060ustar00rootroot00000000000000 $ cork-test c2 --help Usage: cork-test c2 [] This command is pretty decent. libcork-1.0.0-rc3/tests/cork-test/help1-root.t000066400000000000000000000007651367645320400211000ustar00rootroot00000000000000 $ cork-test --help Usage: cork-test [] Available commands: c1 Command 1 (now with subcommands) c2 Command 2 pwd Print working directory mkdir Create a directory rm Remove a file or directory find Search for a file in a list of directories paths Print out standard paths for the current user dir Print the contents of a directory sub Run a subcommand cleanup Test process cleanup functions libcork-1.0.0-rc3/tests/cork-test/help2-c1-s1.t000066400000000000000000000001531367645320400207310ustar00rootroot00000000000000 $ cork-test c1 s1 -h Usage: cork-test c1 s1 [] This is a pretty cool command. libcork-1.0.0-rc3/tests/cork-test/help2-c1-s2.t000066400000000000000000000001521367645320400207310ustar00rootroot00000000000000 $ cork-test c1 s2 -h Usage: cork-test c1 s2 [] This is an excellent command. libcork-1.0.0-rc3/tests/cork-test/help2-c1.t000066400000000000000000000002031367645320400204040ustar00rootroot00000000000000 $ cork-test c1 -h Usage: cork-test c1 [] Available commands: s1 Subcommand 1 s2 Subcommand 2 libcork-1.0.0-rc3/tests/cork-test/help2-c2.t000066400000000000000000000001451367645320400204120ustar00rootroot00000000000000 $ cork-test c2 -h Usage: cork-test c2 [] This command is pretty decent. libcork-1.0.0-rc3/tests/cork-test/help2-root.t000066400000000000000000000007611367645320400210750ustar00rootroot00000000000000 $ cork-test -h Usage: cork-test [] Available commands: c1 Command 1 (now with subcommands) c2 Command 2 pwd Print working directory mkdir Create a directory rm Remove a file or directory find Search for a file in a list of directories paths Print out standard paths for the current user dir Print the contents of a directory sub Run a subcommand cleanup Test process cleanup functions libcork-1.0.0-rc3/tests/cork-test/help3-c1-s1.t000066400000000000000000000001551367645320400207340ustar00rootroot00000000000000 $ cork-test help c1 s1 Usage: cork-test c1 s1 [] This is a pretty cool command. libcork-1.0.0-rc3/tests/cork-test/help3-c1-s2.t000066400000000000000000000001541367645320400207340ustar00rootroot00000000000000 $ cork-test help c1 s2 Usage: cork-test c1 s2 [] This is an excellent command. libcork-1.0.0-rc3/tests/cork-test/help3-c1.t000066400000000000000000000002051367645320400204070ustar00rootroot00000000000000 $ cork-test help c1 Usage: cork-test c1 [] Available commands: s1 Subcommand 1 s2 Subcommand 2 libcork-1.0.0-rc3/tests/cork-test/help3-c2.t000066400000000000000000000001471367645320400204150ustar00rootroot00000000000000 $ cork-test help c2 Usage: cork-test c2 [] This command is pretty decent. libcork-1.0.0-rc3/tests/cork-test/help3-root.t000066400000000000000000000007631367645320400211000ustar00rootroot00000000000000 $ cork-test help Usage: cork-test [] Available commands: c1 Command 1 (now with subcommands) c2 Command 2 pwd Print working directory mkdir Create a directory rm Remove a file or directory find Search for a file in a list of directories paths Print out standard paths for the current user dir Print the contents of a directory sub Run a subcommand cleanup Test process cleanup functions libcork-1.0.0-rc3/tests/cork-test/no-command-c1.t000066400000000000000000000002321367645320400214240ustar00rootroot00000000000000 $ cork-test c1 No command given. Usage: cork-test c1 [] Available commands: s1 Subcommand 1 s2 Subcommand 2 [1] libcork-1.0.0-rc3/tests/cork-test/no-command-root.t000066400000000000000000000010101367645320400220770ustar00rootroot00000000000000 $ cork-test No command given. Usage: cork-test [] Available commands: c1 Command 1 (now with subcommands) c2 Command 2 pwd Print working directory mkdir Create a directory rm Remove a file or directory find Search for a file in a list of directories paths Print out standard paths for the current user dir Print the contents of a directory sub Run a subcommand cleanup Test process cleanup functions [1] libcork-1.0.0-rc3/tests/cork-test/run-c1-s1-f-t.t000066400000000000000000000002531367645320400212100ustar00rootroot00000000000000 $ cork-test c1 -f foo s1 -t You chose command "c1 s1". Good for you! And you gave the --test option! Look at that. And you want the file to be foo. Sure thing. libcork-1.0.0-rc3/tests/cork-test/run-c1-s1-f.t000066400000000000000000000001671367645320400207530ustar00rootroot00000000000000 $ cork-test c1 -f foo s1 You chose command "c1 s1". Good for you! And you want the file to be foo. Sure thing. libcork-1.0.0-rc3/tests/cork-test/run-c1-s1-t.t000066400000000000000000000001641367645320400207660ustar00rootroot00000000000000 $ cork-test c1 s1 -t You chose command "c1 s1". Good for you! And you gave the --test option! Look at that. libcork-1.0.0-rc3/tests/cork-test/run-c1-s1-test.t000066400000000000000000000001701367645320400214770ustar00rootroot00000000000000 $ cork-test c1 s1 --test You chose command "c1 s1". Good for you! And you gave the --test option! Look at that. libcork-1.0.0-rc3/tests/cork-test/run-c1-s1.t000066400000000000000000000001001367645320400205130ustar00rootroot00000000000000 $ cork-test c1 s1 You chose command "c1 s1". Good for you! libcork-1.0.0-rc3/tests/cork-test/run-c1-s2-f.t000066400000000000000000000002751367645320400207540ustar00rootroot00000000000000 $ cd $TESTDIR $ cork-test c1 -f ../test-input.txt s2 You chose command "c1 s2". Fantastico! And you want the file to be ../test-input.txt. Sure thing. Hello, world What is up libcork-1.0.0-rc3/tests/cork-test/run-c1-s2-file.t000066400000000000000000000003011367645320400214340ustar00rootroot00000000000000 $ cd $TESTDIR $ cork-test c1 --file ../test-input.txt s2 You chose command "c1 s2". Fantastico! And you want the file to be ../test-input.txt. Sure thing. Hello, world What is up libcork-1.0.0-rc3/tests/cork-test/run-c1-s2.t000066400000000000000000000000761367645320400205300ustar00rootroot00000000000000 $ cork-test c1 s2 You chose command "c1 s2". Fantastico! libcork-1.0.0-rc3/tests/cork-test/run-c2.t000066400000000000000000000001001367645320400201730ustar00rootroot00000000000000 $ cork-test c2 You chose command "c2". That's pretty good. libcork-1.0.0-rc3/tests/cork-test/run-find-01.t000066400000000000000000000006101367645320400210330ustar00rootroot00000000000000 $ cork-test mkdir --recursive a/b/c/b $ cork-test find b a a/b $ cork-test find b a/b/c a/b/c/b $ cork-test find b a:a/b/c a/b $ cork-test find b a/b/c:a a/b/c/b $ cork-test find b/c a a/b/c $ cork-test find b/c a/b/c b/c not found in a/b/c [1] $ cork-test find d a d not found in a [1] $ cork-test find d a:a/b:a/b/c d not found in a:a/b:a/b/c [1] libcork-1.0.0-rc3/tests/cork-test/run-find-all-01.t000066400000000000000000000005551367645320400216110ustar00rootroot00000000000000 $ cork-test mkdir --recursive a/b/c/b $ cork-test find --all b a a/b $ cork-test find --all b a/b/c a/b/c/b $ cork-test find --all b a:a/b/c a/b a/b/c/b $ cork-test find --all b a/b/c:a a/b/c/b a/b $ cork-test find --all b/c a a/b/c $ cork-test find --all b/c a/b/c $ cork-test find --all d a $ cork-test find --all d a:a/b:a/b/c libcork-1.0.0-rc3/tests/cork-test/run-mkdir-01.t000066400000000000000000000007741367645320400212340ustar00rootroot00000000000000 $ cork-test mkdir a $ find a | sort a $ cork-test mkdir a $ find a | sort a $ cork-test mkdir --require a File exists [1] $ find a | sort a $ cork-test mkdir --recursive a/b/c $ find a | sort a a/b a/b/c $ cork-test mkdir --recursive a/b $ find a | sort a a/b a/b/c $ cork-test mkdir --recursive --require a/b File exists [1] $ find a | sort a a/b a/b/c $ cork-test mkdir --recursive --require a/b/d $ find a | sort a a/b a/b/c a/b/d libcork-1.0.0-rc3/tests/cork-test/run-paths-01.t000066400000000000000000000023511367645320400212360ustar00rootroot00000000000000 $ HOME= \ > cork-test paths Cannot determine home directory [1] $ HOME=/home/test \ > XDG_CONFIG_HOME= \ > XDG_DATA_HOME= \ > XDG_CONFIG_DIRS= \ > XDG_DATA_DIRS= \ > XDG_CACHE_HOME= \ > XDG_RUNTIME_DIR= \ > cork-test paths Home: /home/test Config: /home/test/.config:/etc/xdg Data: /home/test/.local/share:/usr/local/share:/usr/share Cache: /home/test/.cache Cannot determine user-specific runtime directory [1] $ HOME=/home/test \ > XDG_CONFIG_HOME= \ > XDG_DATA_HOME= \ > XDG_CONFIG_DIRS= \ > XDG_DATA_DIRS= \ > XDG_CACHE_HOME= \ > XDG_RUNTIME_DIR=/run/user/test \ > cork-test paths Home: /home/test Config: /home/test/.config:/etc/xdg Data: /home/test/.local/share:/usr/local/share:/usr/share Cache: /home/test/.cache Runtime: /run/user/test $ HOME=/home/test \ > XDG_CONFIG_HOME=/home/test/custom-config \ > XDG_DATA_HOME=/home/test/share \ > XDG_CONFIG_DIRS=/etc \ > XDG_DATA_DIRS=/usr/share \ > XDG_CACHE_HOME=/tmp/cache/test \ > XDG_RUNTIME_DIR=/run/user/test \ > cork-test paths Home: /home/test Config: /home/test/custom-config:/etc Data: /home/test/share:/usr/share Cache: /tmp/cache/test Runtime: /run/user/test libcork-1.0.0-rc3/tests/cork-test/run-pwd-01.t000066400000000000000000000000701367645320400207050ustar00rootroot00000000000000 $ a=$(cork-test pwd) $ b=$(pwd) $ [ "$a" = "$b" ] libcork-1.0.0-rc3/tests/cork-test/run-rm-01.t000066400000000000000000000007621367645320400205410ustar00rootroot00000000000000 $ cork-test mkdir --recursive --require a/b/c $ cork-test mkdir --recursive --require a/b/d $ find a 2>/dev/null | sort a a/b a/b/c a/b/d $ cork-test rm --require a/b/d $ find a 2>/dev/null | sort a a/b a/b/c $ cork-test rm --require a/b/d No such file or directory [1] $ find a 2>/dev/null | sort a a/b a/b/c $ cork-test rm --require --recursive a/b/c $ find a 2>/dev/null | sort a a/b $ cork-test rm --recursive a $ find a 2>/dev/null | sort libcork-1.0.0-rc3/tests/cork-test/run-sub-01.t000066400000000000000000000001041367645320400207020ustar00rootroot00000000000000 $ cork-test sub echo Hello world echo Hello world Hello world libcork-1.0.0-rc3/tests/cork-test/run-sub-02.t000066400000000000000000000000401367645320400207020ustar00rootroot00000000000000 $ cork-test sub false false libcork-1.0.0-rc3/tests/cork-test/run-sub-03.t000066400000000000000000000001641367645320400207120ustar00rootroot00000000000000 $ CORK_TEST_VAR="Hello world" cork-test sub sh -c 'echo $CORK_TEST_VAR' sh -c echo $CORK_TEST_VAR Hello world libcork-1.0.0-rc3/tests/cork-test/run-sub-04.t000066400000000000000000000001101367645320400207020ustar00rootroot00000000000000 $ cork-test sub bad-command bad-command No such file or directory libcork-1.0.0-rc3/tests/cork-test/run-sub-05.t000066400000000000000000000000451367645320400207120ustar00rootroot00000000000000 $ cork-test sub -d / pwd pwd / libcork-1.0.0-rc3/tests/cork-test/run-sub-06.t000066400000000000000000000000511367645320400207100ustar00rootroot00000000000000 $ cork-test sub -i foo cat cat foo libcork-1.0.0-rc3/tests/cram.py000077500000000000000000000436521367645320400163070ustar00rootroot00000000000000#!/usr/bin/env python """Functional testing framework for command line applications""" import difflib import itertools import optparse import os import re import signal import subprocess import sys import shutil import time import tempfile try: import configparser except ImportError: import ConfigParser as configparser __all__ = ['main', 'test'] def findtests(paths): """Yield tests in paths in sorted order""" for p in paths: if os.path.isdir(p): for root, dirs, files in os.walk(p): if os.path.basename(root).startswith('.'): continue for f in sorted(files): if not f.startswith('.') and f.endswith('.t'): yield os.path.normpath(os.path.join(root, f)) else: yield os.path.normpath(p) def regex(pattern, s): """Match a regular expression or return False if invalid. >>> [bool(regex(r, 'foobar')) for r in ('foo.*', '***')] [True, False] """ try: return re.match(pattern + r'\Z', s) except re.error: return False def glob(el, l): r"""Match a glob-like pattern. The only supported special characters are * and ?. Escaping is supported. >>> bool(glob(r'\* \\ \? fo?b*', '* \\ ? foobar')) True """ i, n = 0, len(el) res = '' while i < n: c = el[i] i += 1 if c == '\\' and el[i] in '*?\\': res += el[i - 1:i + 1] i += 1 elif c == '*': res += '.*' elif c == '?': res += '.' else: res += re.escape(c) return regex(res, l) annotations = {'glob': glob, 're': regex} def match(el, l): """Match patterns based on annotations""" for k in annotations: ann = ' (%s)\n' % k if el.endswith(ann) and annotations[k](el[:-len(ann)], l[:-1]): return True return False class SequenceMatcher(difflib.SequenceMatcher, object): """Like difflib.SequenceMatcher, but matches globs and regexes""" def find_longest_match(self, alo, ahi, blo, bhi): """Find longest matching block in a[alo:ahi] and b[blo:bhi]""" # SequenceMatcher uses find_longest_match() to slowly whittle down # the differences between a and b until it has each matching block. # Because of this, we can end up doing the same matches many times. matches = [] for n, (el, line) in enumerate(zip(self.a[alo:ahi], self.b[blo:bhi])): if el != line and match(el, line): # This fools the superclass's method into thinking that the # regex/glob in a is identical to b by replacing a's line (the # expected output) with b's line (the actual output). self.a[alo + n] = line matches.append((n, el)) ret = super(SequenceMatcher, self).find_longest_match(alo, ahi, blo, bhi) # Restore the lines replaced above. Otherwise, the diff output # would seem to imply that the tests never had any regexes/globs. for n, el in matches: self.a[alo + n] = el return ret def unified_diff(a, b, fromfile='', tofile='', fromfiledate='', tofiledate='', n=3, lineterm='\n', matcher=SequenceMatcher): """Compare two sequences of lines; generate the delta as a unified diff. This is like difflib.unified_diff(), but allows custom matchers. """ started = False for group in matcher(None, a, b).get_grouped_opcodes(n): if not started: fromdate = fromfiledate and '\t%s' % fromfiledate or '' todate = fromfiledate and '\t%s' % tofiledate or '' yield '--- %s%s%s' % (fromfile, fromdate, lineterm) yield '+++ %s%s%s' % (tofile, todate, lineterm) started = True i1, i2, j1, j2 = group[0][1], group[-1][2], group[0][3], group[-1][4] yield "@@ -%d,%d +%d,%d @@%s" % (i1 + 1, i2 - i1, j1 + 1, j2 - j1, lineterm) for tag, i1, i2, j1, j2 in group: if tag == 'equal': for line in a[i1:i2]: yield ' ' + line continue if tag == 'replace' or tag == 'delete': for line in a[i1:i2]: yield '-' + line if tag == 'replace' or tag == 'insert': for line in b[j1:j2]: yield '+' + line needescape = re.compile(r'[\x00-\x09\x0b-\x1f\x7f-\xff]').search escapesub = re.compile(r'[\x00-\x09\x0b-\x1f\\\x7f-\xff]').sub escapemap = dict((chr(i), r'\x%02x' % i) for i in range(256)) escapemap.update({'\\': '\\\\', '\r': r'\r', '\t': r'\t'}) def escape(s): """Like the string-escape codec, but doesn't escape quotes""" return escapesub(lambda m: escapemap[m.group(0)], s[:-1]) + ' (esc)\n' def makeresetsigpipe(): """Make a function to reset SIGPIPE to SIG_DFL (for use in subprocesses). Doing subprocess.Popen(..., preexec_fn=makeresetsigpipe()) will prevent Python's SIGPIPE handler (SIG_IGN) from being inherited by the child process. """ if sys.platform == 'win32' or getattr(signal, 'SIGPIPE', None) is None: return None return lambda: signal.signal(signal.SIGPIPE, signal.SIG_DFL) def test(path, shell, indent=2): """Run test at path and return input, output, and diff. This returns a 3-tuple containing the following: (list of lines in test, same list with actual output, diff) diff is a generator that yields the diff between the two lists. If a test exits with return code 80, the actual output is set to None and diff is set to []. """ indent = ' ' * indent cmdline = '%s$ ' % indent conline = '%s> ' % indent f = open(path) abspath = os.path.abspath(path) env = os.environ.copy() env['TESTDIR'] = os.path.dirname(abspath) env['TESTFILE'] = os.path.basename(abspath) p = subprocess.Popen([shell, '-'], bufsize=-1, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, env=env, preexec_fn=makeresetsigpipe(), close_fds=os.name == 'posix') salt = 'CRAM%s' % time.time() after = {} refout, postout = [], [] i = pos = prepos = -1 stdin = [] for i, line in enumerate(f): refout.append(line) if line.startswith(cmdline): after.setdefault(pos, []).append(line) prepos = pos pos = i stdin.append('echo "\n%s %s $?"\n' % (salt, i)) stdin.append(line[len(cmdline):]) elif line.startswith(conline): after.setdefault(prepos, []).append(line) stdin.append(line[len(conline):]) elif not line.startswith(indent): after.setdefault(pos, []).append(line) stdin.append('echo "\n%s %s $?"\n' % (salt, i + 1)) output = p.communicate(input=''.join(stdin))[0] if p.returncode == 80: return (refout, None, []) # Add a trailing newline to the input script if it's missing. if refout and not refout[-1].endswith('\n'): refout[-1] += '\n' # We use str.split instead of splitlines to get consistent # behavior between Python 2 and 3. In 3, we use unicode strings, # which has more line breaks than \n and \r. pos = -1 ret = 0 for i, line in enumerate(output[:-1].split('\n')): line += '\n' if line.startswith(salt): presalt = postout.pop() if presalt != '%s\n' % indent: postout.append(presalt[:-1] + ' (no-eol)\n') ret = int(line.split()[2]) if ret != 0: postout.append('%s[%s]\n' % (indent, ret)) postout += after.pop(pos, []) pos = int(line.split()[1]) else: if needescape(line): line = escape(line) postout.append(indent + line) postout += after.pop(pos, []) diffpath = os.path.basename(abspath) diff = unified_diff(refout, postout, diffpath, diffpath + '.err') for firstline in diff: return refout, postout, itertools.chain([firstline], diff) return refout, postout, [] def prompt(question, answers, auto=None): """Write a prompt to stdout and ask for answer in stdin. answers should be a string, with each character a single answer. An uppercase letter is considered the default answer. If an invalid answer is given, this asks again until it gets a valid one. If auto is set, the question is answered automatically with the specified value. """ default = [c for c in answers if c.isupper()] while True: sys.stdout.write('%s [%s] ' % (question, answers)) sys.stdout.flush() if auto is not None: sys.stdout.write(auto + '\n') sys.stdout.flush() return auto answer = sys.stdin.readline().strip().lower() if not answer and default: return default[0] elif answer and answer in answers.lower(): return answer def log(msg=None, verbosemsg=None, verbose=False): """Write msg to standard out and flush. If verbose is True, write verbosemsg instead. """ if verbose: msg = verbosemsg if msg: sys.stdout.write(msg) sys.stdout.flush() def patch(cmd, diff, path): """Run echo [lines from diff] | cmd -p0""" p = subprocess.Popen([cmd, '-p0'], bufsize=-1, stdin=subprocess.PIPE, universal_newlines=True, preexec_fn=makeresetsigpipe(), cwd=path, close_fds=os.name == 'posix') p.communicate(''.join(diff)) return p.returncode == 0 def run(paths, tmpdir, shell, quiet=False, verbose=False, patchcmd=None, answer=None, indent=2): """Run tests in paths in tmpdir. If quiet is True, diffs aren't printed. If verbose is True, filenames and status information are printed. If patchcmd is set, a prompt is written to stdout asking if changed output should be merged back into the original test. The answer is read from stdin. If 'y', the test is patched using patch based on the changed output. """ cwd = os.getcwd() seen = set() basenames = set() skipped = failed = 0 for i, path in enumerate(findtests(paths)): abspath = os.path.abspath(path) if abspath in seen: continue seen.add(abspath) log(None, '%s: ' % path, verbose) if not os.stat(abspath).st_size: skipped += 1 log('s', 'empty\n', verbose) else: basename = os.path.basename(path) if basename in basenames: basename = '%s-%s' % (basename, i) else: basenames.add(basename) testdir = os.path.join(tmpdir, basename) os.mkdir(testdir) try: os.chdir(testdir) refout, postout, diff = test(abspath, shell, indent) finally: os.chdir(cwd) errpath = abspath + '.err' if postout is None: skipped += 1 log('s', 'skipped\n', verbose) elif not diff: log('.', 'passed\n', verbose) if os.path.exists(errpath): os.remove(errpath) else: failed += 1 log('!', 'failed\n', verbose) if not quiet: log('\n', None, verbose) errfile = open(errpath, 'w') try: for line in postout: errfile.write(line) finally: errfile.close() if not quiet: if patchcmd: diff = list(diff) for line in diff: log(line) if (patchcmd and prompt('Accept this change?', 'yN', answer) == 'y'): if patch(patchcmd, diff, os.path.dirname(abspath)): log(None, '%s: merged output\n' % path, verbose) os.remove(errpath) else: log('%s: merge failed\n' % path) log('\n', None, verbose) log('# Ran %s tests, %s skipped, %s failed.\n' % (len(seen), skipped, failed)) return bool(failed) def which(cmd): """Return the patch to cmd or None if not found""" for p in os.environ['PATH'].split(os.pathsep): path = os.path.join(p, cmd) if os.path.isfile(path) and os.access(path, os.X_OK): return os.path.abspath(path) return None def expandpath(path): """Expands ~ and environment variables in path""" return os.path.expanduser(os.path.expandvars(path)) class OptionParser(optparse.OptionParser): """Like optparse.OptionParser, but supports setting values through CRAM= and .cramrc.""" def __init__(self, *args, **kwargs): self._config_opts = {} optparse.OptionParser.__init__(self, *args, **kwargs) def add_option(self, *args, **kwargs): option = optparse.OptionParser.add_option(self, *args, **kwargs) if option.dest and option.dest != 'version': key = option.dest.replace('_', '-') self._config_opts[key] = option.action == 'store_true' return option def parse_args(self, args=None, values=None): config = configparser.RawConfigParser() config.read(expandpath(os.environ.get('CRAMRC', '.cramrc'))) defaults = {} for key, isbool in self._config_opts.items(): try: if isbool: try: value = config.getboolean('cram', key) except ValueError: value = config.get('cram', key) self.error('--%s: invalid boolean value: %r' % (key, value)) else: value = config.get('cram', key) except (configparser.NoSectionError, configparser.NoOptionError): pass else: defaults[key] = value self.set_defaults(**defaults) eargs = os.environ.get('CRAM', '').strip() if eargs: import shlex args = args or [] args += shlex.split(eargs) try: return optparse.OptionParser.parse_args(self, args, values) except optparse.OptionValueError: self.error(str(sys.exc_info()[1])) def main(args): """Main entry point. args should not contain the script name. """ p = OptionParser(usage='cram [OPTIONS] TESTS...', prog='cram') p.add_option('-V', '--version', action='store_true', help='show version information and exit') p.add_option('-q', '--quiet', action='store_true', help="don't print diffs") p.add_option('-v', '--verbose', action='store_true', help='show filenames and test status') p.add_option('-i', '--interactive', action='store_true', help='interactively merge changed test output') p.add_option('-y', '--yes', action='store_true', help='answer yes to all questions') p.add_option('-n', '--no', action='store_true', help='answer no to all questions') p.add_option('-E', '--preserve-env', action='store_true', help="don't reset common environment variables") p.add_option('--keep-tmpdir', action='store_true', help='keep temporary directories') p.add_option('--shell', action='store', default='/bin/sh', metavar='PATH', help='shell to use for running tests') p.add_option('--indent', action='store', default=2, metavar='NUM', type='int', help='number of spaces to use for indentation') opts, paths = p.parse_args(args) if opts.version: sys.stdout.write("""Cram CLI testing framework (version 0.6) Copyright (C) 2010-2011 Brodie Rao and others This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. """) return conflicts = [('-y', opts.yes, '-n', opts.no), ('-q', opts.quiet, '-i', opts.interactive)] for s1, o1, s2, o2 in conflicts: if o1 and o2: sys.stderr.write('options %s and %s are mutually exclusive\n' % (s1, s2)) return 2 patchcmd = None if opts.interactive: patchcmd = which('patch') if not patchcmd: sys.stderr.write('patch(1) required for -i\n') return 2 if not paths: sys.stdout.write(p.get_usage()) return 2 badpaths = [path for path in paths if not os.path.exists(path)] if badpaths: sys.stderr.write('no such file: %s\n' % badpaths[0]) return 2 tmpdir = os.environ['CRAMTMP'] = tempfile.mkdtemp('', 'cramtests-') proctmp = os.path.join(tmpdir, 'tmp') os.mkdir(proctmp) for s in ('TMPDIR', 'TEMP', 'TMP'): os.environ[s] = proctmp if not opts.preserve_env: for s in ('LANG', 'LC_ALL', 'LANGUAGE'): os.environ[s] = 'C' os.environ['TZ'] = 'GMT' os.environ['CDPATH'] = '' os.environ['COLUMNS'] = '80' os.environ['GREP_OPTIONS'] = '' if opts.yes: answer = 'y' elif opts.no: answer = 'n' else: answer = None try: return run(paths, tmpdir, opts.shell, opts.quiet, opts.verbose, patchcmd, answer, opts.indent) finally: if opts.keep_tmpdir: log('# Kept temporary directory: %s\n' % tmpdir) else: shutil.rmtree(tmpdir) if __name__ == '__main__': try: sys.exit(main(sys.argv[1:])) except KeyboardInterrupt: pass libcork-1.0.0-rc3/tests/create-u128-test-cases.py000066400000000000000000000106501367645320400213630ustar00rootroot00000000000000# -*- coding: utf-8 -*- # ---------------------------------------------------------------------- # Copyright © 2017, libcork authors # All rights reserved. # # Please see the COPYING file in this distribution for license details. # ---------------------------------------------------------------------- # Generates some test cases for our u128 types. from __future__ import print_function import operator import random import sys test_count = 25000 random.seed() def random_shift_count(): # We want to test values [0, 128) return int(random.random() * 128) def random_128(): result = 0 for i in range(4): result = result << 32 result = result + int(random.random() * 2**32) return result def dec_128(value): return format(value, "40d") def hex_128(value): return ("UINT64_C(0x" + format(value >> 64, "016x") + "), " + "UINT64_C(0x" + format(value & 0xffffffffffffffff, "016x") + ")") def output_one_cmp_test_case(op, op_str, lhs, rhs): result = op(lhs, rhs) result_str = " " if result else "not " result_c_str = "true" if result else "false" print() print("/* ", dec_128(lhs), sep="") print(" * ", result_str, op_str, " ", dec_128(rhs), sep="") print(" */") print("{", sep="") print(" ", hex_128(lhs), ",", sep="") print(" ", hex_128(rhs), ",", sep="") print(" ", result_c_str, sep="") print("},", sep="") def create_one_cmp_test_case(op, op_str): lhs = random_128() rhs = random_128() output_one_cmp_test_case(op, op_str, lhs, lhs) output_one_cmp_test_case(op, op_str, lhs, rhs) output_one_cmp_test_case(op, op_str, rhs, rhs) def create_one_shl_test_case(): lhs = random_128() rhs = random_shift_count() result = (lhs << rhs) % 2**128 print() print("/* ", dec_128(lhs), sep="") print(" * << ", rhs, sep="") print(" * = ", dec_128(result), sep="") print(" */") print("{", sep="") print(" ", hex_128(lhs), ",", sep="") print(" ", rhs, ",", sep="") print(" ", hex_128(result), sep="") print("},", sep="") def create_one_shr_test_case(): lhs = random_128() rhs = random_shift_count() result = (lhs >> rhs) % 2**128 print() print("/* ", dec_128(lhs), sep="") print(" * >> ", rhs, sep="") print(" * = ", dec_128(result), sep="") print(" */") print("{", sep="") print(" ", hex_128(lhs), ",", sep="") print(" ", rhs, ",", sep="") print(" ", hex_128(result), sep="") print("},", sep="") def create_one_add_test_case(): lhs = random_128() rhs = random_128() result = (lhs + rhs) % 2**128 print() print("/* ", dec_128(lhs), sep="") print(" * + ", dec_128(rhs), sep="") print(" * = ", dec_128(result), sep="") print(" */") print("{", sep="") print(" ", hex_128(lhs), ",", sep="") print(" ", hex_128(rhs), ",", sep="") print(" ", hex_128(result), sep="") print("},", sep="") def create_one_sub_test_case(): lhs = random_128() rhs = random_128() if lhs < rhs: lhs, rhs = rhs, lhs result = (lhs - rhs) % 2**128 print() print("/* ", dec_128(lhs), sep="") print(" * - ", dec_128(rhs), sep="") print(" * = ", dec_128(result), sep="") print(" */") print("{", sep="") print(" ", hex_128(lhs), ",", sep="") print(" ", hex_128(rhs), ",", sep="") print(" ", hex_128(result), sep="") print("},", sep="") if len(sys.argv) == 1: print("Usage: create-u128-test-cases.py [operator]") sys.exit(1) if len(sys.argv) > 2: sys.stdout = open(sys.argv[2], 'w') print("/* This file is autogenerated. DO NOT EDIT! */") for i in range(test_count): if sys.argv[1] == "eq": create_one_cmp_test_case(operator.eq, "==") elif sys.argv[1] == "ne": create_one_cmp_test_case(operator.ne, "!=") elif sys.argv[1] == "lt": create_one_cmp_test_case(operator.lt, "< ") elif sys.argv[1] == "le": create_one_cmp_test_case(operator.le, "<=") elif sys.argv[1] == "gt": create_one_cmp_test_case(operator.gt, "> ") elif sys.argv[1] == "ge": create_one_cmp_test_case(operator.ge, ">=") elif sys.argv[1] == "shl": create_one_shl_test_case() elif sys.argv[1] == "shr": create_one_shr_test_case() elif sys.argv[1] == "add": create_one_add_test_case() elif sys.argv[1] == "sub": create_one_sub_test_case() libcork-1.0.0-rc3/tests/helpers.h000066400000000000000000000044151367645320400166150ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2011, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #ifndef TESTS_HELPERS_H #define TESTS_HELPERS_H #include "libcork/core/allocator.h" #include "libcork/core/error.h" /*----------------------------------------------------------------------- * Allocators */ /* For the "embedded" tests, use a custom allocator that debugs every * allocation. For the "shared" tests, use the default allocator. */ #if CORK_EMBEDDED_TEST static void setup_allocator(void) { struct cork_alloc *debug = cork_debug_alloc_new(cork_allocator); cork_set_allocator(debug); } #else /* !CORK_EMBEDDED_TEST */ static void setup_allocator(void) { /* do nothing */ } #endif /*----------------------------------------------------------------------- * Error reporting */ #if !defined(PRINT_EXPECTED_FAILURES) #define PRINT_EXPECTED_FAILURES 1 #endif #if PRINT_EXPECTED_FAILURES #define print_expected_failure() \ printf("[expected: %s]\n", cork_error_message()); #else #define print_expected_failure() /* do nothing */ #endif #define DESCRIBE_TEST \ fprintf(stderr, "--- %s\n", __func__); #define fail_if_error(call) \ do { \ call; \ if (cork_error_occurred()) { \ fail("%s", cork_error_message()); \ } \ } while (0) #define fail_unless_error(call, ...) \ do { \ call; \ if (!cork_error_occurred()) { \ fail(__VA_ARGS__); \ } else { \ print_expected_failure(); \ } \ cork_error_clear(); \ } while (0) #define fail_unless_equal(what, format, expected, actual) \ (fail_unless((expected) == (actual), \ "%s not equal (expected " format \ ", got " format ")", \ (what), (expected), (actual))) #define fail_unless_streq(what, expected, actual) \ (fail_unless(strcmp((expected), (actual)) == 0, \ "%s not equal (expected \"%s\", got \"%s\")", \ (char *) (what), (char *) (expected), (char *) (actual))) #endif /* TESTS_HELPERS_H */ libcork-1.0.0-rc3/tests/test-array.c000066400000000000000000000303141367645320400172360ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2011, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #include #include #include #include #include #include "libcork/core/types.h" #include "libcork/ds/array.h" #include "helpers.h" /*----------------------------------------------------------------------- * Resizable arrays */ #define verify_size(array, expected_size) \ fail_unless(cork_array_size((array)) == expected_size, \ "Unexpected size of array: got %zu, expected %zu", \ cork_array_size((array)), expected_size); #define add_element(element, expected_new_size) \ fail_if_error(cork_array_append(&array, element)); \ verify_size(&array, expected_new_size); #define add_element0(element, expected_new_size, int_type) \ do { \ int_type* __element; \ fail_if_error(__element = cork_array_append_get(&array)); \ *__element = element; \ verify_size(&array, expected_new_size); \ } while (0) #define test_sum(array, expected) \ do { \ int64_t sum = 0; \ size_t i; \ for (i = 0; i < cork_array_size(array); i++) { \ sum += cork_array_at(array, i); \ } \ fail_unless(sum == expected, \ "Unexpected sum, got %ld, expected %ld", \ (long) sum, (long) expected); \ } while (0) #define test_int(int_type) \ START_TEST(test_array_##int_type) \ { \ DESCRIBE_TEST; \ \ cork_array(int_type) array; \ cork_array(int_type) copy; \ cork_array_init(&array); \ \ fail_unless(cork_array_size(&array) == 0, \ "Unexpected size of array: got %zu, expected 0", \ cork_array_size(&array)); \ \ test_sum(&array, 0); \ add_element(1, 1); \ test_sum(&array, 1); \ add_element0(2, 2, int_type); \ test_sum(&array, 3); \ add_element(3, 3); \ test_sum(&array, 6); \ add_element0(4, 4, int_type); \ test_sum(&array, 10); \ add_element0(5, 5, int_type); \ test_sum(&array, 15); \ add_element(6, 6); \ test_sum(&array, 21); \ add_element(7, 7); \ test_sum(&array, 28); \ add_element0(8, 8, int_type); \ test_sum(&array, 36); \ add_element(9, 9); \ test_sum(&array, 45); \ add_element0(10, 10, int_type); \ test_sum(&array, 55); \ \ cork_array_init(©); \ fail_if_error(cork_array_copy(©, &array, NULL, NULL)); \ test_sum(©, 55); \ \ cork_array_remove(©, 9); \ verify_size(©, 9); \ test_sum(©, 45); \ \ cork_array_remove_range(©, 2, 3); \ verify_size(©, 6); \ test_sum(©, 33); \ \ cork_array_done(&array); \ cork_array_done(©); \ } \ END_TEST test_int(int8_t) test_int(int16_t) test_int(int32_t) test_int(int64_t) /*----------------------------------------------------------------------- * String arrays */ #define add_string(element, expected_new_size) \ fail_if_error(cork_string_array_append(&array, element)); \ verify_size(&array, expected_new_size); #define test_string(array, index, expected) \ do { \ const char *actual = cork_array_at(array, index); \ fail_unless_streq("Array elements", expected, actual); \ } while (0) START_TEST(test_array_string) { DESCRIBE_TEST; struct cork_string_array array; struct cork_string_array copy; cork_string_array_init(&array); add_string("hello", 1); add_string("there", 2); add_string("world", 3); test_string(&array, 0, "hello"); test_string(&array, 1, "there"); test_string(&array, 2, "world"); cork_array_remove(&array, 0); test_string(&array, 0, "there"); test_string(&array, 1, "world"); cork_array_clear(&array); add_string("reusing", 1); add_string("entries", 2); test_string(&array, 0, "reusing"); test_string(&array, 1, "entries"); cork_string_array_init(©); cork_string_array_copy(©, &array); test_string(©, 0, "reusing"); test_string(©, 1, "entries"); cork_array_done(©); add_string("hello", 3); add_string("there", 4); add_string("world", 5); cork_array_remove_range(&array, 1, 2); test_string(&array, 0, "reusing"); test_string(&array, 1, "there"); test_string(&array, 2, "world"); cork_array_remove(&array, 2); test_string(&array, 0, "reusing"); test_string(&array, 1, "there"); cork_array_done(&array); } END_TEST /*----------------------------------------------------------------------- * Array callbacks */ struct callback_counts { size_t init; size_t done; size_t reuse; size_t remove; }; static void test_array__init(void *user_data, void *vvalue) { struct callback_counts *counts = user_data; counts->init++; } static void test_array__done(void *user_data, void *vvalue) { struct callback_counts *counts = user_data; counts->done++; } static void test_array__reuse(void *user_data, void *vvalue) { struct callback_counts *counts = user_data; counts->reuse++; } static void test_array__remove(void *user_data, void *vvalue) { struct callback_counts *counts = user_data; counts->remove++; } typedef cork_array(unsigned int) test_array; static void test_array_init(test_array *array, struct callback_counts *counts) { memset(counts, 0, sizeof(struct callback_counts)); cork_array_init(array); cork_array_set_callback_data(array, counts, NULL); cork_array_set_init(array, test_array__init); cork_array_set_done(array, test_array__done); cork_array_set_reuse(array, test_array__reuse); cork_array_set_remove(array, test_array__remove); } #define check_counts(counts, e_init, e_done, e_reuse, e_remove) \ do { \ fail_unless_equal \ ("init counts", "%zu", (size_t) e_init, (counts)->init); \ fail_unless_equal \ ("done counts", "%zu", (size_t) e_done, (counts)->done); \ fail_unless_equal \ ("reuse counts", "%zu", (size_t) e_reuse, (counts)->reuse); \ fail_unless_equal \ ("remove counts", "%zu", (size_t) e_remove, (counts)->remove); \ } while (0) START_TEST(test_array_callbacks) { DESCRIBE_TEST; struct callback_counts counts; struct callback_counts copy_counts; test_array array; test_array copy; test_array_init(&array, &counts); check_counts(&counts, 0, 0, 0, 0); cork_array_append(&array, 0); cork_array_append(&array, 1); check_counts(&counts, 2, 0, 0, 0); cork_array_append(&array, 2); cork_array_append(&array, 3); check_counts(&counts, 4, 0, 0, 0); cork_array_clear(&array); check_counts(&counts, 4, 0, 0, 4); cork_array_append(&array, 0); cork_array_append(&array, 1); check_counts(&counts, 4, 0, 2, 4); cork_array_append(&array, 2); cork_array_append(&array, 3); check_counts(&counts, 4, 0, 4, 4); cork_array_append(&array, 4); check_counts(&counts, 5, 0, 4, 4); test_array_init(©, ©_counts); check_counts(©_counts, 0, 0, 0, 0); cork_array_copy(©, &array, NULL, NULL); check_counts(©_counts, 5, 0, 0, 0); cork_array_done(©); check_counts(©_counts, 5, 5, 0, 0); test_array_init(©, ©_counts); check_counts(©_counts, 0, 0, 0, 0); cork_array_append(©, 0); cork_array_append(©, 1); check_counts(©_counts, 2, 0, 0, 0); cork_array_copy(©, &array, NULL, NULL); check_counts(©_counts, 5, 0, 2, 2); cork_array_done(©); check_counts(©_counts, 5, 5, 2, 2); test_array_init(©, ©_counts); check_counts(©_counts, 0, 0, 0, 0); cork_array_append(©, 0); cork_array_append(©, 1); cork_array_append(©, 2); cork_array_append(©, 3); cork_array_append(©, 4); cork_array_append(©, 5); cork_array_append(©, 6); check_counts(©_counts, 7, 0, 0, 0); cork_array_copy(©, &array, NULL, NULL); check_counts(©_counts, 7, 0, 5, 7); cork_array_done(©); check_counts(©_counts, 7, 7, 5, 7); cork_array_done(&array); check_counts(&counts, 5, 5, 4, 4); } END_TEST /*----------------------------------------------------------------------- * Testing harness */ Suite * test_suite() { Suite *s = suite_create("array"); TCase *tc_ds = tcase_create("array"); tcase_add_test(tc_ds, test_array_int8_t); tcase_add_test(tc_ds, test_array_int16_t); tcase_add_test(tc_ds, test_array_int32_t); tcase_add_test(tc_ds, test_array_int64_t); tcase_add_test(tc_ds, test_array_string); tcase_add_test(tc_ds, test_array_callbacks); suite_add_tcase(s, tc_ds); return s; } int main(int argc, const char **argv) { int number_failed; Suite *suite = test_suite(); SRunner *runner = srunner_create(suite); setup_allocator(); srunner_run_all(runner, CK_NORMAL); number_failed = srunner_ntests_failed(runner); srunner_free(runner); return (number_failed == 0)? EXIT_SUCCESS: EXIT_FAILURE; } libcork-1.0.0-rc3/tests/test-bitset.c000066400000000000000000000054431367645320400174170ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2013, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #include #include #include #include #include "libcork/core/types.h" #include "libcork/ds/bitset.h" #include "helpers.h" /*----------------------------------------------------------------------- * Bit sets */ static void test_bitset_of_size(size_t bit_count) { size_t i; struct cork_bitset *set1 = cork_bitset_new(bit_count); struct cork_bitset set2; for (i = 0; i < bit_count; i++) { cork_bitset_set(set1, i, true); fail_unless(cork_bitset_get(set1, i), "Unexpected value for bit %zu", i); } for (i = 0; i < bit_count; i++) { cork_bitset_set(set1, i, false); fail_if(cork_bitset_get(set1, i), "Unexpected value for bit %zu", i); } cork_bitset_free(set1); cork_bitset_init(&set2, bit_count); for (i = 0; i < bit_count; i++) { cork_bitset_set(&set2, i, true); fail_unless(cork_bitset_get(&set2, i), "Unexpected value for bit %zu", i); } for (i = 0; i < bit_count; i++) { cork_bitset_set(&set2, i, false); fail_if(cork_bitset_get(&set2, i), "Unexpected value for bit %zu", i); } cork_bitset_done(&set2); } START_TEST(test_bitset) { DESCRIBE_TEST; /* Test a variety of sizes, with and without spillover bits. */ test_bitset_of_size(1); test_bitset_of_size(2); test_bitset_of_size(3); test_bitset_of_size(4); test_bitset_of_size(5); test_bitset_of_size(6); test_bitset_of_size(7); test_bitset_of_size(8); test_bitset_of_size(9); test_bitset_of_size(10); test_bitset_of_size(11); test_bitset_of_size(12); test_bitset_of_size(13); test_bitset_of_size(14); test_bitset_of_size(15); test_bitset_of_size(16); test_bitset_of_size(65535); test_bitset_of_size(65536); test_bitset_of_size(65537); } END_TEST /*----------------------------------------------------------------------- * Testing harness */ Suite * test_suite() { Suite *s = suite_create("bits"); TCase *tc_ds = tcase_create("bits"); tcase_set_timeout(tc_ds, 120.0); tcase_add_test(tc_ds, test_bitset); suite_add_tcase(s, tc_ds); return s; } int main(int argc, const char **argv) { int number_failed; Suite *suite = test_suite(); SRunner *runner = srunner_create(suite); setup_allocator(); srunner_run_all(runner, CK_NORMAL); number_failed = srunner_ntests_failed(runner); srunner_free(runner); return (number_failed == 0)? EXIT_SUCCESS: EXIT_FAILURE; } libcork-1.0.0-rc3/tests/test-buffer.c000066400000000000000000000242001367645320400173660ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2009, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #include #include #include #include #include "libcork/core/types.h" #include "libcork/ds/buffer.h" #include "libcork/ds/managed-buffer.h" #include "libcork/ds/stream.h" #include "helpers.h" /*----------------------------------------------------------------------- * Buffers */ static void check_buffers(const struct cork_buffer *buf1, const struct cork_buffer *buf2) { fail_unless(cork_buffer_equal(buf1, buf2), "Buffers should be equal: got %zu:%s, expected %zu:%s", buf1->size, buf1->buf, buf2->size, buf2->buf); } static void check_buffer(const struct cork_buffer *buf, const char *expected) { size_t expected_len = strlen(expected); fail_unless(buf->size == expected_len, "Unexpected buffer content: got %zu:%s, expected %zu:%s", buf->size, buf->buf, expected_len, expected); } START_TEST(test_buffer) { static char SRC[] = "Here is some text."; size_t SRC_LEN = strlen(SRC); struct cork_buffer buffer1; struct cork_buffer *buffer2; struct cork_buffer *buffer3; struct cork_buffer buffer4; cork_buffer_init(&buffer1); fail_if_error(cork_buffer_set(&buffer1, SRC, SRC_LEN)); fail_unless(cork_buffer_char(&buffer1, 0) == 'H', "Unexpected character at position 0: got %c, expected %c", (int) cork_buffer_char(&buffer1, 0), (int) 'H'); fail_unless(cork_buffer_byte(&buffer1, 1) == (uint8_t) 'e', "Unexpected character at position 1: got %c, expected %c", (int) cork_buffer_byte(&buffer1, 1), (int) 'e'); fail_if_error(buffer2 = cork_buffer_new()); fail_if_error(cork_buffer_set_string(buffer2, SRC)); check_buffers(&buffer1, buffer2); fail_if_error(buffer3 = cork_buffer_new()); fail_if_error(cork_buffer_printf (buffer3, "Here is %s text.", "some")); check_buffers(&buffer1, buffer3); cork_buffer_init(&buffer4); cork_buffer_copy(&buffer4, &buffer1); check_buffers(&buffer1, &buffer4); cork_buffer_done(&buffer1); cork_buffer_free(buffer2); cork_buffer_free(buffer3); cork_buffer_done(&buffer4); } END_TEST START_TEST(test_buffer_append) { static char SRC1[] = "abcd"; size_t SRC1_LEN = 4; static char SRC2[] = "efg"; size_t SRC2_LEN = 3; static char SRC3[] = "hij"; static char SRC4[] = "kl"; static char EXPECTED[] = "abcdefghijkl"; struct cork_buffer buffer1; struct cork_buffer buffer2; struct cork_buffer *buffer3; cork_buffer_init(&buffer1); /* * Let's try setting some data, then clearing it, before we do our * appends. */ fail_if_error(cork_buffer_set(&buffer1, SRC2, SRC2_LEN)); cork_buffer_clear(&buffer1); /* * Okay now do the appends. */ fail_if_error(cork_buffer_append(&buffer1, SRC1, SRC1_LEN)); fail_if_error(cork_buffer_append(&buffer1, SRC2, SRC2_LEN)); fail_if_error(cork_buffer_append_string(&buffer1, SRC3)); fail_if_error(cork_buffer_append_string(&buffer1, SRC4)); cork_buffer_init(&buffer2); fail_if_error(cork_buffer_set_string(&buffer2, EXPECTED)); check_buffers(&buffer1, &buffer2); fail_if_error(buffer3 = cork_buffer_new()); fail_if_error(cork_buffer_set(buffer3, SRC1, SRC1_LEN)); fail_if_error(cork_buffer_append_printf (buffer3, "%s%s%s", SRC2, SRC3, SRC4)); check_buffers(&buffer1, buffer3); fail_unless(cork_buffer_equal(&buffer1, buffer3), "Buffers should be equal: got %zu:%s, expected %zu:%s", buffer1.size, buffer1.buf, buffer3->size, buffer3->buf); cork_buffer_done(&buffer1); cork_buffer_done(&buffer2); cork_buffer_free(buffer3); } END_TEST START_TEST(test_buffer_slicing) { static char SRC[] = "Here is some text."; struct cork_buffer *buffer; struct cork_managed_buffer *managed; struct cork_slice slice1; struct cork_slice slice2; fail_if_error(buffer = cork_buffer_new()); fail_if_error(cork_buffer_set_string(buffer, SRC)); fail_if_error(managed = cork_buffer_to_managed_buffer (buffer)); cork_managed_buffer_unref(managed); fail_if_error(buffer = cork_buffer_new()); fail_if_error(cork_buffer_set_string(buffer, SRC)); fail_if_error(cork_buffer_to_slice(buffer, &slice1)); fail_if_error(cork_slice_copy_offset(&slice2, &slice1, 2)); cork_slice_finish(&slice2); fail_if_error(cork_slice_copy_offset(&slice2, &slice1, buffer->size)); cork_slice_finish(&slice2); fail_if_error(cork_slice_copy_fast(&slice2, &slice1, 2, 2)); cork_slice_finish(&slice2); fail_if_error(cork_slice_copy_offset_fast(&slice2, &slice1, 2)); cork_slice_finish(&slice2); fail_if_error(cork_slice_copy_offset(&slice2, &slice1, 0)); fail_if_error(cork_slice_slice_offset_fast(&slice2, 2)); fail_if_error(cork_slice_slice_fast(&slice2, 0, 2)); fail_if_error(cork_slice_slice(&slice1, 2, 2)); fail_unless(cork_slice_equal(&slice1, &slice2), "Slices should be equal"); cork_slice_finish(&slice2); cork_slice_finish(&slice1); } END_TEST START_TEST(test_buffer_stream) { static char SRC1[] = "abcd"; size_t SRC1_LEN = 4; static char SRC2[] = "efg"; size_t SRC2_LEN = 3; static char EXPECTED[] = "000abcdefg"; static size_t EXPECTED_SIZE = 10; struct cork_buffer buffer1; struct cork_buffer buffer2; struct cork_stream_consumer *consumer; cork_buffer_init(&buffer1); cork_buffer_append_string(&buffer1, "000"); fail_if_error(consumer = cork_buffer_to_stream_consumer(&buffer1)); /* chunk #1 */ fail_if_error(cork_stream_consumer_data(consumer, SRC1, SRC1_LEN, true)); /* chunk #2 */ fail_if_error(cork_stream_consumer_data(consumer, SRC2, SRC2_LEN, false)); /* eof */ fail_if_error(cork_stream_consumer_eof(consumer)); /* check the result */ cork_buffer_init(&buffer2); fail_if_error(cork_buffer_set(&buffer2, EXPECTED, EXPECTED_SIZE)); check_buffers(&buffer1, &buffer2); cork_stream_consumer_free(consumer); cork_buffer_done(&buffer1); cork_buffer_done(&buffer2); } END_TEST static void check_c_string_(const char *content, size_t length, const char *expected) { struct cork_buffer buf = CORK_BUFFER_INIT(); cork_buffer_append_c_string(&buf, content, length); check_buffer(&buf, expected); cork_buffer_done(&buf); } #define check_c_string(c) (check_c_string_(c, sizeof(c) - 1, #c)) #define check_c_string_ex(c, e) (check_c_string_(c, sizeof(c) - 1, e)) START_TEST(test_buffer_c_string) { check_c_string(""); check_c_string("hello world"); check_c_string("\x00"); check_c_string("\f\n\r\t\v"); check_c_string_ex("\x0d\x0a", "\"\\n\\r\""); } END_TEST static void check_pretty_print_(size_t indent, const char *content, size_t length, const char *expected) { struct cork_buffer buf = CORK_BUFFER_INIT(); cork_buffer_append_binary(&buf, indent, content, length); check_buffer(&buf, expected); cork_buffer_done(&buf); } #define check_pretty_print(i, c, e) \ (check_pretty_print_((i), (c), sizeof((c)) - 1, (e))) START_TEST(test_buffer_pretty_print) { check_pretty_print(0, "", ""); check_pretty_print(0, "hello world", "hello world"); check_pretty_print (0, "hello\nworld", "(multiline)\n" "hello\n" "world"); check_pretty_print (0, "hello\n\x00world\r\x80hello again", "(hex)\n" "68 65 6c 6c 6f 0a 00 77 6f 72 6c 64 0d 80 68 65 |hello..world..he|\n" "6c 6c 6f 20 61 67 61 69 6e |llo again|"); check_pretty_print(2, "", ""); check_pretty_print(2, "hello world", "hello world"); check_pretty_print (2, "hello\nworld", "(multiline)\n" " hello\n" " world"); check_pretty_print (2, "hello\n\x00world\r\x80hello again", "(hex)\n" " " "68 65 6c 6c 6f 0a 00 77 6f 72 6c 64 0d 80 68 65 |hello..world..he|\n" " " "6c 6c 6f 20 61 67 61 69 6e |llo again|"); check_pretty_print(4, "", ""); check_pretty_print(4, "hello world", "hello world"); check_pretty_print (4, "hello\nworld", "(multiline)\n" " hello\n" " world"); check_pretty_print (4, "hello\n\x00world\r\x80hello again", "(hex)\n" " " "68 65 6c 6c 6f 0a 00 77 6f 72 6c 64 0d 80 68 65 |hello..world..he|\n" " " "6c 6c 6f 20 61 67 61 69 6e |llo again|"); } END_TEST /*----------------------------------------------------------------------- * Testing harness */ Suite * test_suite() { Suite *s = suite_create("buffer"); TCase *tc_buffer = tcase_create("buffer"); tcase_add_test(tc_buffer, test_buffer); tcase_add_test(tc_buffer, test_buffer_append); tcase_add_test(tc_buffer, test_buffer_slicing); tcase_add_test(tc_buffer, test_buffer_stream); tcase_add_test(tc_buffer, test_buffer_c_string); tcase_add_test(tc_buffer, test_buffer_pretty_print); suite_add_tcase(s, tc_buffer); return s; } int main(int argc, const char **argv) { int number_failed; Suite *suite = test_suite(); SRunner *runner = srunner_create(suite); setup_allocator(); srunner_run_all(runner, CK_NORMAL); number_failed = srunner_ntests_failed(runner); srunner_free(runner); return (number_failed == 0)? EXIT_SUCCESS: EXIT_FAILURE; } /*----------------------------------------------------------------------- * Inline declarations */ void cork_buffer_copy(struct cork_buffer* dest, const struct cork_buffer* src); void cork_buffer_append_copy(struct cork_buffer* dest, const struct cork_buffer* src); libcork-1.0.0-rc3/tests/test-core.c000066400000000000000000000706751367645320400170660ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2011, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #include #include #include #include #include #include #include "libcork/config.h" #include "libcork/core/byte-order.h" #include "libcork/core/error.h" #include "libcork/core/hash.h" #include "libcork/core/id.h" #include "libcork/core/net-addresses.h" #include "libcork/core/timestamp.h" #include "libcork/core/types.h" #include "libcork/core/u128.h" #include "libcork/os/subprocess.h" #include "helpers.h" /*----------------------------------------------------------------------- * Core types */ START_TEST(test_bool) { bool value; value = true; fail_unless(value, "Unexpected true value"); value = false; fail_if(value, "Unexpected false value"); } END_TEST START_TEST(test_int_types) { /* * Make sure we have all of the C99 fixed-size integer types * available. */ #define TEST_INT_TYPE(type) \ { \ type i = 0; \ fail_unless(i == 0, "Unexpected value for " #type); \ } TEST_INT_TYPE(int8_t); TEST_INT_TYPE(int16_t); TEST_INT_TYPE(int32_t); TEST_INT_TYPE(int64_t); TEST_INT_TYPE(uint8_t); TEST_INT_TYPE(uint16_t); TEST_INT_TYPE(uint32_t); TEST_INT_TYPE(uint64_t); TEST_INT_TYPE(size_t); TEST_INT_TYPE(ptrdiff_t); TEST_INT_TYPE(intptr_t); TEST_INT_TYPE(uintptr_t); #undef TEST_INT_TYPE } END_TEST START_TEST(test_int_sizeof) { /* * Test that our CORK_SIZEOF_FOO preprocessor macros match the * results of the builtin sizeof operator. */ #define TEST_SIZEOF(TYPE, type) \ { \ fail_unless(CORK_SIZEOF_##TYPE == sizeof(type), \ "Incorrect size for " #type ": got %zu, expected %zu", \ (size_t) CORK_SIZEOF_##TYPE, \ (size_t) sizeof(type)); \ } TEST_SIZEOF(SHORT, short) TEST_SIZEOF(SHORT, unsigned short) TEST_SIZEOF(INT, int) TEST_SIZEOF(INT, unsigned int) TEST_SIZEOF(LONG, long) TEST_SIZEOF(LONG, unsigned long) TEST_SIZEOF(POINTER, void *) TEST_SIZEOF(POINTER, int *) TEST_SIZEOF(POINTER, void (*)(void)) #undef TEST_SIZEOF } END_TEST /*----------------------------------------------------------------------- * Strings */ static void test_strndup(const char *string, size_t size) { const char *copy; copy = cork_strndup(string, size); if (memcmp(string, copy, size) != 0) { fail("cork_strndup failed"); } if (cork_strlen(copy) != size) { fail("cork_strlen failed"); } cork_strfree(copy); copy = cork_xstrndup(string, size); fail_if(copy == NULL, "cork_xstrndup couldn't allocate copy"); if (memcmp(string, copy, size) != 0) { fail("cork_xstrndup failed"); } if (cork_strlen(copy) != size) { fail("cork_strlen failed"); } cork_strfree(copy); } START_TEST(test_string) { DESCRIBE_TEST; test_strndup("", 0); test_strndup("abc", 3); test_strndup("abc\x00xyz", 7); } END_TEST /*----------------------------------------------------------------------- * Endianness */ #define uint8_t_eq(a, b) ((a) == (b)) #define uint16_t_eq(a, b) ((a) == (b)) #define uint32_t_eq(a, b) ((a) == (b)) #define uint64_t_eq(a, b) ((a) == (b)) START_TEST(test_endianness) { #define TEST_ENDIAN(TYPE, type, sz, expected, ...) \ { \ union { uint8_t octets[sz]; type val; } u = \ { { __VA_ARGS__ } }; \ \ type from_big = CORK_##TYPE##_BIG_TO_HOST(u.val); \ fail_unless(type##_eq(from_big, expected), \ "Unexpected big-to-host " #type " value"); \ \ type from_big_in_place = u.val; \ CORK_##TYPE##_BIG_TO_HOST_IN_PLACE(from_big_in_place); \ fail_unless(type##_eq(from_big_in_place, expected), \ "Unexpected in-place big-to-host " #type " value"); \ \ type to_big = CORK_##TYPE##_HOST_TO_BIG(expected); \ fail_unless(type##_eq(to_big, u.val), \ "Unexpected host-to-big " #type " value"); \ \ type to_big_in_place = expected; \ CORK_##TYPE##_HOST_TO_BIG_IN_PLACE(to_big_in_place); \ fail_unless(type##_eq(to_big_in_place, u.val), \ "Unexpected in-place host-to-big " #type " value"); \ \ int i; \ for (i = 0; i < sz/2; i++) { \ uint8_t tmp = u.octets[i]; \ u.octets[i] = u.octets[sz-i-1]; \ u.octets[sz-i-1] = tmp; \ } \ \ type from_little = CORK_##TYPE##_LITTLE_TO_HOST(u.val); \ fail_unless(type##_eq(from_little, expected), \ "Unexpected little-to-host " #type " value"); \ \ type from_little_in_place = u.val; \ CORK_##TYPE##_LITTLE_TO_HOST_IN_PLACE(from_little_in_place); \ fail_unless(type##_eq(from_little_in_place, expected), \ "Unexpected in-place little-to-host " #type " value"); \ \ type to_little = CORK_##TYPE##_HOST_TO_LITTLE(expected); \ fail_unless(type##_eq(to_little, u.val), \ "Unexpected host-to-little " #type " value"); \ \ type to_little_in_place = expected; \ CORK_##TYPE##_HOST_TO_LITTLE_IN_PLACE(to_little_in_place); \ fail_unless(type##_eq(to_little_in_place, u.val), \ "Unexpected in-place host-to-little " #type " value"); \ } TEST_ENDIAN(UINT16, uint16_t, 2, 0x0102, 1, 2); TEST_ENDIAN(UINT32, uint32_t, 4, 0x01020304, 1, 2, 3, 4); TEST_ENDIAN(UINT64, uint64_t, 8, UINT64_C(0x0102030405060708), 1, 2, 3, 4, 5, 6, 7, 8); TEST_ENDIAN( UINT128, cork_u128, 16, cork_u128_from_32(0x01020304, 0x05060708, 0x090a0b0c, 0x0d0e0f00), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0); #undef TEST_ENDIAN } END_TEST /*----------------------------------------------------------------------- * Built-in errors */ START_TEST(test_error_prefix) { DESCRIBE_TEST; cork_error_clear(); cork_error_set_printf (CORK_UNKNOWN_ERROR, "%u errors occurred", (unsigned int) 17); fail_unless_streq("Error messages", "17 errors occurred", cork_error_message()); cork_error_prefix("The %s is aborting because ", "program"); fail_unless_streq("Error messages", "The program is aborting because 17 errors occurred", cork_error_message()); cork_error_clear(); } END_TEST START_TEST(test_system_error) { DESCRIBE_TEST; /* Artificially flag a system error and make sure we can detect it */ errno = ENOMEM; cork_error_clear(); cork_system_error_set(); fail_unless(cork_error_code() == ENOMEM, "Expected a system error"); printf("Got error: %s\n", cork_error_message()); cork_error_clear(); } END_TEST /*----------------------------------------------------------------------- * Hash values */ #define test_hash_func(func, expected, ...) \ fail_unless(func(0, __VA_ARGS__) == expected, \ "Unexpected hash value 0x%08" PRIx32 \ " (expected 0x%08" PRIx32 ")", \ func(0, __VA_ARGS__), expected); #if CORK_HOST_ENDIANNESS == CORK_LITTLE_ENDIAN #if CORK_SIZEOF_POINTER == 8 #define test_hash_buf(buf, len, little32, big32, little64, big64) \ test_hash_func(cork_hash_buffer, little64, buf, len) #define test_hash_var(var, little32, big32, little64, big64) \ test_hash_func(cork_hash_variable, little64, var) #else #define test_hash_buf(buf, len, little32, big32, little64, big64) \ test_hash_func(cork_hash_buffer, little32, buf, len) #define test_hash_var(var, little32, big32, little64, big64) \ test_hash_func(cork_hash_variable, little32, var) #endif #else #if CORK_SIZEOF_POINTER == 8 #define test_hash_buf(buf, len, little32, big32, little64, big64) \ test_hash_func(cork_hash_buffer, big64, buf, len) #define test_hash_var(var, little32, big32, little64, big64) \ test_hash_func(cork_hash_variable, big64, var) #else #define test_hash_buf(buf, len, little32, big32, little64, big64) \ test_hash_func(cork_hash_buffer, big32, buf, len) #define test_hash_var(var, little32, big32, little64, big64) \ test_hash_func(cork_hash_variable, big32, var) #endif #endif #define test_stable_hash_buf(buf, len, expected) \ test_hash_func(cork_stable_hash_buffer, expected, buf, len) #define test_stable_hash_var(var, expected) \ test_hash_func(cork_stable_hash_variable, expected, var) #define test_big_hash_func(buf, len, e1, e2) \ do { \ cork_big_hash seed = CORK_BIG_HASH_INIT(); \ cork_big_hash expected = {cork_u128_from_64(e1, e2)}; \ cork_big_hash actual = cork_big_hash_buffer(seed, buf, len); \ fail_unless(cork_big_hash_equal(actual, expected), \ "\nUnexpected hash value 0x%016" PRIx64 ".%016" PRIx64 \ "\n (expected 0x%016" PRIx64 ".%016" PRIx64 ")", \ cork_u128_be64(actual.u128, 0), \ cork_u128_be64(actual.u128, 1), \ cork_u128_be64(expected.u128, 0), \ cork_u128_be64(expected.u128, 1)); \ } while (0) #if CORK_HOST_ENDIANNESS == CORK_LITTLE_ENDIAN #if CORK_SIZEOF_POINTER == 8 #define test_big_hash_buf(buf,len,l32a,l32b,b32a,b32b,l64a,l64b,b64a,b64b) \ test_big_hash_func(buf, len, l64a, l64b) #else #define test_big_hash_buf(buf,len,l32a,l32b,b32a,b32b,l64a,l64b,b64a,b64b) \ test_big_hash_func(buf, len, l32a, l32b) #endif #else #if CORK_SIZEOF_POINTER == 8 #define test_big_hash_buf(buf,len,l32a,l32b,b32a,b32b,l64a,l64b,b64a,b64b) \ test_big_hash_func(buf, len, b64a, b64b) #else #define test_big_hash_buf(buf,len,l32a,l32b,b32a,b32b,l64a,l64b,b64a,b64b) \ test_big_hash_func(buf, len, b32a, b32b) #endif #endif START_TEST(test_hash) { DESCRIBE_TEST; static const char BUF[] = "test"; static size_t LEN = sizeof(BUF); static const char LONG_BUF[] = "this is a much longer test string in the hopes that we have to " "go through a few iterations of the hashing loop in order to " "calculate the value of the hash which we are trying to compute."; static size_t LONG_LEN = sizeof(LONG_BUF); uint32_t val32 = 1234; uint64_t val64 = 1234; uint32_t stable_val32 = CORK_UINT32_HOST_TO_LITTLE(1234); uint64_t stable_val64 = CORK_UINT64_HOST_TO_LITTLE(1234); /* without the NUL terminator */ test_stable_hash_buf(BUF, LEN-1, 0xba6bd213); test_hash_buf(BUF, LEN-1, /* little 32 */ 0xba6bd213, /* big 32 */ 0x29d175e5, /* little 64 */ 0xac7d28cc, /* big 64 */ 0xac7d28cc); test_big_hash_buf(BUF, LEN-1, /* little 32 */ 0x6f02ef30550c7d68LL, 0x550c7d68550c7d68LL, /* big 32 */ 0x6f02ef30550c7d68LL, 0x550c7d68550c7d68LL, /* little 64 */ 0xac7d28cc74bde19dLL, 0x9a128231f9bd4d82LL, /* big 64 */ 0xac7d28cc74bde19dLL, 0x9a128231f9bd4d82LL); /* with the NUL terminator */ test_stable_hash_buf(BUF, LEN, 0x586fce33); test_hash_buf(BUF, LEN, /* little 32 */ 0x586fce33, /* big 32 */ 0xe31d1ce0, /* little 64 */ 0xc3812fdf, /* big 64 */ 0xc3812fdf); test_big_hash_buf(BUF, LEN, /* little 32 */ 0x98c2b52b29ab177cLL, 0x29ab177c29ab177cLL, /* big 32 */ 0x98c2b52b29ab177cLL, 0x29ab177c29ab177cLL, /* little 64 */ 0xc3812fdf4d18f852LL, 0xc81a9057aa737aecLL, /* big 64 */ 0xc3812fdf4d18f852LL, 0xc81a9057aa737aecLL); /* without the NUL terminator */ test_stable_hash_buf(LONG_BUF, LONG_LEN-1, 0x5caacc30); test_hash_buf(LONG_BUF, LONG_LEN-1, /* little 32 */ 0x5caacc30, /* big 32 */ 0x88f94165, /* little 64 */ 0xcbdc2092, /* big 64 */ 0x5935f90a); test_big_hash_buf(LONG_BUF, LONG_LEN-1, /* little 32 */ 0x4240d5134fb7793cLL, 0xee7e281c799f335aLL, /* big 32 */ 0xab564a5e029c92a4LL, 0x0bd80c741093400fLL, /* little 64 */ 0xcbdc20928fa72e9cLL, 0x48de52d2c680420eLL, /* big 64 */ 0x5935f90a03578c96LL, 0x163e514fff9c30a8LL); /* with the NUL terminator */ test_stable_hash_buf(LONG_BUF, LONG_LEN, 0x5e37d33d); test_hash_buf(LONG_BUF, LONG_LEN, /* little 32 */ 0x5e37d33d, /* big 32 */ 0x4977421a, /* little 64 */ 0xe89ec005, /* big 64 */ 0xf00a12ab); test_big_hash_buf(LONG_BUF, LONG_LEN, /* little 32 */ 0x63bcdcd0c2615146LL, 0x8e7fd7aaece3cab6LL, /* big 32 */ 0x250b47cda3fc07fdLL, 0x840c4bb606aafbd0LL, /* little 64 */ 0xe89ec0054becb434LL, 0x826391b83f0b4d3eLL, /* big 64 */ 0xf00a12ab8c919559LL, 0x684ecf4973c66eacLL); test_stable_hash_var(stable_val32, 0x6bb65380); test_hash_var(val32, /* little 32 */ 0x6bb65380, /* big 32 */ 0x6bb65380, /* little 64 */ 0x061fecc8, /* big 64 */ 0x5b3f7a70); test_stable_hash_var(stable_val64, 0x4d5c4063); test_hash_var(val64, /* little 32 */ 0x4d5c4063, /* big 32 */ 0xbaeee6e9, /* little 64 */ 0xb119ee69, /* big 64 */ 0x2304b12d); } END_TEST /*----------------------------------------------------------------------- * IP addresses */ #define IPV4_TESTS(good, bad) \ good("192.168.1.100", "192.168.1.100"); \ good("01.002.0003.00000004", "1.2.3.4"); \ good("010.0020.00034.00000089", "10.20.34.89"); \ good("0100.000200.00.000", "100.200.0.0"); \ bad("", -1); \ bad(".", -1); \ bad("192.168.0.", -1); \ bad("192.168.0.1.", -1); \ bad("192..168.0.1", -1); \ bad("192.168.0.1.2", -1); \ bad(".168.0.1.2", -1); \ bad("256.0.0.0", -1); \ bad("00256.0.0.0", -1); \ bad("392.0.0.0", -1); \ bad("1920.0.0.0", -1); \ bad("stuv", -1); \ #define IPV6_TESTS(good, bad) \ good("::", "::"); \ good("0:0:0:0:0:0:0:0", "::"); \ good("0000:0000:0000:0000:0000:0000:0000:0000", "::"); \ good("fe80::", "fe80::"); \ good("fe80:0:0:0:0:0:0:0", "fe80::"); \ good("fe80:0000:0000:0000:0000:0000:0000:0000", "fe80::"); \ good("::1", "::1"); \ good("0:0:0:0:0:0:0:1", "::1"); \ good("0000:0000:0000:0000:0000:0000:0000:0001", "::1"); \ good("fe80::1", "fe80::1"); \ good("fe80:0:0:0:0:0:0:1", "fe80::1"); \ good("fe80:0000:0000:0000:0000:0000:0000:0001", "fe80::1"); \ good("0:1:2:3:4:5:6:7", "0:1:2:3:4:5:6:7"); \ good("1230:4567:89ab:cdef:1230:4567:89ab:cdef", \ "1230:4567:89ab:cdef:1230:4567:89ab:cdef"); \ good("::ffff:192.168.1.100", "::ffff:192.168.1.100"); \ bad("", -1); \ bad(":", -1); \ bad("fe80:", -1); \ bad("fe80::1::2", -1); \ bad("1:2:3:4:5:6:7", -1); \ bad("1:2:3:4:5:6:7:8:9", -1); \ bad("::1:", -1); \ bad("fe800::", -1); \ bad("stuv", -1); \ /* RFC 5952 recommendations */ \ good("2001:0db8::0001", "2001:db8::1"); \ good("2001:db8:0:0:0:0:2:1", "2001:db8::2:1"); \ good("2001:db8:0:1:1:1:1:1", "2001:db8:0:1:1:1:1:1"); \ good("2001:0:0:1:0:0:0:1", "2001:0:0:1::1"); \ good("2001:db8:0:0:1:0:0:1", "2001:db8::1:0:0:1"); \ good("0:1:A:B:C:D:E:F", "0:1:a:b:c:d:e:f"); \ START_TEST(test_ipv4_address) { DESCRIBE_TEST; #define GOOD(str, normalized) \ { \ struct cork_ipv4 addr; \ fail_if_error(cork_ipv4_init(&addr, str)); \ char actual[CORK_IPV4_STRING_LENGTH]; \ cork_ipv4_to_raw_string(&addr, actual); \ fail_unless(strcmp(actual, normalized) == 0, \ "Unexpected string representation: " \ "got \"%s\", expected \"%s\"", \ actual, normalized); \ \ struct cork_ipv4 addr2; \ cork_ipv4_init(&addr2, normalized); \ fail_unless(cork_ipv4_equal(&addr, &addr2), \ "IPv4 instances should be equal"); \ } #define BAD(str, unused) \ { \ struct cork_ipv4 addr; \ fail_unless_error \ (cork_ipv4_init(&addr, str), \ "Shouldn't be able to initialize IPv4 address from \"%s\"", \ str); \ } IPV4_TESTS(GOOD, BAD); IPV6_TESTS(BAD, BAD); #undef GOOD #undef BAD struct cork_ipv4 addr4; unsigned int ipv4_cidr_good = 30; unsigned int ipv4_cidr_bad_value = 24; unsigned int ipv4_cidr_bad_range = 33; fprintf(stderr, "Testing network prefixes\n"); cork_ipv4_init(&addr4, "1.2.3.4"); fail_unless(cork_ipv4_is_valid_network(&addr4, ipv4_cidr_good), "Bad CIDR block for 1.2.3.4 and %u", ipv4_cidr_good); fail_if(cork_ipv4_is_valid_network(&addr4, ipv4_cidr_bad_value), "IPv4 CIDR check should fail for %u", ipv4_cidr_bad_value); fail_if(cork_ipv4_is_valid_network(&addr4, ipv4_cidr_bad_range), "IPv4 CIDR check should fail for %u", ipv4_cidr_bad_range); } END_TEST START_TEST(test_ipv6_address) { DESCRIBE_TEST; #define GOOD(str, normalized) \ { \ struct cork_ipv6 addr; \ fail_if_error(cork_ipv6_init(&addr, str)); \ char actual[CORK_IPV6_STRING_LENGTH]; \ cork_ipv6_to_raw_string(&addr, actual); \ fail_unless(strcmp(actual, normalized) == 0, \ "Unexpected string representation: " \ "got \"%s\", expected \"%s\"", \ actual, normalized); \ \ struct cork_ipv6 addr2; \ cork_ipv6_init(&addr2, normalized); \ fail_unless(cork_ipv6_equal(&addr, &addr2), \ "IPv6 instances should be equal"); \ } #define BAD(str, unused) \ { \ struct cork_ipv6 addr; \ fail_unless_error \ (cork_ipv6_init(&addr, str), \ "Shouldn't be able to initialize IPv6 address from \"%s\"", \ str); \ } IPV6_TESTS(GOOD, BAD); IPV4_TESTS(BAD, BAD); #undef GOOD #undef BAD struct cork_ipv6 addr6; unsigned int ipv6_cidr_good = 127; unsigned int ipv6_cidr_bad_value = 64; unsigned int ipv6_cidr_bad_range = 129; fprintf(stderr, "Testing network prefixes\n"); cork_ipv6_init(&addr6, "fe80::200:f8ff:fe21:6000"); fail_unless(cork_ipv6_is_valid_network(&addr6, ipv6_cidr_good), "Bad CIDR block %u", ipv6_cidr_good); fail_if(cork_ipv6_is_valid_network(&addr6, ipv6_cidr_bad_value), "IPv6 CIDR check should fail for %u", ipv6_cidr_bad_value); fail_if(cork_ipv6_is_valid_network(&addr6, ipv6_cidr_bad_range), "IPv6 CIDR check should fail for %u", ipv6_cidr_bad_range); } END_TEST START_TEST(test_ip_address) { DESCRIBE_TEST; struct cork_ip addr; #define GOOD(str, normalized) \ { \ struct cork_ip addr; \ fail_if_error(cork_ip_init(&addr, str)); \ char actual[CORK_IP_STRING_LENGTH]; \ cork_ip_to_raw_string(&addr, actual); \ fail_unless(strcmp(actual, normalized) == 0, \ "Unexpected string representation: " \ "got \"%s\", expected \"%s\"", \ actual, normalized); \ \ struct cork_ip addr2; \ cork_ip_init(&addr2, normalized); \ fail_unless(cork_ip_equal(&addr, &addr2), \ "IP instances should be equal"); \ } #define BAD(str, unused) \ { \ struct cork_ip addr; \ fail_unless_error \ (cork_ip_init(&addr, str), \ "Shouldn't be able to initialize IP address from \"%s\"", \ str); \ } IPV4_TESTS(GOOD, BAD); IPV6_TESTS(GOOD, BAD); #undef GOOD #undef BAD struct cork_ipv4 addr4; struct cork_ipv6 addr6; fprintf(stderr, "Testing IP address versions\n"); cork_ip_init(&addr, "192.168.1.1"); cork_ipv4_init(&addr4, "192.168.1.1"); fail_unless(addr.version == 4, "Unexpected IP address version (expected 4, got %u)", addr.version); fail_unless(cork_ipv4_equal(&addr.ip.v4, &addr4), "IP addresses should be equal"); cork_ip_init(&addr, "fe80::1"); cork_ipv6_init(&addr6, "fe80::1"); fail_unless(addr.version == 6, "Unexpected IP address version (expected 6, got %u)", addr.version); fail_unless(cork_ipv6_equal(&addr.ip.v6, &addr6), "IP addresses should be equal"); } END_TEST /*----------------------------------------------------------------------- * Timestamps */ static void test_timestamp_bad_format(cork_timestamp ts, const char *format) { struct cork_buffer buf = CORK_BUFFER_INIT(); fail_unless_error(cork_timestamp_format_utc(ts, format, &buf)); cork_buffer_done(&buf); } static void test_timestamp_utc_format(cork_timestamp ts, const char *format, const char *expected) { struct cork_buffer buf = CORK_BUFFER_INIT(); fail_if_error(cork_timestamp_format_utc(ts, format, &buf)); fail_unless(strcmp(buf.buf, expected) == 0, "Unexpected formatted UTC time " "(got \"%s\", expected \"%s\")", (char *) buf.buf, expected); cork_buffer_done(&buf); } static void test_timestamp_local_format(cork_timestamp ts, const char *format, const char *expected) { struct cork_buffer buf = CORK_BUFFER_INIT(); fail_if_error(cork_timestamp_format_local(ts, format, &buf)); fail_unless(strcmp(buf.buf, expected) == 0, "Unexpected formatted local time " "(got \"%s\", expected \"%s\")", (char *) buf.buf, expected); cork_buffer_done(&buf); } START_TEST(test_timestamp) { /* All of the local times here are in America/Los_Angeles. Down at the * bottom of the file we override the TZ environment variable to ensure that * we use a consistent local time zone in the test cases, regardless of the * actual time zone of the current machine. */ static const uint32_t TEST_TIME_1 = 700000000; static const char *FORMATTED_UTC_TIME_1 = " 1992-03-07 20:26:40 "; static const char *FORMATTED_LOCAL_TIME_1 = " 1992-03-07 12:26:40 "; static const uint32_t TEST_TIME_2 = 1200000000; static const char *FORMATTED_UTC_TIME_2 = " 2008-01-10 21:20:00 "; static const char *FORMATTED_LOCAL_TIME_2 = " 2008-01-10 13:20:00 "; static const uint32_t TEST_TIME_3 = 1305180745; static const char *FORMATTED_UTC_TIME_3 = " 2011-05-12 06:12:25 "; static const char *FORMATTED_LOCAL_TIME_3 = " 2011-05-11 23:12:25 "; cork_timestamp ts; DESCRIBE_TEST; #define test(unit, expected) \ fail_unless(cork_timestamp_##unit(ts) == expected, \ "Unexpected " #unit " portion of timestamp " \ "(got %lu, expected %lu)", \ (unsigned long) cork_timestamp_##unit(ts), \ (unsigned long) expected); #define test_format(utc, local) \ test_timestamp_utc_format(ts, " %Y-%m-%d %H:%M:%S ", utc); \ test_timestamp_local_format(ts, " %Y-%m-%d %H:%M:%S ", local); cork_timestamp_init_sec(&ts, TEST_TIME_1); test(sec, TEST_TIME_1); test(gsec, 0); test(msec, 0); test(usec, 0); test(nsec, 0); test_format(FORMATTED_UTC_TIME_1, FORMATTED_LOCAL_TIME_1); cork_timestamp_init_sec(&ts, TEST_TIME_2); test(sec, TEST_TIME_2); test(gsec, 0); test(msec, 0); test(usec, 0); test(nsec, 0); test_format(FORMATTED_UTC_TIME_2, FORMATTED_LOCAL_TIME_2); cork_timestamp_init_sec(&ts, TEST_TIME_3); test(sec, TEST_TIME_3); test(gsec, 0); test(msec, 0); test(usec, 0); test(nsec, 0); test_format(FORMATTED_UTC_TIME_3, FORMATTED_LOCAL_TIME_3); cork_timestamp_init_gsec(&ts, TEST_TIME_1, 1 << 30); test(sec, TEST_TIME_1); test(gsec, 1 << 30); test(msec, 250); test(usec, 250000); test(nsec, 250000000); cork_timestamp_init_msec(&ts, TEST_TIME_1, 500); test(sec, TEST_TIME_1); test(gsec, 1 << 31); test(msec, 500); test(usec, 500000); test(nsec, 500000000); cork_timestamp_init_usec(&ts, TEST_TIME_1, 500000); test(sec, TEST_TIME_1); test(gsec, 1 << 31); test(msec, 500); test(usec, 500000); test(nsec, 500000000); cork_timestamp_init_nsec(&ts, TEST_TIME_1, 500000000); test(sec, TEST_TIME_1); test(gsec, 1 << 31); test(msec, 500); test(usec, 500000); test(nsec, 500000000); } END_TEST START_TEST(test_timestamp_format) { cork_timestamp ts; DESCRIBE_TEST; cork_timestamp_init_nsec(&ts, 0, 123456789); test_timestamp_bad_format(ts, "%f"); test_timestamp_bad_format(ts, "%0f"); test_timestamp_bad_format(ts, "%10f"); test_timestamp_utc_format(ts, "%1f", "1"); test_timestamp_utc_format(ts, "%2f", "12"); test_timestamp_utc_format(ts, "%3f", "123"); test_timestamp_utc_format(ts, "%4f", "1235"); test_timestamp_utc_format(ts, "%5f", "12346"); test_timestamp_utc_format(ts, "%6f", "123457"); test_timestamp_utc_format(ts, "%7f", "1234568"); test_timestamp_utc_format(ts, "%8f", "12345679"); test_timestamp_utc_format(ts, "%9f", "123456789"); test_timestamp_utc_format(ts, "%009f", "123456789"); cork_timestamp_init_nsec(&ts, 1200000000, 123456789); } END_TEST /*----------------------------------------------------------------------- * Statement expressions */ START_TEST(test_statement_expr) { #if CORK_CONFIG_HAVE_GCC_STATEMENT_EXPRS int value = ({ int __x = 0; __x += 2; __x;}); fail_unless_equal("Statement expression result", "%d", 2, value); #endif } END_TEST /*----------------------------------------------------------------------- * Unique identifiers */ START_TEST(test_uid) { DESCRIBE_TEST; cork_uid_define(test_id_01); cork_uid_define(test_id_02); cork_uid id1; cork_uid id2; fail_unless_streq("UID name", "test_id_01", cork_uid_name(test_id_01)); fail_unless_streq("UID name", "test_id_02", cork_uid_name(test_id_02)); id1 = test_id_01; id2 = test_id_02; fail_if(cork_uid_equal(id1, id2), "Unique IDs aren't unique"); id1 = test_id_01; id2 = test_id_01; fail_unless(cork_uid_equal(id1, id2), "Unique ID isn't equal to itself"); id1 = test_id_01; id2 = CORK_UID_NONE; fail_if(cork_uid_equal(id1, id2), "NULL unique ID isn't unique"); } END_TEST /*----------------------------------------------------------------------- * Testing harness */ Suite * test_suite() { Suite *s = suite_create("core"); TCase *tc_types = tcase_create("types"); tcase_add_test(tc_types, test_bool); tcase_add_test(tc_types, test_int_types); tcase_add_test(tc_types, test_int_sizeof); suite_add_tcase(s, tc_types); TCase *tc_string = tcase_create("string"); tcase_add_test(tc_string, test_string); suite_add_tcase(s, tc_string); TCase *tc_endianness = tcase_create("endianness"); tcase_add_test(tc_endianness, test_endianness); suite_add_tcase(s, tc_endianness); TCase *tc_errors = tcase_create("errors"); tcase_add_test(tc_errors, test_error_prefix); tcase_add_test(tc_errors, test_system_error); suite_add_tcase(s, tc_errors); TCase *tc_hash = tcase_create("hash"); tcase_add_test(tc_hash, test_hash); suite_add_tcase(s, tc_hash); TCase *tc_addresses = tcase_create("net-addresses"); tcase_add_test(tc_addresses, test_ipv4_address); tcase_add_test(tc_addresses, test_ipv6_address); tcase_add_test(tc_addresses, test_ip_address); suite_add_tcase(s, tc_addresses); TCase *tc_timestamp = tcase_create("timestamp"); tcase_add_test(tc_timestamp, test_timestamp); tcase_add_test(tc_timestamp, test_timestamp_format); suite_add_tcase(s, tc_timestamp); TCase *tc_statement_expr = tcase_create("statement_expr"); tcase_add_test(tc_statement_expr, test_statement_expr); suite_add_tcase(s, tc_statement_expr); TCase *tc_uid = tcase_create("uid"); tcase_add_test(tc_uid, test_uid); suite_add_tcase(s, tc_uid); return s; } int main(int argc, const char **argv) { int number_failed; Suite *suite = test_suite(); SRunner *runner = srunner_create(suite); setup_allocator(); /* Before anything starts, override the TZ environment variable so that we * get consistent test results. */ cork_env_add(NULL, "TZ", "America/Los_Angeles"); srunner_run_all(runner, CK_NORMAL); number_failed = srunner_ntests_failed(runner); srunner_free(runner); return (number_failed == 0)? EXIT_SUCCESS: EXIT_FAILURE; } libcork-1.0.0-rc3/tests/test-dllist.c000066400000000000000000000124771367645320400174250ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2011, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #include #include #include #include #include "libcork/core/types.h" #include "libcork/ds/buffer.h" #include "libcork/ds/dllist.h" #include "helpers.h" /*----------------------------------------------------------------------- * Doubly-linked lists */ struct int64_item { int64_t value; struct cork_dllist_item element; }; static int int64_sum(void *user_data, struct cork_dllist_item *element) { int64_t *sum = user_data; struct int64_item *item = cork_container_of(element, struct int64_item, element); *sum += item->value; return 0; } static void check_int64_list(struct cork_dllist *list, const char *expected) { bool first = true; struct cork_buffer buf = CORK_BUFFER_INIT(); struct cork_dllist_item *curr; struct cork_dllist_item *next; struct int64_item *item; cork_buffer_append_literal(&buf, ""); cork_dllist_foreach(list, curr, next, struct int64_item, item, element) { if (first) { first = false; } else { cork_buffer_append_literal(&buf, ","); } cork_buffer_append_printf(&buf, "%" PRId64, item->value); } fail_unless_streq("List contents", expected, buf.buf); cork_buffer_done(&buf); } START_TEST(test_dllist) { struct cork_dllist list; struct cork_dllist_item *curr; struct cork_dllist_item *next; struct int64_item *item; struct int64_item item1; struct int64_item item2; struct int64_item item3; int64_t sum; cork_dllist_init(&list); fail_unless(cork_dllist_size(&list) == 0, "Unexpected size of list: got %zu, expected 0", cork_dllist_size(&list)); fail_unless(cork_dllist_is_empty(&list), "Expected empty list"); check_int64_list(&list, ""); item1.value = 1; cork_dllist_add(&list, &item1.element); fail_unless(cork_dllist_size(&list) == 1, "Unexpected size of list: got %zu, expected 1", cork_dllist_size(&list)); check_int64_list(&list, "1"); item2.value = 2; cork_dllist_add(&list, &item2.element); fail_unless(cork_dllist_size(&list) == 2, "Unexpected size of list: got %zu, expected 2", cork_dllist_size(&list)); check_int64_list(&list, "1,2"); item3.value = 3; cork_dllist_add(&list, &item3.element); fail_unless(cork_dllist_size(&list) == 3, "Unexpected size of list: got %zu, expected 3", cork_dllist_size(&list)); check_int64_list(&list, "1,2,3"); sum = 0; fail_if(cork_dllist_visit(&list, &sum, int64_sum)); fail_unless(sum == 6, "Unexpected sum, got %ld, expected 6", (long) sum); sum = 0; cork_dllist_foreach(&list, curr, next, struct int64_item, item, element) { sum += item->value; } fail_unless(sum == 6, "Unexpected sum, got %ld, expected 6", (long) sum); cork_dllist_remove(&item2.element); fail_unless(cork_dllist_size(&list) == 2, "Unexpected size of list: got %zu, expected 2", cork_dllist_size(&list)); } END_TEST START_TEST(test_dllist_append) { struct cork_dllist list1 = CORK_DLLIST_INIT(list1); struct cork_dllist list2 = CORK_DLLIST_INIT(list2); struct int64_item item1; struct int64_item item2; struct int64_item item3; struct int64_item item4; struct int64_item item5; struct int64_item item6; struct int64_item item7; item1.value = 1; cork_dllist_add_to_head(&list1, &item1.element); check_int64_list(&list1, "1"); item2.value = 2; cork_dllist_add_to_head(&list1, &item2.element); check_int64_list(&list1, "2,1"); item3.value = 3; cork_dllist_add_to_tail(&list1, &item3.element); check_int64_list(&list1, "2,1,3"); item4.value = 4; cork_dllist_add_to_tail(&list2, &item4.element); item5.value = 5; cork_dllist_add_to_tail(&list2, &item5.element); cork_dllist_add_list_to_head(&list1, &list2); check_int64_list(&list1, "4,5,2,1,3"); item6.value = 6; cork_dllist_add_to_tail(&list2, &item6.element); item7.value = 7; cork_dllist_add_to_tail(&list2, &item7.element); cork_dllist_add_list_to_tail(&list1, &list2); check_int64_list(&list1, "4,5,2,1,3,6,7"); } END_TEST /*----------------------------------------------------------------------- * Testing harness */ Suite * test_suite() { Suite *s = suite_create("dllist"); TCase *tc_ds = tcase_create("dllist"); tcase_add_test(tc_ds, test_dllist); tcase_add_test(tc_ds, test_dllist_append); suite_add_tcase(s, tc_ds); return s; } int main(int argc, const char **argv) { int number_failed; Suite *suite = test_suite(); SRunner *runner = srunner_create(suite); setup_allocator(); srunner_run_all(runner, CK_NORMAL); number_failed = srunner_ntests_failed(runner); srunner_free(runner); return (number_failed == 0)? EXIT_SUCCESS: EXIT_FAILURE; } libcork-1.0.0-rc3/tests/test-files.c000066400000000000000000000246341367645320400172320ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2013, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #include #include #include #include #include #include "libcork/core/types.h" #include "libcork/ds/buffer.h" #include "libcork/os/files.h" #include "helpers.h" static const char *program_path; /*----------------------------------------------------------------------- * Paths */ void verify_path_content(struct cork_path *path, const char *expected) { fail_if(cork_path_get(path) == NULL, "Path should not have NULL content"); fail_unless_streq("Paths", expected, cork_path_get(path)); } void test_path(const char *p, const char *expected) { struct cork_path *path; struct cork_path *cloned; struct cork_path *set; fprintf(stderr, "path(\"%s\") ?= \"%s\"\n", (p == NULL)? "": p, expected); path = cork_path_new(p); verify_path_content(path, expected); cloned = cork_path_clone(path); verify_path_content(cloned, expected); cork_path_free(cloned); cork_path_free(path); set = cork_path_new(NULL); cork_path_set(set, p); verify_path_content(set, expected); cork_path_free(set); } START_TEST(test_path_01) { DESCRIBE_TEST; test_path(NULL, ""); test_path("a", "a"); test_path("a/b", "a/b"); } END_TEST void test_join(const char *p1, const char *p2, const char *expected) { struct cork_path *path1; struct cork_path *path2; struct cork_path *actual; fprintf(stderr, "join(\"%s\", \"%s\") ?= \"%s\"\n", (p1 == NULL)? "": p1, (p2 == NULL)? "": p2, expected); /* Try cork_path_join */ path1 = cork_path_new(p1); actual = cork_path_join(path1, p2); verify_path_content(actual, expected); cork_path_free(path1); cork_path_free(actual); /* Try cork_path_join_path */ path1 = cork_path_new(p1); path2 = cork_path_new(p2); actual = cork_path_join_path(path1, path2); verify_path_content(actual, expected); cork_path_free(path1); cork_path_free(path2); cork_path_free(actual); /* Try cork_path_append */ actual = cork_path_new(p1); cork_path_append(actual, p2); verify_path_content(actual, expected); cork_path_free(actual); /* Try cork_path_append_path */ actual = cork_path_new(p1); path2 = cork_path_new(p2); cork_path_append_path(actual, path2); verify_path_content(actual, expected); cork_path_free(path2); cork_path_free(actual); } START_TEST(test_path_join_01) { DESCRIBE_TEST; test_join("a", "b", "a/b"); test_join("a/", "b", "a/b"); test_join("", "a/b", "a/b"); test_join("a/b", "", "a/b"); test_join(NULL, "a/b", "a/b"); test_join("a/b", NULL, "a/b"); } END_TEST START_TEST(test_path_join_02) { DESCRIBE_TEST; test_join("", "/b", "/b"); test_join(NULL, "/b", "/b"); test_join("a", "/b", "/b"); test_join("a/", "/b", "/b"); } END_TEST void test_basename(const char *p, const char *expected) { struct cork_path *path; struct cork_path *actual; fprintf(stderr, "basename(\"%s\") ?= \"%s\"\n", (p == NULL)? "": p, expected); /* Try cork_path_basename */ path = cork_path_new(p); actual = cork_path_basename(path); verify_path_content(actual, expected); cork_path_free(path); cork_path_free(actual); /* Try cork_path_set_basename */ actual = cork_path_new(p); cork_path_set_basename(actual); verify_path_content(actual, expected); cork_path_free(actual); } START_TEST(test_path_basename_01) { DESCRIBE_TEST; test_basename("", ""); test_basename(NULL, ""); test_basename("a", "a"); test_basename("a/", ""); test_basename("a/b", "b"); test_basename("a/b/", ""); test_basename("a/b/c", "c"); test_basename("/a", "a"); test_basename("/a/", ""); test_basename("/a/b", "b"); test_basename("/a/b/", ""); test_basename("/a/b/c", "c"); } END_TEST void test_dirname(const char *p, const char *expected) { struct cork_path *path; struct cork_path *actual; fprintf(stderr, "dirname(\"%s\") ?= \"%s\"\n", (p == NULL)? "": p, expected); /* Try cork_path_dirname */ path = cork_path_new(p); actual = cork_path_dirname(path); verify_path_content(actual, expected); cork_path_free(path); cork_path_free(actual); /* Try cork_path_set_dirname */ actual = cork_path_new(p); cork_path_set_dirname(actual); verify_path_content(actual, expected); cork_path_free(actual); } START_TEST(test_path_dirname_01) { DESCRIBE_TEST; test_dirname(NULL, ""); test_dirname("", ""); test_dirname("a", ""); test_dirname("a/", "a"); test_dirname("a/b", "a"); test_dirname("a/b/", "a/b"); test_dirname("a/b/c", "a/b"); test_dirname("/", "/"); test_dirname("/a", "/"); test_dirname("/a/", "/a"); test_dirname("/a/b", "/a"); test_dirname("/a/b/", "/a/b"); test_dirname("/a/b/c", "/a/b"); } END_TEST void test_relative_child(const char *p, const char *f, const char *expected) { struct cork_path *actual; fprintf(stderr, "relative_child(\"%s\", \"%s\") ?= \"%s\"\n", (p == NULL)? "": p, (f == NULL)? "": f, expected); actual = cork_path_new(p); cork_path_set_basename(actual); cork_path_append(actual, f); verify_path_content(actual, expected); cork_path_free(actual); } START_TEST(test_path_relative_child_01) { DESCRIBE_TEST; test_relative_child(NULL, "a", "a"); test_relative_child("", "a", "a"); test_relative_child("a", "b", "a/b"); test_relative_child("a/b", "c", "b/c"); } END_TEST void test_set_absolute(const char *p, const char *expected) { struct cork_path *actual; fprintf(stderr, "set_absolute(\"%s\") ?= \"%s\"\n", (p == NULL)? "": p, expected); actual = cork_path_new(p); cork_path_set_absolute(actual); if ((p != NULL) && (p[0] == '/')) { /* If the first char in p is a '/', then we want to * test that already have an absolute path string. */ verify_path_content(actual, expected); } else { /* Otherwise, we have to construct a new expected path * string using cork_path_cwd since the prepended $ROOT * path is unknown. */ struct cork_path *root_path; struct cork_buffer *expected_path = cork_buffer_new(); root_path = cork_path_cwd(); cork_buffer_append_printf (expected_path, "%s/%s", cork_path_get(root_path), expected); verify_path_content(actual, (char *) expected_path->buf); cork_path_free(root_path); cork_buffer_free(expected_path); } cork_path_free(actual); } START_TEST(test_path_set_absolute_01) { /* We test that an absolute path really is so. */ DESCRIBE_TEST test_set_absolute("", ""); test_set_absolute("/", "/"); test_set_absolute("/a", "/a"); test_set_absolute("/a/b", "/a/b"); test_set_absolute("/a/b/", "/a/b/"); test_set_absolute("c/d", "c/d"); test_set_absolute("c/d/", "c/d/"); } END_TEST /*----------------------------------------------------------------------- * Lists of paths */ void verify_path_list_content(struct cork_path_list *list, const char *expected) { struct cork_buffer buf = CORK_BUFFER_INIT(); size_t count = cork_path_list_size(list); size_t i; for (i = 0; i < count; i++) { const struct cork_path *path = cork_path_list_get(list, i); cork_buffer_append_string(&buf, cork_path_get(path)); cork_buffer_append(&buf, "\n", 1); } fail_unless_streq("path lists", expected, buf.buf); cork_buffer_done(&buf); } void test_path_list(const char *p, const char *expected) { struct cork_path_list *list; fprintf(stderr, "path_list(\"%s\")\n", p); list = cork_path_list_new(p); verify_path_list_content(list, expected); fail_unless_streq("path lists", p, cork_path_list_to_string(list)); cork_path_list_free(list); } START_TEST(test_path_list_01) { DESCRIBE_TEST; test_path_list("a", "a\n"); test_path_list("a/b", "a/b\n"); test_path_list(":a/b", "\na/b\n"); test_path_list("a:a/b:", "a\na/b\n\n"); } END_TEST /*----------------------------------------------------------------------- * Files */ void test_file_exists(const char *filename, bool expected) { struct cork_path *path; struct cork_file *file; bool actual; path = cork_path_new(program_path); cork_path_set_dirname(path); cork_path_append(path, filename); file = cork_file_new_from_path(path); fail_if_error(cork_file_exists(file, &actual)); fail_unless(actual == expected, "File %s should%s exist", cork_path_get(path), expected? "": " not"); cork_file_free(file); } START_TEST(test_file_exists_01) { DESCRIBE_TEST; test_file_exists("test-files", true); test_file_exists("test-nonexistent", false); } END_TEST /*----------------------------------------------------------------------- * Testing harness */ Suite * test_suite() { Suite *s = suite_create("path"); TCase *tc_path = tcase_create("path"); tcase_add_test(tc_path, test_path_01); tcase_add_test(tc_path, test_path_join_01); tcase_add_test(tc_path, test_path_join_02); tcase_add_test(tc_path, test_path_basename_01); tcase_add_test(tc_path, test_path_dirname_01); tcase_add_test(tc_path, test_path_relative_child_01); tcase_add_test(tc_path, test_path_set_absolute_01); suite_add_tcase(s, tc_path); TCase *tc_path_list = tcase_create("path-list"); tcase_add_test(tc_path_list, test_path_list_01); suite_add_tcase(s, tc_path_list); TCase *tc_file = tcase_create("file"); tcase_add_test(tc_file, test_file_exists_01); suite_add_tcase(s, tc_file); return s; } int main(int argc, const char **argv) { int number_failed; Suite *suite = test_suite(); SRunner *runner = srunner_create(suite); setup_allocator(); assert(argc > 0); program_path = argv[0]; srunner_run_all(runner, CK_NORMAL); number_failed = srunner_ntests_failed(runner); srunner_free(runner); return (number_failed == 0)? EXIT_SUCCESS: EXIT_FAILURE; } libcork-1.0.0-rc3/tests/test-gc.c000066400000000000000000000064701367645320400165170ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2011, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #include #include #include #include #include "libcork/core/attributes.h" #include "libcork/core/gc.h" #include "libcork/core/types.h" #include "libcork/helpers/gc.h" #include "helpers.h" /*----------------------------------------------------------------------- * Garbage collector */ struct tree { int id; struct tree *left; struct tree *right; }; _free_(tree) { /* nothing to do, I just want to test the macro */ } _recurse_(tree) { struct tree *self = obj; recurse(gc, self->left, ud); recurse(gc, self->right, ud); } /* test all the variations, even though we're only going to use "tree" */ _gc_(tree); #define tree0__recurse tree__recurse CORK_ATTR_UNUSED static struct cork_gc_obj_iface tree0__gc; _gc_no_free_(tree0); #define tree1__free tree__free CORK_ATTR_UNUSED static struct cork_gc_obj_iface tree1__gc; _gc_no_recurse_(tree1); CORK_ATTR_UNUSED static struct cork_gc_obj_iface tree2__gc; _gc_leaf_(tree2); struct tree * tree_new(int id, struct tree *l, struct tree *r) { struct tree *self = cork_gc_new(tree); self->id = id; self->left = cork_gc_incref(l); self->right = cork_gc_incref(r); return self; } START_TEST(test_gc_acyclic_01) { DESCRIBE_TEST; cork_gc_init(); struct tree *t1 = tree_new(0, NULL, NULL); struct tree *t2 = tree_new(0, NULL, NULL); struct tree *t0 = tree_new(0, t1, t2); cork_gc_decref(t1); cork_gc_decref(t2); cork_gc_decref(t0); cork_gc_done(); } END_TEST START_TEST(test_gc_cyclic_01) { DESCRIBE_TEST; cork_gc_init(); struct tree *t1 = tree_new(0, NULL, NULL); struct tree *t2 = tree_new(0, NULL, NULL); struct tree *t0 = tree_new(0, t1, t2); t1->left = cork_gc_incref(t0); cork_gc_decref(t1); cork_gc_decref(t2); cork_gc_decref(t0); cork_gc_done(); } END_TEST START_TEST(test_gc_cyclic_02) { DESCRIBE_TEST; cork_gc_init(); struct tree *t1 = tree_new(0, NULL, NULL); struct tree *t2 = tree_new(0, NULL, NULL); struct tree *t0 = tree_new(0, t1, t2); t1->left = cork_gc_incref(t0); t2->left = cork_gc_incref(t2); t2->right = cork_gc_incref(t0); cork_gc_decref(t1); cork_gc_decref(t2); cork_gc_decref(t0); cork_gc_done(); } END_TEST /*----------------------------------------------------------------------- * Testing harness */ Suite * test_suite() { Suite *s = suite_create("gc"); TCase *tc_gc = tcase_create("gc"); tcase_add_test(tc_gc, test_gc_acyclic_01); tcase_add_test(tc_gc, test_gc_cyclic_01); tcase_add_test(tc_gc, test_gc_cyclic_02); suite_add_tcase(s, tc_gc); return s; } int main(int argc, const char **argv) { int number_failed; Suite *suite = test_suite(); SRunner *runner = srunner_create(suite); setup_allocator(); srunner_run_all(runner, CK_NORMAL); number_failed = srunner_ntests_failed(runner); srunner_free(runner); return (number_failed == 0)? EXIT_SUCCESS: EXIT_FAILURE; } libcork-1.0.0-rc3/tests/test-hash-table.c000066400000000000000000000245741367645320400201430ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2011, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #include #include #include #include #include "libcork/core/allocator.h" #include "libcork/core/hash.h" #include "libcork/core/types.h" #include "libcork/ds/buffer.h" #include "libcork/ds/hash-table.h" #include "helpers.h" /*----------------------------------------------------------------------- * Integer hash tables */ static void uint64__free(void *vi) { uint64_t *i = vi; cork_delete(uint64_t, i); } static bool uint64__equals(void *user_data, const void *va, const void *vb) { const uint64_t *a = va; const uint64_t *b = vb; #if 0 printf("Testing %p (%" PRIu64 ") and %p (%" PRIu64 ")\n", a, *a, b, *b); #endif return *a == *b; } static cork_hash uint64__hash(void *user_data, const void *velement) { const uint64_t *element = velement; #if 0 printf("Hashing %p (%" PRIu64 ")\n", element, *element); #endif return (cork_hash) *element; } static enum cork_hash_table_map_result uint64_sum(void *vsum, struct cork_hash_table_entry *entry) { uint64_t *sum = vsum; uint64_t *value = entry->value; *sum += *value; return CORK_HASH_TABLE_MAP_CONTINUE; } static void test_map_sum(struct cork_hash_table *table, uint64_t expected) { uint64_t sum = 0; cork_hash_table_map(table, &sum, uint64_sum); fail_unless(sum == expected, "Unexpected map sum, got %" PRIu64 ", expected %" PRIu64, sum, expected); } static void test_iterator_sum(struct cork_hash_table *table, uint64_t expected) { uint64_t sum = 0; struct cork_hash_table_iterator iterator; struct cork_hash_table_entry *entry; cork_hash_table_iterator_init(table, &iterator); while ((entry = cork_hash_table_iterator_next(&iterator)) != NULL) { uint64_t *value_ptr = entry->value; sum += *value_ptr; } fail_unless(sum == expected, "Unexpected iterator sum, got %" PRIu64 ", expected %" PRIu64 "", sum, expected); } static enum cork_hash_table_map_result uint64_to_string(void *vdest, struct cork_hash_table_entry *entry) { struct cork_buffer *dest = vdest; uint64_t *key = entry->key; uint64_t *value = entry->value; if (dest->size > 1) { cork_buffer_append(dest, ", ", 2); } cork_buffer_append_printf(dest, "%" PRIu64 ":%" PRIu64, *key, *value); return CORK_HASH_TABLE_MAP_CONTINUE; } static void test_map_to_string(struct cork_hash_table *table, const char *expected) { struct cork_buffer buf = CORK_BUFFER_INIT(); cork_buffer_set(&buf, "[", 1); cork_hash_table_map(table, &buf, uint64_to_string); cork_buffer_append(&buf, "]", 1); fail_unless_streq("Integer arrays", expected, buf.buf); cork_buffer_done(&buf); } static void test_iterator_to_string(struct cork_hash_table *table, const char *expected) { struct cork_buffer buf = CORK_BUFFER_INIT(); struct cork_hash_table_iterator iterator; struct cork_hash_table_entry *entry; cork_buffer_set(&buf, "[", 1); cork_hash_table_iterator_init(table, &iterator); while ((entry = cork_hash_table_iterator_next(&iterator)) != NULL) { uint64_t *key = entry->key; uint64_t *value = entry->value; if (buf.size > 1) { cork_buffer_append(&buf, ", ", 2); } cork_buffer_append_printf(&buf, "%" PRIu64 ":%" PRIu64, *key, *value); } cork_buffer_append(&buf, "]", 1); fail_unless_streq("Integer arrays", expected, buf.buf); cork_buffer_done(&buf); } START_TEST(test_uint64_hash_table) { struct cork_hash_table *table; uint64_t key, *key_ptr, *old_key; void *v_key, *v_value; uint64_t *value_ptr, *old_value; bool is_new; struct cork_hash_table_entry *entry; table = cork_hash_table_new(0, 0); cork_hash_table_set_hash(table, uint64__hash); cork_hash_table_set_equals(table, uint64__equals); cork_hash_table_set_free_key(table, uint64__free); cork_hash_table_set_free_value(table, uint64__free); fail_unless(cork_hash_table_size(table) == 0, "Hash table should start empty"); key = 0; fail_unless(cork_hash_table_get(table, &key) == NULL, "Shouldn't get value pointer from empty hash table"); test_map_sum(table, 0); test_map_to_string(table, "[]"); test_iterator_sum(table, 0); test_iterator_to_string(table, "[]"); key_ptr = cork_new(uint64_t); *key_ptr = 0; value_ptr = cork_new(uint64_t); *value_ptr = 32; fail_if_error(cork_hash_table_put (table, key_ptr, value_ptr, &is_new, &v_key, &v_value)); fail_unless(is_new, "Couldn't append {0=>32} to hash table"); old_key = v_key; old_value = v_value; fail_unless(old_key == NULL, "Unexpected previous key"); fail_unless(old_value == NULL, "Unexpected previous value"); fail_unless(cork_hash_table_size(table) == 1, "Unexpected size after adding {0->32}"); fail_if_error(entry = cork_hash_table_get_or_create (table, &key, &is_new)); fail_if(is_new, "Shouldn't create new {0=>X} entry"); value_ptr = entry->value; fail_unless(*value_ptr == 32, "Unexpected value for {0=>X} entry"); fail_unless(cork_hash_table_size(table) == 1, "Unexpected size after retrieving {0->32}"); key = 1; fail_if_error(entry = cork_hash_table_get_or_create (table, &key, &is_new)); fail_unless(is_new, "Should create new {1=>X} entry"); key_ptr = cork_new(uint64_t); *key_ptr = key; entry->key = key_ptr; value_ptr = cork_new(uint64_t); *value_ptr = 2; entry->value = value_ptr; fail_unless(cork_hash_table_size(table) == 2, "Unexpected size after adding {1=>2}"); test_map_sum(table, 34); test_map_to_string(table, "[0:32, 1:2]"); test_iterator_sum(table, 34); test_iterator_to_string(table, "[0:32, 1:2]"); key = 0; fail_unless(cork_hash_table_delete(table, &key, NULL, NULL), "Couldn't delete {0=>32}"); fail_unless(cork_hash_table_size(table) == 1, "Unexpected size after deleting entry"); test_map_to_string(table, "[1:2]"); test_iterator_to_string(table, "[1:2]"); key = 3; fail_if(cork_hash_table_delete(table, &key, NULL, NULL), "Shouldn't be able to delete nonexistent {3=>X}"); cork_hash_table_delete_entry(table, entry); fail_unless(cork_hash_table_size(table) == 0, "Unexpected size after deleting last entry"); /* * Add the entries back so that we can try deleting them using * cork_hash_table_map. */ key_ptr = cork_new(uint64_t); *key_ptr = 0; value_ptr = cork_new(uint64_t); *value_ptr = 32; fail_if_error(cork_hash_table_put (table, key_ptr, value_ptr, &is_new, &v_key, &v_value)); fail_unless(is_new, "Couldn't append {0=>32} to hash table"); old_key = v_key; old_value = v_value; key_ptr = cork_new(uint64_t); *key_ptr = 1; value_ptr = cork_new(uint64_t); *value_ptr = 2; fail_if_error(cork_hash_table_put (table, key_ptr, value_ptr, &is_new, &v_key, &v_value)); fail_unless(is_new, "Couldn't append {1=>2} to hash table"); old_key = v_key; old_value = v_value; cork_hash_table_clear(table); fail_unless(cork_hash_table_size(table) == 0, "Unexpected size after deleting entries using map"); /* And we're done, so let's free everything. */ cork_hash_table_free(table); } END_TEST /*----------------------------------------------------------------------- * String hash tables */ START_TEST(test_string_hash_table) { struct cork_hash_table *table; char key[256]; void *value; table = cork_string_hash_table_new(0, 0); fail_if_error(cork_hash_table_put (table, "key1", (void *) (uintptr_t) 1, NULL, NULL, NULL)); fail_unless(cork_hash_table_size(table) == 1, "Unexpected size after adding {key1->1}"); strncpy(key, "key1", sizeof(key)); fail_if((value = cork_hash_table_get(table, key)) == NULL, "No entry for key1"); fail_unless(value == (void *) (uintptr_t) 1, "Unexpected value for key1"); strncpy(key, "key2", sizeof(key)); fail_unless((value = cork_hash_table_get(table, key)) == NULL, "Unexpected entry for key2"); cork_hash_table_free(table); } END_TEST /*----------------------------------------------------------------------- * Pointer hash tables */ START_TEST(test_pointer_hash_table) { struct cork_hash_table *table; int key1; int key2; void *value; table = cork_pointer_hash_table_new(0, 0); fail_if_error(cork_hash_table_put (table, &key1, (void *) (uintptr_t) 1, NULL, NULL, NULL)); fail_unless(cork_hash_table_size(table) == 1, "Unexpected size after adding {key1->1}"); fail_if((value = cork_hash_table_get(table, &key1)) == NULL, "No entry for key1"); fail_unless(value == (void *) (uintptr_t) 1, "Unexpected value for key1"); fail_unless((value = cork_hash_table_get(table, &key2)) == NULL, "Unexpected entry for key2"); cork_hash_table_free(table); } END_TEST /*----------------------------------------------------------------------- * Testing harness */ Suite * test_suite() { Suite *s = suite_create("hash_table"); TCase *tc_ds = tcase_create("hash_table"); tcase_add_test(tc_ds, test_uint64_hash_table); tcase_add_test(tc_ds, test_string_hash_table); tcase_add_test(tc_ds, test_pointer_hash_table); suite_add_tcase(s, tc_ds); return s; } int main(int argc, const char **argv) { int number_failed; Suite *suite = test_suite(); SRunner *runner = srunner_create(suite); setup_allocator(); srunner_run_all(runner, CK_NORMAL); number_failed = srunner_ntests_failed(runner); srunner_free(runner); return (number_failed == 0)? EXIT_SUCCESS: EXIT_FAILURE; } libcork-1.0.0-rc3/tests/test-input.txt000066400000000000000000000000301367645320400176440ustar00rootroot00000000000000Hello, world What is up libcork-1.0.0-rc3/tests/test-managed-buffer.c000066400000000000000000000215071367645320400207670ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2009, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #include #include #include #include "libcork/core/allocator.h" #include "libcork/core/types.h" #include "libcork/ds/managed-buffer.h" #include "libcork/ds/slice.h" #include "libcork/helpers/errors.h" #include "helpers.h" /*----------------------------------------------------------------------- * Helper functions */ struct flag_buffer { struct cork_managed_buffer parent; bool *flag; }; static void set_flag_on_free(struct cork_managed_buffer *mbuf) { struct flag_buffer *fbuf = cork_container_of(mbuf, struct flag_buffer, parent); *fbuf->flag = true; cork_delete(struct flag_buffer, fbuf); } static struct cork_managed_buffer_iface FLAG__MANAGED_BUFFER = { set_flag_on_free }; static struct cork_managed_buffer * flag_buffer_new(const void *buf, size_t size, bool *flag) { struct flag_buffer *fbuf = cork_new(struct flag_buffer); fbuf->parent.buf = buf; fbuf->parent.size = size; fbuf->parent.ref_count = 1; fbuf->parent.iface = &FLAG__MANAGED_BUFFER; fbuf->flag = flag; return &fbuf->parent; } /*----------------------------------------------------------------------- * Buffer reference counting */ START_TEST(test_managed_buffer_refcount) { bool flag = false; /* * Make a bunch of references, unreference them all, and then * verify that the free function got called. */ struct cork_managed_buffer *pb0; fail_if_error(pb0 = flag_buffer_new(NULL, 0, &flag)); struct cork_managed_buffer *pb1 = cork_managed_buffer_ref(pb0); struct cork_managed_buffer *pb2 = cork_managed_buffer_ref(pb0); struct cork_managed_buffer *pb3 = cork_managed_buffer_ref(pb2); cork_managed_buffer_unref(pb0); cork_managed_buffer_unref(pb1); cork_managed_buffer_unref(pb2); cork_managed_buffer_unref(pb3); fail_unless(flag, "Managed buffer free function never called."); } END_TEST START_TEST(test_managed_buffer_bad_refcount) { bool flag = false; /* * Make a bunch of references, forget to unreference one of them, * and then verify that the free function didn't called. */ struct cork_managed_buffer *pb0; fail_if_error(pb0 = flag_buffer_new(NULL, 0, &flag)); struct cork_managed_buffer *pb1 = cork_managed_buffer_ref(pb0); struct cork_managed_buffer *pb2 = cork_managed_buffer_ref(pb0); struct cork_managed_buffer *pb3 = cork_managed_buffer_ref(pb2); cork_managed_buffer_unref(pb0); cork_managed_buffer_unref(pb1); cork_managed_buffer_unref(pb2); /* cork_managed_buffer_unref(pb3); OH NO! */ (void) pb3; fail_if(flag, "Managed buffer free function was called unexpectedly."); /* free the buffer here to quiet valgrind */ cork_managed_buffer_unref(pb3); } END_TEST /*----------------------------------------------------------------------- * Slicing */ START_TEST(test_slice) { /* Try to slice a NULL buffer. */ struct cork_slice ps1; fail_unless_error(cork_managed_buffer_slice (&ps1, NULL, 0, 0), "Shouldn't be able to slice a NULL buffer"); fail_unless_error(cork_managed_buffer_slice_offset (&ps1, NULL, 0), "Shouldn't be able to slice a NULL buffer"); fail_unless_error(cork_slice_copy (&ps1, NULL, 0, 0), "Shouldn't be able to slice a NULL slice"); fail_unless_error(cork_slice_copy_offset (&ps1, NULL, 0), "Shouldn't be able to slice a NULL slice"); } END_TEST /*----------------------------------------------------------------------- * Slice reference counting */ START_TEST(test_slice_refcount) { bool flag = false; /* * Make a bunch of slices, finish them all, and then verify that * the free function got called. */ static char *BUF = "abcdefg"; static size_t LEN = 7; struct cork_managed_buffer *pb; fail_if_error(pb = flag_buffer_new(BUF, LEN, &flag)); struct cork_slice ps1; struct cork_slice ps2; struct cork_slice ps3; fail_if_error(cork_managed_buffer_slice(&ps1, pb, 0, 7)); fail_if_error(cork_managed_buffer_slice(&ps2, pb, 1, 1)); fail_if_error(cork_managed_buffer_slice(&ps3, pb, 4, 3)); cork_managed_buffer_unref(pb); cork_slice_finish(&ps1); cork_slice_finish(&ps2); cork_slice_finish(&ps3); fail_unless(flag, "Managed buffer free function never called."); } END_TEST START_TEST(test_slice_bad_refcount) { bool flag = false; /* * Make a bunch of slices, forget to finish one of them, and then * verify that the free function didn't called. */ static char *BUF = "abcdefg"; static size_t LEN = 7; struct cork_managed_buffer *pb; fail_if_error(pb = flag_buffer_new(BUF, LEN, &flag)); struct cork_slice ps1; struct cork_slice ps2; struct cork_slice ps3; fail_if_error(cork_managed_buffer_slice(&ps1, pb, 0, 7)); fail_if_error(cork_managed_buffer_slice(&ps2, pb, 1, 1)); fail_if_error(cork_managed_buffer_slice(&ps3, pb, 4, 3)); cork_managed_buffer_unref(pb); cork_slice_finish(&ps1); cork_slice_finish(&ps2); /* cork_slice_finish(&ps3); OH NO! */ fail_if(flag, "Managed buffer free function was called unexpectedly."); /* free the slice here to quiet valgrind */ cork_slice_finish(&ps3); } END_TEST /*----------------------------------------------------------------------- * Slice equality */ START_TEST(test_slice_equals_01) { /* * Make a bunch of slices, finish them all, and then verify that * the free function got called. */ static char *BUF = "abcdefg"; static size_t LEN = 7; struct cork_managed_buffer *pb; fail_if_error(pb = cork_managed_buffer_new_copy(BUF, LEN)); struct cork_slice ps1; struct cork_slice ps2; fail_if_error(cork_managed_buffer_slice_offset(&ps1, pb, 0)); fail_if_error(cork_managed_buffer_slice(&ps2, pb, 0, LEN)); fail_unless(cork_slice_equal(&ps1, &ps2), "Slices aren't equal"); cork_managed_buffer_unref(pb); cork_slice_finish(&ps1); cork_slice_finish(&ps2); } END_TEST START_TEST(test_slice_equals_02) { /* * Make a bunch of slices, finish them all, and then verify that * the free function got called. */ static char *BUF = "abcdefg"; static size_t LEN = 7; struct cork_managed_buffer *pb; fail_if_error(pb = cork_managed_buffer_new_copy(BUF, LEN)); struct cork_slice ps1; struct cork_slice ps2; struct cork_slice ps3; fail_if_error(cork_managed_buffer_slice(&ps1, pb, 3, 3)); fail_if_error(cork_managed_buffer_slice_offset(&ps2, pb, 1)); fail_if_error(cork_slice_copy(&ps3, &ps2, 2, 3)); fail_if_error(cork_slice_slice(&ps2, 2, 3)); fail_unless(cork_slice_equal(&ps1, &ps2), "Slices aren't equal"); fail_unless(cork_slice_equal(&ps1, &ps3), "Slices aren't equal"); cork_managed_buffer_unref(pb); cork_slice_finish(&ps1); cork_slice_finish(&ps2); cork_slice_finish(&ps3); } END_TEST /*----------------------------------------------------------------------- * Testing harness */ Suite * test_suite() { Suite *s = suite_create("managed-buffer"); TCase *tc_buffer_refcount = tcase_create("managed-buffer-refcount"); tcase_add_test(tc_buffer_refcount, test_managed_buffer_refcount); tcase_add_test(tc_buffer_refcount, test_managed_buffer_bad_refcount); suite_add_tcase(s, tc_buffer_refcount); TCase *tc_slice = tcase_create("slice"); tcase_add_test(tc_slice, test_slice); suite_add_tcase(s, tc_slice); TCase *tc_slice_refcount = tcase_create("slice-refcount"); tcase_add_test(tc_slice_refcount, test_slice_refcount); tcase_add_test(tc_slice_refcount, test_slice_bad_refcount); suite_add_tcase(s, tc_slice_refcount); TCase *tc_slice_equality = tcase_create("slice-equality"); tcase_add_test(tc_slice_equality, test_slice_equals_01); tcase_add_test(tc_slice_equality, test_slice_equals_02); suite_add_tcase(s, tc_slice_equality); return s; } int main(int argc, const char **argv) { int number_failed; Suite *suite = test_suite(); SRunner *runner = srunner_create(suite); setup_allocator(); srunner_run_all(runner, CK_NORMAL); number_failed = srunner_ntests_failed(runner); srunner_free(runner); return (number_failed == 0)? EXIT_SUCCESS: EXIT_FAILURE; } libcork-1.0.0-rc3/tests/test-mempool.c000066400000000000000000000105511367645320400175710ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2012, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #include #include #include #include #include #include "libcork/core/mempool.h" #include "libcork/core/types.h" #include "helpers.h" /*----------------------------------------------------------------------- * Memory pools */ START_TEST(test_mempool_01) { #define OBJECT_COUNT 16 DESCRIBE_TEST; struct cork_mempool *mp; /* Small enough that we'll have to allocate a couple of blocks */ mp = cork_mempool_new_ex(int64_t, 64); size_t i; int64_t *objects[OBJECT_COUNT]; for (i = 0; i < OBJECT_COUNT; i++) { fail_if((objects[i] = cork_mempool_new_object(mp)) == NULL, "Cannot allocate object #%zu", i); } for (i = 0; i < OBJECT_COUNT; i++) { cork_mempool_free_object(mp, objects[i]); } for (i = 0; i < OBJECT_COUNT; i++) { fail_if((objects[i] = cork_mempool_new_object(mp)) == NULL, "Cannot reallocate object #%zu", i); } for (i = 0; i < OBJECT_COUNT; i++) { cork_mempool_free_object(mp, objects[i]); } cork_mempool_free(mp); } END_TEST START_TEST(test_mempool_fail_01) { DESCRIBE_TEST; struct cork_mempool *mp; mp = cork_mempool_new(int64_t); int64_t *obj; fail_if((obj = cork_mempool_new_object(mp)) == NULL, "Cannot allocate object"); /* This should raise an assertion since we never freed obj. */ cork_mempool_free(mp); } END_TEST static void int64_init(void *user_data, void *vobj) { int64_t *obj = vobj; *obj = 12; } static void int64_done(void *user_data, void *vobj) { size_t *done_call_count = user_data; (*done_call_count)++; } /* This is based on our knowledge of the internal structure of a memory * pool's blocks and objects. */ #define OBJECTS_PER_BLOCK(block_size, obj_size) \ (((block_size) - CORK_SIZEOF_POINTER) / (obj_size + CORK_SIZEOF_POINTER)) START_TEST(test_mempool_reuse_01) { #define BLOCK_SIZE 64 DESCRIBE_TEST; size_t done_call_count = 0; struct cork_mempool *mp; mp = cork_mempool_new_ex(int64_t, BLOCK_SIZE); cork_mempool_set_user_data(mp, &done_call_count, NULL); cork_mempool_set_init_object(mp, int64_init); cork_mempool_set_done_object(mp, int64_done); int64_t *obj; fail_if((obj = cork_mempool_new_object(mp)) == NULL, "Cannot allocate object"); /* The init_object function sets the value to 12 */ fail_unless(*obj == 12, "Unexpected value %" PRId64, *obj); /* Set the value to something new, free the object, then reallocate. * Since we know memory pools are LIFO, we should get back the same * object, unchanged. */ *obj = 42; cork_mempool_free_object(mp, obj); fail_if((obj = cork_mempool_new_object(mp)) == NULL, "Cannot allocate object"); fail_unless(*obj == 42, "Unexpected value %" PRId64, *obj); cork_mempool_free_object(mp, obj); cork_mempool_free(mp); fail_unless(done_call_count == OBJECTS_PER_BLOCK(BLOCK_SIZE, sizeof(int64_t)), "done_object called an unexpected number of times: %zu", done_call_count); } END_TEST /*----------------------------------------------------------------------- * Testing harness */ Suite * test_suite() { Suite *s = suite_create("mempool"); TCase *tc_mempool = tcase_create("mempool"); tcase_add_test(tc_mempool, test_mempool_01); #if NDEBUG /* If we're not compiling assertions then this test won't abort */ tcase_add_test(tc_mempool, test_mempool_fail_01); #else tcase_add_test_raise_signal(tc_mempool, test_mempool_fail_01, SIGABRT); #endif tcase_add_test(tc_mempool, test_mempool_reuse_01); suite_add_tcase(s, tc_mempool); return s; } int main(int argc, const char **argv) { int number_failed; Suite *suite = test_suite(); SRunner *runner = srunner_create(suite); setup_allocator(); srunner_run_all(runner, CK_NORMAL); number_failed = srunner_ntests_failed(runner); srunner_free(runner); return (number_failed == 0)? EXIT_SUCCESS: EXIT_FAILURE; } libcork-1.0.0-rc3/tests/test-ring-buffer.c000066400000000000000000000121301367645320400203220ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2011, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #include #include #include #include #include "libcork/core/types.h" #include "libcork/ds/ring-buffer.h" #include "helpers.h" /*----------------------------------------------------------------------- * Ring buffers */ START_TEST(test_ring_buffer_1) { struct cork_ring_buffer buf; cork_ring_buffer_init(&buf, 4); fail_unless(cork_ring_buffer_add(&buf, (void *) 1) == 0, "Cannot add to ring buffer"); fail_unless(cork_ring_buffer_add(&buf, (void *) 2) == 0, "Cannot add to ring buffer"); fail_unless(cork_ring_buffer_add(&buf, (void *) 3) == 0, "Cannot add to ring buffer"); fail_unless(cork_ring_buffer_add(&buf, (void *) 4) == 0, "Cannot add to ring buffer"); fail_if(cork_ring_buffer_add(&buf, (void *) 5) == 0, "Shouldn't be able to add to ring buffer"); fail_unless(((intptr_t) cork_ring_buffer_peek(&buf)) == 1, "Unexpected head of ring buffer (peek)"); fail_unless(((intptr_t) cork_ring_buffer_pop(&buf)) == 1, "Unexpected head of ring buffer (pop)"); fail_unless(((intptr_t) cork_ring_buffer_pop(&buf)) == 2, "Unexpected head of ring buffer (pop)"); fail_unless(cork_ring_buffer_add(&buf, (void *) 5) == 0, "Cannot add to ring buffer"); fail_unless(cork_ring_buffer_add(&buf, (void *) 6) == 0, "Cannot add to ring buffer"); fail_if(cork_ring_buffer_add(&buf, (void *) 7) == 0, "Shouldn't be able to add to ring buffer"); fail_unless(((intptr_t) cork_ring_buffer_pop(&buf)) == 3, "Unexpected head of ring buffer (pop)"); fail_unless(((intptr_t) cork_ring_buffer_pop(&buf)) == 4, "Unexpected head of ring buffer (pop)"); fail_unless(((intptr_t) cork_ring_buffer_pop(&buf)) == 5, "Unexpected head of ring buffer (pop)"); fail_unless(((intptr_t) cork_ring_buffer_pop(&buf)) == 6, "Unexpected head of ring buffer (pop)"); fail_unless(cork_ring_buffer_pop(&buf) == NULL, "Shouldn't be able to pop from ring buffer"); cork_ring_buffer_done(&buf); } END_TEST START_TEST(test_ring_buffer_2) { struct cork_ring_buffer *buf = cork_ring_buffer_new(4); fail_unless(cork_ring_buffer_add(buf, (void *) 1) == 0, "Cannot add to ring buffer"); fail_unless(cork_ring_buffer_add(buf, (void *) 2) == 0, "Cannot add to ring buffer"); fail_unless(cork_ring_buffer_add(buf, (void *) 3) == 0, "Cannot add to ring buffer"); fail_unless(cork_ring_buffer_add(buf, (void *) 4) == 0, "Cannot add to ring buffer"); fail_if(cork_ring_buffer_add(buf, (void *) 5) == 0, "Shouldn't be able to add to ring buffer"); fail_unless(((intptr_t) cork_ring_buffer_peek(buf)) == 1, "Unexpected head of ring buffer (peek)"); fail_unless(((intptr_t) cork_ring_buffer_pop(buf)) == 1, "Unexpected head of ring buffer (pop)"); fail_unless(((intptr_t) cork_ring_buffer_pop(buf)) == 2, "Unexpected head of ring buffer (pop)"); fail_unless(cork_ring_buffer_add(buf, (void *) 5) == 0, "Cannot add to ring buffer"); fail_unless(cork_ring_buffer_add(buf, (void *) 6) == 0, "Cannot add to ring buffer"); fail_if(cork_ring_buffer_add(buf, (void *) 7) == 0, "Shouldn't be able to add to ring buffer"); fail_unless(((intptr_t) cork_ring_buffer_pop(buf)) == 3, "Unexpected head of ring buffer (pop)"); fail_unless(((intptr_t) cork_ring_buffer_pop(buf)) == 4, "Unexpected head of ring buffer (pop)"); fail_unless(((intptr_t) cork_ring_buffer_pop(buf)) == 5, "Unexpected head of ring buffer (pop)"); fail_unless(((intptr_t) cork_ring_buffer_pop(buf)) == 6, "Unexpected head of ring buffer (pop)"); fail_unless(cork_ring_buffer_pop(buf) == NULL, "Shouldn't be able to pop from ring buffer"); cork_ring_buffer_free(buf); } END_TEST /*----------------------------------------------------------------------- * Testing harness */ Suite * test_suite() { Suite *s = suite_create("ring_buffer"); TCase *tc_ds = tcase_create("ring_buffer"); tcase_add_test(tc_ds, test_ring_buffer_1); tcase_add_test(tc_ds, test_ring_buffer_2); suite_add_tcase(s, tc_ds); return s; } int main(int argc, const char **argv) { int number_failed; Suite *suite = test_suite(); SRunner *runner = srunner_create(suite); setup_allocator(); srunner_run_all(runner, CK_NORMAL); number_failed = srunner_ntests_failed(runner); srunner_free(runner); return (number_failed == 0)? EXIT_SUCCESS: EXIT_FAILURE; } libcork-1.0.0-rc3/tests/test-slice.c000066400000000000000000000113011367645320400172120ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2009, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #include #include #include #include #include "libcork/ds/slice.h" #include "helpers.h" /*----------------------------------------------------------------------- * Static slices */ START_TEST(test_static_slice) { static char SRC[] = "Here is some text."; size_t SRC_LEN = sizeof(SRC) - 1; struct cork_slice slice; struct cork_slice advanced; const void* original; struct cork_slice copy1; struct cork_slice lcopy1; cork_slice_init_static(&slice, SRC, SRC_LEN); fail_if_error(cork_slice_copy_offset(&advanced, &slice, 0)); fail_if_error(original = cork_slice_advance(&advanced, 4)); fail_unless(strcmp(SRC, original) == 0, "Advance should return original buffer"); fail_if_error(cork_slice_copy(©1, &slice, 8, 4)); fail_if_error(cork_slice_light_copy(&lcopy1, &slice, 8, 4)); fail_if_error(cork_slice_slice(&slice, 8, 4)); fail_unless(cork_slice_equal(&slice, ©1), "Slices should be equal"); fail_unless(cork_slice_equal(&slice, &lcopy1), "Slices should be equal"); /* We have to finish lcopy1 first, since it's a light copy. */ cork_slice_finish(&lcopy1); cork_slice_finish(&slice); cork_slice_finish(©1); cork_slice_finish(&advanced); } END_TEST /*----------------------------------------------------------------------- * Copy-once slices */ START_TEST(test_copy_once_slice) { static char SRC[] = "Here is some text."; size_t SRC_LEN = sizeof(SRC) - 1; struct cork_slice slice; struct cork_slice copy1; struct cork_slice copy2; struct cork_slice lcopy1; struct cork_slice lcopy2; cork_slice_init_copy_once(&slice, SRC, SRC_LEN); fail_unless(slice.buf == SRC, "Unexpected slice buffer"); fail_if_error(cork_slice_light_copy(&lcopy1, &slice, 8, 4)); /* We should still be using the original SRC buffer directly, since we only * created a light copy. */ fail_unless(slice.buf == SRC, "Unexpected slice buffer"); fail_unless(slice.buf + 8 == lcopy1.buf, "Unexpected slice buffer"); fail_if_error(cork_slice_copy(©1, &slice, 8, 4)); fail_if_error(cork_slice_slice(&slice, 8, 4)); /* Once we create a full copy, the content should have been moved into a * managed buffer, which will exist somewhere else in memory than the * original SRC pointer. */ fail_unless(slice.buf != SRC, "Unexpected slice buffer"); fail_unless(slice.buf == copy1.buf, "Unexpected slice buffer"); /* The light copy that we made previously won't have been moved over to the * new managed buffer, though. */ fail_unless(cork_slice_equal(&slice, ©1), "Slices should be equal"); /* Once we've switched over to the managed buffer, a new light copy should * still point into the managed buffer. */ fail_if_error(cork_slice_light_copy(&lcopy2, &slice, 0, 4)); fail_unless(slice.buf != SRC, "Unexpected slice buffer"); fail_unless(slice.buf == lcopy2.buf, "Unexpected slice buffer"); fail_if_error(cork_slice_copy(©2, &slice, 0, 4)); /* The second full copy should not create a new managed buffer, it should * just increment the existing managed buffer's refcount. */ fail_unless(slice.buf == copy2.buf, "Unexpected slice buffer"); fail_unless(copy1.buf == copy2.buf, "Unexpected slice buffer"); fail_unless(cork_slice_equal(&slice, ©2), "Slices should be equal"); fail_unless(cork_slice_equal(©1, ©2), "Slices should be equal"); /* We have to finish the light copies first. */ cork_slice_finish(&lcopy1); cork_slice_finish(&lcopy2); cork_slice_finish(&slice); cork_slice_finish(©1); cork_slice_finish(©2); } END_TEST /*----------------------------------------------------------------------- * Testing harness */ Suite * test_suite() { Suite *s = suite_create("slice"); TCase *tc_slice = tcase_create("slice"); tcase_add_test(tc_slice, test_static_slice); tcase_add_test(tc_slice, test_copy_once_slice); suite_add_tcase(s, tc_slice); return s; } int main(int argc, const char **argv) { int number_failed; Suite *suite = test_suite(); SRunner *runner = srunner_create(suite); setup_allocator(); srunner_run_all(runner, CK_NORMAL); number_failed = srunner_ntests_failed(runner); srunner_free(runner); return (number_failed == 0)? EXIT_SUCCESS: EXIT_FAILURE; } libcork-1.0.0-rc3/tests/test-subprocess.c000066400000000000000000000157541367645320400203230ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2011, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #include #include #include #include #include #include #include "libcork/config.h" #include "libcork/core.h" #include "libcork/ds.h" #include "libcork/os.h" #include "helpers.h" /*----------------------------------------------------------------------- * Verifying stream consumer */ struct verify_consumer { struct cork_stream_consumer parent; struct cork_buffer buf; const char *name; const char *expected; }; static int verify_consumer__data(struct cork_stream_consumer *vself, const void *buf, size_t size, bool is_first) { struct verify_consumer *self = cork_container_of(vself, struct verify_consumer, parent); if (is_first) { cork_buffer_clear(&self->buf); } cork_buffer_append(&self->buf, buf, size); return 0; } static int verify_consumer__eof(struct cork_stream_consumer *vself) { struct verify_consumer *self = cork_container_of(vself, struct verify_consumer, parent); const char *actual = self->buf.buf; if (actual == NULL) { actual = ""; } fail_unless(strcmp(actual, self->expected) == 0, "Unexpected %s: got\n%s\nexpected\n%s\n", self->name, actual, self->expected); return 0; } static void verify_consumer__free(struct cork_stream_consumer *vself) { struct verify_consumer *self = cork_container_of(vself, struct verify_consumer, parent); cork_buffer_done(&self->buf); cork_strfree(self->name); cork_strfree(self->expected); cork_delete(struct verify_consumer, self); } struct cork_stream_consumer * verify_consumer_new(const char *name, const char *expected) { struct verify_consumer *self = cork_new(struct verify_consumer); self->parent.data = verify_consumer__data; self->parent.eof = verify_consumer__eof; self->parent.free = verify_consumer__free; cork_buffer_init(&self->buf); self->name = cork_strdup(name); self->expected = cork_strdup(expected); return &self->parent; } /*----------------------------------------------------------------------- * Helpers */ struct env { const char *name; const char *value; }; struct spec { char *program; char * const *params; struct env *env; const char *expected_stdout; const char *expected_stderr; int expected_exit_code; struct cork_stream_consumer *verify_stdout; struct cork_stream_consumer *verify_stderr; int exit_code; }; static struct cork_env * test_env(struct env *env_spec) { struct cork_env *env; struct env *curr; if (env_spec == NULL) { return NULL; } env = cork_env_new(); for (curr = env_spec; curr->name != NULL; curr++) { cork_env_add_printf(env, curr->name, "%s", curr->value); } return env; } static void test_subprocesses_(size_t spec_count, struct spec **specs) { size_t i; struct cork_subprocess_group *group = cork_subprocess_group_new(); for (i = 0; i < spec_count; i++) { struct spec *spec = specs[i]; struct cork_exec *exec; struct cork_env *env = test_env(spec->env); struct cork_subprocess *sub; spec->verify_stdout = verify_consumer_new("stdout", spec->expected_stdout); spec->verify_stderr = verify_consumer_new("stderr", spec->expected_stderr); fail_if_error(exec = cork_exec_new_with_param_array (spec->program, spec->params)); cork_exec_set_env(exec, env); fail_if_error(sub = cork_subprocess_new_exec (exec, spec->verify_stdout, spec->verify_stderr, &spec->exit_code)); cork_subprocess_group_add(group, sub); } fail_if_error(cork_subprocess_group_start(group)); fail_if_error(cork_subprocess_group_wait(group)); for (i = 0; i < spec_count; i++) { struct spec *spec = specs[i]; fail_unless_equal("Exit codes", "%d", spec->expected_exit_code, spec->exit_code); cork_stream_consumer_free(spec->verify_stdout); cork_stream_consumer_free(spec->verify_stderr); } cork_subprocess_group_free(group); } #define test_subprocesses(specs) \ test_subprocesses_(sizeof(specs) / sizeof(specs[0]), specs) /*----------------------------------------------------------------------- * Subprocesses */ static char *echo_01_params[] = { "echo", "hello", "world", NULL }; static struct spec echo_01 = { "echo", echo_01_params, NULL, "hello world\n", "", 0 }; static char *echo_02_params[] = { "echo", "foo", "bar", "baz", NULL }; static struct spec echo_02 = { "echo", echo_02_params, NULL, "foo bar baz\n", "", 0 }; static char *echo_03_params[] = { "sh", "-c", "echo $CORK_TEST_VAR", NULL }; static struct env echo_03_env[] = { { "CORK_TEST_VAR", "hello world" }, { NULL } }; static struct spec echo_03 = { "sh", echo_03_params, echo_03_env, "hello world\n", "", 0 }; static char *false_01_params[] = { "false", NULL }; static struct spec false_01 = { "false", false_01_params, NULL, "", "", 1 }; START_TEST(test_subprocess_01) { DESCRIBE_TEST; struct spec *specs[] = { &echo_01 }; test_subprocesses(specs); } END_TEST START_TEST(test_subprocess_02) { DESCRIBE_TEST; struct spec *specs[] = { &echo_02 }; test_subprocesses(specs); } END_TEST START_TEST(test_subprocess_03) { DESCRIBE_TEST; struct spec *specs[] = { &echo_03 }; test_subprocesses(specs); } END_TEST START_TEST(test_subprocess_group_01) { DESCRIBE_TEST; struct spec *specs[] = { &echo_01, &echo_02 }; test_subprocesses(specs); } END_TEST START_TEST(test_subprocess_exit_code_01) { DESCRIBE_TEST; struct spec *specs[] = { &false_01 }; test_subprocesses(specs); } END_TEST /*----------------------------------------------------------------------- * Testing harness */ Suite * test_suite() { Suite *s = suite_create("subprocess"); TCase *tc_subprocess = tcase_create("subprocess"); tcase_add_test(tc_subprocess, test_subprocess_01); tcase_add_test(tc_subprocess, test_subprocess_02); tcase_add_test(tc_subprocess, test_subprocess_03); tcase_add_test(tc_subprocess, test_subprocess_group_01); tcase_add_test(tc_subprocess, test_subprocess_exit_code_01); suite_add_tcase(s, tc_subprocess); return s; } int main(int argc, const char **argv) { int number_failed; Suite *suite = test_suite(); SRunner *runner = srunner_create(suite); setup_allocator(); srunner_run_all(runner, CK_NORMAL); number_failed = srunner_ntests_failed(runner); srunner_free(runner); return (number_failed == 0)? EXIT_SUCCESS: EXIT_FAILURE; } libcork-1.0.0-rc3/tests/test-threads.c000066400000000000000000000201461367645320400175540ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2012, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #include #include #include #include #include #include "libcork/core/allocator.h" #include "libcork/core/types.h" #include "libcork/threads/atomics.h" #include "libcork/threads/basics.h" #include "helpers.h" /*----------------------------------------------------------------------- * Atomics */ #define test_atomic_op(name, type, fmt, op, expected) \ do { \ type actual = cork_##name##_atomic_##op(&val, 1); \ fail_unless_equal(#name, fmt, expected, actual); \ } while (0) #define test_cas(name, type, fmt, ov, nv) \ do { \ type actual = cork_##name##_cas(&val, ov, nv); \ fail_unless_equal(#name, fmt, ov, actual); \ } while (0) #define test_atomic(name, type, fmt) \ START_TEST(test_atomic_##name) \ { \ DESCRIBE_TEST; \ volatile type val = 0; \ test_atomic_op(name, type, fmt, add, 1); \ test_atomic_op(name, type, fmt, pre_add, 1); \ test_atomic_op(name, type, fmt, add, 3); \ test_atomic_op(name, type, fmt, pre_add, 3); \ fail_unless_equal(#name, fmt, 4, val); \ test_atomic_op(name, type, fmt, sub, 3); \ test_atomic_op(name, type, fmt, pre_sub, 3); \ test_atomic_op(name, type, fmt, sub, 1); \ test_atomic_op(name, type, fmt, pre_sub, 1); \ fail_unless_equal(#name, fmt, 0, val); \ \ test_cas(name, type, fmt, 0, 1); \ test_cas(name, type, fmt, 1, 10); \ test_cas(name, type, fmt, 10, 2); \ test_cas(name, type, fmt, 2, 0); \ fail_unless_equal(#name, fmt, 0, val); \ } \ END_TEST test_atomic(int, int, "%d"); test_atomic(uint, unsigned int, "%u"); test_atomic(size, size_t, "%zu"); START_TEST(test_atomic_ptr) { DESCRIBE_TEST; uint64_t v0 = 0; uint64_t v1 = 0; uint64_t v2 = 0; uint64_t v3 = 0; uint64_t * volatile val = &v0; test_cas(ptr, uint64_t *, "%p", &v0, &v1); test_cas(ptr, uint64_t *, "%p", &v1, &v2); test_cas(ptr, uint64_t *, "%p", &v2, &v3); test_cas(ptr, uint64_t *, "%p", &v3, &v0); fail_unless_equal("ptr", "%p", &v0, val); } END_TEST /*----------------------------------------------------------------------- * Once */ START_TEST(test_once) { DESCRIBE_TEST; cork_once_barrier(once); static size_t call_count = 0; static int value = 0; #define go \ do { \ call_count++; \ value = 1; \ } while (0) cork_once(once, go); fail_unless_equal("Value", "%d", 1, value); cork_once(once, go); fail_unless_equal("Value", "%d", 1, value); cork_once(once, go); fail_unless_equal("Value", "%d", 1, value); cork_once(once, go); fail_unless_equal("Value", "%d", 1, value); fail_unless_equal("Call count", "%zu", 1, call_count); } END_TEST START_TEST(test_once_recursive) { DESCRIBE_TEST; cork_once_barrier(once); static size_t call_count = 0; static int value = 0; #define go \ do { \ call_count++; \ value = 1; \ } while (0) cork_once_recursive(once, go); fail_unless_equal("Value", "%d", 1, value); cork_once_recursive(once, go); fail_unless_equal("Value", "%d", 1, value); cork_once_recursive(once, go); fail_unless_equal("Value", "%d", 1, value); cork_once_recursive(once, go); fail_unless_equal("Value", "%d", 1, value); fail_unless_equal("Call count", "%zu", 1, call_count); } END_TEST /*----------------------------------------------------------------------- * Thread IDs */ START_TEST(test_thread_ids) { DESCRIBE_TEST; cork_thread_id id = cork_current_thread_get_id(); fail_if(id == CORK_THREAD_NONE, "Expected a valid thread ID"); } END_TEST /*----------------------------------------------------------------------- * Threads */ struct cork_test_thread { int *dest; int value; }; static int cork_test_thread__run(void *vself) { struct cork_test_thread *self = vself; *self->dest = self->value; return 0; } static void cork_test_thread__free(void *vself) { struct cork_test_thread *self = vself; cork_delete(struct cork_test_thread, self); } static struct cork_thread * cork_test_thread_new(const char *name, int *dest, int value) { struct cork_test_thread *self = cork_new(struct cork_test_thread); self->dest = dest; self->value = value; return cork_thread_new (name, self, cork_test_thread__free, cork_test_thread__run); } static int cork_error_thread__run(void *vself) { /* The particular error doesn't matter; just want to make sure it gets * propagated from the cork_thread_join call. */ cork_system_error_set_explicit(ENOMEM); return -1; } START_TEST(test_threads_01) { struct cork_thread *t1; int v1 = -1; DESCRIBE_TEST; fail_if_error(t1 = cork_test_thread_new("test", &v1, 1)); fail_unless_equal("Values", "%d", -1, v1); cork_thread_free(t1); } END_TEST START_TEST(test_threads_02) { struct cork_thread *t1; int v1 = -1; DESCRIBE_TEST; fail_if_error(t1 = cork_test_thread_new("test", &v1, 1)); fail_if_error(cork_thread_start(t1)); fail_if_error(cork_thread_join(t1)); fail_unless_equal("Values", "%d", 1, v1); } END_TEST START_TEST(test_threads_03) { struct cork_thread *t1; struct cork_thread *t2; int v1 = -1; int v2 = -1; DESCRIBE_TEST; fail_if_error(t1 = cork_test_thread_new("test1", &v1, 1)); fail_if_error(t2 = cork_test_thread_new("test2", &v2, 2)); fail_if_error(cork_thread_start(t1)); fail_if_error(cork_thread_start(t2)); fail_if_error(cork_thread_join(t1)); fail_if_error(cork_thread_join(t2)); fail_unless_equal("Values", "%d", 1, v1); fail_unless_equal("Values", "%d", 2, v2); } END_TEST START_TEST(test_threads_04) { struct cork_thread *t1; struct cork_thread *t2; int v1 = -1; int v2 = -1; DESCRIBE_TEST; fail_if_error(t1 = cork_test_thread_new("test1", &v1, 1)); fail_if_error(t2 = cork_test_thread_new("test2", &v2, 2)); fail_if_error(cork_thread_start(t1)); fail_if_error(cork_thread_start(t2)); fail_if_error(cork_thread_join(t2)); fail_if_error(cork_thread_join(t1)); fail_unless_equal("Values", "%d", 1, v1); fail_unless_equal("Values", "%d", 2, v2); } END_TEST START_TEST(test_threads_error_01) { DESCRIBE_TEST; struct cork_thread *t1; fail_if_error(t1 = cork_thread_new ("test", NULL, NULL, cork_error_thread__run)); fail_if_error(cork_thread_start(t1)); fail_unless_error(cork_thread_join(t1)); } END_TEST /*----------------------------------------------------------------------- * Testing harness */ Suite * test_suite() { Suite *s = suite_create("threads"); TCase *tc_atomic = tcase_create("atomic"); tcase_add_test(tc_atomic, test_atomic_int); tcase_add_test(tc_atomic, test_atomic_uint); tcase_add_test(tc_atomic, test_atomic_size); tcase_add_test(tc_atomic, test_atomic_ptr); suite_add_tcase(s, tc_atomic); TCase *tc_basics = tcase_create("basics"); tcase_add_test(tc_basics, test_once); tcase_add_test(tc_basics, test_once_recursive); tcase_add_test(tc_basics, test_thread_ids); suite_add_tcase(s, tc_basics); TCase *tc_threads = tcase_create("threads"); tcase_add_test(tc_threads, test_threads_01); tcase_add_test(tc_threads, test_threads_02); tcase_add_test(tc_threads, test_threads_03); tcase_add_test(tc_threads, test_threads_04); tcase_add_test(tc_threads, test_threads_error_01); suite_add_tcase(s, tc_threads); return s; } int main(int argc, const char **argv) { int number_failed; Suite *suite = test_suite(); SRunner *runner = srunner_create(suite); setup_allocator(); srunner_run_all(runner, CK_NORMAL); number_failed = srunner_ntests_failed(runner); srunner_free(runner); return (number_failed == 0)? EXIT_SUCCESS: EXIT_FAILURE; } libcork-1.0.0-rc3/tests/test-u128.c000066400000000000000000000314471367645320400166270ustar00rootroot00000000000000/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2017, libcork authors * All rights reserved. * * Please see the COPYING file in this distribution for license details. * ---------------------------------------------------------------------- */ #include #include "libcork/config.h" #include "libcork/core/u128.h" #include "libcork/os/subprocess.h" #include "helpers.h" /*----------------------------------------------------------------------- * 128-bit integers */ static void test_one_u128_decimal(cork_u128 value, const char *expected) { char buf[CORK_U128_DECIMAL_LENGTH]; const char *actual = cork_u128_to_decimal(buf, value); fail_unless_streq("Integers", expected, actual); } static void test_one_u128_hex(cork_u128 value, const char *expected) { char buf[CORK_U128_HEX_LENGTH]; const char *actual = cork_u128_to_hex(buf, value); fail_unless_streq("Integers", expected, actual); } static void test_one_u128_padded_hex(cork_u128 value, const char *expected) { char buf[CORK_U128_HEX_LENGTH]; const char *actual = cork_u128_to_padded_hex(buf, value); fail_unless_streq("Integers", expected, actual); } static void test_one_u128_print_from_32(uint32_t i0, uint32_t i1, uint32_t i2, uint32_t i3, const char *expected_decimal, const char *expected_hex, const char *expected_padded_hex) { cork_u128 value = cork_u128_from_32(i0, i1, i2, i3); test_one_u128_decimal(value, expected_decimal); test_one_u128_hex(value, expected_hex); test_one_u128_padded_hex(value, expected_padded_hex); } static void test_one_u128_print_from_64(uint64_t i0, uint64_t i1, const char *expected_decimal, const char *expected_hex, const char *expected_padded_hex) { cork_u128 value = cork_u128_from_64(i0, i1); test_one_u128_decimal(value, expected_decimal); test_one_u128_hex(value, expected_hex); test_one_u128_padded_hex(value, expected_padded_hex); } START_TEST(test_u128_print) { DESCRIBE_TEST; test_one_u128_print_from_32( 0, 0, 0, 0, "0", "0", "00000000000000000000000000000000" ); test_one_u128_print_from_32( 0, 0, 0, 2, "2", "2", "00000000000000000000000000000002" ); test_one_u128_print_from_32( 0, 0, 0, 20, "20", "14", "00000000000000000000000000000014" ); test_one_u128_print_from_32( 0, 0, 0, 0xffffffff, "4294967295", "ffffffff", "000000000000000000000000ffffffff" ); test_one_u128_print_from_32( 0, 0, 1, 0, "4294967296", "100000000", "00000000000000000000000100000000" ); test_one_u128_print_from_32( 0, 0, 0xffffffff, 0xffffffff, "18446744073709551615", "ffffffffffffffff", "0000000000000000ffffffffffffffff" ); test_one_u128_print_from_32( 0, 1, 0, 0, "18446744073709551616", "10000000000000000", "00000000000000010000000000000000" ); test_one_u128_print_from_64( 0, 0, "0", "0", "00000000000000000000000000000000" ); test_one_u128_print_from_64( 0, 2, "2", "2", "00000000000000000000000000000002" ); test_one_u128_print_from_64( 0, 20, "20", "14", "00000000000000000000000000000014" ); test_one_u128_print_from_64( 0, UINT64_C(0xffffffffffffffff), "18446744073709551615", "ffffffffffffffff", "0000000000000000ffffffffffffffff" ); test_one_u128_print_from_64( 1, 0, "18446744073709551616", "10000000000000000", "00000000000000010000000000000000" ); } END_TEST struct shift_test { uint64_t i0; uint64_t i1; unsigned int j; uint64_t res0; uint64_t res1; }; static void check_shift_test(cork_u128(op)(cork_u128, unsigned int), const char *op_str, const struct shift_test *test) { cork_u128 value1 = cork_u128_from_64(test->i0, test->i1); cork_u128 expected = cork_u128_from_64(test->res0, test->res1); cork_u128 result = op(value1, test->j); if (!cork_u128_eq(result, expected)) { char buf1[CORK_U128_HEX_LENGTH]; char buf2[CORK_U128_HEX_LENGTH]; char buf3[CORK_U128_HEX_LENGTH]; const char *value1_str = cork_u128_to_hex(buf1, value1); const char *expected_str = cork_u128_to_hex(buf2, expected); const char *result_str = cork_u128_to_hex(buf3, result); fprintf(stderr, "# %40s\n", value1_str); fprintf(stderr, "# %s %40u\n", op_str, test->j); fprintf(stderr, "# = %40s\n", expected_str); fprintf(stderr, "# got %40s\n", result_str); fail("Unexpected shift error"); } } static void check_shift_tests_(cork_u128(op)(cork_u128, unsigned int), const char *op_str, const struct shift_test *test, size_t count) { size_t i; for (i = 0; i < count; i++) { check_shift_test(op, op_str, test + i); } } #define check_shift_tests(op, op_str, tests) \ check_shift_tests_(op, op_str, \ (tests), sizeof(tests) / sizeof(tests[0])) static const struct shift_test SHL_TESTS[] = { {0, 1, 1, 0, 2}, {0, UINT64_C(0x8000000000000000), 1, 1, 0}, {UINT64_C(0x8000000000000000), 0, 1, 0, 0}, #include "u128-tests-shl.c.in" }; START_TEST(test_u128_shl) { DESCRIBE_TEST; check_shift_tests(cork_u128_shl, "<<", SHL_TESTS); } END_TEST static const struct shift_test SHR_TESTS[] = { {0, 1, 1, 0, 0}, {0, 2, 1, 0, 1}, {1, 0, 1, 0, UINT64_C(0x8000000000000000)}, #include "u128-tests-shr.c.in" }; START_TEST(test_u128_shr) { DESCRIBE_TEST; check_shift_tests(cork_u128_shr, ">>", SHR_TESTS); } END_TEST struct arithmetic_test { uint64_t i0; uint64_t i1; uint64_t j0; uint64_t j1; uint64_t res0; uint64_t res1; }; static void check_arithmetic_test(cork_u128(op)(cork_u128, cork_u128), const char *op_str, const struct arithmetic_test *test) { cork_u128 value1 = cork_u128_from_64(test->i0, test->i1); cork_u128 value2 = cork_u128_from_64(test->j0, test->j1); cork_u128 expected = cork_u128_from_64(test->res0, test->res1); cork_u128 result = op(value1, value2); if (!cork_u128_eq(result, expected)) { char buf1[CORK_U128_HEX_LENGTH]; char buf2[CORK_U128_HEX_LENGTH]; char buf3[CORK_U128_HEX_LENGTH]; char buf4[CORK_U128_HEX_LENGTH]; const char *value1_str = cork_u128_to_hex(buf1, value1); const char *value2_str = cork_u128_to_hex(buf2, value2); const char *expected_str = cork_u128_to_hex(buf3, expected); const char *result_str = cork_u128_to_hex(buf4, result); fprintf(stderr, "# %40s\n", value1_str); fprintf(stderr, "# %s %40s\n", op_str, value2_str); fprintf(stderr, "# = %40s\n", expected_str); fprintf(stderr, "# got %40s\n", result_str); fail("Unexpected arithmetic error"); } } static void check_arithmetic_tests_(cork_u128(op)(cork_u128, cork_u128), const char *op_str, const struct arithmetic_test *test, size_t count) { size_t i; for (i = 0; i < count; i++) { check_arithmetic_test(op, op_str, test + i); } } #define check_arithmetic_tests(op, op_str, tests) \ check_arithmetic_tests_(op, op_str, \ (tests), sizeof(tests) / sizeof(tests[0])) static const struct arithmetic_test ADD_TESTS[] = { {0, 0, 0, 0, 0, 0}, {0, 1, 0, 1, 0, 2}, {0, 1, 0, UINT64_C(0xffffffffffffffff), 1, 0}, {0, 1, UINT64_C(0xffffffffffffffff), UINT64_C(0xffffffffffffffff), 0, 0}, #include "u128-tests-add.c.in" }; START_TEST(test_u128_add) { DESCRIBE_TEST; check_arithmetic_tests(cork_u128_add, "+", ADD_TESTS); } END_TEST static const struct arithmetic_test SUB_TESTS[] = { {0, 0, 0, 0, 0, 0}, {0, 1, 0, 1, 0, 0}, {0, 2, 0, 1, 0, 1}, {1, 0, 0, 1, 0, UINT64_C(0xffffffffffffffff)}, {0, 1, 0, 2, UINT64_C(0xffffffffffffffff), UINT64_C(0xffffffffffffffff)}, #include "u128-tests-sub.c.in" }; START_TEST(test_u128_sub) { DESCRIBE_TEST; check_arithmetic_tests(cork_u128_sub, "-", SUB_TESTS); } END_TEST struct comparison_test { uint64_t i0; uint64_t i1; uint64_t j0; uint64_t j1; bool expected; }; static void check_comparison_test(bool(op)(cork_u128, cork_u128), const char *op_str, const struct comparison_test *test) { cork_u128 value1 = cork_u128_from_64(test->i0, test->i1); cork_u128 value2 = cork_u128_from_64(test->j0, test->j1); bool actual = op(value1, value2); if (actual != test->expected) { char buf1[CORK_U128_HEX_LENGTH]; char buf2[CORK_U128_HEX_LENGTH]; const char *value1_str = cork_u128_to_hex(buf1, value1); const char *value2_str = cork_u128_to_hex(buf2, value2); fprintf(stderr, "# %40s\n", value1_str); fprintf(stderr, "# %s %40s\n", op_str, value2_str); fprintf(stderr, "# = %s\n", test->expected? "true": "false"); fprintf(stderr, "# got %s\n", actual? "true": "false"); fail("Unexpected comparison error"); } } static void check_comparison_tests_(bool(op)(cork_u128, cork_u128), const char *op_str, const struct comparison_test *test, size_t count) { size_t i; for (i = 0; i < count; i++) { check_comparison_test(op, op_str, test + i); } } #define check_comparison_tests(op, op_str, tests) \ check_comparison_tests_(op, op_str, \ (tests), sizeof(tests) / sizeof(tests[0])) static const struct comparison_test EQ_TESTS[] = { {0, 0, 0, 0, true}, {0, 0, 0, 1, false}, {0, 2, 0, 1, false}, {0, 1, 0, UINT64_C(0x100000000), false}, {0, UINT64_C(0x100000000), 0, UINT64_C(0x100000000), true}, #include "u128-tests-eq.c.in" }; START_TEST(test_u128_eq) { DESCRIBE_TEST; check_comparison_tests(cork_u128_eq, "==", EQ_TESTS); } END_TEST static const struct comparison_test NE_TESTS[] = { {0, 0, 0, 0, false}, {0, 0, 0, 1, true}, {0, 2, 0, 1, true}, {0, 1, 0, UINT64_C(0x100000000), true}, {0, UINT64_C(0x100000000), 0, UINT64_C(0x100000000), false}, #include "u128-tests-ne.c.in" }; START_TEST(test_u128_ne) { DESCRIBE_TEST; check_comparison_tests(cork_u128_ne, "!=", NE_TESTS); } END_TEST static const struct comparison_test LT_TESTS[] = { {0, 0, 0, 0, false}, {0, 0, 0, 1, true}, {0, 2, 0, 1, false}, {0, 1, 0, UINT64_C(0x100000000), true}, #include "u128-tests-lt.c.in" }; START_TEST(test_u128_lt) { DESCRIBE_TEST; check_comparison_tests(cork_u128_lt, "< ", LT_TESTS); } END_TEST static const struct comparison_test LE_TESTS[] = { {0, 0, 0, 0, true}, {0, 1, 0, 0, false}, {0, 1, 0, 2, true}, {0, UINT64_C(0x100000000), 0, 1, false}, #include "u128-tests-le.c.in" }; START_TEST(test_u128_le) { DESCRIBE_TEST; check_comparison_tests(cork_u128_le, "<=", LE_TESTS); } END_TEST static const struct comparison_test GT_TESTS[] = { {0, 0, 0, 0, false}, {0, 1, 0, 0, true}, {0, 1, 0, 2, false}, {0, UINT64_C(0x100000000), 0, 1, true}, #include "u128-tests-gt.c.in" }; START_TEST(test_u128_gt) { DESCRIBE_TEST; check_comparison_tests(cork_u128_gt, "> ", GT_TESTS); } END_TEST static const struct comparison_test GE_TESTS[] = { {0, 0, 0, 0, true}, {0, 0, 0, 1, false}, {0, 2, 0, 1, true}, {0, 1, 0, UINT64_C(0x100000000), false}, #include "u128-tests-ge.c.in" }; START_TEST(test_u128_ge) { DESCRIBE_TEST; check_comparison_tests(cork_u128_ge, ">=", GE_TESTS); } END_TEST /*----------------------------------------------------------------------- * Testing harness */ Suite * test_suite() { Suite *s = suite_create("core"); TCase *tc_u128 = tcase_create("u128"); tcase_add_test(tc_u128, test_u128_print); tcase_add_test(tc_u128, test_u128_shl); tcase_add_test(tc_u128, test_u128_shr); tcase_add_test(tc_u128, test_u128_add); tcase_add_test(tc_u128, test_u128_sub); tcase_add_test(tc_u128, test_u128_eq); tcase_add_test(tc_u128, test_u128_ne); tcase_add_test(tc_u128, test_u128_lt); tcase_add_test(tc_u128, test_u128_le); tcase_add_test(tc_u128, test_u128_gt); tcase_add_test(tc_u128, test_u128_ge); suite_add_tcase(s, tc_u128); return s; } int main(int argc, const char **argv) { int number_failed; Suite *suite = test_suite(); SRunner *runner = srunner_create(suite); setup_allocator(); srunner_run_all(runner, CK_NORMAL); number_failed = srunner_ntests_failed(runner); srunner_free(runner); return (number_failed == 0)? EXIT_SUCCESS: EXIT_FAILURE; }