diff options
Diffstat (limited to '3rdparty/plibsys/src')
151 files changed, 35204 insertions, 0 deletions
diff --git a/3rdparty/plibsys/src/CMakeLists.txt b/3rdparty/plibsys/src/CMakeLists.txt new file mode 100644 index 0000000..2f13fbf --- /dev/null +++ b/3rdparty/plibsys/src/CMakeLists.txt @@ -0,0 +1,996 @@ +# The MIT License +# +# Copyright (C) 2018-2019 Alexander Saprykin <saprykin.spb@gmail.com> +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# 'Software'), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +if (POLICY CMP0075) + cmake_policy (SET CMP0075 NEW) +endif() + +include (CheckCSourceCompiles) +include (CheckTypeSize) +include (CheckIncludeFile) +include (TestBigEndian) + +# First of all, detect size of the pointer for the target machine. +# We can't rely on CMAKE_SIZEOF_VOID_P when building universal +# binaries. + +check_type_size ("void *" PLIBSYS_SIZEOF_VOID_P) +check_type_size ("size_t" PLIBSYS_SIZEOF_SIZE_T) +check_type_size ("long" PLIBSYS_SIZEOF_LONG) + +# Without generated code multi-arch builds are not supported anyway, +# so the best we can do is to fallback to detected size values + +if (CMAKE_VERSION VERSION_LESS 2.8.1) + set (PLIBSYS_SIZEOF_VOID_P_CODE "#define PLIBSYS_SIZEOF_VOID_P ${PLIBSYS_SIZEOF_VOID_P}") + set (PLIBSYS_SIZEOF_SIZE_T_CODE "#define PLIBSYS_SIZEOF_SIZE_T ${PLIBSYS_SIZEOF_SIZE_T}") + set (PLIBSYS_SIZEOF_LONG_CODE "#define PLIBSYS_SIZEOF_LONG ${PLIBSYS_SIZEOF_LONG}") +endif() + +include (${PROJECT_SOURCE_DIR}/cmake/PlatformDetect.cmake) +include (${PROJECT_SOURCE_DIR}/cmake/VisibilityDetect.cmake) +include (${PROJECT_SOURCE_DIR}/cmake/StdargDetect.cmake) +include (${PROJECT_SOURCE_DIR}/cmake/ThreadNameDetect.cmake) +set (OUTPUT_DIR ${CMAKE_BINARY_DIR}) + +# Try to detect target platform +plibsys_detect_target_platform (PLIBSYS_TARGET_PLATFORM) +plibsys_detect_c_compiler (PLIBSYS_C_COMPILER) +plibsys_detect_target_os (PLIBSYS_TARGET_OS) +plibsys_detect_os_bits (PLIBSYS_OS_BITS) + +if (PLIBSYS_OS_BITS STREQUAL "unknown") + message (FATAL_ERROR "Failed to detect bitness of the target system") +endif() + +if ((PLIBSYS_TARGET_OS STREQUAL windows) AND NOT (PLIBSYS_TARGET_OS STREQUAL cygwin) + AND NOT (PLIBSYS_TARGET_OS STREQUAL msys)) + set (PLIBSYS_NATIVE_WINDOWS TRUE) +endif() + +if (PLIBSYS_COVERAGE) + if (PLIBSYS_C_COMPILER MATCHES "gcc|clang") + set (CMAKE_BUILD_TYPE "Debug") + list (APPEND CMAKE_C_FLAGS "--coverage") + endif() +endif() + +# CMP0042, see http://www.cmake.org/Wiki/CMake_RPATH_handling +if (PLIBSYS_TARGET_OS STREQUAL darwin) + if (POLICY CMP0068) + cmake_policy (SET CMP0068 NEW) + endif() + + set (CMAKE_MACOSX_RPATH TRUE) + set (CMAKE_SKIP_BUILD_RPATH FALSE) + + # Fix runtime paths on macOS 10.5 and less + if (CMAKE_SYSTEM_VERSION VERSION_LESS "10.0.0") + set (CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) + else() + set (CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) + endif() + + set (CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib") + set (CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) + + list (FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${CMAKE_INSTALL_PREFIX}/lib" isSystemDir) + + if ("${isSystemDir}" STREQUAL "-1") + set (CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib") + endif ("${isSystemDir}" STREQUAL "-1") +endif() + +if (NOT EXISTS "${PROJECT_SOURCE_DIR}/platforms/${PLIBSYS_TARGET_PLATFORM}/") + message (FATAL_ERROR "plibsys doesn't support unknown platform ${PLIBSYS_TARGET_PLATFORM}") +endif() + +include (${PROJECT_SOURCE_DIR}/platforms/${PLIBSYS_TARGET_PLATFORM}/platform.cmake) + +set (PLIBSYS_INCLUDE_DIRS + ${PROJECT_SOURCE_DIR}/src + ${CMAKE_BINARY_DIR} +) + +set (PLIBSYS_PUBLIC_HDRS + patomic.h + ptypes.h + pmacros.h + pmacroscompiler.h + pmacroscpu.h + pmacrosos.h + pcondvariable.h + pcryptohash.h + perror.h + perrortypes.h + pdir.h + pfile.h + phashtable.h + pinifile.h + plibsys.h + plibraryloader.h + plist.h + pmain.h + pmem.h + pmutex.h + pprocess.h + prwlock.h + psemaphore.h + pshm.h + pshmbuffer.h + psocket.h + psocketaddress.h + pspinlock.h + pstdarg.h + pstring.h + ptimeprofiler.h + ptree.h + puthread.h +) + +set (PLIBSYS_PRIVATE_HDRS + pcryptohash-gost3411.h + pcryptohash-md5.h + pcryptohash-sha1.h + pcryptohash-sha2-256.h + pcryptohash-sha2-512.h + pcryptohash-sha3.h + perror-private.h + plibsys-private.h + psysclose-private.h + ptimeprofiler-private.h + ptree-avl.h + ptree-bst.h + ptree-rb.h + ptree-private.h + puthread-private.h + ${CMAKE_BINARY_DIR}/plibsysconfig.h +) + +set (PLIBSYS_SRCS + pcryptohash.c + pcryptohash-gost3411.c + pcryptohash-md5.c + pcryptohash-sha1.c + pcryptohash-sha2-256.c + pcryptohash-sha2-512.c + pcryptohash-sha3.c + pdir.c + perror.c + pfile.c + phashtable.c + pinifile.c + plist.c + pmain.c + pmem.c + pprocess.c + pshmbuffer.c + psocket.c + psocketaddress.c + pstring.c + ptimeprofiler.c + ptree.c + ptree-avl.c + ptree-bst.c + ptree-rb.c + puthread.c +) + +if (PLIBSYS_NATIVE_WINDOWS) + set (PLIBSYS_CLOSE_MODEL win) +elseif (PLIBSYS_TARGET_OS STREQUAL darwin) + set (PLIBSYS_CLOSE_MODEL darwin) +else() + set (PLIBSYS_CLOSE_MODEL unix) +endif() + +if (PLIBSYS_THREAD_MODEL STREQUAL "") + set (PLIBSYS_THREAD_MODEL none) +endif() + +if (PLIBSYS_IPC_MODEL STREQUAL "") + set (PLIBSYS_IPC_MODEL none) +endif() + +if (PLIBSYS_TIME_PROFILER_MODEL STREQUAL "") + set (PLIBSYS_TIME_PROFILER_MODEL generic) +endif() + +if (PLIBSYS_DIR_MODEL STREQUAL "") + set (PLIBSYS_DIR_MODEL none) +endif() + +if (PLIBSYS_LIBRARYLOADER_MODEL STREQUAL "") + set (PLIBSYS_LIBRARYLOADER_MODEL none) +endif() + +set (PLIBSYS_PLATFORM_SRCS + pcondvariable-${PLIBSYS_THREAD_MODEL}.c + pmutex-${PLIBSYS_THREAD_MODEL}.c + psemaphore-${PLIBSYS_IPC_MODEL}.c + pshm-${PLIBSYS_IPC_MODEL}.c + psysclose-${PLIBSYS_CLOSE_MODEL}.c + puthread-${PLIBSYS_THREAD_MODEL}.c + ptimeprofiler-${PLIBSYS_TIME_PROFILER_MODEL}.c + pdir-${PLIBSYS_DIR_MODEL}.c + plibraryloader-${PLIBSYS_LIBRARYLOADER_MODEL}.c +) + +if (NOT PLIBSYS_IPC_MODEL STREQUAL none) + list (APPEND PLIBSYS_PRIVATE_HDRS pipc-private.h) + list (APPEND PLIBSYS_SRCS pipc.c) +endif() + +# Save compiler flags +set (SAVED_CMAKE_REQUIRED_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS}) +set (SAVED_CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES}) +set (SAVED_CMAKE_REQUIRED_FLAGS ${SAVED_CMAKE_REQUIRED_FLAGS}) + +list (APPEND CMAKE_REQUIRED_DEFINITIONS ${PLIBSYS_PLATFORM_DEFINES}) +list (APPEND CMAKE_REQUIRED_LIBRARIES ${PLIBSYS_PLATFORM_LINK_LIBRARIES}) +list (APPEND CMAKE_REQUIRED_FLAGS ${PLIBSYS_PLATFORM_LDFLAGS}) + +set (PLIBSYS_ATOMIC_LOCK_FREE FALSE) + +if (NOT PLIBSYS_ATOMIC_MODEL) + message (STATUS "Checking for lock-free atomic intrinsics") + + if (NOT PLIBSYS_C_COMPILER STREQUAL clang) + # GCC __atomic* intrinsics + check_c_source_compiles ( + "int main () { + int i, tmp_int = 0; + void *ptr, *tmp_ptr = 0; + __atomic_store_4 (&i, 0, __ATOMIC_SEQ_CST); + __atomic_compare_exchange_n (&i, &tmp_int, 1, 0, + __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + __atomic_compare_exchange_n ((unsigned long long *) &ptr, + (unsigned long long *) &tmp_ptr, + (unsigned long long) 1, 0, + __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + __atomic_fetch_add (&i, 1, __ATOMIC_SEQ_CST); + return 0; + }" + PLIBSYS_ATOMIC_IMPL_GCCATOMIC + ) + + if (PLIBSYS_ATOMIC_IMPL_GCCATOMIC) + set (PLIBSYS_ATOMIC_LOCK_FREE yes) + set (PLIBSYS_ATOMIC_MODEL "c11") + endif() + endif() + + # GCC __sync* intinsics + if (NOT PLIBSYS_ATOMIC_LOCK_FREE) + check_c_source_compiles ( + "int main () { + int i; + __sync_synchronize (); + __sync_val_compare_and_swap (&i, 0, 1); + __sync_fetch_and_add (&i, 1); + return 0; + }" + PLIBSYS_ATOMIC_IMPL_GCCSYNC + ) + + if (PLIBSYS_ATOMIC_IMPL_GCCSYNC) + set (PLIBSYS_ATOMIC_LOCK_FREE yes) + set (PLIBSYS_ATOMIC_MODEL "sync") + endif() + endif() + + if (NOT PLIBSYS_ATOMIC_LOCK_FREE) + if (PLIBSYS_TARGET_OS STREQUAL windows AND (PLIBSYS_C_COMPILER STREQUAL borland OR + PLIBSYS_C_COMPILER STREQUAL watcom OR + PLIBSYS_C_COMPILER STREQUAL icc OR + PLIBSYS_C_COMPILER STREQUAL msvc)) + set (PLIBSYS_ATOMIC_LOCK_FREE TRUE) + set (PLIBSYS_ATOMIC_MODEL "win") + else() + set (PLIBSYS_ATOMIC_LOCK_FREE FALSE) + set (PLIBSYS_ATOMIC_MODEL "sim") + endif() + endif() + + if (PLIBSYS_ATOMIC_LOCK_FREE) + message (STATUS "Checking for lock-free atomic intrinsics - works") + else() + message (STATUS "Checking for lock-free atomic intrinsics - not works") + endif() +endif() + +list (APPEND PLIBSYS_SRCS + patomic-${PLIBSYS_ATOMIC_MODEL}.c + pspinlock-${PLIBSYS_ATOMIC_MODEL}.c +) + +if (EXISTS PLIBSYS_CONFIG_FILE) + file (REMOVE ${PLIBSYS_CONFIG_FILE}) +endif() + +test_big_endian (PLIBSYS_IS_BIGENDIAN) + +check_include_file ("float.h" PLIBSYS_HAVE_FLOAT_H) +check_include_file ("values.h" PLIBSYS_HAVE_VALUES_H) +check_include_file ("limits.h" PLIBSYS_HAVE_LIMITS_H) + +if (PLIBSYS_HAVE_FLOAT_H) + set (PLIBSYS_NEED_FLOAT_H TRUE) + set (PLIBSYS_FLOAT_MIN FLT_MIN) + set (PLIBSYS_FLOAT_MAX FLT_MAX) + set (PLIBSYS_DOUBLE_MIN DBL_MIN) + set (PLIBSYS_DOUBLE_MAX DBL_MAX) +elseif (PLIBSYS_HAVE_VALUES_H) + set (PLIBSYS_NEED_VALUES_H TRUE) + set (PLIBSYS_FLOAT_MIN MINFLOAT) + set (PLIBSYS_FLOAT_MAX MAXFLOAT) + set (PLIBSYS_DOUBLE_MIN MINDOUBLE) + set (PLIBSYS_DOUBLE_MAX MAXDOUBLE) +endif() + +if (PLIBSYS_HAVE_LIMITS_H) + set (PLIBSYS_NEED_LIMITS_H TRUE) + set (PLIBSYS_SHORT_MIN SHRT_MIN) + set (PLIBSYS_SHORT_MAX SHRT_MAX) + set (PLIBSYS_USHORT_MAX USHRT_MAX) + set (PLIBSYS_INT_MIN INT_MIN) + set (PLIBSYS_INT_MAX INT_MAX) + set (PLIBSYS_UINT_MAX UINT_MAX) + set (PLIBSYS_LONG_MIN LONG_MIN) + set (PLIBSYS_LONG_MAX LONG_MAX) + set (PLIBSYS_ULONG_MAX ULONG_MAX) +elseif (PLIBSYS_HAVE_VALUES_H) + set (PLIBSYS_NEED_VALUES_H TRUE) + set (PLIBSYS_SHORT_MIN MINSHORT) + set (PLIBSYS_SHORT_MAX MAXSHORT) + set (PLIBSYS_USHORT_MAX "(((pushort) P_MAXSHORT) * 2 + 1)") + set (PLIBSYS_INT_MIN MININT) + set (PLIBSYS_INT_MAX MAXINT) + set (PLIBSYS_UINT_MAX "(((puint) P_MAXINT) * 2 + 1)") + set (PLIBSYS_LONG_MIN MINLONG) + set (PLIBSYS_LONG_MAX MAXLONG) + set (PLIBSYS_ULONG_MAX "(((pulong) P_MAXLONG) * 2 + 1)") +endif() + +if (PLIBSYS_NATIVE_WINDOWS) + set (PLIBSYS_NEED_WINDOWS_H TRUE) +endif() + +if (NOT PLIBSYS_NATIVE_WINDOWS) + # Check for anonymous mmap() + message (STATUS "Checking whether mmap has anonymous mapping") + + check_c_source_compiles ( + "#include <sys/types.h> + #include <sys/mman.h> + int main () { + mmap (0, 1024, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); + return 0; + }" + PLIBSYS_MMAP_HAS_MAP_ANON + ) + + check_c_source_compiles ( + "#include <sys/types.h> + #include <sys/mman.h> + int main () { + mmap (0, 1024, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + return 0; + }" + PLIBSYS_MMAP_HAS_MAP_ANONYMOUS + ) + + if (PLIBSYS_MMAP_HAS_MAP_ANONYMOUS OR PLIBSYS_MMAP_HAS_MAP_ANON) + message (STATUS "Checking whether mmap has anonymous mapping - yes") + else() + message (STATUS "Checking whether mmap has anonymous mapping - no") + endif() + + if (PLIBSYS_MMAP_HAS_MAP_ANONYMOUS) + list (APPEND PLIBSYS_COMPILE_DEFS -DPLIBSYS_MMAP_HAS_MAP_ANONYMOUS) + elseif (PLIBSYS_MMAP_HAS_MAP_ANON) + list (APPEND PLIBSYS_COMPILE_DEFS -DPLIBSYS_MMAP_HAS_MAP_ANON) + endif() + + # Check for clock_nanosleep() call + message (STATUS "Checking whether clock_nanosleep() presents") + + check_c_source_compiles ( + "#include <time.h> + int main () { + struct timespec time_sp = {0, 500000000L}; + clock_nanosleep (CLOCK_MONOTONIC, 0, &time_sp, NULL); + return 0; + }" + PLIBSYS_HAS_CLOCKNANOSLEEP + ) + + if (PLIBSYS_HAS_CLOCKNANOSLEEP) + message (STATUS "Checking whether clock_nanosleep() presents - yes") + else() + message (STATUS "Checking whether clock_nanosleep() presents - no") + endif() + + # Check for nanosleep() call + message (STATUS "Checking whether nanosleep() presents") + + check_c_source_compiles ( + "#include <time.h> + int main () { + struct timespec time_sp = {0, 500000000L}; + nanosleep (&time_sp, NULL); + return 0; + }" + PLIBSYS_HAS_NANOSLEEP + ) + + if (PLIBSYS_HAS_NANOSLEEP) + message (STATUS "Checking whether nanosleep() presents - yes") + list (APPEND PLIBSYS_COMPILE_DEFS -DPLIBSYS_HAS_NANOSLEEP) + else() + message (STATUS "Checking whether nanosleep() presents - no") + endif() + + # Prefere clock_nanosleep() over nanosleep() for power consumption + if (PLIBSYS_HAS_CLOCKNANOSLEEP) + list (APPEND PLIBSYS_COMPILE_DEFS -DPLIBSYS_HAS_CLOCKNANOSLEEP) + elseif(PLIBSYS_HAS_NANOSLEEP) + list (APPEND PLIBSYS_COMPILE_DEFS -DPLIBSYS_HAS_NANOSLEEP) + endif() + + # Check for getaddrinfo() call + message (STATUS "Checking whether getaddrinfo() presents") + + check_c_source_compiles ( + "#include <sys/socket.h> + #include <netdb.h> + int main () { + getaddrinfo (0, 0, 0, 0); + freeaddrinfo (0); + + return 0; + }" + PLIBSYS_HAS_GETADDRINFO + ) + + if (PLIBSYS_HAS_GETADDRINFO) + message (STATUS "Checking whether getaddrinfo() presents - yes") + list (APPEND PLIBSYS_COMPILE_DEFS -DPLIBSYS_HAS_GETADDRINFO) + else() + message (STATUS "Checking whether getaddrinfo() presents - no") + endif() +endif() + +if (NOT PLIBSYS_RWLOCK_MODEL) + set (PLIBSYS_RWLOCK_MODEL ${PLIBSYS_THREAD_MODEL}) +else() + if (NOT PLIBSYS_RWLOCK_MODEL STREQUAL general AND + NOT PLIBSYS_RWLOCK_MODEL STREQUAL none) + message (WARNING "It's not recommended to mix threading and read-write lock models") + endif() +endif() + +if (PLIBSYS_THREAD_MODEL STREQUAL posix) + # Some systems only need the difinition to be available + if (PLIBSYS_TARGET_OS MATCHES "darwin|aix") + set (PLIBSYS_SCHED_CHECK "defined (_POSIX_THREAD_PRIORITY_SCHEDULING)") + elseif (PLIBSYS_TARGET_OS STREQUAL android) + set (PLIBSYS_SCHED_CHECK "0") + else() + set (PLIBSYS_SCHED_CHECK "defined (_POSIX_THREAD_PRIORITY_SCHEDULING) && (_POSIX_THREAD_PRIORITY_SCHEDULING - 0 >= 0)") + endif() + + # Check for thread scheduling + message (STATUS "Checking whether POSIX thread scheduling presents") + + check_c_source_compiles ( + "#include <unistd.h> + #include <pthread.h> + #include <sched.h> + + int main () { + #if ${PLIBSYS_SCHED_CHECK} + sched_get_priority_min (0); + sched_get_priority_max (0); + #else + stop_compile_here + #endif + return 0; + }" + PLIBSYS_HAS_POSIX_SCHEDULING + ) + + if (PLIBSYS_HAS_POSIX_SCHEDULING) + message (STATUS "Checking whether POSIX thread scheduling presents - yes") + list (APPEND PLIBSYS_COMPILE_DEFS -DPLIBSYS_HAS_POSIX_SCHEDULING) + else() + message (STATUS "Checking whether POSIX thread scheduling presents - no") + endif() + + # Check for thread stack size + message (STATUS "Checking whether POSIX thread stack size is supported") + + check_c_source_compiles ( + "#include <pthread.h> + + int main () { + pthread_attr_t attr; + + pthread_attr_setstacksize (&attr, 0); + return 0; + }" + PLIBSYS_HAS_POSIX_STACKSIZE + ) + + if (PLIBSYS_HAS_POSIX_STACKSIZE) + message (STATUS "Checking whether POSIX thread stack size is supported - yes") + list (APPEND PLIBSYS_COMPILE_DEFS -DPLIBSYS_HAS_POSIX_STACKSIZE) + else() + message (STATUS "Checking whether POSIX thread stack size is supported - no") + endif() +endif() + +# Some platforms may have headers, but lack actual implementation, +# thus we can let platform to override read-write lock model with +# general implementation +if (PLIBSYS_THREAD_MODEL STREQUAL posix AND + PLIBSYS_RWLOCK_MODEL STREQUAL posix) + # Check for read-write lock support + message (STATUS "Checking whether POSIX read-write locks are supported") + + check_c_source_compiles ( + "#include <pthread.h> + + int main () { + pthread_rwlock_t rwl; + + pthread_rwlock_init (&rwl, 0); + pthread_rwlock_destroy (&rwl); + return 0; + }" + PLIBSYS_HAS_POSIX_RWLOCK + ) + + if (PLIBSYS_HAS_POSIX_RWLOCK) + message (STATUS "Checking whether POSIX read-write locks are supported - yes") + else() + message (STATUS "Checking whether POSIX read-write locks are supported - no") + set (PLIBSYS_RWLOCK_MODEL "general") + endif() +endif() + +list (APPEND PLIBSYS_PLATFORM_SRCS prwlock-${PLIBSYS_RWLOCK_MODEL}.c) + +# POSIX thread naming functions +check_c_source_compiles ( + "#include <pthread.h> + #include <pthread_np.h> + + int main () {return 0;}" + PLIBSYS_HAS_PTHREAD_NP + ) + +if(PLIBSYS_HAS_PTHREAD_NP) + set (PLIBSYS_NEED_PTHREAD_NP_H TRUE) +else() + set (PLIBSYS_NEED_PTHREAD_NP_H FALSE) +endif() + +message (STATUS "Checking whether POSIX thread names are supported") + +plibsys_detect_thread_name (PLIBSYS_NEED_PTHREAD_NP_H PLIBSYS_THREADNAME_DEF) + +if (NOT PLIBSYS_THREADNAME_DEF STREQUAL "NONE") + list (APPEND PLIBSYS_COMPILE_DEFS -D${PLIBSYS_THREADNAME_DEF}) +endif() + +if (PLIBSYS_THREADNAME_DEF STREQUAL "NONE") + message (STATUS "Checking whether POSIX thread names are supported - no") +else() + message (STATUS "Checking whether POSIX thread names are supported - yes") +endif() + +# Windows sockets +if (PLIBSYS_NATIVE_WINDOWS) + set (PLIBSYS_SOCKET_INCLUDES "#include <winsock2.h> + #include <ws2tcpip.h> + #include <windows.h>") +else() + set (PLIBSYS_SOCKET_INCLUDES "#include <sys/types.h> + #include <sys/socket.h> + #include <netinet/in.h>") +endif() + +# Check for socklen_t definition +message (STATUS "Checking whether socklen_t is defined") + +check_c_source_compiles ( + "${PLIBSYS_SOCKET_INCLUDES} + int main () { + socklen_t len = sizeof (socklen_t); + return len > 0 ? 0 : -1; + }" + PLIBSYS_HAS_SOCKLEN_T + ) + +if (PLIBSYS_HAS_SOCKLEN_T) + message (STATUS "Checking whether socklen_t is defined - yes") + list (APPEND PLIBSYS_COMPILE_DEFS -DPLIBSYS_HAS_SOCKLEN_T) +else() + message (STATUS "Checking whether socklen_t is defined - no") +endif() + +# Check for sockaddr_storage structure +message (STATUS "Checking whether struct sockaddr_storage is defined") + +check_c_source_compiles ( + "${PLIBSYS_SOCKET_INCLUDES} + int main () { + struct sockaddr_storage sock_addr; + sock_addr.ss_family = AF_INET; + + return 0; + }" + PLIBSYS_HAS_SOCKADDR_STORAGE + ) + +if (PLIBSYS_HAS_SOCKADDR_STORAGE) + message (STATUS "Checking whether struct sockaddr_storage is defined - yes") + list (APPEND PLIBSYS_COMPILE_DEFS -DPLIBSYS_HAS_SOCKADDR_STORAGE) +else() + message (STATUS "Checking whether struct sockaddr_storage is defined - no") +endif() + +# Check sa_len field in struct sockaddr +message (STATUS "Checking whether struct sockaddr has sa_len") + +check_c_source_compiles ( + "${PLIBSYS_SOCKET_INCLUDES} + int main () { + struct sockaddr sock_addr; + sock_addr.sa_len = 0; + + return 0; + }" + PLIBSYS_SOCKADDR_HAS_SA_LEN + ) + +if (PLIBSYS_SOCKADDR_HAS_SA_LEN) + message (STATUS "Checking whether struct sockaddr has sa_len - yes") + list (APPEND PLIBSYS_COMPILE_DEFS -DPLIBSYS_SOCKADDR_HAS_SA_LEN) +else() + message (STATUS "Checking whether struct sockaddr has sa_len - no") +endif() + +# Check sin6_scope_id field in struct sockaddr_in6 +message (STATUS "Checking whether struct sockaddr_in6 has sin6_scope_id") + +check_c_source_compiles ( + "${PLIBSYS_SOCKET_INCLUDES} + int main () { + struct sockaddr_in6 sock_addr; + sock_addr.sin6_scope_id = 0; + + return 0; + }" + PLIBSYS_SOCKADDR_IN6_HAS_SCOPEID + ) + +if (PLIBSYS_SOCKADDR_IN6_HAS_SCOPEID) + message (STATUS "Checking whether struct sockaddr_in6 has sin6_scope_id - yes") + list (APPEND PLIBSYS_COMPILE_DEFS -DPLIBSYS_SOCKADDR_IN6_HAS_SCOPEID) +else() + message (STATUS "Checking whether struct sockaddr_in6 has sin6_scope_id - no") +endif() + +# Check sin6_flowinfo field in struct sockaddr_in6 +message (STATUS "Checking whether struct sockaddr_in6 has sin6_flowinfo") + +check_c_source_compiles ( + "${PLIBSYS_SOCKET_INCLUDES} + int main () { + struct sockaddr_in6 sock_addr; + sock_addr.sin6_flowinfo = 0; + + return 0; + }" + PLIBSYS_SOCKADDR_IN6_HAS_FLOWINFO + ) + +if (PLIBSYS_SOCKADDR_IN6_HAS_FLOWINFO) + message (STATUS "Checking whether struct sockaddr_in6 has sin6_flowinfo - yes") + list (APPEND PLIBSYS_COMPILE_DEFS -DPLIBSYS_SOCKADDR_IN6_HAS_FLOWINFO) +else() + message (STATUS "Checking whether struct sockaddr_in6 has sin6_flowinfo - no") +endif() + +# Check sa_family_t type size in struct sockaddr +set (CMAKE_EXTRA_INCLUDE_FILES_OLD ${CMAKE_EXTRA_INCLUDE_FILES}) + +if (PLIBSYS_NATIVE_WINDOWS) + set (CMAKE_EXTRA_INCLUDE_FILES + winsock2.h + ws2tcpip.h + windows.h + ) +else() + set (CMAKE_EXTRA_INCLUDE_FILES + sys/types.h + sys/socket.h + netinet/in.h + ) +endif() + +check_type_size ("((struct sockaddr *) 0)->sa_family" PLIBSYS_SIZEOF_SAFAMILY_T LANGUAGE C) + +set (CMAKE_EXTRA_INCLUDE_FILES ${CMAKE_EXTRA_INCLUDE_FILES_OLD}) + +# Check for lldiv() call +message (STATUS "Checking whether lldiv() presents") + +check_c_source_compiles ( + "#define __USE_ISOC99 + #include <stdlib.h> + int main () { + lldiv_t res = lldiv (100LL, 13LL); + res.quot = 0; + res.rem = 0; + + return 0; + }" + PLIBSYS_HAS_LLDIV + ) + +if (PLIBSYS_HAS_LLDIV) + message (STATUS "Checking whether lldiv() presents - yes") + list (APPEND PLIBSYS_COMPILE_DEFS -DPLIBSYS_HAS_LLDIV) +else() + message (STATUS "Checking whether lldiv() presents - no") +endif() + +# Symbols visibility attributes +if (PLIBSYS_VISIBILITY) + message (STATUS "Checking whether compiler supports visibility") + plibsys_detect_visibility (PLIBSYS_VISIBILITY_CFLAGS PLIBSYS_VISIBILITY_LDFLAGS) + + if (NOT PLIBSYS_VISIBILITY_CFLAGS AND NOT PLIBSYS_VISIBILITY_LDFLAGS) + message (STATUS "Checking whether compiler supports visibility - no") + set (PLIBSYS_VISIBILITY OFF) + else() + message (STATUS "Checking whether compiler supports visibility - yes") + + if (PLIBSYS_VISIBILITY_CFLAGS) + list (APPEND PLIBSYS_PLATFORM_CFLAGS ${PLIBSYS_VISIBILITY_CFLAGS}) + endif() + + if (PLIBSYS_VISIBILITY_LDFLAGS) + list (APPEND PLIBSYS_PLATFORM_LDFLAGS ${PLIBSYS_VISIBILITY_LDFLAGS}) + endif() + endif() +endif() + +# Variable arguments +check_include_file ("stdarg.h" PLIBSYS_HAVE_STDARG_H) + +if (NOT PLIBSYS_HAVE_STDARG_H) + message (FATAL_ERROR "Support for <stdarg.h> is required for target plarform, not found") +endif() + +plibsys_detect_va_copy (PLIBSYS_VA_COPY) + +configure_file (${CMAKE_CURRENT_SOURCE_DIR}/plibsysconfig.h.in ${CMAKE_BINARY_DIR}/plibsysconfig.h) + +# Restore compiler flags +set (CMAKE_REQUIRED_DEFINITIONS ${SAVED_CMAKE_REQUIRED_DEFINITIONS}) +set (CMAKE_REQUIRED_LIBRARIES ${SAVED_CMAKE_REQUIRED_LIBRARIES}) +set (SAVED_CMAKE_REQUIRED_FLAGS ${SAVED_CMAKE_REQUIRED_FLAGS}) + +# Disable useless warnings +if (MSVC) + list (APPEND PLIBSYS_COMPILE_DEFS -D_CRT_SECURE_NO_WARNINGS) + list (APPEND PLIBSYS_COMPILE_DEFS -D_WINSOCK_DEPRECATED_NO_WARNINGS) +endif() + +# Prepare compile definitions +list (APPEND PLIBSYS_COMPILE_DEFS -DPLIBSYS_COMPILATION) + +if (PLIBSYS_PLATFORM_DEFINES) + list (APPEND PLIBSYS_COMPILE_DEFS ${PLIBSYS_PLATFORM_DEFINES}) +endif() + +# Add targets +add_library (plibsys SHARED ${PLIBSYS_SRCS} ${PLIBSYS_PLATFORM_SRCS} ${PLIBSYS_PUBLIC_HDRS} ${PLIBSYS_PRIVATE_HDRS}) + +if (PLIBSYS_BUILD_STATIC) + add_library (plibsysstatic STATIC ${PLIBSYS_SRCS} ${PLIBSYS_PLATFORM_SRCS} ${PLIBSYS_PUBLIC_HDRS} ${PLIBSYS_PRIVATE_HDRS}) +endif() + +# Add include directories +if (COMMAND target_include_directories) + target_include_directories (plibsys PUBLIC ${PLIBSYS_INCLUDE_DIRS}) + + if (PLIBSYS_BUILD_STATIC) + target_include_directories (plibsysstatic PUBLIC ${PLIBSYS_INCLUDE_DIRS}) + endif() +else() + include_directories (${PLIBSYS_INCLUDE_DIRS}) +endif() + +# Add compile definitions +if (COMMAND target_compile_definitions) + target_compile_definitions (plibsys PRIVATE ${PLIBSYS_COMPILE_DEFS}) + + if (PLIBSYS_BUILD_STATIC) + target_compile_definitions (plibsysstatic PRIVATE ${PLIBSYS_COMPILE_DEFS}) + endif() +else() + add_definitions (${PLIBSYS_COMPILE_DEFS}) +endif() + +set_target_properties (plibsys PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${OUTPUT_DIR}) +set_target_properties (plibsys PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${OUTPUT_DIR}) +set_target_properties (plibsys PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${OUTPUT_DIR}) + +# OpenBSD uses a bit different library versioning +if (PLIBSYS_TARGET_OS STREQUAL openbsd) + set (PLIBSYS_SOVERSION ${PLIBSYS_VERSION_MAJOR}.${PLIBSYS_VERSION_MINOR}) +endif() + +if (NOT PLIBSYS_TARGET_OS STREQUAL os2 AND NOT PLIBSYS_TARGET_OS STREQUAL amigaos) + set_target_properties (plibsys PROPERTIES SOVERSION ${PLIBSYS_SOVERSION}) +endif() + +if (PLIBSYS_BUILD_STATIC) + set_target_properties (plibsysstatic PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${OUTPUT_DIR}) + + if (NOT PLIBSYS_TARGET_OS STREQUAL os2) + set_target_properties (plibsysstatic PROPERTIES SOVERSION ${PLIBSYS_SOVERSION}) + endif() +endif() + +if (PLIBSYS_PLATFORM_CFLAGS) + foreach (PLATFORM_CFLAG ${PLIBSYS_PLATFORM_CFLAGS}) + set (PLIBSYS_PLATFORM_CFLAGS_STR "${PLIBSYS_PLATFORM_CFLAGS_STR} ${PLATFORM_CFLAG}") + endforeach() + + set_target_properties (plibsys PROPERTIES COMPILE_FLAGS "${PLIBSYS_PLATFORM_CFLAGS_STR}") + + if (PLIBSYS_BUILD_STATIC) + set_target_properties (plibsysstatic PROPERTIES COMPILE_FLAGS "${PLIBSYS_PLATFORM_CFLAGS_STR}") + endif() +endif() + +if (PLIBSYS_PLATFORM_LDFLAGS) + foreach (PLATFORM_LDFLAG ${PLIBSYS_PLATFORM_LDFLAGS}) + set (PLIBSYS_PLATFORM_LDFLAGS_STR "${PLIBSYS_PLATFORM_LDFLAGS_STR} ${PLATFORM_LDFLAG}") + endforeach() + + set_target_properties (plibsys PROPERTIES LINK_FLAGS "${PLIBSYS_PLATFORM_LDFLAGS_STR}") + + if (PLIBSYS_BUILD_STATIC) + set_target_properties (plibsysstatic PROPERTIES LINK_FLAGS "${PLIBSYS_PLATFORM_LDFLAGS_STR}") + endif() +endif() + +target_link_libraries (plibsys ${PLIBSYS_PLATFORM_LINK_LIBRARIES}) + +if (PLIBSYS_BUILD_STATIC) + target_link_libraries (plibsysstatic ${PLIBSYS_PLATFORM_LINK_LIBRARIES}) +endif() + +if (PLIBSYS_BUILD_STATIC) + set (PLIBSYS_INSTALL_TARGETS plibsys plibsysstatic) +else() + set (PLIBSYS_INSTALL_TARGETS plibsys) +endif() + +if (PLIBSYS_NATIVE_WINDOWS) + install (TARGETS ${PLIBSYS_INSTALL_TARGETS} + DESTINATION lib + RUNTIME DESTINATION lib + COMPONENT Core + ) + + if (NOT DEFINED CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_NO_WARNINGS) + set (CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_NO_WARNINGS TRUE) + endif() + + set (CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP TRUE) + include (InstallRequiredSystemLibraries) + + install (PROGRAMS ${CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS} + DESTINATION lib + COMPONENT Core + ) +endif() + +# Prepare installation dirs +if (NOT CMAKE_INSTALL_LIBDIR) + set (CMAKE_INSTALL_LIBDIR "lib") +endif() + +if (NOT CMAKE_INSTALL_INCLUDEDIR) + set (CMAKE_INSTALL_INCLUDEDIR "include") +endif() + +install (TARGETS ${PLIBSYS_INSTALL_TARGETS} EXPORT plibsys-targets + DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + COMPONENT Core +) +install (TARGETS ${PLIBSYS_INSTALL_TARGETS} + DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + COMPONENT Core +) +install (FILES + ${PLIBSYS_PUBLIC_HDRS} + ${CMAKE_BINARY_DIR}/plibsysconfig.h + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/plibsys + COMPONENT Core +) + +if (PLIBSYS_VA_COPY) + set(PLIBSYS_VA_COPY_STATUS "YES") +else() + set(PLIBSYS_VA_COPY_STATUS "NO") +endif() + +if (PLIBSYS_OS_BITS STREQUAL "universal") + set (PLIBSYS_ADDRESS_MODEL "universal") +else() + set (PLIBSYS_ADDRESS_MODEL "${PLIBSYS_OS_BITS} bit") +endif() + +# Print summary +SET (PLIBSYS_SUMMARY +" + == Build configuration == + + Platfrom: ${PLIBSYS_TARGET_OS} + Compiler: ${PLIBSYS_C_COMPILER} + Address model: ${PLIBSYS_ADDRESS_MODEL} + + Thread model: ${PLIBSYS_THREAD_MODEL} + RW lock model: ${PLIBSYS_RWLOCK_MODEL} + IPC model: ${PLIBSYS_IPC_MODEL} + DIR model: ${PLIBSYS_DIR_MODEL} + Library loader model: ${PLIBSYS_LIBRARYLOADER_MODEL} + Time profiler model: ${PLIBSYS_TIME_PROFILER_MODEL} + Atomic model: ${PLIBSYS_ATOMIC_MODEL} + + Platform defines: ${PLIBSYS_PLATFORM_DEFINES} + Platform CFLAGS: ${PLIBSYS_PLATFORM_CFLAGS} + Platform LDFLAGS: ${PLIBSYS_PLATFORM_LDFLAGS} + Platform libraries: ${PLIBSYS_PLATFORM_LINK_LIBRARIES} + + Build static library: ${PLIBSYS_BUILD_STATIC} + Build tests: ${PLIBSYS_TESTS} + Coverage support: ${PLIBSYS_COVERAGE} + Visibility: ${PLIBSYS_VISIBILITY} + + va_copy availability: ${PLIBSYS_VA_COPY_STATUS} + +") + +message ("${PLIBSYS_SUMMARY}") diff --git a/3rdparty/plibsys/src/patomic-c11.c b/3rdparty/plibsys/src/patomic-c11.c new file mode 100644 index 0000000..e6ed760 --- /dev/null +++ b/3rdparty/plibsys/src/patomic-c11.c @@ -0,0 +1,182 @@ +/* + * The MIT License + * + * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "patomic.h" + +#ifdef P_CC_SUN +# define PATOMIC_INT_CAST(x) (pint *) (x) +# define PATOMIC_SIZE_CAST(x) (psize *) (x) +#else +# define PATOMIC_INT_CAST(x) x +# define PATOMIC_SIZE_CAST(x) x +#endif + +P_LIB_API pint +p_atomic_int_get (const volatile pint *atomic) +{ + return (pint) __atomic_load_4 (PATOMIC_INT_CAST (atomic), __ATOMIC_SEQ_CST); +} + +P_LIB_API void +p_atomic_int_set (volatile pint *atomic, + pint val) +{ + __atomic_store_4 (PATOMIC_INT_CAST (atomic), val, __ATOMIC_SEQ_CST); +} + +P_LIB_API void +p_atomic_int_inc (volatile pint *atomic) +{ + (void) __atomic_fetch_add (atomic, 1, __ATOMIC_SEQ_CST); +} + +P_LIB_API pboolean +p_atomic_int_dec_and_test (volatile pint *atomic) +{ + return (__atomic_fetch_sub (atomic, 1, __ATOMIC_SEQ_CST) == 1) ? TRUE : FALSE; +} + +P_LIB_API pboolean +p_atomic_int_compare_and_exchange (volatile pint *atomic, + pint oldval, + pint newval) +{ + pint tmp_int = oldval; + + return (pboolean) __atomic_compare_exchange_n (PATOMIC_INT_CAST (atomic), + &tmp_int, + newval, + 0, + __ATOMIC_SEQ_CST, + __ATOMIC_SEQ_CST); +} + +P_LIB_API pint +p_atomic_int_add (volatile pint *atomic, + pint val) +{ + return (pint) __atomic_fetch_add (atomic, val, __ATOMIC_SEQ_CST); +} + +P_LIB_API puint +p_atomic_int_and (volatile puint *atomic, + puint val) +{ + return (puint) __atomic_fetch_and (atomic, val, __ATOMIC_SEQ_CST); +} + +P_LIB_API puint +p_atomic_int_or (volatile puint *atomic, + puint val) +{ + return (puint) __atomic_fetch_or (atomic, val, __ATOMIC_SEQ_CST); +} + +P_LIB_API puint +p_atomic_int_xor (volatile puint *atomic, + puint val) +{ + return (puint) __atomic_fetch_xor (atomic, val, __ATOMIC_SEQ_CST); +} + +P_LIB_API ppointer +p_atomic_pointer_get (const volatile void *atomic) +{ +#if (PLIBSYS_SIZEOF_VOID_P == 8) + return (ppointer) __atomic_load_8 (PATOMIC_SIZE_CAST ((const volatile psize *) atomic), __ATOMIC_SEQ_CST); +#else + return (ppointer) __atomic_load_4 (PATOMIC_SIZE_CAST ((const volatile psize *) atomic), __ATOMIC_SEQ_CST); +#endif +} + +P_LIB_API void +p_atomic_pointer_set (volatile void *atomic, + ppointer val) +{ +#if (PLIBSYS_SIZEOF_VOID_P == 8) + __atomic_store_8 (PATOMIC_SIZE_CAST ((volatile psize *) atomic), (psize) val, __ATOMIC_SEQ_CST); +#else + __atomic_store_4 (PATOMIC_SIZE_CAST ((volatile psize *) atomic), (psize) val, __ATOMIC_SEQ_CST); +#endif +} + +P_LIB_API pboolean +p_atomic_pointer_compare_and_exchange (volatile void *atomic, + ppointer oldval, + ppointer newval) +{ + ppointer tmp_pointer = oldval; + + return (pboolean) __atomic_compare_exchange_n (PATOMIC_SIZE_CAST ((volatile psize *) atomic), + (psize *) &tmp_pointer, + PPOINTER_TO_PSIZE (newval), + 0, + __ATOMIC_SEQ_CST, + __ATOMIC_SEQ_CST); +} + +P_LIB_API pssize +p_atomic_pointer_add (volatile void *atomic, + pssize val) +{ + return (pssize) __atomic_fetch_add ((volatile pssize *) atomic, val, __ATOMIC_SEQ_CST); +} + +P_LIB_API psize +p_atomic_pointer_and (volatile void *atomic, + psize val) +{ + return (psize) __atomic_fetch_and ((volatile psize *) atomic, val, __ATOMIC_SEQ_CST); +} + +P_LIB_API psize +p_atomic_pointer_or (volatile void *atomic, + psize val) +{ + return (psize) __atomic_fetch_or ((volatile pssize *) atomic, val, __ATOMIC_SEQ_CST); +} + +P_LIB_API psize +p_atomic_pointer_xor (volatile void *atomic, + psize val) +{ + return (psize) __atomic_fetch_xor ((volatile pssize *) atomic, val, __ATOMIC_SEQ_CST); +} + +P_LIB_API pboolean +p_atomic_is_lock_free (void) +{ + return TRUE; +} + +void +p_atomic_thread_init (void) +{ +} + +void +p_atomic_thread_shutdown (void) +{ +} diff --git a/3rdparty/plibsys/src/patomic-decc.c b/3rdparty/plibsys/src/patomic-decc.c new file mode 100644 index 0000000..d103aa9 --- /dev/null +++ b/3rdparty/plibsys/src/patomic-decc.c @@ -0,0 +1,286 @@ +/* + * The MIT License + * + * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "patomic.h" + +#ifdef P_OS_VMS +# include <builtins.h> +#else +# include <machine/builtins.h> +#endif + +#ifdef __ia64 +# define PATOMIC_DECC_CAS_LONG(atomic_src, oldval, newval, atomic_dst) \ + __CMP_SWAP_LONG ((volatile void *) (atomic_src), \ + (pint) (oldval), \ + (pint) (newval)) +# define PATOMIC_DECC_CAS_QUAD(atomic_src, oldval, newval, atomic_dst) \ + __CMP_SWAP_QUAD ((volatile void *) (atomic_src), \ + (pint64) (oldval), \ + (pint64) (newval)) +#else +# define PATOMIC_DECC_CAS_LONG(atomic_src, oldval, newval, atomic_dst) \ + __CMP_STORE_LONG ((volatile void *) (atomic_src), \ + (pint) (oldval), \ + (pint) (newval), \ + (volatile void *) (atomic_dst)) +# define PATOMIC_DECC_CAS_QUAD(atomic_src, oldval, newval, atomic_dst) \ + __CMP_STORE_QUAD ((volatile void *) (atomic_src), \ + (pint64) (oldval), \ + (pint64) (newval), \ + (volatile void *) (atomic_dst)) +#endif + +P_LIB_API pint +p_atomic_int_get (const volatile pint *atomic) +{ + __MB (); + return (pint) *atomic; +} + +P_LIB_API void +p_atomic_int_set (volatile pint *atomic, + pint val) +{ + (void) __ATOMIC_EXCH_LONG ((volatile void *) atomic, val); + __MB (); +} + +P_LIB_API void +p_atomic_int_inc (volatile pint *atomic) +{ + __MB (); + (void) __ATOMIC_INCREMENT_LONG ((volatile void *) atomic); + __MB (); +} + +P_LIB_API pboolean +p_atomic_int_dec_and_test (volatile pint *atomic) +{ + pboolean result; + + __MB (); + result = __ATOMIC_DECREMENT_LONG ((volatile void *) atomic) == 1 ? TRUE : FALSE; + __MB (); + + return result; +} + +P_LIB_API pboolean +p_atomic_int_compare_and_exchange (volatile pint *atomic, + pint oldval, + pint newval) +{ + pboolean result; + + __MB (); + result = PATOMIC_DECC_CAS_LONG (atomic, oldval, newval, atomic) == 1 ? TRUE : FALSE; + __MB (); + + return result; +} + +P_LIB_API pint +p_atomic_int_add (volatile pint *atomic, + pint val) +{ + pint result; + + __MB (); + result = __ATOMIC_ADD_LONG ((volatile void *) atomic, val); + __MB (); + + return result; +} + +P_LIB_API puint +p_atomic_int_and (volatile puint *atomic, + puint val) +{ + puint result; + + __MB (); + result = (puint) __ATOMIC_AND_LONG ((volatile void *) atomic, (pint) val); + __MB (); + + return result; +} + +P_LIB_API puint +p_atomic_int_or (volatile puint *atomic, + puint val) +{ + puint result; + + __MB (); + result = (puint) __ATOMIC_OR_LONG ((volatile void *) atomic, (pint) val); + __MB (); + + return result; +} + +P_LIB_API puint +p_atomic_int_xor (volatile puint *atomic, + puint val) +{ + pint i; + + do { + __MB (); + i = (pint) (*atomic); + } while (PATOMIC_DECC_CAS_LONG (atomic, i, i ^ ((pint) val), atomic) != 1); + + __MB (); + + return i; +} + +P_LIB_API ppointer +p_atomic_pointer_get (const volatile void *atomic) +{ + __MB (); + return (ppointer) (* ((const volatile psize *) atomic)); +} + +P_LIB_API void +p_atomic_pointer_set (volatile void *atomic, + ppointer val) +{ +#if (PLIBSYS_SIZEOF_VOID_P == 8) + (void) __ATOMIC_EXCH_QUAD (atomic, (pint64) val); +#else + (void) __ATOMIC_EXCH_LONG (atomic, (pint) val); +#endif + __MB (); +} + +P_LIB_API pboolean +p_atomic_pointer_compare_and_exchange (volatile void *atomic, + ppointer oldval, + ppointer newval) +{ + pboolean result; + + __MB (); +#if (PLIBSYS_SIZEOF_VOID_P == 8) + result = PATOMIC_DECC_CAS_QUAD (atomic, oldval, newval, atomic) == 1 ? TRUE : FALSE; +#else + result = PATOMIC_DECC_CAS_LONG (atomic, oldval, newval, atomic) == 1 ? TRUE : FALSE; +#endif + __MB (); + + return result; +} + +P_LIB_API pssize +p_atomic_pointer_add (volatile void *atomic, + pssize val) +{ + pssize result; + + __MB (); +#if (PLIBSYS_SIZEOF_VOID_P == 8) + result = (pssize) __ATOMIC_ADD_QUAD (atomic, (pint64) val); +#else + result = (pssize) __ATOMIC_ADD_LONG (atomic, (pint) val); +#endif + __MB (); + + return result; +} + +P_LIB_API psize +p_atomic_pointer_and (volatile void *atomic, + psize val) +{ + psize result; + + __MB (); +#if (PLIBSYS_SIZEOF_VOID_P == 8) + result = (psize) __ATOMIC_AND_QUAD (atomic, (pint64) val); +#else + result = (psize) __ATOMIC_AND_LONG (atomic, (pint) val); +#endif + __MB (); + + return result; +} + +P_LIB_API psize +p_atomic_pointer_or (volatile void *atomic, + psize val) +{ + psize result; + + __MB (); +#if (PLIBSYS_SIZEOF_VOID_P == 8) + result = (psize) __ATOMIC_OR_QUAD (atomic, (pint64) val); +#else + result = (psize) __ATOMIC_OR_LONG (atomic, (pint) val); +#endif + __MB (); + + return result; +} + +P_LIB_API psize +p_atomic_pointer_xor (volatile void *atomic, + psize val) +{ +#if (PLIBSYS_SIZEOF_VOID_P == 8) + pint64 i; + + do { + __MB (); + i = (pint64) (* ((volatile psize *) atomic)); + } while (PATOMIC_DECC_CAS_QUAD (atomic, i, i ^ ((pint64) val), atomic) != 1); +#else + pint i; + + do { + __MB (); + i = (pint) (* ((volatile psize *) atomic)); + } while (PATOMIC_DECC_CAS_LONG (atomic, i, i ^ ((pint) val), atomic) != 1); +#endif + __MB (); + + return (psize) i; +} + +P_LIB_API pboolean +p_atomic_is_lock_free (void) +{ + return TRUE; +} + +void +p_atomic_thread_init (void) +{ +} + +void +p_atomic_thread_shutdown (void) +{ +} diff --git a/3rdparty/plibsys/src/patomic-sim.c b/3rdparty/plibsys/src/patomic-sim.c new file mode 100644 index 0000000..12d0604 --- /dev/null +++ b/3rdparty/plibsys/src/patomic-sim.c @@ -0,0 +1,268 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "patomic.h" +#include "pmutex.h" + +/* We have to use the slow, but safe locking method. */ +static PMutex *pp_atomic_mutex = NULL; + +P_LIB_API pint +p_atomic_int_get (const volatile pint *atomic) +{ + pint value; + + p_mutex_lock (pp_atomic_mutex); + value = *atomic; + p_mutex_unlock (pp_atomic_mutex); + + return value; +} + +P_LIB_API void +p_atomic_int_set (volatile pint *atomic, + pint val) +{ + p_mutex_lock (pp_atomic_mutex); + *atomic = val; + p_mutex_unlock (pp_atomic_mutex); +} + +P_LIB_API void +p_atomic_int_inc (volatile pint *atomic) +{ + p_mutex_lock (pp_atomic_mutex); + (*atomic)++; + p_mutex_unlock (pp_atomic_mutex); +} + +P_LIB_API pboolean +p_atomic_int_dec_and_test (volatile pint *atomic) +{ + pboolean is_zero; + + p_mutex_lock (pp_atomic_mutex); + is_zero = --(*atomic) == 0; + p_mutex_unlock (pp_atomic_mutex); + + return is_zero; +} + +P_LIB_API pboolean +p_atomic_int_compare_and_exchange (volatile pint *atomic, + pint oldval, + pint newval) +{ + pboolean success; + + p_mutex_lock (pp_atomic_mutex); + + if ((success = (*atomic == oldval))) + *atomic = newval; + + p_mutex_unlock (pp_atomic_mutex); + + return success; +} + +P_LIB_API pint +p_atomic_int_add (volatile pint *atomic, + pint val) +{ + pint oldval; + + p_mutex_lock (pp_atomic_mutex); + oldval = *atomic; + *atomic = oldval + val; + p_mutex_unlock (pp_atomic_mutex); + + return oldval; +} + +P_LIB_API puint +p_atomic_int_and (volatile puint *atomic, + puint val) +{ + puint oldval; + + p_mutex_lock (pp_atomic_mutex); + oldval = *atomic; + *atomic = oldval & val; + p_mutex_unlock (pp_atomic_mutex); + + return oldval; +} + +P_LIB_API puint +p_atomic_int_or (volatile puint *atomic, + puint val) +{ + puint oldval; + + p_mutex_lock (pp_atomic_mutex); + oldval = *atomic; + *atomic = oldval | val; + p_mutex_unlock (pp_atomic_mutex); + + return oldval; +} + +P_LIB_API puint +p_atomic_int_xor (volatile puint *atomic, + puint val) +{ + puint oldval; + + p_mutex_lock (pp_atomic_mutex); + oldval = *atomic; + *atomic = oldval ^ val; + p_mutex_unlock (pp_atomic_mutex); + + return oldval; +} + +P_LIB_API ppointer +p_atomic_pointer_get (const volatile void *atomic) +{ + const volatile ppointer *ptr = atomic; + ppointer value; + + p_mutex_lock (pp_atomic_mutex); + value = *ptr; + p_mutex_unlock (pp_atomic_mutex); + + return value; +} + +P_LIB_API void +p_atomic_pointer_set (volatile void *atomic, + ppointer val) +{ + volatile ppointer *ptr = atomic; + + p_mutex_lock (pp_atomic_mutex); + *ptr = val; + p_mutex_unlock (pp_atomic_mutex); +} + +P_LIB_API pboolean +p_atomic_pointer_compare_and_exchange (volatile void *atomic, + ppointer oldval, + ppointer newval) +{ + volatile ppointer *ptr = atomic; + pboolean success; + + p_mutex_lock (pp_atomic_mutex); + + if ((success = (*ptr == oldval))) + *ptr = newval; + + p_mutex_unlock (pp_atomic_mutex); + + return success; +} + +P_LIB_API pssize +p_atomic_pointer_add (volatile void *atomic, + pssize val) +{ + volatile pssize *ptr = atomic; + pssize oldval; + + p_mutex_lock (pp_atomic_mutex); + oldval = *ptr; + *ptr = oldval + val; + p_mutex_unlock (pp_atomic_mutex); + + return oldval; +} + +P_LIB_API psize +p_atomic_pointer_and (volatile void *atomic, + psize val) +{ + volatile psize *ptr = atomic; + psize oldval; + + p_mutex_lock (pp_atomic_mutex); + oldval = *ptr; + *ptr = oldval & val; + p_mutex_unlock (pp_atomic_mutex); + + return oldval; +} + +P_LIB_API psize +p_atomic_pointer_or (volatile void *atomic, + psize val) +{ + volatile psize *ptr = atomic; + psize oldval; + + p_mutex_lock (pp_atomic_mutex); + oldval = *ptr; + *ptr = oldval | val; + p_mutex_unlock (pp_atomic_mutex); + + return oldval; +} + +P_LIB_API psize +p_atomic_pointer_xor (volatile void *atomic, + psize val) +{ + volatile psize *ptr = atomic; + psize oldval; + + p_mutex_lock (pp_atomic_mutex); + oldval = *ptr; + *ptr = oldval ^ val; + p_mutex_unlock (pp_atomic_mutex); + + return oldval; +} + +P_LIB_API pboolean +p_atomic_is_lock_free (void) +{ + return FALSE; +} + +void +p_atomic_thread_init (void) +{ + if (P_LIKELY (pp_atomic_mutex == NULL)) + pp_atomic_mutex = p_mutex_new (); +} + +void +p_atomic_thread_shutdown (void) +{ + if (P_LIKELY (pp_atomic_mutex != NULL)) { + p_mutex_free (pp_atomic_mutex); + pp_atomic_mutex = NULL; + } +} diff --git a/3rdparty/plibsys/src/patomic-sync.c b/3rdparty/plibsys/src/patomic-sync.c new file mode 100644 index 0000000..04a8d81 --- /dev/null +++ b/3rdparty/plibsys/src/patomic-sync.c @@ -0,0 +1,190 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2017 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "patomic.h" + +#ifdef P_CC_CRAY +# include <intrinsics.h> +#endif + +P_LIB_API pint +p_atomic_int_get (const volatile pint *atomic) +{ +#ifdef P_CC_CRAY + __builtin_ia32_mfence (); +#else + __sync_synchronize (); +#endif + return *atomic; +} + +P_LIB_API void +p_atomic_int_set (volatile pint *atomic, + pint val) +{ + *atomic = val; +#ifdef P_CC_CRAY + __builtin_ia32_mfence (); +#else + __sync_synchronize (); +#endif +} + +P_LIB_API void +p_atomic_int_inc (volatile pint *atomic) +{ + (void) __sync_fetch_and_add (atomic, 1); +} + +P_LIB_API pboolean +p_atomic_int_dec_and_test (volatile pint *atomic) +{ + return __sync_fetch_and_sub (atomic, 1) == 1 ? TRUE : FALSE; +} + +P_LIB_API pboolean +p_atomic_int_compare_and_exchange (volatile pint *atomic, + pint oldval, + pint newval) +{ +#ifdef P_CC_CRAY + return __sync_val_compare_and_swap (atomic, oldval, newval) == oldval ? TRUE : FALSE; +#else + return (pboolean) __sync_bool_compare_and_swap (atomic, oldval, newval); +#endif +} + +P_LIB_API pint +p_atomic_int_add (volatile pint *atomic, + pint val) +{ + return (pint) __sync_fetch_and_add (atomic, val); +} + +P_LIB_API puint +p_atomic_int_and (volatile puint *atomic, + puint val) +{ + return (puint) __sync_fetch_and_and (atomic, val); +} + +P_LIB_API puint +p_atomic_int_or (volatile puint *atomic, + puint val) +{ + return (puint) __sync_fetch_and_or (atomic, val); +} + +P_LIB_API puint +p_atomic_int_xor (volatile puint *atomic, + puint val) +{ + return (puint) __sync_fetch_and_xor (atomic, val); +} + +P_LIB_API ppointer +p_atomic_pointer_get (const volatile void *atomic) +{ +#ifdef P_CC_CRAY + __builtin_ia32_mfence (); +#else + __sync_synchronize (); +#endif + return (ppointer) *((const volatile psize *) atomic); +} + +P_LIB_API void +p_atomic_pointer_set (volatile void *atomic, + ppointer val) +{ + volatile psize *cur_val = (volatile psize *) atomic; + + *cur_val = (psize) val; +#ifdef P_CC_CRAY + __builtin_ia32_mfence (); +#else + __sync_synchronize (); +#endif +} + +P_LIB_API pboolean +p_atomic_pointer_compare_and_exchange (volatile void *atomic, + ppointer oldval, + ppointer newval) +{ +#ifdef P_CC_CRAY + return __sync_val_compare_and_swap ((volatile psize *) atomic, + (psize) oldval, + (psize) newval) == ((psize) oldval) ? TRUE : FALSE; +#else + return (pboolean) __sync_bool_compare_and_swap ((volatile psize *) atomic, + (psize) oldval, + (psize) newval); +#endif +} + +P_LIB_API pssize +p_atomic_pointer_add (volatile void *atomic, + pssize val) +{ + return (pssize) __sync_fetch_and_add ((volatile pssize *) atomic, val); +} + +P_LIB_API psize +p_atomic_pointer_and (volatile void *atomic, + psize val) +{ + return (psize) __sync_fetch_and_and ((volatile psize *) atomic, val); +} + +P_LIB_API psize +p_atomic_pointer_or (volatile void *atomic, + psize val) +{ + return (psize) __sync_fetch_and_or ((volatile psize *) atomic, val); +} + +P_LIB_API psize +p_atomic_pointer_xor (volatile void *atomic, + psize val) +{ + return (psize) __sync_fetch_and_xor ((volatile psize *) atomic, val); +} + +P_LIB_API pboolean +p_atomic_is_lock_free (void) +{ + return TRUE; +} + +void +p_atomic_thread_init (void) +{ +} + +void +p_atomic_thread_shutdown (void) +{ +} diff --git a/3rdparty/plibsys/src/patomic-win.c b/3rdparty/plibsys/src/patomic-win.c new file mode 100644 index 0000000..597151d --- /dev/null +++ b/3rdparty/plibsys/src/patomic-win.c @@ -0,0 +1,272 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "patomic.h" + +/* Prepare MemoryBarrier() */ +#if defined (P_CC_WATCOM) || defined (P_CC_BORLAND) +# if defined (_M_X64) || defined (_M_AMD64) +# define MemoryBarrier __faststorefence +# elseif defined (_M_IA64) +# define MemoryBarrier __mf +# else +# ifdef P_CC_WATCOM +inline +# else +FORCEINLINE +# endif /* P_CC_WATCOM */ +VOID MemoryBarrier (VOID) +{ + LONG Barrier = 0; + (void) (Barrier); + __asm { + xchg Barrier, eax + } +} +# endif /* _M_X64 || _M_AMD64 */ +#endif /* P_CC_WATCOM || P_CC_BORLAND */ + +#if !defined (P_OS_WIN64) && (defined (P_CC_MSVC) && _MSC_VER > 1200) + /* Tell compiler about intrinsics to suppress warnings, + * see: https://msdn.microsoft.com/en-us/library/hh977023.aspx */ +# include <intrin.h> +# define InterlockedAnd _InterlockedAnd +# define InterlockedOr _InterlockedOr +# define InterlockedXor _InterlockedXor +# pragma intrinsic(_InterlockedAnd) +# pragma intrinsic(_InterlockedOr) +# pragma intrinsic(_InterlockedXor) +#endif + +#if (defined (P_CC_MSVC) && _MSC_VER <= 1200) || defined (P_CC_WATCOM) \ + || defined (P_CC_BORLAND) +/* Inlined versions for older compilers */ +static LONG +ppInterlockedAnd (LONG volatile *atomic, + LONG val) +{ + LONG i, j; + + j = *atomic; + do { + i = j; + j = InterlockedCompareExchange (atomic, i & val, i); + } while (i != j); + + return j; +} + +# define InterlockedAnd(a,b) ppInterlockedAnd(a,b) + +static LONG +ppInterlockedOr (LONG volatile *atomic, + LONG val) +{ + LONG i, j; + + j = *atomic; + do { + i = j; + j = InterlockedCompareExchange (atomic, i | val, i); + } while (i != j); + + return j; +} + +# define InterlockedOr(a,b) ppInterlockedOr(a,b) + +static LONG +ppInterlockedXor (LONG volatile *atomic, + LONG val) +{ + LONG i, j; + + j = *atomic; + do { + i = j; + j = InterlockedCompareExchange (atomic, i ^ val, i); + } while (i != j); + + return j; +} + +# define InterlockedXor(a,b) ppInterlockedXor(a,b) +#endif + +/* http://msdn.microsoft.com/en-us/library/ms684122(v=vs.85).aspx */ + +P_LIB_API pint +p_atomic_int_get (const volatile pint *atomic) +{ + MemoryBarrier (); + return *atomic; +} + +P_LIB_API void +p_atomic_int_set (volatile pint *atomic, + pint val) +{ + *atomic = val; + MemoryBarrier (); +} + +P_LIB_API void +p_atomic_int_inc (volatile pint *atomic) +{ + InterlockedIncrement ((LONG volatile *) atomic); +} + +P_LIB_API pboolean +p_atomic_int_dec_and_test (volatile pint *atomic) +{ + return InterlockedDecrement ((LONG volatile *) atomic) == 0 ? TRUE : FALSE; +} + +P_LIB_API pboolean +p_atomic_int_compare_and_exchange (volatile pint *atomic, + pint oldval, + pint newval) +{ + return InterlockedCompareExchange ((LONG volatile *) atomic, + (LONG) newval, + (LONG) oldval) == oldval; +} + +P_LIB_API pint +p_atomic_int_add (volatile pint *atomic, + pint val) +{ + return (pint) InterlockedExchangeAdd ((LONG volatile *) atomic, (LONG) val); +} + +P_LIB_API puint +p_atomic_int_and (volatile puint *atomic, + puint val) +{ + return (puint) InterlockedAnd ((LONG volatile *) atomic, (LONG) val); +} + +P_LIB_API puint +p_atomic_int_or (volatile puint *atomic, + puint val) +{ + return (puint) InterlockedOr ((LONG volatile *) atomic, (LONG) val); +} + +P_LIB_API puint +p_atomic_int_xor (volatile puint *atomic, + puint val) +{ + return (puint) InterlockedXor ((LONG volatile *) atomic, (LONG) val); +} + +P_LIB_API ppointer +p_atomic_pointer_get (const volatile void *atomic) +{ + const volatile ppointer *ptr = (const volatile ppointer *) atomic; + + MemoryBarrier (); + return *ptr; +} + +P_LIB_API void +p_atomic_pointer_set (volatile void *atomic, + ppointer val) +{ + volatile ppointer *ptr = (volatile ppointer *) atomic; + + *ptr = val; + MemoryBarrier (); +} + +P_LIB_API pboolean +p_atomic_pointer_compare_and_exchange (volatile void *atomic, + ppointer oldval, + ppointer newval) +{ + return InterlockedCompareExchangePointer ((volatile PVOID *) atomic, + (PVOID) newval, + (PVOID) oldval) == oldval ? TRUE : FALSE; +} + +P_LIB_API pssize +p_atomic_pointer_add (volatile void *atomic, + pssize val) +{ +#if PLIBSYS_SIZEOF_VOID_P == 8 + return (pssize) InterlockedExchangeAdd64 ((LONGLONG volatile *) atomic, (LONGLONG) val); +#else + return (pssize) InterlockedExchangeAdd ((LONG volatile *) atomic, (LONG) val); +#endif +} + +P_LIB_API psize +p_atomic_pointer_and (volatile void *atomic, + psize val) +{ +#if PLIBSYS_SIZEOF_VOID_P == 8 + return (psize) InterlockedAnd64 ((LONGLONG volatile *) atomic, (LONGLONG) val); +#else + return (psize) InterlockedAnd ((LONG volatile *) atomic, (LONG) val); +#endif +} + +P_LIB_API psize +p_atomic_pointer_or (volatile void *atomic, + psize val) +{ +#if PLIBSYS_SIZEOF_VOID_P == 8 + return (psize) InterlockedOr64 ((LONGLONG volatile *) atomic, (LONGLONG) val); +#else + return (psize) InterlockedOr ((LONG volatile *) atomic, (LONG) val); +#endif +} + +P_LIB_API psize +p_atomic_pointer_xor (volatile void *atomic, + psize val) +{ +#if PLIBSYS_SIZEOF_VOID_P == 8 + return (psize) InterlockedXor64 ((LONGLONG volatile *) atomic, (LONGLONG) val); +#else + return (psize) InterlockedXor ((LONG volatile *) atomic, (LONG) val); +#endif +} + +P_LIB_API pboolean +p_atomic_is_lock_free (void) +{ + return TRUE; +} + +void +p_atomic_thread_init (void) +{ +} + +void +p_atomic_thread_shutdown (void) +{ +} diff --git a/3rdparty/plibsys/src/patomic.h b/3rdparty/plibsys/src/patomic.h new file mode 100644 index 0000000..fc451fe --- /dev/null +++ b/3rdparty/plibsys/src/patomic.h @@ -0,0 +1,310 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * @file patomic.h + * @brief Atomic operations + * @author Alexander Saprykin + * + * Atomic operations can be used to avoid heavy thread synchronization + * primitives such as mutexes, semaphores and so on. These operations are + * performed atomically and can't be preempted by another thread. + * + * Lock-free atomic operations require software and hardware support. Usually + * lock-free atomic operations are implemented with low-level using assembly + * inlines. Some of the compilers provide built-in routines to perform atomic + * operations. You can use the p_atomic_is_lock_free() call to check whether + * such a support is provided or not. + * + * If there is no hardware or software support for lock-free atomic operations + * then they can be simulated (though in rather slower manner) using a thread + * global synchronization primitive (i.e. mutex), but it could block threads + * while performing atomic operations on distinct variables from distinct + * threads. + * + * The Windows platform provides all the required lock-free operations in most + * cases, so it always has lock-free support. + */ + +#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION) +# error "Header files shouldn't be included directly, consider using <plibsys.h> instead." +#endif + +#ifndef PLIBSYS_HEADER_PATOMIC_H +#define PLIBSYS_HEADER_PATOMIC_H + +#include <ptypes.h> +#include <pmacros.h> + +P_BEGIN_DECLS + +/** + * @brief Gets #pint value from @a atomic. + * @param atomic Pointer to #pint to get the value from. + * @return Integer value. + * @since 0.0.1 + * + * This call acts as a full compiler and hardware memory barrier (before the + * get). + */ +P_LIB_API pint p_atomic_int_get (const volatile pint *atomic); + +/** + * @brief Sets #pint value to @a atomic. + * @param[out] atomic Pointer to #pint to set the value for. + * @param val New #pint value. + * @since 0.0.1 + * + * This call acts as a full compiler and hardware memory barrier (after the + * set). + */ +P_LIB_API void p_atomic_int_set (volatile pint *atomic, + pint val); + +/** + * @brief Increments #pint value from @a atomic by 1. + * @param[in,out] atomic Pointer to #pint to increment the value. + * @since 0.0.1 + * + * Think of this operation as an atomic version of `{ *atomic += 1; }`. + * + * This call acts as a full compiler and hardware memory barrier. + */ +P_LIB_API void p_atomic_int_inc (volatile pint *atomic); + +/** + * @brief Decrements #pint value from @a atomic by 1 and tests the result for + * zero. + * @param[in,out] atomic Pointer to #pint to decrement the value. + * @return TRUE if the new value is equal to zero, FALSE otherwise. + * @since 0.0.1 + * + * Think of this operation as an atomic version of + * `{ *atomic -= 1; return (*atomic == 0); }`. + * + * This call acts as a full compiler and hardware memory barrier. + */ +P_LIB_API pboolean p_atomic_int_dec_and_test (volatile pint *atomic); + +/** + * @brief Compares @a oldval with the value pointed to by @a atomic and if + * they are equal, atomically exchanges the value of @a atomic with @a newval. + * @param[in,out] atomic Pointer to #pint. + * @param oldval Old #pint value. + * @param newval New #pint value. + * @return TRUE if @a atomic value was equal @a oldval, FALSE otherwise. + * @since 0.0.1 + * + * This compare and exchange is done atomically. + * + * Think of this operation as an atomic version of + * `{ if (*atomic == oldval) { *atomic = newval; return TRUE; } else return FALSE; }`. + * + * This call acts as a full compiler and hardware memory barrier. + */ +P_LIB_API pboolean p_atomic_int_compare_and_exchange (volatile pint *atomic, + pint oldval, + pint newval); + +/** + * @brief Atomically adds #pint value to @a atomic value. + * @param[in,out] atomic Pointer to #pint. + * @param val Integer to add to @a atomic value. + * @return Old value before the addition. + * @since 0.0.1 + * + * Think of this operation as an atomic version of + * `{ tmp = *atomic; *atomic += val; return tmp; }`. + * + * This call acts as a full compiler and hardware memory barrier. + */ +P_LIB_API pint p_atomic_int_add (volatile pint *atomic, + pint val); + +/** + * @brief Atomically performs the bitwise 'and' operation of @a atomic value + * and @a val storing the result back in @a atomic. + * @param[in,out] atomic Pointer to #puint. + * @param val #puint to perform bitwise 'and' with @a atomic value. + * @return Old @a atomic value before the operation. + * @since 0.0.1 + * + * This call acts as a full compiler and hardware memory barrier. + * + * Think of this operation as an atomic version of + * `{ tmp = *atomic; *atomic &= val; return tmp; }`. + */ +P_LIB_API puint p_atomic_int_and (volatile puint *atomic, + puint val); + +/** + * @brief Atomically performs the bitwise 'or' operation of @a atomic value + * and @a val storing the result back in @a atomic. + * @param[in,out] atomic Pointer to #puint. + * @param val #puint to perform bitwise 'or' with @a atomic value. + * @return Old @a atomic value before the operation. + * @since 0.0.1 + * + * Think of this operation as an atomic version of + * `{ tmp = *atomic; *atomic |= val; return tmp; }`. + * + * This call acts as a full compiler and hardware memory barrier. + */ +P_LIB_API puint p_atomic_int_or (volatile puint *atomic, + puint val); + +/** + * @brief Atomically performs the bitwise 'xor' operation of @a atomic value + * and @a val storing the result back in @a atomic. + * @param[in,out] atomic Pointer to #puint. + * @param val #puint to perform bitwise 'xor' with @a atomic value. + * @return Old @a atomic value before the operation. + * @since 0.0.1 + * + * Think of this operation as an atomic version of + * `{ tmp = *atomic; *atomic ^= val; return tmp; }`. + * + * This call acts as a full compiler and hardware memory barrier. + */ +P_LIB_API puint p_atomic_int_xor (volatile puint *atomic, + puint val); + +/** + * @brief Gets #ppointer-sized value from @a atomic. + * @param atomic Pointer to get the value from. + * @return Value from the pointer. + * @since 0.0.1 + * + * This call acts as a full compiler and hardware memory barrier (before the get). + */ +P_LIB_API ppointer p_atomic_pointer_get (const volatile void *atomic); + +/** + * @brief Sets @a val to #ppointer-sized @a atomic. + * @param[out] atomic Pointer to set the value for. + * @param val New value for @a atomic. + * @since 0.0.1 + * + * This call acts as a full compiler and hardware memory barrier (after the set). + */ +P_LIB_API void p_atomic_pointer_set (volatile void *atomic, + ppointer val); + +/** + * @brief Compares @a oldval with the value pointed to by @a atomic and if + * they are equal, atomically exchanges the value of @a atomic with @a newval. + * @param[in,out] atomic Pointer to #ppointer-sized value. + * @param oldval Old #ppointer-sized value. + * @param newval New #ppointer-sized value. + * @return TRUE if @a atomic value was equal @a oldval, FALSE otherwise. + * @since 0.0.1 + * + * This compare and exchange is done atomically. + * + * Think of this operation as an atomic version of + * `{ if (*atomic == oldval) { *atomic = newval; return TRUE; } else return FALSE; }`. + * + * This call acts as a full compiler and hardware memory barrier. + */ +P_LIB_API pboolean p_atomic_pointer_compare_and_exchange (volatile void *atomic, + ppointer oldval, + ppointer newval); + +/** + * @brief Atomically adds #ppointer-sized value to @a atomic value. + * @param[in,out] atomic Pointer to #ppointer-sized value. + * @param val Value to add to @a atomic value. + * @return Old value before the addition. + * @since 0.0.1 + * + * Think of this operation as an atomic version of + * `{ tmp = *atomic; *atomic += val; return tmp; }`. + * + * This call acts as a full compiler and hardware memory barrier. + */ +P_LIB_API pssize p_atomic_pointer_add (volatile void *atomic, + pssize val); + +/** + * @brief Atomically performs the bitwise 'and' operation of #ppointer-sized + * @a atomic value and @a val storing the result back in @a atomic. + * @param[in,out] atomic Pointer to #ppointer-size value. + * @param val #psize to perform bitwise 'and' with @a atomic value. + * @return Old @a atomic value before the operation. + * @since 0.0.1 + * + * Think of this operation as an atomic version of + * `{ tmp = *atomic; *atomic &= val; return tmp; }`. + * + * This call acts as a full compiler and hardware memory barrier. + */ +P_LIB_API psize p_atomic_pointer_and (volatile void *atomic, + psize val); + +/** + * @brief Atomically performs the bitwise 'or' operation of #ppointer-sized + * @a atomic value and @a val storing the result back in @a atomic. + * @param[in,out] atomic Pointer to #ppointer-size value. + * @param val #psize to perform bitwise 'or' with @a atomic value. + * @return Old @a atomic value before the operation. + * @since 0.0.1 + * + * Think of this operation as an atomic version of + * `{ tmp = *atomic; *atomic |= val; return tmp; }`. + * + * This call acts as a full compiler and hardware memory barrier. + */ +P_LIB_API psize p_atomic_pointer_or (volatile void *atomic, + psize val); + +/** + * @brief Atomically performs the bitwise 'xor' operation of #ppointer-sized + * @a atomic value and @a val storing the result back in @a atomic. + * @param[in,out] atomic Pointer to #ppointer-size value. + * @param val #psize to perform bitwise 'xor' with @a atomic value. + * @return Old @a atomic value before the operation. + * @since 0.0.1 + * + * Think of this operation as an atomic version of + * `{ tmp = *atomic; *atomic ^= val; return tmp; }`. + * + * This call acts as a full compiler and hardware memory barrier. + */ +P_LIB_API psize p_atomic_pointer_xor (volatile void *atomic, + psize val); + +/** + * @brief Checks whether atomic operations are lock-free. + * @return TRUE in case of success, FALSE otherwise. + * @since 0.0.1 + * + * Some underlying atomic model implementations may not support lock-free + * operations depending on hardware or software. + */ +P_LIB_API pboolean p_atomic_is_lock_free (void); + +P_END_DECLS + +#endif /* PLIBSYS_HEADER_PATOMIC_H */ diff --git a/3rdparty/plibsys/src/pcondvariable-amiga.c b/3rdparty/plibsys/src/pcondvariable-amiga.c new file mode 100644 index 0000000..1c91522 --- /dev/null +++ b/3rdparty/plibsys/src/pcondvariable-amiga.c @@ -0,0 +1,230 @@ +/* + * The MIT License + * + * Copyright (C) 2017 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pcondvariable.h" +#include "patomic.h" +#include "pmem.h" +#include "pspinlock.h" + +#include <stdlib.h> + +#include <proto/exec.h> + +typedef struct _PCondThread { + struct Task *thread; + struct _PCondThread *next; + ULONG sigmask; +} PCondThread; + +struct PCondVariable_ { + PSpinLock *lock; + PCondThread *wait_head; + pint wait_count; +}; + +P_LIB_API PCondVariable * +p_cond_variable_new (void) +{ + PCondVariable *ret; + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PCondVariable))) == NULL)) { + P_ERROR ("PCondVariable::p_cond_variable_new: failed to allocate memory"); + return NULL; + } + + if ((ret->lock = p_spinlock_new ()) == NULL) { + P_ERROR ("PCondVariable::p_cond_variable_new: failed to initialize"); + p_free (ret); + return NULL; + } + + return ret; +} + +P_LIB_API void +p_cond_variable_free (PCondVariable *cond) +{ + if (P_UNLIKELY (cond == NULL)) + return; + + if ((cond->wait_count > 0) || (cond->wait_head != NULL)) + P_WARNING ("PCondVariable::p_cond_variable_free: destroying while threads are waiting"); + + p_spinlock_free (cond->lock); + p_free (cond); +} + +P_LIB_API pboolean +p_cond_variable_wait (PCondVariable *cond, + PMutex *mutex) +{ + PCondThread *wait_thread; + BYTE signal; + ULONG wait_singnals; + + if (P_UNLIKELY (cond == NULL || mutex == NULL)) + return FALSE; + + if ((wait_thread = p_malloc0 (sizeof (PCondThread))) == NULL) { + P_ERROR ("PCondVariable::p_cond_variable_wait: failed to allocate memory"); + return FALSE; + } + + wait_thread->thread = IExec->FindTask (NULL); + wait_thread->next = NULL; + + signal = IExec->AllocSignal (-1); + + if (signal == -1) { + P_WARNING ("PCondVariable::p_cond_variable_wait: no free signal slot left"); + return FALSE; + } + + wait_thread->sigmask = 1 << signal; + + if (p_spinlock_lock (cond->lock) != TRUE) { + P_ERROR ("PCondVariable::p_cond_variable_wait: failed to lock internal spinlock"); + return FALSE; + } + + if (cond->wait_head != NULL) + cond->wait_head->next = wait_thread; + else + cond->wait_head = wait_thread; + + p_atomic_int_inc ((volatile pint *) &cond->wait_count); + + if (p_spinlock_unlock (cond->lock) != TRUE) { + P_ERROR ("PCondVariable::p_cond_variable_wait: failed to unlock internal spinlock"); + return FALSE; + } + + if (p_mutex_unlock (mutex) != TRUE) { + P_ERROR ("PCondVariable::p_cond_variable_wait: failed to unlock mutex"); + return FALSE; + } + + wait_singnals = IExec->Wait (wait_thread->sigmask); + + if (p_mutex_lock (mutex) != TRUE) { + P_ERROR ("PCondVariable::p_cond_variable_wait: failed to lock mutex"); + return FALSE; + } + + IExec->FreeSignal (signal); + + return TRUE; +} + +P_LIB_API pboolean +p_cond_variable_signal (PCondVariable *cond) +{ + PCondThread *wait_thread; + + if (P_UNLIKELY (cond == NULL)) + return FALSE; + + if (p_spinlock_lock (cond->lock) != TRUE) { + P_ERROR ("PCondVariable::p_cond_variable_signal: failed to lock internal spinlock"); + return FALSE; + } + + if (cond->wait_head == NULL) { + if (p_spinlock_unlock (cond->lock) != TRUE) { + P_ERROR ("PCondVariable::p_cond_variable_signal(1): failed to unlock internal spinlock"); + return FALSE; + } else + return TRUE; + } + + wait_thread = cond->wait_head; + cond->wait_head = wait_thread->next; + + p_atomic_int_add ((volatile pint *) &cond->wait_count, -1); + + if (p_spinlock_unlock (cond->lock) != TRUE) { + P_ERROR ("PCondVariable::p_cond_variable_signal(2): failed to unlock internal spinlock"); + return FALSE; + } + + IExec->Signal (wait_thread->thread, wait_thread->sigmask); + + p_free (wait_thread); + return TRUE; +} + +P_LIB_API pboolean +p_cond_variable_broadcast (PCondVariable *cond) +{ + if (P_UNLIKELY (cond == NULL)) + return FALSE; + + PCondThread *cur_thread; + PCondThread *next_thread; + + if (p_spinlock_lock (cond->lock) != TRUE) { + P_ERROR ("PCondVariable::p_cond_variable_broadcast: failed to lock internal spinlock"); + return FALSE; + } + + if (cond->wait_head == NULL) { + if (p_spinlock_unlock (cond->lock) != TRUE) { + P_ERROR ("PCondVariable::p_cond_variable_broadcast(1): failed to unlock internal spinlock"); + return FALSE; + } else + return TRUE; + } + + cur_thread = cond->wait_head; + + do { + IExec->Signal (cur_thread->thread, cur_thread->sigmask); + + next_thread = cur_thread->next; + p_free (cur_thread); + + cur_thread = next_thread; + } while (cur_thread != NULL); + + cond->wait_head = NULL; + cond->wait_count = 0; + + if (p_spinlock_unlock (cond->lock) != TRUE) { + P_ERROR ("PCondVariable::p_cond_variable_broadcast(2): failed to unlock internal spinlock"); + return FALSE; + } + + return TRUE; +} + +void +p_cond_variable_init (void) +{ +} + +void +p_cond_variable_shutdown (void) +{ +} diff --git a/3rdparty/plibsys/src/pcondvariable-atheos.c b/3rdparty/plibsys/src/pcondvariable-atheos.c new file mode 100644 index 0000000..f68e98b --- /dev/null +++ b/3rdparty/plibsys/src/pcondvariable-atheos.c @@ -0,0 +1,234 @@ +/* + * The MIT License + * + * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pcondvariable.h" +#include "pspinlock.h" +#include "patomic.h" +#include "pmem.h" + +#include <stdlib.h> +#include <string.h> + +#include <atheos/semaphore.h> +#include <atheos/threads.h> + +typedef struct _PCondThread { + thread_id thread; + struct _PCondThread *next; +} PCondThread; + +struct PCondVariable_ { + PSpinLock *lock; + PCondThread *wait_head; + pint wait_count; +}; + +P_LIB_API PCondVariable * +p_cond_variable_new (void) +{ + PCondVariable *ret; + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PCondVariable))) == NULL)) { + P_ERROR ("PCondVariable::p_cond_variable_new: failed to allocate memory"); + return NULL; + } + + if ((ret->lock = p_spinlock_new ()) == NULL) { + P_ERROR ("PCondVariable::p_cond_variable_new: failed to initialize"); + p_free (ret); + return NULL; + } + + return ret; +} + +P_LIB_API void +p_cond_variable_free (PCondVariable *cond) +{ + if (P_UNLIKELY (cond == NULL)) + return; + + if ((cond->wait_count > 0) || (cond->wait_head != NULL)) + P_WARNING ("PCondVariable::p_cond_variable_free: destroying while threads are waiting"); + + p_spinlock_free (cond->lock); + p_free (cond); +} + +P_LIB_API pboolean +p_cond_variable_wait (PCondVariable *cond, + PMutex *mutex) +{ + PCondThread *wait_thread; + + if (P_UNLIKELY (cond == NULL || mutex == NULL)) + return FALSE; + + if ((wait_thread = p_malloc0 (sizeof (PCondThread))) == NULL) { + P_ERROR ("PCondVariable::p_cond_variable_wait: failed to allocate memory"); + return FALSE; + } + + wait_thread->thread = get_thread_id (NULL); + wait_thread->next = NULL; + + if (p_spinlock_lock (cond->lock) != TRUE) { + P_ERROR ("PCondVariable::p_cond_variable_wait: failed to lock internal spinlock"); + return FALSE; + } + + if (cond->wait_head != NULL) + cond->wait_head->next = wait_thread; + else + cond->wait_head = wait_thread; + + p_atomic_int_inc ((volatile pint *) &cond->wait_count); + + if (p_spinlock_unlock (cond->lock) != TRUE) { + P_ERROR ("PCondVariable::p_cond_variable_wait: failed to unlock internal spinlock"); + return FALSE; + } + + if (p_mutex_unlock (mutex) != TRUE) { + P_ERROR ("PCondVariable::p_cond_variable_wait: failed to unlock mutex"); + return FALSE; + } + + suspend_thread (wait_thread->thread); + + if (p_mutex_lock (mutex) != TRUE) { + P_ERROR ("PCondVariable::p_cond_variable_wait: failed to lock mutex"); + return FALSE; + } + + return TRUE; +} + +P_LIB_API pboolean +p_cond_variable_signal (PCondVariable *cond) +{ + PCondThread *wait_thread; + thread_info thr_info; + + if (P_UNLIKELY (cond == NULL)) + return FALSE; + + if (p_spinlock_lock (cond->lock) != TRUE) { + P_ERROR ("PCondVariable::p_cond_variable_signal: failed to lock internal spinlock"); + return FALSE; + } + + if (cond->wait_head == NULL) { + if (p_spinlock_unlock (cond->lock) != TRUE) { + P_ERROR ("PCondVariable::p_cond_variable_signal(1): failed to unlock internal spinlock"); + return FALSE; + } else + return TRUE; + } + + wait_thread = cond->wait_head; + cond->wait_head = wait_thread->next; + + p_atomic_int_add ((volatile pint *) &cond->wait_count, -1); + + if (p_spinlock_unlock (cond->lock) != TRUE) { + P_ERROR ("PCondVariable::p_cond_variable_signal(2): failed to unlock internal spinlock"); + return FALSE; + } + + memset (&thr_info, 0, sizeof (thr_info)); + + while (get_thread_info (wait_thread->thread, &thr_info) == 0) { + if (thr_info.ti_state != TS_READY) + break; + } + + resume_thread (wait_thread->thread); + + p_free (wait_thread); + return TRUE; +} + +P_LIB_API pboolean +p_cond_variable_broadcast (PCondVariable *cond) +{ + if (P_UNLIKELY (cond == NULL)) + return FALSE; + + PCondThread *cur_thread; + PCondThread *next_thread; + thread_info thr_info; + + if (p_spinlock_lock (cond->lock) != TRUE) { + P_ERROR ("PCondVariable::p_cond_variable_broadcast: failed to lock internal spinlock"); + return FALSE; + } + + if (cond->wait_head == NULL) { + if (p_spinlock_unlock (cond->lock) != TRUE) { + P_ERROR ("PCondVariable::p_cond_variable_broadcast(1): failed to unlock internal spinlock"); + return FALSE; + } else + return TRUE; + } + + cur_thread = cond->wait_head; + + do { + memset (&thr_info, 0, sizeof (thr_info)); + + while (get_thread_info (cur_thread->thread, &thr_info) == 0) { + if (thr_info.ti_state != TS_READY) + break; + } + + resume_thread (cur_thread->thread); + + next_thread = cur_thread->next; + p_free (cur_thread); + + cur_thread = next_thread; + } while (cur_thread != NULL); + + cond->wait_head = NULL; + cond->wait_count = 0; + + if (p_spinlock_unlock (cond->lock) != TRUE) { + P_ERROR ("PCondVariable::p_cond_variable_broadcast(2): failed to unlock internal spinlock"); + return FALSE; + } + + return TRUE; +} + +void +p_cond_variable_init (void) +{ +} + +void +p_cond_variable_shutdown (void) +{ +} diff --git a/3rdparty/plibsys/src/pcondvariable-beos.c b/3rdparty/plibsys/src/pcondvariable-beos.c new file mode 100644 index 0000000..78199d1 --- /dev/null +++ b/3rdparty/plibsys/src/pcondvariable-beos.c @@ -0,0 +1,233 @@ +/* + * The MIT License + * + * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pcondvariable.h" +#include "pspinlock.h" +#include "patomic.h" +#include "pmem.h" + +#include <stdlib.h> +#include <string.h> + +#include <kernel/OS.h> + +typedef struct _PCondThread { + thread_id thread; + struct _PCondThread *next; +} PCondThread; + +struct PCondVariable_ { + PSpinLock *lock; + PCondThread *wait_head; + pint wait_count; +}; + +P_LIB_API PCondVariable * +p_cond_variable_new (void) +{ + PCondVariable *ret; + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PCondVariable))) == NULL)) { + P_ERROR ("PCondVariable::p_cond_variable_new: failed to allocate memory"); + return NULL; + } + + if ((ret->lock = p_spinlock_new ()) == NULL) { + P_ERROR ("PCondVariable::p_cond_variable_new: failed to initialize"); + p_free (ret); + return NULL; + } + + return ret; +} + +P_LIB_API void +p_cond_variable_free (PCondVariable *cond) +{ + if (P_UNLIKELY (cond == NULL)) + return; + + if ((cond->wait_count > 0) || (cond->wait_head != NULL)) + P_WARNING ("PCondVariable::p_cond_variable_free: destroying while threads are waiting"); + + p_spinlock_free (cond->lock); + p_free (cond); +} + +P_LIB_API pboolean +p_cond_variable_wait (PCondVariable *cond, + PMutex *mutex) +{ + PCondThread *wait_thread; + + if (P_UNLIKELY (cond == NULL || mutex == NULL)) + return FALSE; + + if ((wait_thread = p_malloc0 (sizeof (PCondThread))) == NULL) { + P_ERROR ("PCondVariable::p_cond_variable_wait: failed to allocate memory"); + return FALSE; + } + + wait_thread->thread = find_thread (NULL); + wait_thread->next = NULL; + + if (p_spinlock_lock (cond->lock) != TRUE) { + P_ERROR ("PCondVariable::p_cond_variable_wait: failed to lock internal spinlock"); + return FALSE; + } + + if (cond->wait_head != NULL) + cond->wait_head->next = wait_thread; + else + cond->wait_head = wait_thread; + + p_atomic_int_inc ((volatile pint *) &cond->wait_count); + + if (p_spinlock_unlock (cond->lock) != TRUE) { + P_ERROR ("PCondVariable::p_cond_variable_wait: failed to unlock internal spinlock"); + return FALSE; + } + + if (p_mutex_unlock (mutex) != TRUE) { + P_ERROR ("PCondVariable::p_cond_variable_wait: failed to unlock mutex"); + return FALSE; + } + + suspend_thread (wait_thread->thread); + + if (p_mutex_lock (mutex) != TRUE) { + P_ERROR ("PCondVariable::p_cond_variable_wait: failed to lock mutex"); + return FALSE; + } + + return TRUE; +} + +P_LIB_API pboolean +p_cond_variable_signal (PCondVariable *cond) +{ + PCondThread *wait_thread; + thread_info thr_info; + + if (P_UNLIKELY (cond == NULL)) + return FALSE; + + if (p_spinlock_lock (cond->lock) != TRUE) { + P_ERROR ("PCondVariable::p_cond_variable_signal: failed to lock internal spinlock"); + return FALSE; + } + + if (cond->wait_head == NULL) { + if (p_spinlock_unlock (cond->lock) != TRUE) { + P_ERROR ("PCondVariable::p_cond_variable_signal(1): failed to unlock internal spinlock"); + return FALSE; + } else + return TRUE; + } + + wait_thread = cond->wait_head; + cond->wait_head = wait_thread->next; + + p_atomic_int_add ((volatile pint *) &cond->wait_count, -1); + + if (p_spinlock_unlock (cond->lock) != TRUE) { + P_ERROR ("PCondVariable::p_cond_variable_signal(2): failed to unlock internal spinlock"); + return FALSE; + } + + memset (&thr_info, 0, sizeof (thr_info)); + + while (get_thread_info (wait_thread->thread, &thr_info) == B_OK) { + if (thr_info.state != B_THREAD_READY) + break; + } + + resume_thread (wait_thread->thread); + + p_free (wait_thread); + return TRUE; +} + +P_LIB_API pboolean +p_cond_variable_broadcast (PCondVariable *cond) +{ + PCondThread *cur_thread; + PCondThread *next_thread; + thread_info thr_info; + + if (P_UNLIKELY (cond == NULL)) + return FALSE; + + if (p_spinlock_lock (cond->lock) != TRUE) { + P_ERROR ("PCondVariable::p_cond_variable_broadcast: failed to lock internal spinlock"); + return FALSE; + } + + if (cond->wait_head == NULL) { + if (p_spinlock_unlock (cond->lock) != TRUE) { + P_ERROR ("PCondVariable::p_cond_variable_broadcast(1): failed to unlock internal spinlock"); + return FALSE; + } else + return TRUE; + } + + cur_thread = cond->wait_head; + + do { + memset (&thr_info, 0, sizeof (thr_info)); + + while (get_thread_info (cur_thread->thread, &thr_info) == B_OK) { + if (thr_info.state != B_THREAD_READY) + break; + } + + resume_thread (cur_thread->thread); + + next_thread = cur_thread->next; + p_free (cur_thread); + + cur_thread = next_thread; + } while (cur_thread != NULL); + + cond->wait_head = NULL; + cond->wait_count = 0; + + if (p_spinlock_unlock (cond->lock) != TRUE) { + P_ERROR ("PCondVariable::p_cond_variable_broadcast(2): failed to unlock internal spinlock"); + return FALSE; + } + + return TRUE; +} + +void +p_cond_variable_init (void) +{ +} + +void +p_cond_variable_shutdown (void) +{ +} diff --git a/3rdparty/plibsys/src/pcondvariable-none.c b/3rdparty/plibsys/src/pcondvariable-none.c new file mode 100644 index 0000000..b89baa3 --- /dev/null +++ b/3rdparty/plibsys/src/pcondvariable-none.c @@ -0,0 +1,80 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pcondvariable.h" + +#include <stdlib.h> + +struct PCondVariable_ { + pint hdl; +}; + +P_LIB_API PCondVariable * +p_cond_variable_new (void) +{ + return NULL; +} + +P_LIB_API void +p_cond_variable_free (PCondVariable *cond) +{ + P_UNUSED (cond); +} + +P_LIB_API pboolean +p_cond_variable_wait (PCondVariable *cond, + PMutex *mutex) +{ + P_UNUSED (cond); + P_UNUSED (mutex); + + return FALSE; +} + +P_LIB_API pboolean +p_cond_variable_signal (PCondVariable *cond) +{ + P_UNUSED (cond); + + return FALSE; +} + +P_LIB_API pboolean +p_cond_variable_broadcast (PCondVariable *cond) +{ + P_UNUSED (cond); + + return FALSE; +} + +void +p_cond_variable_init (void) +{ +} + +void +p_cond_variable_shutdown (void) +{ +} diff --git a/3rdparty/plibsys/src/pcondvariable-os2.c b/3rdparty/plibsys/src/pcondvariable-os2.c new file mode 100644 index 0000000..e3f0b0c --- /dev/null +++ b/3rdparty/plibsys/src/pcondvariable-os2.c @@ -0,0 +1,163 @@ +/* + * The MIT License + * + * Copyright (C) 2017 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "patomic.h" +#include "pmem.h" +#include "pcondvariable.h" + +#include <stdlib.h> + +#define INCL_DOSSEMAPHORES +#define INCL_DOSERRORS +#include <os2.h> + +struct PCondVariable_ { + HEV waiters_sema; + pint waiters_count; + pint signaled; +}; + +P_LIB_API PCondVariable * +p_cond_variable_new (void) +{ + PCondVariable *ret; + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PCondVariable))) == NULL)) { + P_ERROR ("PCondVariable::p_cond_variable_new: failed to allocate memory"); + return NULL; + } + + if (P_UNLIKELY (DosCreateEventSem (NULL, + (PHEV) &ret->waiters_sema, + 0, + FALSE) != NO_ERROR)) { + P_ERROR ("PCondVariable::p_cond_variable_new: failed to initialize"); + p_free (ret); + return NULL; + } + + return ret; +} + +P_LIB_API void +p_cond_variable_free (PCondVariable *cond) +{ + if (P_UNLIKELY (cond == NULL)) + return; + + if (P_UNLIKELY (DosCloseEventSem (cond->waiters_sema) != NO_ERROR)) + P_WARNING ("PCondVariable::p_cond_variable_free: DosCloseEventSem() failed"); + + p_free (cond); +} + +P_LIB_API pboolean +p_cond_variable_wait (PCondVariable *cond, + PMutex *mutex) +{ + APIRET ulrc; + APIRET reset_ulrc; + + if (P_UNLIKELY (cond == NULL || mutex == NULL)) + return FALSE; + + do { + p_atomic_int_inc (&cond->waiters_count); + p_mutex_unlock (mutex); + + do { + ULONG post_count; + + ulrc = DosWaitEventSem (cond->waiters_sema, SEM_INDEFINITE_WAIT); + + if (ulrc == NO_ERROR) { + reset_ulrc = DosResetEventSem (cond->waiters_sema, &post_count); + + if (P_UNLIKELY (reset_ulrc != NO_ERROR && + reset_ulrc != ERROR_ALREADY_RESET)) + P_WARNING ("PCondVariable::p_cond_variable_wait: DosResetEventSem() failed"); + } + } while (ulrc == NO_ERROR && + p_atomic_int_compare_and_exchange (&cond->signaled, 1, 0) == FALSE); + + p_atomic_int_add (&cond->waiters_count, -1); + p_mutex_lock (mutex); + } while (ulrc == ERROR_INTERRUPT); + + return (ulrc == NO_ERROR) ? TRUE : FALSE; +} + +P_LIB_API pboolean +p_cond_variable_signal (PCondVariable *cond) +{ + pboolean result = TRUE; + + if (P_UNLIKELY (cond == NULL)) + return FALSE; + + if (p_atomic_int_get (&cond->waiters_count) > 0) { + ULONG post_count; + APIRET ulrc; + + p_atomic_int_set (&cond->signaled, 1); + + ulrc = DosPostEventSem (cond->waiters_sema); + + if (P_UNLIKELY (ulrc != NO_ERROR && + ulrc != ERROR_ALREADY_POSTED && + ulrc != ERROR_TOO_MANY_POSTS)) { + P_WARNING ("PCondVariable::p_cond_variable_signal: DosPostEventSem() failed"); + result = FALSE; + } + } + + return result; +} + +P_LIB_API pboolean +p_cond_variable_broadcast (PCondVariable *cond) +{ + if (P_UNLIKELY (cond == NULL)) + return FALSE; + + pboolean result = TRUE; + + while (p_atomic_int_get (&cond->waiters_count) != 0) { + if (P_UNLIKELY (p_cond_variable_signal (cond) == FALSE)) + result = FALSE; + } + + return result; +} + +void +p_cond_variable_init (void) +{ +} + +void +p_cond_variable_shutdown (void) +{ +} diff --git a/3rdparty/plibsys/src/pcondvariable-posix.c b/3rdparty/plibsys/src/pcondvariable-posix.c new file mode 100644 index 0000000..54d7682 --- /dev/null +++ b/3rdparty/plibsys/src/pcondvariable-posix.c @@ -0,0 +1,121 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pmem.h" +#include "pcondvariable.h" + +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <pthread.h> + +struct PCondVariable_ { + pthread_cond_t hdl; +}; + +P_LIB_API PCondVariable * +p_cond_variable_new (void) +{ + PCondVariable *ret; + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PCondVariable))) == NULL)) { + P_ERROR ("PCondVariable::p_cond_variable_new: failed to allocate memory"); + return NULL; + } + + if (P_UNLIKELY (pthread_cond_init (&ret->hdl, NULL) != 0)) { + P_ERROR ("PCondVariable::p_cond_variable_new: failed to initialize"); + p_free (ret); + return NULL; + } + + return ret; +} + +P_LIB_API void +p_cond_variable_free (PCondVariable *cond) +{ + if (P_UNLIKELY (cond == NULL)) + return; + + if (P_UNLIKELY (pthread_cond_destroy (&cond->hdl) != 0)) + P_WARNING ("PCondVariable::p_cond_variable_free: pthread_cond_destroy() failed"); + + p_free (cond); +} + +P_LIB_API pboolean +p_cond_variable_wait (PCondVariable *cond, + PMutex *mutex) +{ + if (P_UNLIKELY (cond == NULL || mutex == NULL)) + return FALSE; + + /* Cast is eligible since there is only one field in the PMutex structure */ + if (P_UNLIKELY (pthread_cond_wait (&cond->hdl, (pthread_mutex_t *) mutex) != 0)) { + P_ERROR ("PCondVariable::p_cond_variable_wait: pthread_cond_wait() failed"); + return FALSE; + } + + return TRUE; +} + +P_LIB_API pboolean +p_cond_variable_signal (PCondVariable *cond) +{ + if (P_UNLIKELY (cond == NULL)) + return FALSE; + + if (P_UNLIKELY (pthread_cond_signal (&cond->hdl) != 0)) { + P_ERROR ("PCondVariable::p_cond_variable_signal: pthread_cond_signal() failed"); + return FALSE; + } + + return TRUE; +} + +P_LIB_API pboolean +p_cond_variable_broadcast (PCondVariable *cond) +{ + if (P_UNLIKELY (cond == NULL)) + return FALSE; + + if (P_UNLIKELY (pthread_cond_broadcast (&cond->hdl) != 0)) { + P_ERROR ("PCondVariable::p_cond_variable_broadcast: thread_cond_broadcast() failed"); + return FALSE; + } + + return TRUE; +} + +void +p_cond_variable_init (void) +{ +} + +void +p_cond_variable_shutdown (void) +{ +} diff --git a/3rdparty/plibsys/src/pcondvariable-solaris.c b/3rdparty/plibsys/src/pcondvariable-solaris.c new file mode 100644 index 0000000..53fdacf --- /dev/null +++ b/3rdparty/plibsys/src/pcondvariable-solaris.c @@ -0,0 +1,122 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pmem.h" +#include "pcondvariable.h" + +#include <stdlib.h> +#include <time.h> +#include <string.h> +#include <thread.h> +#include <synch.h> + +struct PCondVariable_ { + cond_t hdl; +}; + +P_LIB_API PCondVariable * +p_cond_variable_new (void) +{ + PCondVariable *ret; + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PCondVariable))) == NULL)) { + P_ERROR ("PCondVariable::p_cond_variable_new: failed to allocate memory"); + return NULL; + } + + if (P_UNLIKELY (cond_init (&ret->hdl, NULL, NULL) != 0)) { + P_ERROR ("PCondVariable::p_cond_variable_new: failed to initialize"); + p_free (ret); + return NULL; + } + + return ret; +} + +P_LIB_API void +p_cond_variable_free (PCondVariable *cond) +{ + if (P_UNLIKELY (cond == NULL)) + return; + + if (P_UNLIKELY (cond_destroy (&cond->hdl) != 0)) + P_WARNING ("PCondVariable::p_cond_variable_free: cond_destroy() failed"); + + p_free (cond); +} + +P_LIB_API pboolean +p_cond_variable_wait (PCondVariable *cond, + PMutex *mutex) +{ + if (P_UNLIKELY (cond == NULL || mutex == NULL)) + return FALSE; + + /* Cast is eligible since there is only one field in the PMutex structure */ + if (P_UNLIKELY (cond_wait (&cond->hdl, (mutex_t *) mutex) != 0)) { + P_ERROR ("PCondVariable::p_cond_variable_wait: cond_wait() failed"); + return FALSE; + } + + return TRUE; +} + +P_LIB_API pboolean +p_cond_variable_signal (PCondVariable *cond) +{ + if (P_UNLIKELY (cond == NULL)) + return FALSE; + + if (P_UNLIKELY (cond_signal (&cond->hdl) != 0)) { + P_ERROR ("PCondVariable::p_cond_variable_signal: cond_signal() failed"); + return FALSE; + } + + return TRUE; +} + +P_LIB_API pboolean +p_cond_variable_broadcast (PCondVariable *cond) +{ + if (P_UNLIKELY (cond == NULL)) + return FALSE; + + if (P_UNLIKELY (cond_broadcast (&cond->hdl) != 0)) { + P_ERROR ("PCondVariable::p_cond_variable_broadcast: cond_broadcast() failed"); + return FALSE; + } + + return TRUE; +} + +void +p_cond_variable_init (void) +{ +} + +void +p_cond_variable_shutdown (void) +{ +} diff --git a/3rdparty/plibsys/src/pcondvariable-win.c b/3rdparty/plibsys/src/pcondvariable-win.c new file mode 100644 index 0000000..0cf7dcb --- /dev/null +++ b/3rdparty/plibsys/src/pcondvariable-win.c @@ -0,0 +1,312 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2017 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* Taken from "Strategies for Implementing POSIX Condition Variables on Win32" + * by Douglas C. Schmidt and Irfan Pyarali. + * See: http://www.cse.wustl.edu/~schmidt/win32-cv-1.html + * See: https://github.com/python/cpython/blob/master/Python/condvar.h + */ + +#include "patomic.h" +#include "pmem.h" +#include "pcondvariable.h" + +#include <stdlib.h> + +typedef VOID (WINAPI * InitializeConditionVariableFunc) (ppointer cv); +typedef BOOL (WINAPI * SleepConditionVariableCSFunc) (ppointer cv, PCRITICAL_SECTION cs, DWORD ms); +typedef VOID (WINAPI * WakeConditionVariableFunc) (ppointer cv); +typedef VOID (WINAPI * WakeAllConditionVariableFunc) (ppointer cv); + +typedef pboolean (* PWin32CondInit) (PCondVariable *cond); +typedef void (* PWin32CondClose) (PCondVariable *cond); +typedef pboolean (* PWin32CondWait) (PCondVariable *cond, PMutex *mutex); +typedef pboolean (* PWin32CondSignal) (PCondVariable *cond); +typedef pboolean (* PWin32CondBrdcast) (PCondVariable *cond); + +static PWin32CondInit pp_cond_variable_init_func = NULL; +static PWin32CondClose pp_cond_variable_close_func = NULL; +static PWin32CondWait pp_cond_variable_wait_func = NULL; +static PWin32CondSignal pp_cond_variable_signal_func = NULL; +static PWin32CondBrdcast pp_cond_variable_brdcast_func = NULL; + +typedef struct PCondVariableVistaTable_ { + InitializeConditionVariableFunc cv_init; + SleepConditionVariableCSFunc cv_wait; + WakeConditionVariableFunc cv_wake; + WakeAllConditionVariableFunc cv_brdcast; +} PCondVariableVistaTable; + +typedef struct PCondVariableXP_ { + HANDLE waiters_sema; + pint waiters_count; +} PCondVariableXP; + +struct PCondVariable_ { + ppointer cv; +}; + +static PCondVariableVistaTable pp_cond_variable_vista_table = {NULL, NULL, NULL, NULL}; + +/* CONDITION_VARIABLE routines */ +static pboolean pp_cond_variable_init_vista (PCondVariable *cond); +static void pp_cond_variable_close_vista (PCondVariable *cond); +static pboolean pp_cond_variable_wait_vista (PCondVariable *cond, PMutex *mutex); +static pboolean pp_cond_variable_signal_vista (PCondVariable *cond); +static pboolean pp_cond_variable_broadcast_vista (PCondVariable *cond); + +/* Windows XP emulation routines */ +static pboolean pp_cond_variable_init_xp (PCondVariable *cond); +static void pp_cond_variable_close_xp (PCondVariable *cond); +static pboolean pp_cond_variable_wait_xp (PCondVariable *cond, PMutex *mutex); +static pboolean pp_cond_variable_signal_xp (PCondVariable *cond); +static pboolean pp_cond_variable_broadcast_xp (PCondVariable *cond); + +/* CONDITION_VARIABLE routines */ + +static pboolean +pp_cond_variable_init_vista (PCondVariable *cond) +{ + pp_cond_variable_vista_table.cv_init (cond); + + return TRUE; +} + +static void +pp_cond_variable_close_vista (PCondVariable *cond) +{ + P_UNUSED (cond); +} + +static pboolean +pp_cond_variable_wait_vista (PCondVariable *cond, PMutex *mutex) +{ + return pp_cond_variable_vista_table.cv_wait (cond, + (PCRITICAL_SECTION) mutex, + INFINITE) != 0 ? TRUE : FALSE; +} + +static pboolean +pp_cond_variable_signal_vista (PCondVariable *cond) +{ + pp_cond_variable_vista_table.cv_wake (cond); + + return TRUE; +} + +static pboolean +pp_cond_variable_broadcast_vista (PCondVariable *cond) +{ + pp_cond_variable_vista_table.cv_brdcast (cond); + + return TRUE; +} + +/* Windows XP emulation routines */ + +static pboolean +pp_cond_variable_init_xp (PCondVariable *cond) +{ + PCondVariableXP *cv_xp; + + if ((cond->cv = p_malloc0 (sizeof (PCondVariableXP))) == NULL) { + P_ERROR ("PCondVariable::pp_cond_variable_init_xp: failed to allocate memory (internal)"); + return FALSE; + } + + cv_xp = ((PCondVariableXP *) cond->cv); + + cv_xp->waiters_count = 0; + cv_xp->waiters_sema = CreateSemaphoreA (NULL, 0, MAXLONG, NULL); + + if (P_UNLIKELY (cv_xp->waiters_sema == NULL)) { + P_ERROR ("PCondVariable::pp_cond_variable_init_xp: failed to initialize semaphore"); + p_free (cond->cv); + cond->cv = NULL; + return FALSE; + } + + return TRUE; +} + +static void +pp_cond_variable_close_xp (PCondVariable *cond) +{ + CloseHandle (((PCondVariableXP *) cond->cv)->waiters_sema); + p_free (cond->cv); +} + +static pboolean +pp_cond_variable_wait_xp (PCondVariable *cond, PMutex *mutex) +{ + PCondVariableXP *cv_xp = ((PCondVariableXP *) cond->cv); + DWORD wait; + + p_atomic_int_inc (&cv_xp->waiters_count); + + p_mutex_unlock (mutex); + wait = WaitForSingleObjectEx (cv_xp->waiters_sema, INFINITE, FALSE); + p_mutex_lock (mutex); + + if (wait != WAIT_OBJECT_0) + p_atomic_int_add (&cv_xp->waiters_count, -1); + + return wait == WAIT_OBJECT_0 ? TRUE : FALSE; +} + +static pboolean +pp_cond_variable_signal_xp (PCondVariable *cond) +{ + PCondVariableXP *cv_xp = ((PCondVariableXP *) cond->cv); + + if (p_atomic_int_get (&cv_xp->waiters_count) > 0) { + p_atomic_int_add (&cv_xp->waiters_count, -1); + return ReleaseSemaphore (cv_xp->waiters_sema, 1, 0) != 0 ? TRUE : FALSE; + } + + return TRUE; +} + +static pboolean +pp_cond_variable_broadcast_xp (PCondVariable *cond) +{ + PCondVariableXP *cv_xp = ((PCondVariableXP *) cond->cv); + pint waiters; + + waiters = p_atomic_int_get (&cv_xp->waiters_count); + + if (waiters > 0) { + p_atomic_int_set (&cv_xp->waiters_count, 0); + return ReleaseSemaphore (cv_xp->waiters_sema, waiters, 0) != 0 ? TRUE : FALSE; + } + + return TRUE; +} + +P_LIB_API PCondVariable * +p_cond_variable_new (void) +{ + PCondVariable *ret; + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PCondVariable))) == NULL)) { + P_ERROR ("PCondVariable::p_cond_variable_new: failed to allocate memory"); + return NULL; + } + + if (P_UNLIKELY (pp_cond_variable_init_func (ret) != TRUE)) { + P_ERROR ("PCondVariable::p_cond_variable_new: failed to initialize"); + p_free (ret); + return NULL; + } + + return ret; +} + +P_LIB_API void +p_cond_variable_free (PCondVariable *cond) +{ + if (P_UNLIKELY (cond == NULL)) + return; + + pp_cond_variable_close_func (cond); + p_free (cond); +} + +P_LIB_API pboolean +p_cond_variable_wait (PCondVariable *cond, + PMutex *mutex) +{ + if (P_UNLIKELY (cond == NULL || mutex == NULL)) + return FALSE; + + return pp_cond_variable_wait_func (cond, mutex); +} + +P_LIB_API pboolean +p_cond_variable_signal (PCondVariable *cond) +{ + if (P_UNLIKELY (cond == NULL)) + return FALSE; + + return pp_cond_variable_signal_func (cond); +} + +P_LIB_API pboolean +p_cond_variable_broadcast (PCondVariable *cond) +{ + if (P_UNLIKELY (cond == NULL)) + return FALSE; + + return pp_cond_variable_brdcast_func (cond); +} + +void +p_cond_variable_init (void) +{ + HMODULE hmodule; + + hmodule = GetModuleHandleA ("kernel32.dll"); + + if (P_UNLIKELY (hmodule == NULL)) { + P_ERROR ("PCondVariable::p_cond_variable_init: failed to load kernel32.dll module"); + return; + } + + pp_cond_variable_vista_table.cv_init = (InitializeConditionVariableFunc) GetProcAddress (hmodule, + "InitializeConditionVariable"); + + if (P_LIKELY (pp_cond_variable_vista_table.cv_init != NULL)) { + pp_cond_variable_vista_table.cv_wait = (SleepConditionVariableCSFunc) GetProcAddress (hmodule, + "SleepConditionVariableCS"); + pp_cond_variable_vista_table.cv_wake = (WakeConditionVariableFunc) GetProcAddress (hmodule, + "WakeConditionVariable"); + pp_cond_variable_vista_table.cv_brdcast = (WakeAllConditionVariableFunc) GetProcAddress (hmodule, + "WakeAllConditionVariable"); + + pp_cond_variable_init_func = pp_cond_variable_init_vista; + pp_cond_variable_close_func = pp_cond_variable_close_vista; + pp_cond_variable_wait_func = pp_cond_variable_wait_vista; + pp_cond_variable_signal_func = pp_cond_variable_signal_vista; + pp_cond_variable_brdcast_func = pp_cond_variable_broadcast_vista; + } else { + pp_cond_variable_init_func = pp_cond_variable_init_xp; + pp_cond_variable_close_func = pp_cond_variable_close_xp; + pp_cond_variable_wait_func = pp_cond_variable_wait_xp; + pp_cond_variable_signal_func = pp_cond_variable_signal_xp; + pp_cond_variable_brdcast_func = pp_cond_variable_broadcast_xp; + } +} + +void +p_cond_variable_shutdown (void) +{ + memset (&pp_cond_variable_vista_table, 0, sizeof (pp_cond_variable_vista_table)); + + pp_cond_variable_init_func = NULL; + pp_cond_variable_close_func = NULL; + pp_cond_variable_wait_func = NULL; + pp_cond_variable_signal_func = NULL; + pp_cond_variable_brdcast_func = NULL; +} diff --git a/3rdparty/plibsys/src/pcondvariable.h b/3rdparty/plibsys/src/pcondvariable.h new file mode 100644 index 0000000..d69c8b9 --- /dev/null +++ b/3rdparty/plibsys/src/pcondvariable.h @@ -0,0 +1,138 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * @file pcondvariable.h + * @brief Condition variable + * @author Alexander Saprykin + * + * A condition variable is an inter-thread synchronization primitive, often + * used in the classical 'producers-consumers' concurrent data access models. + * + * The main idea is to notify waiting thread(s) for some events before they + * can enter a critical section. Hence the name of the primitive: a thread + * enters the critical section upon an accomplished condition. Compare it with a + * mutex where the thread enters the critical section as soon as no one holds a + * lock. + * + * Several threads can be notified at once, but only one of them can enter the + * critical section. The order of the threads in that case is implementation + * dependent. + * + * As the thread enters the critical section upon a condition it still requires + * a mutex to guard its code against concurrent access from other threads. The + * mutex provided in pair with a condition variable will be automatically locked + * on the condition, the thread should unlock it explicitly after leaving the + * critical section. That mutex is unlocked while waiting for the condition and + * should be locked prior calling the condition waiting routine. + * + * The waiting thread behavior: create a new condition variable with + * p_cond_variable_new(), create and lock a mutex before a critical section and + * wait for a signal from another thread on this condition variable + * using p_cond_variable_wait(). + * + * The signaling thread behavior: upon reaching event time emit a signal with + * p_cond_variable_signal() to wake up a single waiting thread or + * p_cond_variable_broadcast() to wake up all the waiting threads. + * + * After emitting the signal only the one thread will get the locked mutex back + * to continue executing the critical section. + * + * It is implementation dependent whether a thread will receive a missed signal + * (when a notification from the one thread was emitted prior another thread has + * been called for waiting), so do not rely on this behavior. + */ + +#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION) +# error "Header files shouldn't be included directly, consider using <plibsys.h> instead." +#endif + +#ifndef PLIBSYS_HEADER_PCONDVARIABLE_H +#define PLIBSYS_HEADER_PCONDVARIABLE_H + +#include <pmacros.h> +#include <ptypes.h> +#include <pmutex.h> + +P_BEGIN_DECLS + +/** Condition variable opaque data structure. */ +typedef struct PCondVariable_ PCondVariable; + +/** + * @brief Creates a new #PCondVariable. + * @return Pointer to a newly created #PCondVariable structure, or NULL if + * failed. + * @since 0.0.1 + */ +P_LIB_API PCondVariable * p_cond_variable_new (void); + +/** + * @brief Frees #PCondVariable structure. + * @param cond Condtion variable to free. + * @since 0.0.1 + */ +P_LIB_API void p_cond_variable_free (PCondVariable *cond); + +/** + * @brief Waits for a signal on a given condition variable. + * @param cond Condition variable to wait on. + * @param mutex Locked mutex which will remain locked after waiting. + * @return TRUE on success, FALSE otherwise. + * @since 0.0.1 + * + * The calling thread will sleep until the signal on @a cond arrived. + */ +P_LIB_API pboolean p_cond_variable_wait (PCondVariable *cond, + PMutex *mutex); + +/** + * @brief Emitts a signal on a given condition variable for one waiting thread. + * @param cond Condition variable to emit the signal on. + * @return TRUE on success, FALSE otherwise. + * @since 0.0.1 + * + * After emitting the signal only the one thread waiting for it will be waken + * up. Do not rely on a queue concept for waiting threads. Though the + * implementation is intended to be much close to a queue, it's not fairly + * enough. Due that any thread can be waken up, even if it has just called + * p_cond_variable_wait() while there are other waiting threads. + */ +P_LIB_API pboolean p_cond_variable_signal (PCondVariable *cond); + +/** + * @brief Emitts a signal on a given condition variable for all the waiting + * threads. + * @param cond Condition variable to emit the signal on. + * @return TRUE on success, FALSE otherwise. + * @since 0.0.1 + * + * After emitting the signal all the threads waiting for it will be waken up. + */ +P_LIB_API pboolean p_cond_variable_broadcast (PCondVariable *cond); + +P_END_DECLS + +#endif /* PLIBSYS_HEADER_PCONDVARIABLE_H */ diff --git a/3rdparty/plibsys/src/pcryptohash-gost3411.c b/3rdparty/plibsys/src/pcryptohash-gost3411.c new file mode 100644 index 0000000..9430453 --- /dev/null +++ b/3rdparty/plibsys/src/pcryptohash-gost3411.c @@ -0,0 +1,484 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2017 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pmem.h" +#include "pcryptohash-gost3411.h" + +#include <string.h> +#include <stdlib.h> + +struct PHashGOST3411_ { + puint32 buf[8]; /* Buffer to handle incoming data. */ + puint32 hash[8]; /* State of calculated hash. */ + puint32 len[8]; /* Length of hashed data, in bits. */ + puint32 sum[8]; /* 256-bit sum of hashed data. */ +}; + +static void pp_crypto_hash_gost3411_swap_bytes (puint32 *data, puint words); +static void pp_crypto_hash_gost3411_sum_256 (puint32 a[8], const puint32 b[8]); +static void pp_crypto_hash_gost3411_process (PHashGOST3411 *ctx, const puint32 data[8]); + +/* K block data from RFC4357 for GOST 28147-89 */ +/* static const puchar pp_crypto_hash_gost3411_K_block[8][16] = { + {0x9, 0x6, 0x3, 0x2, 0x8, 0xB, 0x1, 0x7, 0xA, 0x4, 0xE, 0xF, 0xC, 0x0, 0xD, 0x5}, + {0x3, 0x7, 0xE, 0x9, 0x8, 0xA, 0xF, 0x0, 0x5, 0x2, 0x6, 0xC, 0xB, 0x4, 0xD, 0x1}, + {0xE, 0x4, 0x6, 0x2, 0xB, 0x3, 0xD, 0x8, 0xC, 0xF, 0x5, 0xA, 0x0, 0x7, 0x1, 0x9}, + {0xE, 0x7, 0xA, 0xC, 0xD, 0x1, 0x3, 0x9, 0x0, 0x2, 0xB, 0x4, 0xF, 0x8, 0x5, 0x6}, + {0xB, 0x5, 0x1, 0x9, 0x8, 0xD, 0xF, 0x0, 0xE, 0x4, 0x2, 0x3, 0xC, 0x7, 0xA, 0x6}, + {0x3, 0xA, 0xD, 0xC, 0x1, 0x2, 0x0, 0xB, 0x7, 0x5, 0x9, 0x4, 0x8, 0xF, 0xE, 0x6}, + {0x1, 0xD, 0x2, 0x9, 0x7, 0xA, 0x6, 0x0, 0x8, 0xC, 0x4, 0x5, 0xF, 0x3, 0xB, 0xE}, + {0xB, 0xA, 0xF, 0x5, 0x0, 0xC, 0xE, 0x8, 0x6, 0x2, 0x3, 0x9, 0x1, 0x7, 0xD, 0x4} +}; */ + +/* K block data used by Russian Central Bank (see RFC 4357, sec. 11.2) */ +/* static const puchar pp_crypto_hash_gost3411_K_block[8][16] = { + {0x4, 0xA, 0x9, 0x2, 0xD, 0x8, 0x0, 0xE, 0x6, 0xB, 0x1, 0xC, 0x7, 0xF, 0x5, 0x3}, + {0xE, 0xB, 0x4, 0xC, 0x6, 0xD, 0xF, 0xA, 0x2, 0x3, 0x8, 0x1, 0x0, 0x7, 0x5, 0x9}, + {0x5, 0x8, 0x1, 0xD, 0xA, 0x3, 0x4, 0x2, 0xE, 0xF, 0xC, 0x7, 0x6, 0x0, 0x9, 0xB}, + {0x7, 0xD, 0xA, 0x1, 0x0, 0x8, 0x9, 0xF, 0xE, 0x4, 0x6, 0xC, 0xB, 0x2, 0x5, 0x3}, + {0x6, 0xC, 0x7, 0x1, 0x5, 0xF, 0xD, 0x8, 0x4, 0xA, 0x9, 0xE, 0x0, 0x3, 0xB, 0x2}, + {0x4, 0xB, 0xA, 0x0, 0x7, 0x2, 0x1, 0xD, 0x3, 0x6, 0x8, 0x5, 0x9, 0xC, 0xF, 0xE}, + {0xD, 0xB, 0x4, 0x1, 0x3, 0xF, 0x5, 0x9, 0x0, 0xA, 0xE, 0x7, 0x6, 0x8, 0x2, 0xC}, + {0x1, 0xF, 0xD, 0x0, 0x5, 0x7, 0xA, 0x4, 0x9, 0x2, 0x3, 0xE, 0x6, 0xB, 0x8, 0xC} +}; */ + + /* K block data id-GostR3411-94-CryptoProParamSet (see RFC 4357, sec. 11.2) */ + static const puchar pp_crypto_hash_gost3411_K_block[8][16] = { + {0xA, 0x4, 0x5, 0x6, 0x8, 0x1, 0x3, 0x7, 0xD, 0xC, 0xE, 0x0, 0x9, 0x2, 0xB, 0xF}, + {0x5, 0xF, 0x4, 0x0, 0x2, 0xD, 0xB, 0x9, 0x1, 0x7, 0x6, 0x3, 0xC, 0xE, 0xA, 0x8}, + {0x7, 0xF, 0xC, 0xE, 0x9, 0x4, 0x1, 0x0, 0x3, 0xB, 0x5, 0x2, 0x6, 0xA, 0x8, 0xD}, + {0x4, 0xA, 0x7, 0xC, 0x0, 0xF, 0x2, 0x8, 0xE, 0x1, 0x6, 0x5, 0xD, 0xB, 0x9, 0x3}, + {0x7, 0x6, 0x4, 0xB, 0x9, 0xC, 0x2, 0xA, 0x1, 0x8, 0x0, 0xE, 0xF, 0xD, 0x3, 0x5}, + {0x7, 0x6, 0x2, 0x4, 0xD, 0x9, 0xF, 0x0, 0xA, 0x1, 0x5, 0xB, 0x8, 0xE, 0xC, 0x3}, + {0xD, 0xE, 0x4, 0x1, 0x7, 0x0, 0x5, 0xA, 0x3, 0xC, 0x8, 0xF, 0x6, 0x2, 0x9, 0xB}, + {0x1, 0x3, 0xA, 0x9, 0x5, 0xB, 0x4, 0xF, 0x8, 0x6, 0x7, 0xE, 0xD, 0x0, 0x2, 0xC} + }; + +/* GOST 28147-89 transformation to generate keys */ +#define P_GOST_28147_ROUND(N, key) \ +{ \ + puint32 CM1; \ + \ + CM1 = (N)[0] + (key); \ + \ + CM1 = ((puint32) pp_crypto_hash_gost3411_K_block [0][CM1 & 0xF] \ + | (puint32) pp_crypto_hash_gost3411_K_block [1][(CM1 >> 4) & 0xF] << 4 \ + | (puint32) pp_crypto_hash_gost3411_K_block [2][(CM1 >> 8) & 0xF] << 8 \ + | (puint32) pp_crypto_hash_gost3411_K_block [3][(CM1 >> 12) & 0xF] << 12 \ + | (puint32) pp_crypto_hash_gost3411_K_block [4][(CM1 >> 16) & 0xF] << 16 \ + | (puint32) pp_crypto_hash_gost3411_K_block [5][(CM1 >> 20) & 0xF] << 20 \ + | (puint32) pp_crypto_hash_gost3411_K_block [6][(CM1 >> 24) & 0xF] << 24 \ + | (puint32) pp_crypto_hash_gost3411_K_block [7][(CM1 >> 28) & 0xF] << 28); \ + \ + CM1 = ((CM1 << 11) | (CM1 >> 21)) ^ (N)[1]; \ + (N)[1] = (N)[0]; \ + (N)[0] = CM1; \ +} + +/* Core GOST 28147-89 transformation */ +#define P_GOST_28147_E(data, key, out) \ +{ \ + puint32 N[2]; \ + \ + memcpy (N, data, 8); \ + \ + P_GOST_28147_ROUND (N, (key)[0]); \ + P_GOST_28147_ROUND (N, (key)[1]); \ + P_GOST_28147_ROUND (N, (key)[2]); \ + P_GOST_28147_ROUND (N, (key)[3]); \ + P_GOST_28147_ROUND (N, (key)[4]); \ + P_GOST_28147_ROUND (N, (key)[5]); \ + P_GOST_28147_ROUND (N, (key)[6]); \ + P_GOST_28147_ROUND (N, (key)[7]); \ + \ + P_GOST_28147_ROUND (N, (key)[0]); \ + P_GOST_28147_ROUND (N, (key)[1]); \ + P_GOST_28147_ROUND (N, (key)[2]); \ + P_GOST_28147_ROUND (N, (key)[3]); \ + P_GOST_28147_ROUND (N, (key)[4]); \ + P_GOST_28147_ROUND (N, (key)[5]); \ + P_GOST_28147_ROUND (N, (key)[6]); \ + P_GOST_28147_ROUND (N, (key)[7]); \ + \ + P_GOST_28147_ROUND (N, (key)[0]); \ + P_GOST_28147_ROUND (N, (key)[1]); \ + P_GOST_28147_ROUND (N, (key)[2]); \ + P_GOST_28147_ROUND (N, (key)[3]); \ + P_GOST_28147_ROUND (N, (key)[4]); \ + P_GOST_28147_ROUND (N, (key)[5]); \ + P_GOST_28147_ROUND (N, (key)[6]); \ + P_GOST_28147_ROUND (N, (key)[7]); \ + \ + P_GOST_28147_ROUND (N, (key)[7]); \ + P_GOST_28147_ROUND (N, (key)[6]); \ + P_GOST_28147_ROUND (N, (key)[5]); \ + P_GOST_28147_ROUND (N, (key)[4]); \ + P_GOST_28147_ROUND (N, (key)[3]); \ + P_GOST_28147_ROUND (N, (key)[2]); \ + P_GOST_28147_ROUND (N, (key)[1]); \ + P_GOST_28147_ROUND (N, (key)[0]); \ + \ + (out)[0] = N[1]; \ + (out)[1] = N[0]; \ +} + +/* P transformation from GOST R 34.11-94 */ +#define P_GOST_3411_P(data, out) \ +{ \ + (out)[0] = ((data) [0] & 0x000000FF) \ + | (((data)[2] << 8) & 0x0000FF00) \ + | (((data)[4] << 16) & 0x00FF0000) \ + | (((data)[6] << 24) & 0xFF000000); \ + (out)[1] = (((data)[0] >> 8) & 0x000000FF) \ + | ((data) [2] & 0x0000FF00) \ + | (((data)[4] << 8) & 0x00FF0000) \ + | (((data)[6] << 16) & 0xFF000000); \ + (out)[2] = (((data)[0] >> 16) & 0x000000FF) \ + | (((data)[2] >> 8) & 0x0000FF00) \ + | ((data) [4] & 0x00FF0000) \ + | (((data)[6] << 8) & 0xFF000000); \ + (out)[3] = (((data)[0] >> 24) & 0x000000FF) \ + | (((data)[2] >> 16) & 0x0000FF00) \ + | (((data)[4] >> 8) & 0x00FF0000) \ + | ((data) [6] & 0xFF000000); \ + (out)[4] = ((data) [1] & 0x000000FF) \ + | (((data)[3] << 8) & 0x0000FF00) \ + | (((data)[5] << 16) & 0x00FF0000) \ + | (((data)[7] << 24) & 0xFF000000); \ + (out)[5] = (((data)[1] >> 8) & 0x000000FF) \ + | ((data) [3] & 0x0000FF00) \ + | (((data)[5] << 8) & 0x00FF0000) \ + | (((data)[7] << 16) & 0xFF000000); \ + (out)[6] = (((data)[1] >> 16) & 0x000000FF) \ + | (((data)[3] >> 8) & 0x0000FF00) \ + | ((data) [5] & 0x00FF0000) \ + | (((data)[7] << 8) & 0xFF000000); \ + (out)[7] = (((data)[1] >> 24) & 0x000000FF) \ + | (((data)[3] >> 16) & 0x0000FF00) \ + | (((data)[5] >> 8) & 0x00FF0000) \ + | ((data) [7] & 0xFF000000); \ +} + +static void +pp_crypto_hash_gost3411_swap_bytes (puint32 *data, + puint words) +{ +#ifndef PLIBSYS_IS_BIGENDIAN + P_UNUSED (data); + P_UNUSED (words); +#else + while (words-- > 0) { + *data = PUINT32_TO_LE (*data); + ++data; + } +#endif +} + +/* 256-bit sum */ +static void +pp_crypto_hash_gost3411_sum_256 (puint32 a[8], + const puint32 b[8]) +{ + puint i; + puint32 old; + pboolean carry; + + carry = FALSE; + for (i = 0; i < 8; ++i) { + old = a[i]; + a[i] = a[i] + b[i] + (carry ? 1 : 0); + carry = (a[i] < old || a[i] < b[i]) ? TRUE : FALSE; + } +} + +/* Core GOST R 34.11-94 transformation */ +static void pp_crypto_hash_gost3411_process (PHashGOST3411 *ctx, + const puint32 data[8]) +{ + puint32 U[8], V[8], W[8], S[8], K[4][8]; + + memcpy (U, ctx->hash, 32); + memcpy (V, data, 32); + + /* Generate first key: P (U xor V) */ + W[0] = U[0] ^ V[0]; + W[1] = U[1] ^ V[1]; + W[2] = U[2] ^ V[2]; + W[3] = U[3] ^ V[3]; + W[4] = U[4] ^ V[4]; + W[5] = U[5] ^ V[5]; + W[6] = U[6] ^ V[6]; + W[7] = U[7] ^ V[7]; + + P_GOST_3411_P (W, K[0]); + + /* Generate second key: P (A (U) xor A^2 (V)) */ + W[0] = U[2] ^ V[4]; + W[1] = U[3] ^ V[5]; + W[2] = U[4] ^ V[6]; + W[3] = U[5] ^ V[7]; + W[4] = U[6] ^ (V[0] ^= V[2]); + W[5] = U[7] ^ (V[1] ^= V[3]); + W[6] = (U[0] ^= U[2]) ^ (V[2] ^= V[4]); + W[7] = (U[1] ^= U[3]) ^ (V[3] ^= V[5]); + + P_GOST_3411_P (W, K[1]); + + /* Generate third key: P ((A^2 (U) + C3) xor A^4 (V)) */ + /* C3 = FF00FFFF 000000FF FF0000FF 00FFFF00 00FF00FF 00FF00FF FF00FF00 FF00FF00 */ + U[2] ^= U[4] ^ 0x000000FF; + U[3] ^= U[5] ^ 0xFF00FFFF; + U[4] ^= 0xFF00FF00; + U[5] ^= 0xFF00FF00; + U[6] ^= 0x00FF00FF; + U[7] ^= 0x00FF00FF; + U[0] ^= 0x00FFFF00; + U[1] ^= 0xFF0000FF; + + W[0] = U[4] ^ V[0]; + W[2] = U[6] ^ V[2]; + W[4] = U[0] ^ (V[4] ^= V[6]); + W[6] = U[2] ^ (V[6] ^= V[0]); + W[1] = U[5] ^ V[1]; + W[3] = U[7] ^ V[3]; + W[5] = U[1] ^ (V[5] ^= V[7]); + W[7] = U[3] ^ (V[7] ^= V[1]); + + P_GOST_3411_P (W, K[2]); + + /* Generate forth key: P (A (A^2 (U) xor C3) xor A^6 (V)) */ + W[0] = U[6] ^ V[4]; + W[1] = U[7] ^ V[5]; + W[2] = U[0] ^ V[6]; + W[3] = U[1] ^ V[7]; + W[4] = U[2] ^ (V[0] ^= V[2]); + W[5] = U[3] ^ (V[1] ^= V[3]); + W[6] = (U[4] ^= U[6]) ^ (V[2] ^= V[4]); + W[7] = (U[5] ^= U[7]) ^ (V[3] ^= V[5]); + + P_GOST_3411_P (W, K[3]); + + /* Perform GOST 28147-89 encryption */ + P_GOST_28147_E (ctx->hash, K[0], S); + P_GOST_28147_E (ctx->hash + 2, K[1], S + 2); + P_GOST_28147_E (ctx->hash + 4, K[2], S + 4); + P_GOST_28147_E (ctx->hash + 6, K[3], S + 6); + + /* Step hash function: H (M, Hprev) = PSI^61 (Hprev xor PSI (M xor PSI^12 (S))) */ + + /* (12 rounds of LFSR) xor M */ + U[0] = data[0] ^ S[6]; + + U[1] = data[1] ^ S[7]; + + U[2] = data[2] ^ (S[0] & 0x0000FFFF) ^ (S[0] >> 16) ^ (S[0] << 16) + ^ (S[1] & 0x0000FFFF) ^ (S[1] >> 16) ^ (S[2] << 16) + ^ (S[7] & 0xFFFF0000) ^ (S[6] << 16) ^ (S[7] >> 16) + ^ S[6]; + + U[3] = data[3] ^ (S[0] & 0x0000FFFF) ^ (S[0] << 16) ^ (S[2] << 16) + ^ (S[1] & 0x0000FFFF) ^ (S[1] << 16) ^ (S[1] >> 16) + ^ (S[7] & 0x0000FFFF) ^ (S[2] >> 16) ^ (S[3] << 16) + ^ (S[6] << 16) ^ (S[6] >> 16) ^ (S[7] << 16) + ^ (S[7] >> 16) ^ S[6]; + + U[4] = data[4] ^ (S[0] & 0xFFFF0000) ^ (S[0] << 16) ^ (S[0] >> 16) + ^ (S[1] & 0xFFFF0000) ^ (S[1] >> 16) ^ (S[2] << 16) + ^ (S[7] & 0x0000FFFF) ^ (S[3] << 16) ^ (S[3] >> 16) + ^ (S[4] << 16) ^ (S[6] << 16) ^ (S[6] >> 16) + ^ (S[2] >> 16) ^ (S[7] << 16) ^ (S[7] >> 16); + + U[5] = data[5] ^ (S[0] & 0xFFFF0000) ^ (S[0] >> 16) ^ (S[0] << 16) + ^ (S[1] & 0x0000FFFF) ^ (S[7] >> 16) ^ (S[2] >> 16) + ^ (S[7] & 0xFFFF0000) ^ (S[3] >> 16) ^ (S[4] << 16) + ^ (S[4] >> 16) ^ (S[5] << 16) ^ (S[6] << 16) + ^ (S[6] >> 16) ^ (S[3] << 16) ^ (S[7] << 16) + ^ S[2]; + + U[6] = data[6] ^ (S[4] >> 16) ^ (S[1] >> 16) ^ (S[2] << 16) + ^ (S[7] << 16) ^ (S[3] >> 16) ^ (S[4] << 16) + ^ (S[5] << 16) ^ (S[5] >> 16) ^ (S[6] << 16) + ^ (S[6] >> 16) ^ S[6] ^ S[0] + ^ S[3]; + + U[7] = data[7] ^ (S[0] & 0xFFFF0000) ^ (S[0] << 16) ^ (S[1] << 16) + ^ (S[1] & 0x0000FFFF) ^ (S[2] >> 16) ^ (S[3] << 16) + ^ (S[7] & 0x0000FFFF) ^ (S[4] >> 16) ^ (S[5] << 16) + ^ (S[5] >> 16) ^ (S[6] >> 16) ^ (S[7] << 16) + ^ (S[7] >> 16) ^ S[4]; + + /* (1 round of LFSR) xor Hprev */ + V[0] = ctx->hash[0] ^ (U[1] << 16) ^ (U[0] >> 16); + V[1] = ctx->hash[1] ^ (U[2] << 16) ^ (U[1] >> 16); + V[2] = ctx->hash[2] ^ (U[3] << 16) ^ (U[2] >> 16); + V[3] = ctx->hash[3] ^ (U[4] << 16) ^ (U[3] >> 16); + V[4] = ctx->hash[4] ^ (U[5] << 16) ^ (U[4] >> 16); + V[5] = ctx->hash[5] ^ (U[6] << 16) ^ (U[5] >> 16); + V[6] = ctx->hash[6] ^ (U[7] << 16) ^ (U[6] >> 16); + V[7] = ctx->hash[7] ^ (U[7] >> 16) + ^ (U[0] << 16) ^ (U[1] & 0xFFFF0000) + ^ (U[1] << 16) ^ (U[7] & 0xFFFF0000) + ^ (U[6] << 16) ^ (U[0] & 0xFFFF0000); + + /* Final 61 rounds of LFSR */ + ctx->hash[0] = (V[0] & 0xFFFF0000) ^ (V[0] << 16) ^ (V[0] >> 16) + ^ (V[1] & 0xFFFF0000) ^ (V[1] >> 16) ^ (V[2] << 16) + ^ (V[7] & 0x0000FFFF) ^ (V[3] >> 16) ^ (V[4] << 16) + ^ (V[5] >> 16) ^ (V[6] >> 16) ^ (V[7] << 16) + ^ (V[7] >> 16) ^ V[5]; + ctx->hash[1] = (V[0] & 0xFFFF0000) ^ (V[0] << 16) ^ (V[0] >> 16) + ^ (V[1] & 0x0000FFFF) ^ (V[2] >> 16) ^ (V[3] << 16) + ^ (V[7] & 0xFFFF0000) ^ (V[4] >> 16) ^ (V[5] << 16) + ^ (V[6] << 16) ^ (V[7] >> 16) ^ V[6] + ^ V[2] ; + ctx->hash[2] = (V[0] & 0x0000FFFF) ^ (V[0] << 16) ^ (V[1] << 16) + ^ (V[7] & 0x0000FFFF) ^ (V[1] >> 16) ^ (V[2] << 16) + ^ (V[1] & 0xFFFF0000) ^ (V[3] >> 16) ^ (V[4] << 16) + ^ (V[5] >> 16) ^ (V[6] >> 16) ^ (V[7] << 16) + ^ (V[7] >> 16) ^ V[3] ^ V[6]; + ctx->hash[3] = (V[0] & 0xFFFF0000) ^ (V[0] << 16) ^ (V[0] >> 16) + ^ (V[1] & 0xFFFF0000) ^ (V[1] >> 16) ^ (V[2] << 16) + ^ (V[7] & 0x0000FFFF) ^ (V[2] >> 16) ^ (V[3] << 16) + ^ (V[4] >> 16) ^ (V[5] << 16) ^ (V[6] << 16) + ^ (V[7] >> 16) ^ V[2] ^ V[4]; + ctx->hash[4] = (V[0] >> 16) ^ (V[1] << 16) ^ (V[2] >> 16) + ^ (V[3] << 16) ^ (V[3] >> 16) ^ (V[4] << 16) + ^ (V[5] >> 16) ^ (V[6] << 16) ^ (V[6] >> 16) + ^ (V[7] << 16) ^ V[1] ^ V[2] + ^ V[3] ^ V[5]; + ctx->hash[5] = (V[0] & 0xFFFF0000) ^ (V[0] << 16) ^ (V[1] << 16) + ^ (V[1] & 0xFFFF0000) ^ (V[1] >> 16) ^ (V[2] << 16) + ^ (V[7] & 0xFFFF0000) ^ (V[3] >> 16) ^ (V[4] << 16) + ^ (V[4] >> 16) ^ (V[5] << 16) ^ (V[6] << 16) + ^ (V[6] >> 16) ^ (V[7] << 16) ^ (V[7] >> 16) + ^ V[2] ^ V[3] ^ V[4] + ^ V[6]; + ctx->hash[6] = (V[2] >> 16) ^ (V[3] << 16) ^ (V[4] >> 16) + ^ (V[5] << 16) ^ (V[5] >> 16) ^ (V[6] << 16) + ^ (V[6] >> 16) ^ (V[7] << 16) ^ V[7] + ^ V[0] ^ V[2] ^ V[3] + ^ V[4] ^ V[5] ^ V[6]; + ctx->hash[7] = (V[0] >> 16) ^ (V[1] << 16) ^ (V[1] >> 16) + ^ (V[2] << 16) ^ (V[3] >> 16) ^ (V[4] << 16) + ^ (V[5] >> 16) ^ (V[6] << 16) ^ (V[6] >> 16) + ^ (V[7] << 16) ^ V[7] ^ V[0] + ^ V[3] ^ V[4] ^ V[5]; +} + +PHashGOST3411 * +p_crypto_hash_gost3411_new (void) +{ + PHashGOST3411 *ret; + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PHashGOST3411))) == NULL)) + return NULL; + + p_crypto_hash_gost3411_reset (ret); + + return ret; +} + +void +p_crypto_hash_gost3411_update (PHashGOST3411 *ctx, + const puchar *data, + psize len) +{ + puint32 left, to_fill, len256[8]; + + left = (ctx->len[0] & 0xFF) >> 3; + to_fill = 32 - left; + + memset (len256, 0, 32); + len256[0] = (puint32) (len << 3); + len256[1] = (puint32) (len >> 29); + + pp_crypto_hash_gost3411_sum_256 (ctx->len, len256); + + if (left && (puint32) len >= to_fill) { + memcpy ((pchar *) ctx->buf + left, data, to_fill); + pp_crypto_hash_gost3411_swap_bytes (ctx->buf, 8); + pp_crypto_hash_gost3411_process (ctx, ctx->buf); + pp_crypto_hash_gost3411_sum_256 (ctx->sum, ctx->buf); + + data += to_fill; + len -= to_fill; + left = 0; + } + + while (len >= 32) { + memcpy (ctx->buf, data, 32); + pp_crypto_hash_gost3411_swap_bytes (ctx->buf, 8); + pp_crypto_hash_gost3411_process (ctx, ctx->buf); + pp_crypto_hash_gost3411_sum_256 (ctx->sum, ctx->buf); + + data += 32; + len -= 32; + } + + if (len > 0) + memcpy ((pchar *) ctx->buf + left, data, len); +} + +void +p_crypto_hash_gost3411_finish (PHashGOST3411 *ctx) +{ + puint32 left, last; + + left = ctx->len[0] & 0xFF; + last = 32 - (left >> 3); + + if (last % 32 != 0) { + memset ((pchar *) ctx->buf + (left >> 3), 0, last); + pp_crypto_hash_gost3411_swap_bytes (ctx->buf, 8); + pp_crypto_hash_gost3411_process (ctx, ctx->buf); + pp_crypto_hash_gost3411_sum_256 (ctx->sum, ctx->buf); + } + + pp_crypto_hash_gost3411_process (ctx, ctx->len); + pp_crypto_hash_gost3411_process (ctx, ctx->sum); + + pp_crypto_hash_gost3411_swap_bytes (ctx->hash, 8); +} + +const puchar * +p_crypto_hash_gost3411_digest (PHashGOST3411 *ctx) +{ + return (const puchar *) ctx->hash; +} + +void +p_crypto_hash_gost3411_reset (PHashGOST3411 *ctx) +{ + memset (ctx->buf, 0, 32); + memset (ctx->hash, 0, 32); + memset (ctx->len, 0, 32); + memset (ctx->sum, 0, 32); +} + +void +p_crypto_hash_gost3411_free (PHashGOST3411 *ctx) +{ + p_free (ctx); +} diff --git a/3rdparty/plibsys/src/pcryptohash-gost3411.h b/3rdparty/plibsys/src/pcryptohash-gost3411.h new file mode 100644 index 0000000..dd0f0ad --- /dev/null +++ b/3rdparty/plibsys/src/pcryptohash-gost3411.h @@ -0,0 +1,53 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* GOST R 34.11-94 interface implementation for #PCryptoHash */ + +#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION) +# error "Header files shouldn't be included directly, consider using <plibsys.h> instead." +#endif + +#ifndef PLIBSYS_HEADER_PCRYPTOHASHGOST3411_H +#define PLIBSYS_HEADER_PCRYPTOHASHGOST3411_H + +#include "ptypes.h" +#include "pmacros.h" + +P_BEGIN_DECLS + +typedef struct PHashGOST3411_ PHashGOST3411; + +PHashGOST3411 * p_crypto_hash_gost3411_new (void); +void p_crypto_hash_gost3411_update (PHashGOST3411 *ctx, + const puchar *data, + psize len); +void p_crypto_hash_gost3411_finish (PHashGOST3411 *ctx); +const puchar * p_crypto_hash_gost3411_digest (PHashGOST3411 *ctx); +void p_crypto_hash_gost3411_reset (PHashGOST3411 *ctx); +void p_crypto_hash_gost3411_free (PHashGOST3411 *ctx); + +P_END_DECLS + +#endif /* PLIBSYS_HEADER_PCRYPTOHASHGOST3411_H */ diff --git a/3rdparty/plibsys/src/pcryptohash-md5.c b/3rdparty/plibsys/src/pcryptohash-md5.c new file mode 100644 index 0000000..ee09033 --- /dev/null +++ b/3rdparty/plibsys/src/pcryptohash-md5.c @@ -0,0 +1,273 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pmem.h" +#include "pcryptohash-md5.h" + +#include <string.h> +#include <stdlib.h> + +struct PHashMD5_ { + union buf_ { + puchar buf[64]; + puint32 buf_w[16]; + } buf; + puint32 hash[4]; + + puint32 len_high; + puint32 len_low; +}; + +static const puchar pp_crypto_hash_md5_pad[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static void pp_crypto_hash_md5_swap_bytes (puint32 *data, puint words); +static void pp_crypto_hash_md5_process (PHashMD5 *ctx, const puint32 data[16]); + +#define P_MD5_ROTL(val, shift) ((val) << (shift) | (val) >> (32 - (shift))) + +#define P_MD5_F(x, y, z) (z ^ (x & (y ^ z))) +#define P_MD5_G(x, y, z) P_MD5_F (z, x, y) +#define P_MD5_H(x, y, z) (x ^ y ^ z) +#define P_MD5_I(x, y, z) (y ^ (x | (~z))) + +#define P_MD5_ROUND_0(a, b, c, d, k, i, s) \ + a += P_MD5_F (b, c, d) + data[k] + i, a = P_MD5_ROTL (a, s) + b + +#define P_MD5_ROUND_1(a, b, c, d, k, i, s) \ + a += P_MD5_G (b, c, d) + data[k] + i, a = P_MD5_ROTL (a, s) + b + + +#define P_MD5_ROUND_2(a, b, c, d, k, i, s) \ + a += P_MD5_H (b, c, d) + data[k] + i, a = P_MD5_ROTL (a, s) + b + +#define P_MD5_ROUND_3(a, b, c, d, k, i, s) \ + a += P_MD5_I (b, c, d) + data[k] + i, a = P_MD5_ROTL (a, s) + b + +static void +pp_crypto_hash_md5_swap_bytes (puint32 *data, + puint words) +{ +#ifndef PLIBSYS_IS_BIGENDIAN + P_UNUSED (data); + P_UNUSED (words); +#else + while (words-- > 0) { + *data = PUINT32_TO_LE (*data); + ++data; + } +#endif +} + +static void +pp_crypto_hash_md5_process (PHashMD5 *ctx, + const puint32 data[16]) +{ + puint32 A, B, C, D; + + A = ctx->hash[0]; + B = ctx->hash[1]; + C = ctx->hash[2]; + D = ctx->hash[3]; + + P_MD5_ROUND_0 (A, B, C, D, 0, 0xD76AA478, 7); + P_MD5_ROUND_0 (D, A, B, C, 1, 0xE8C7B756, 12); + P_MD5_ROUND_0 (C, D, A, B, 2, 0x242070DB, 17); + P_MD5_ROUND_0 (B, C, D, A, 3, 0xC1BDCEEE, 22); + P_MD5_ROUND_0 (A, B, C, D, 4, 0xF57C0FAF, 7); + P_MD5_ROUND_0 (D, A, B, C, 5, 0x4787C62A, 12); + P_MD5_ROUND_0 (C, D, A, B, 6, 0xA8304613, 17); + P_MD5_ROUND_0 (B, C, D, A, 7, 0xFD469501, 22); + P_MD5_ROUND_0 (A, B, C, D, 8, 0x698098D8, 7); + P_MD5_ROUND_0 (D, A, B, C, 9, 0x8B44F7AF, 12); + P_MD5_ROUND_0 (C, D, A, B, 10, 0xFFFF5BB1, 17); + P_MD5_ROUND_0 (B, C, D, A, 11, 0x895CD7BE, 22); + P_MD5_ROUND_0 (A, B, C, D, 12, 0x6B901122, 7); + P_MD5_ROUND_0 (D, A, B, C, 13, 0xFD987193, 12); + P_MD5_ROUND_0 (C, D, A, B, 14, 0xA679438E, 17); + P_MD5_ROUND_0 (B, C, D, A, 15, 0x49B40821, 22); + + P_MD5_ROUND_1 (A, B, C, D, 1, 0xF61E2562, 5); + P_MD5_ROUND_1 (D, A, B, C, 6, 0xC040B340, 9); + P_MD5_ROUND_1 (C, D, A, B, 11, 0x265E5A51, 14); + P_MD5_ROUND_1 (B, C, D, A, 0, 0xE9B6C7AA, 20); + P_MD5_ROUND_1 (A, B, C, D, 5, 0xD62F105D, 5); + P_MD5_ROUND_1 (D, A, B, C, 10, 0x02441453, 9); + P_MD5_ROUND_1 (C, D, A, B, 15, 0xD8A1E681, 14); + P_MD5_ROUND_1 (B, C, D, A, 4, 0xE7D3FBC8, 20); + P_MD5_ROUND_1 (A, B, C, D, 9, 0x21E1CDE6, 5); + P_MD5_ROUND_1 (D, A, B, C, 14, 0xC33707D6, 9); + P_MD5_ROUND_1 (C, D, A, B, 3, 0xF4D50D87, 14); + P_MD5_ROUND_1 (B, C, D, A, 8, 0x455A14ED, 20); + P_MD5_ROUND_1 (A, B, C, D, 13, 0xA9E3E905, 5); + P_MD5_ROUND_1 (D, A, B, C, 2, 0xFCEFA3F8, 9); + P_MD5_ROUND_1 (C, D, A, B, 7, 0x676F02D9, 14); + P_MD5_ROUND_1 (B, C, D, A, 12, 0x8D2A4C8A, 20); + + P_MD5_ROUND_2 (A, B, C, D, 5, 0xFFFA3942, 4); + P_MD5_ROUND_2 (D, A, B, C, 8, 0x8771F681, 11); + P_MD5_ROUND_2 (C, D, A, B, 11, 0x6D9D6122, 16); + P_MD5_ROUND_2 (B, C, D, A, 14, 0xFDE5380C, 23); + P_MD5_ROUND_2 (A, B, C, D, 1, 0xA4BEEA44, 4); + P_MD5_ROUND_2 (D, A, B, C, 4, 0x4BDECFA9, 11); + P_MD5_ROUND_2 (C, D, A, B, 7, 0xF6BB4B60, 16); + P_MD5_ROUND_2 (B, C, D, A, 10, 0xBEBFBC70, 23); + P_MD5_ROUND_2 (A, B, C, D, 13, 0x289B7EC6, 4); + P_MD5_ROUND_2 (D, A, B, C, 0, 0xEAA127FA, 11); + P_MD5_ROUND_2 (C, D, A, B, 3, 0xD4EF3085, 16); + P_MD5_ROUND_2 (B, C, D, A, 6, 0x04881D05, 23); + P_MD5_ROUND_2 (A, B, C, D, 9, 0xD9D4D039, 4); + P_MD5_ROUND_2 (D, A, B, C, 12, 0xE6DB99E5, 11); + P_MD5_ROUND_2 (C, D, A, B, 15, 0x1FA27CF8, 16); + P_MD5_ROUND_2 (B, C, D, A, 2, 0xC4AC5665, 23); + + P_MD5_ROUND_3 (A, B, C, D, 0, 0xF4292244, 6); + P_MD5_ROUND_3 (D, A, B, C, 7, 0x432AFF97, 10); + P_MD5_ROUND_3 (C, D, A, B, 14, 0xAB9423A7, 15); + P_MD5_ROUND_3 (B, C, D, A, 5, 0xFC93A039, 21); + P_MD5_ROUND_3 (A, B, C, D, 12, 0x655B59C3, 6); + P_MD5_ROUND_3 (D, A, B, C, 3, 0x8F0CCC92, 10); + P_MD5_ROUND_3 (C, D, A, B, 10, 0xFFEFF47D, 15); + P_MD5_ROUND_3 (B, C, D, A, 1, 0x85845DD1, 21); + P_MD5_ROUND_3 (A, B, C, D, 8, 0x6FA87E4F, 6); + P_MD5_ROUND_3 (D, A, B, C, 15, 0xFE2CE6E0, 10); + P_MD5_ROUND_3 (C, D, A, B, 6, 0xA3014314, 15); + P_MD5_ROUND_3 (B, C, D, A, 13, 0x4E0811A1, 21); + P_MD5_ROUND_3 (A, B, C, D, 4, 0xF7537E82, 6); + P_MD5_ROUND_3 (D, A, B, C, 11, 0xBD3AF235, 10); + P_MD5_ROUND_3 (C, D, A, B, 2, 0x2AD7D2BB, 15); + P_MD5_ROUND_3 (B, C, D, A, 9, 0xEB86D391, 21); + + ctx->hash[0] += A; + ctx->hash[1] += B; + ctx->hash[2] += C; + ctx->hash[3] += D; +} + +void +p_crypto_hash_md5_reset (PHashMD5 *ctx) +{ + memset (ctx->buf.buf, 0, 64); + + ctx->len_low = 0; + ctx->len_high = 0; + + ctx->hash[0] = 0x67452301; + ctx->hash[1] = 0xEFCDAB89; + ctx->hash[2] = 0x98BADCFE; + ctx->hash[3] = 0x10325476; +} + +PHashMD5 * +p_crypto_hash_md5_new (void) +{ + PHashMD5 *ret; + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PHashMD5))) == NULL)) + return NULL; + + p_crypto_hash_md5_reset (ret); + + return ret; +} + +void +p_crypto_hash_md5_update (PHashMD5 *ctx, + const puchar *data, + psize len) +{ + puint32 left, to_fill; + + left = ctx->len_low & 0x3F; + to_fill = 64 - left; + + ctx->len_low += (puint32) len; + + if (ctx->len_low < (puint32) len) + ++ctx->len_high; + + if (left && (puint32) len >= to_fill) { + memcpy (ctx->buf.buf + left, data, to_fill); + pp_crypto_hash_md5_swap_bytes (ctx->buf.buf_w, 16); + pp_crypto_hash_md5_process (ctx, ctx->buf.buf_w); + + data += to_fill; + len -= to_fill; + left = 0; + } + + while (len >= 64) { + memcpy (ctx->buf.buf, data, 64); + pp_crypto_hash_md5_swap_bytes (ctx->buf.buf_w, 16); + pp_crypto_hash_md5_process (ctx, ctx->buf.buf_w); + + data += 64; + len -= 64; + } + + if (len > 0) + memcpy (ctx->buf.buf + left, data, len); +} + +void +p_crypto_hash_md5_finish (PHashMD5 *ctx) +{ + puint32 high, low; + pint left, last; + + left = ctx->len_low & 0x3F; + last = (left < 56) ? (56 - left) : (120 - left); + + low = ctx->len_low << 3; + high = ctx->len_high << 3 + | ctx->len_low >> 29; + + if (last > 0) + p_crypto_hash_md5_update (ctx, pp_crypto_hash_md5_pad, (psize) last); + + ctx->buf.buf_w[14] = low; + ctx->buf.buf_w[15] = high; + + pp_crypto_hash_md5_swap_bytes (ctx->buf.buf_w, 14); + pp_crypto_hash_md5_process (ctx, ctx->buf.buf_w); + + pp_crypto_hash_md5_swap_bytes (ctx->hash, 4); +} + +const puchar * +p_crypto_hash_md5_digest (PHashMD5 *ctx) +{ + return (const puchar *) ctx->hash; +} + +void +p_crypto_hash_md5_free (PHashMD5 *ctx) +{ + p_free (ctx); +} diff --git a/3rdparty/plibsys/src/pcryptohash-md5.h b/3rdparty/plibsys/src/pcryptohash-md5.h new file mode 100644 index 0000000..142eee0 --- /dev/null +++ b/3rdparty/plibsys/src/pcryptohash-md5.h @@ -0,0 +1,51 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* MD5 interface implementation for #PCryptoHash */ + +#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION) +# error "Header files shouldn't be included directly, consider using <plibsys.h> instead." +#endif + +#ifndef PLIBSYS_HEADER_PCRYPTOHASHMD5_H +#define PLIBSYS_HEADER_PCRYPTOHASHMD5_H + +#include "ptypes.h" +#include "pmacros.h" + +P_BEGIN_DECLS + +typedef struct PHashMD5_ PHashMD5; + +PHashMD5 * p_crypto_hash_md5_new (void); +void p_crypto_hash_md5_update (PHashMD5 *ctx, const puchar *data, psize len); +void p_crypto_hash_md5_finish (PHashMD5 *ctx); +const puchar * p_crypto_hash_md5_digest (PHashMD5 *ctx); +void p_crypto_hash_md5_reset (PHashMD5 *ctx); +void p_crypto_hash_md5_free (PHashMD5 *ctx); + +P_END_DECLS + +#endif /* PLIBSYS_HEADER_PCRYPTOHASHMD5_H */ diff --git a/3rdparty/plibsys/src/pcryptohash-sha1.c b/3rdparty/plibsys/src/pcryptohash-sha1.c new file mode 100644 index 0000000..62e826c --- /dev/null +++ b/3rdparty/plibsys/src/pcryptohash-sha1.c @@ -0,0 +1,321 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include <string.h> +#include <stdlib.h> + +#include "pmem.h" +#include "pcryptohash-sha1.h" + +struct PHashSHA1_ { + union buf_ { + puchar buf[64]; + puint32 buf_w[16]; + } buf; + puint32 hash[5]; + + puint32 len_high; + puint32 len_low; +}; + +static const puchar pp_crypto_hash_sha1_pad[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static void pp_crypto_hash_sha1_swap_bytes (puint32 *data, puint words); +static void pp_crypto_hash_sha1_process (PHashSHA1 *ctx, const puint32 data[16]); + +#define P_SHA1_ROTL(val, shift) ((val) << (shift) | (val) >> (32 - (shift))) + +#define P_SHA1_F1(x, y, z) ((x & y) | ((~x) & z)) +#define P_SHA1_F2(x, y, z) (x ^ y ^ z) +#define P_SHA1_F3(x, y, z) ((x & y) | (x & z) | (y & z)) + +#define P_SHA1_W(W, i) \ +( \ + (W)[i & 0x0F] = P_SHA1_ROTL ( \ + (W)[(i - 3) & 0x0F] \ + ^ (W)[(i - 8) & 0x0F] \ + ^ (W)[(i - 14) & 0x0F] \ + ^ (W)[(i - 16) & 0x0F], \ + 1) \ +) + +#define P_SHA1_ROUND_0(a, b, c, d, e, w) \ +{ \ + e += P_SHA1_ROTL (a, 5) + P_SHA1_F1 (b, c, d) \ + + 0x5A827999 + w; \ + b = P_SHA1_ROTL (b, 30); \ +} + +#define P_SHA1_ROUND_1(a, b, c, d, e, w) \ +{ \ + e += P_SHA1_ROTL (a, 5) + P_SHA1_F2 (b, c, d) \ + + 0x6ED9EBA1 + w; \ + b = P_SHA1_ROTL (b, 30); \ +} + +#define P_SHA1_ROUND_2(a, b, c, d, e, w) \ +{ \ + e += P_SHA1_ROTL (a, 5) + P_SHA1_F3 (b, c, d) \ + + 0x8F1BBCDC + w; \ + b = P_SHA1_ROTL (b, 30); \ +} + +#define P_SHA1_ROUND_3(a, b, c, d, e, w) \ +{ \ + e += P_SHA1_ROTL (a, 5) + P_SHA1_F2 (b, c, d) \ + + 0xCA62C1D6 + w; \ + b = P_SHA1_ROTL (b, 30); \ +} + +static void +pp_crypto_hash_sha1_swap_bytes (puint32 *data, + puint words) +{ +#ifdef PLIBSYS_IS_BIGENDIAN + P_UNUSED (data); + P_UNUSED (words); +#else + while (words-- > 0) { + *data = PUINT32_TO_BE (*data); + ++data; + } +#endif +} + +static void +pp_crypto_hash_sha1_process (PHashSHA1 *ctx, + const puint32 data[16]) +{ + puint32 W[16], A, B, C, D, E; + + if (P_UNLIKELY (ctx == NULL)) + return; + + memcpy (W, data, 64); + + A = ctx->hash[0]; + B = ctx->hash[1]; + C = ctx->hash[2]; + D = ctx->hash[3]; + E = ctx->hash[4]; + + P_SHA1_ROUND_0 (A, B, C, D, E, W[0]); + P_SHA1_ROUND_0 (E, A, B, C, D, W[1]); + P_SHA1_ROUND_0 (D, E, A, B, C, W[2]); + P_SHA1_ROUND_0 (C, D, E, A, B, W[3]); + P_SHA1_ROUND_0 (B, C, D, E, A, W[4]); + P_SHA1_ROUND_0 (A, B, C, D, E, W[5]); + P_SHA1_ROUND_0 (E, A, B, C, D, W[6]); + P_SHA1_ROUND_0 (D, E, A, B, C, W[7]); + P_SHA1_ROUND_0 (C, D, E, A, B, W[8]); + P_SHA1_ROUND_0 (B, C, D, E, A, W[9]); + P_SHA1_ROUND_0 (A, B, C, D, E, W[10]); + P_SHA1_ROUND_0 (E, A, B, C, D, W[11]); + P_SHA1_ROUND_0 (D, E, A, B, C, W[12]); + P_SHA1_ROUND_0 (C, D, E, A, B, W[13]); + P_SHA1_ROUND_0 (B, C, D, E, A, W[14]); + P_SHA1_ROUND_0 (A, B, C, D, E, W[15]); + P_SHA1_ROUND_0 (E, A, B, C, D, P_SHA1_W (W, 16)); + P_SHA1_ROUND_0 (D, E, A, B, C, P_SHA1_W (W, 17)); + P_SHA1_ROUND_0 (C, D, E, A, B, P_SHA1_W (W, 18)); + P_SHA1_ROUND_0 (B, C, D, E, A, P_SHA1_W (W, 19)); + + P_SHA1_ROUND_1 (A, B, C, D, E, P_SHA1_W (W, 20)); + P_SHA1_ROUND_1 (E, A, B, C, D, P_SHA1_W (W, 21)); + P_SHA1_ROUND_1 (D, E, A, B, C, P_SHA1_W (W, 22)); + P_SHA1_ROUND_1 (C, D, E, A, B, P_SHA1_W (W, 23)); + P_SHA1_ROUND_1 (B, C, D, E, A, P_SHA1_W (W, 24)); + P_SHA1_ROUND_1 (A, B, C, D, E, P_SHA1_W (W, 25)); + P_SHA1_ROUND_1 (E, A, B, C, D, P_SHA1_W (W, 26)); + P_SHA1_ROUND_1 (D, E, A, B, C, P_SHA1_W (W, 27)); + P_SHA1_ROUND_1 (C, D, E, A, B, P_SHA1_W (W, 28)); + P_SHA1_ROUND_1 (B, C, D, E, A, P_SHA1_W (W, 29)); + P_SHA1_ROUND_1 (A, B, C, D, E, P_SHA1_W (W, 30)); + P_SHA1_ROUND_1 (E, A, B, C, D, P_SHA1_W (W, 31)); + P_SHA1_ROUND_1 (D, E, A, B, C, P_SHA1_W (W, 32)); + P_SHA1_ROUND_1 (C, D, E, A, B, P_SHA1_W (W, 33)); + P_SHA1_ROUND_1 (B, C, D, E, A, P_SHA1_W (W, 34)); + P_SHA1_ROUND_1 (A, B, C, D, E, P_SHA1_W (W, 35)); + P_SHA1_ROUND_1 (E, A, B, C, D, P_SHA1_W (W, 36)); + P_SHA1_ROUND_1 (D, E, A, B, C, P_SHA1_W (W, 37)); + P_SHA1_ROUND_1 (C, D, E, A, B, P_SHA1_W (W, 38)); + P_SHA1_ROUND_1 (B, C, D, E, A, P_SHA1_W (W, 39)); + + P_SHA1_ROUND_2 (A, B, C, D, E, P_SHA1_W (W, 40)); + P_SHA1_ROUND_2 (E, A, B, C, D, P_SHA1_W (W, 41)); + P_SHA1_ROUND_2 (D, E, A, B, C, P_SHA1_W (W, 42)); + P_SHA1_ROUND_2 (C, D, E, A, B, P_SHA1_W (W, 43)); + P_SHA1_ROUND_2 (B, C, D, E, A, P_SHA1_W (W, 44)); + P_SHA1_ROUND_2 (A, B, C, D, E, P_SHA1_W (W, 45)); + P_SHA1_ROUND_2 (E, A, B, C, D, P_SHA1_W (W, 46)); + P_SHA1_ROUND_2 (D, E, A, B, C, P_SHA1_W (W, 47)); + P_SHA1_ROUND_2 (C, D, E, A, B, P_SHA1_W (W, 48)); + P_SHA1_ROUND_2 (B, C, D, E, A, P_SHA1_W (W, 49)); + P_SHA1_ROUND_2 (A, B, C, D, E, P_SHA1_W (W, 50)); + P_SHA1_ROUND_2 (E, A, B, C, D, P_SHA1_W (W, 51)); + P_SHA1_ROUND_2 (D, E, A, B, C, P_SHA1_W (W, 52)); + P_SHA1_ROUND_2 (C, D, E, A, B, P_SHA1_W (W, 53)); + P_SHA1_ROUND_2 (B, C, D, E, A, P_SHA1_W (W, 54)); + P_SHA1_ROUND_2 (A, B, C, D, E, P_SHA1_W (W, 55)); + P_SHA1_ROUND_2 (E, A, B, C, D, P_SHA1_W (W, 56)); + P_SHA1_ROUND_2 (D, E, A, B, C, P_SHA1_W (W, 57)); + P_SHA1_ROUND_2 (C, D, E, A, B, P_SHA1_W (W, 58)); + P_SHA1_ROUND_2 (B, C, D, E, A, P_SHA1_W (W, 59)); + + P_SHA1_ROUND_3 (A, B, C, D, E, P_SHA1_W (W, 60)); + P_SHA1_ROUND_3 (E, A, B, C, D, P_SHA1_W (W, 61)); + P_SHA1_ROUND_3 (D, E, A, B, C, P_SHA1_W (W, 62)); + P_SHA1_ROUND_3 (C, D, E, A, B, P_SHA1_W (W, 63)); + P_SHA1_ROUND_3 (B, C, D, E, A, P_SHA1_W (W, 64)); + P_SHA1_ROUND_3 (A, B, C, D, E, P_SHA1_W (W, 65)); + P_SHA1_ROUND_3 (E, A, B, C, D, P_SHA1_W (W, 66)); + P_SHA1_ROUND_3 (D, E, A, B, C, P_SHA1_W (W, 67)); + P_SHA1_ROUND_3 (C, D, E, A, B, P_SHA1_W (W, 68)); + P_SHA1_ROUND_3 (B, C, D, E, A, P_SHA1_W (W, 69)); + P_SHA1_ROUND_3 (A, B, C, D, E, P_SHA1_W (W, 70)); + P_SHA1_ROUND_3 (E, A, B, C, D, P_SHA1_W (W, 71)); + P_SHA1_ROUND_3 (D, E, A, B, C, P_SHA1_W (W, 72)); + P_SHA1_ROUND_3 (C, D, E, A, B, P_SHA1_W (W, 73)); + P_SHA1_ROUND_3 (B, C, D, E, A, P_SHA1_W (W, 74)); + P_SHA1_ROUND_3 (A, B, C, D, E, P_SHA1_W (W, 75)); + P_SHA1_ROUND_3 (E, A, B, C, D, P_SHA1_W (W, 76)); + P_SHA1_ROUND_3 (D, E, A, B, C, P_SHA1_W (W, 77)); + P_SHA1_ROUND_3 (C, D, E, A, B, P_SHA1_W (W, 78)); + P_SHA1_ROUND_3 (B, C, D, E, A, P_SHA1_W (W, 79)); + + ctx->hash[0] += A; + ctx->hash[1] += B; + ctx->hash[2] += C; + ctx->hash[3] += D; + ctx->hash[4] += E; +} + +void +p_crypto_hash_sha1_reset (PHashSHA1 *ctx) +{ + memset (ctx->buf.buf, 0, 64); + + ctx->len_low = 0; + ctx->len_high = 0; + + ctx->hash[0] = 0x67452301; + ctx->hash[1] = 0xEFCDAB89; + ctx->hash[2] = 0x98BADCFE; + ctx->hash[3] = 0x10325476; + ctx->hash[4] = 0xC3D2E1F0; +} + +PHashSHA1 * +p_crypto_hash_sha1_new (void) +{ + PHashSHA1 *ret; + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PHashSHA1))) == NULL)) + return NULL; + + p_crypto_hash_sha1_reset (ret); + + return ret; +} + +void +p_crypto_hash_sha1_update (PHashSHA1 *ctx, + const puchar *data, + psize len) +{ + puint32 left, to_fill; + + left = ctx->len_low & 0x3F; + to_fill = 64 - left; + + ctx->len_low += (puint32) len; + + if (ctx->len_low < (puint32) len) + ++ctx->len_high; + + if (left && (puint32) len >= to_fill) { + memcpy (ctx->buf.buf + left, data, to_fill); + pp_crypto_hash_sha1_swap_bytes (ctx->buf.buf_w, 16); + pp_crypto_hash_sha1_process (ctx, ctx->buf.buf_w); + + data += to_fill; + len -= to_fill; + left = 0; + } + + while (len >= 64) { + memcpy (ctx->buf.buf, data, 64); + pp_crypto_hash_sha1_swap_bytes (ctx->buf.buf_w, 16); + pp_crypto_hash_sha1_process (ctx, ctx->buf.buf_w); + + data += 64; + len -= 64; + } + + if (len > 0) + memcpy (ctx->buf.buf + left, data, len); +} + +void +p_crypto_hash_sha1_finish (PHashSHA1 *ctx) +{ + puint32 high, low; + pint left, last; + + left = ctx->len_low & 0x3F; + last = (left < 56) ? (56 - left) : (120 - left); + + low = ctx->len_low << 3; + high = ctx->len_high << 3 + | ctx->len_low >> 29; + + if (last > 0) + p_crypto_hash_sha1_update (ctx, pp_crypto_hash_sha1_pad, (psize) last); + + ctx->buf.buf_w[14] = high; + ctx->buf.buf_w[15] = low; + + pp_crypto_hash_sha1_swap_bytes (ctx->buf.buf_w, 14); + pp_crypto_hash_sha1_process (ctx, ctx->buf.buf_w); + + pp_crypto_hash_sha1_swap_bytes (ctx->hash, 5); +} + +const puchar * +p_crypto_hash_sha1_digest (PHashSHA1 *ctx) +{ + return (const puchar *) ctx->hash; +} + +void +p_crypto_hash_sha1_free (PHashSHA1 *ctx) +{ + p_free (ctx); +} diff --git a/3rdparty/plibsys/src/pcryptohash-sha1.h b/3rdparty/plibsys/src/pcryptohash-sha1.h new file mode 100644 index 0000000..cecbc0d --- /dev/null +++ b/3rdparty/plibsys/src/pcryptohash-sha1.h @@ -0,0 +1,51 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* SHA1 interface implementation for #PCryptoHash */ + +#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION) +# error "Header files shouldn't be included directly, consider using <plibsys.h> instead." +#endif + +#ifndef PLIBSYS_HEADER_PCRYPTOHASHSHA1_H +#define PLIBSYS_HEADER_PCRYPTOHASHSHA1_H + +#include "ptypes.h" +#include "pmacros.h" + +P_BEGIN_DECLS + +typedef struct PHashSHA1_ PHashSHA1; + +PHashSHA1 * p_crypto_hash_sha1_new (void); +void p_crypto_hash_sha1_update (PHashSHA1 *ctx, const puchar *data, psize len); +void p_crypto_hash_sha1_finish (PHashSHA1 *ctx); +const puchar * p_crypto_hash_sha1_digest (PHashSHA1 *ctx); +void p_crypto_hash_sha1_reset (PHashSHA1 *ctx); +void p_crypto_hash_sha1_free (PHashSHA1 *ctx); + +P_END_DECLS + +#endif /* PLIBSYS_HEADER_PCRYPTOHASHSHA1_H */ diff --git a/3rdparty/plibsys/src/pcryptohash-sha2-256.c b/3rdparty/plibsys/src/pcryptohash-sha2-256.c new file mode 100644 index 0000000..29bb0ba --- /dev/null +++ b/3rdparty/plibsys/src/pcryptohash-sha2-256.c @@ -0,0 +1,286 @@ +/* + * The MIT License + * + * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include <string.h> +#include <stdlib.h> + +#include "pmem.h" +#include "pcryptohash-sha2-256.h" + +struct PHashSHA2_256_ { + union buf_ { + puchar buf[64]; + puint32 buf_w[16]; + } buf; + puint32 hash[8]; + + puint32 len_high; + puint32 len_low; + + pboolean is224; +}; + +static const puchar pp_crypto_hash_sha2_256_pad[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static const puint32 pp_crypto_hash_sha2_256_K[] = { + 0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, + 0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5, + 0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3, + 0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174, + 0xE49B69C1, 0xEFBE4786, 0x0FC19DC6, 0x240CA1CC, + 0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA, + 0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7, + 0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967, + 0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13, + 0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85, + 0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3, + 0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070, + 0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5, + 0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3, + 0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208, + 0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2 +}; + +static void pp_crypto_hash_sha2_256_swap_bytes (puint32 *data, puint words); +static void pp_crypto_hash_sha2_256_process (PHashSHA2_256 *ctx, const puint32 data[16]); +static PHashSHA2_256 * pp_crypto_hash_sha2_256_new_internal (pboolean is224); + +#define P_SHA2_256_SHR(val, shift) (((val) & 0xFFFFFFFF) >> (shift)) +#define P_SHA2_256_ROTR(val, shift) (P_SHA2_256_SHR(val, shift) | ((val) << (32 - (shift)))) + +#define P_SHA2_256_S0(x) (P_SHA2_256_ROTR (x, 7) ^ P_SHA2_256_ROTR (x, 18) ^ P_SHA2_256_SHR (x, 3)) +#define P_SHA2_256_S1(x) (P_SHA2_256_ROTR (x, 17) ^ P_SHA2_256_ROTR (x, 19) ^ P_SHA2_256_SHR (x, 10)) +#define P_SHA2_256_S2(x) (P_SHA2_256_ROTR (x, 2) ^ P_SHA2_256_ROTR (x, 13) ^ P_SHA2_256_ROTR (x, 22)) +#define P_SHA2_256_S3(x) (P_SHA2_256_ROTR (x, 6) ^ P_SHA2_256_ROTR (x, 11) ^ P_SHA2_256_ROTR (x, 25)) + +#define P_SHA2_256_F0(x, y, z) ((x & y) | (z & (x | y))) +#define P_SHA2_256_F1(x, y, z) (z ^ (x & (y ^ z))) + +#define P_SHA2_256_R(t) \ +( \ + W[t] = P_SHA2_256_S1 (W[t - 2]) + W[t - 7] + \ + P_SHA2_256_S0 (W[t - 15]) + W[t - 16] \ +) + +#define P_SHA2_256_P(a, b, c, d, e, f, g, h, x, K) \ +{ \ + tmp_sum1 = h + P_SHA2_256_S3 (e) + P_SHA2_256_F1 (e, f, g) + K + x; \ + tmp_sum2 = P_SHA2_256_S2 (a) + P_SHA2_256_F0 (a, b, c); \ + d += tmp_sum1; \ + h = tmp_sum1 + tmp_sum2; \ +} + +static void +pp_crypto_hash_sha2_256_swap_bytes (puint32 *data, + puint words) +{ +#ifdef PLIBSYS_IS_BIGENDIAN + P_UNUSED (data); + P_UNUSED (words); +#else + while (words-- > 0) { + *data = PUINT32_TO_BE (*data); + ++data; + } +#endif +} + +static void +pp_crypto_hash_sha2_256_process (PHashSHA2_256 *ctx, + const puint32 data[16]) +{ + puint32 tmp_sum1, tmp_sum2; + puint32 W[64]; + puint32 A[8]; + puint i; + + for (i = 0; i < 8; i++) + A[i] = ctx->hash[i]; + + memcpy (W, data, 64); + + for (i = 0; i < 16; i += 8) { + P_SHA2_256_P (A[0], A[1], A[2], A[3], A[4], A[5], A[6], A[7], W[i + 0], pp_crypto_hash_sha2_256_K[i + 0]); + P_SHA2_256_P (A[7], A[0], A[1], A[2], A[3], A[4], A[5], A[6], W[i + 1], pp_crypto_hash_sha2_256_K[i + 1]); + P_SHA2_256_P (A[6], A[7], A[0], A[1], A[2], A[3], A[4], A[5], W[i + 2], pp_crypto_hash_sha2_256_K[i + 2]); + P_SHA2_256_P (A[5], A[6], A[7], A[0], A[1], A[2], A[3], A[4], W[i + 3], pp_crypto_hash_sha2_256_K[i + 3]); + P_SHA2_256_P (A[4], A[5], A[6], A[7], A[0], A[1], A[2], A[3], W[i + 4], pp_crypto_hash_sha2_256_K[i + 4]); + P_SHA2_256_P (A[3], A[4], A[5], A[6], A[7], A[0], A[1], A[2], W[i + 5], pp_crypto_hash_sha2_256_K[i + 5]); + P_SHA2_256_P (A[2], A[3], A[4], A[5], A[6], A[7], A[0], A[1], W[i + 6], pp_crypto_hash_sha2_256_K[i + 6]); + P_SHA2_256_P (A[1], A[2], A[3], A[4], A[5], A[6], A[7], A[0], W[i + 7], pp_crypto_hash_sha2_256_K[i + 7]); + } + + for (i = 16; i < 64; i += 8) { + P_SHA2_256_P (A[0], A[1], A[2], A[3], A[4], A[5], A[6], A[7], P_SHA2_256_R (i + 0), pp_crypto_hash_sha2_256_K[i + 0]); + P_SHA2_256_P (A[7], A[0], A[1], A[2], A[3], A[4], A[5], A[6], P_SHA2_256_R (i + 1), pp_crypto_hash_sha2_256_K[i + 1]); + P_SHA2_256_P (A[6], A[7], A[0], A[1], A[2], A[3], A[4], A[5], P_SHA2_256_R (i + 2), pp_crypto_hash_sha2_256_K[i + 2]); + P_SHA2_256_P (A[5], A[6], A[7], A[0], A[1], A[2], A[3], A[4], P_SHA2_256_R (i + 3), pp_crypto_hash_sha2_256_K[i + 3]); + P_SHA2_256_P (A[4], A[5], A[6], A[7], A[0], A[1], A[2], A[3], P_SHA2_256_R (i + 4), pp_crypto_hash_sha2_256_K[i + 4]); + P_SHA2_256_P (A[3], A[4], A[5], A[6], A[7], A[0], A[1], A[2], P_SHA2_256_R (i + 5), pp_crypto_hash_sha2_256_K[i + 5]); + P_SHA2_256_P (A[2], A[3], A[4], A[5], A[6], A[7], A[0], A[1], P_SHA2_256_R (i + 6), pp_crypto_hash_sha2_256_K[i + 6]); + P_SHA2_256_P (A[1], A[2], A[3], A[4], A[5], A[6], A[7], A[0], P_SHA2_256_R (i + 7), pp_crypto_hash_sha2_256_K[i + 7]); + } + + for (i = 0; i < 8; i++) + ctx->hash[i] += A[i]; +} + +static PHashSHA2_256 * +pp_crypto_hash_sha2_256_new_internal (pboolean is224) +{ + PHashSHA2_256 *ret; + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PHashSHA2_256))) == NULL)) + return NULL; + + ret->is224 = is224; + + p_crypto_hash_sha2_256_reset (ret); + + return ret; +} + +void +p_crypto_hash_sha2_256_reset (PHashSHA2_256 *ctx) +{ + memset (ctx->buf.buf, 0, 64); + + ctx->len_low = 0; + ctx->len_high = 0; + + if (ctx->is224 == FALSE) { + /* SHA2-256 */ + ctx->hash[0] = 0x6A09E667; + ctx->hash[1] = 0xBB67AE85; + ctx->hash[2] = 0x3C6EF372; + ctx->hash[3] = 0xA54FF53A; + ctx->hash[4] = 0x510E527F; + ctx->hash[5] = 0x9B05688C; + ctx->hash[6] = 0x1F83D9AB; + ctx->hash[7] = 0x5BE0CD19; + } else { + /* SHA2-224 */ + ctx->hash[0] = 0xC1059ED8; + ctx->hash[1] = 0x367CD507; + ctx->hash[2] = 0x3070DD17; + ctx->hash[3] = 0xF70E5939; + ctx->hash[4] = 0xFFC00B31; + ctx->hash[5] = 0x68581511; + ctx->hash[6] = 0x64F98FA7; + ctx->hash[7] = 0xBEFA4FA4; + } +} + +PHashSHA2_256 * +p_crypto_hash_sha2_256_new (void) +{ + return pp_crypto_hash_sha2_256_new_internal (FALSE); +} + +PHashSHA2_256 * +p_crypto_hash_sha2_224_new (void) +{ + return pp_crypto_hash_sha2_256_new_internal (TRUE); +} + +void +p_crypto_hash_sha2_256_update (PHashSHA2_256 *ctx, + const puchar *data, + psize len) +{ + puint32 left, to_fill; + + left = ctx->len_low & 0x3F; + to_fill = 64 - left; + + ctx->len_low += (puint32) len; + + if (ctx->len_low < (puint32) len) + ++ctx->len_high; + + if (left && (puint32) len >= to_fill) { + memcpy (ctx->buf.buf + left, data, to_fill); + pp_crypto_hash_sha2_256_swap_bytes (ctx->buf.buf_w, 16); + pp_crypto_hash_sha2_256_process (ctx, ctx->buf.buf_w); + + data += to_fill; + len -= to_fill; + left = 0; + } + + while (len >= 64) { + memcpy (ctx->buf.buf, data, 64); + pp_crypto_hash_sha2_256_swap_bytes (ctx->buf.buf_w, 16); + pp_crypto_hash_sha2_256_process (ctx, ctx->buf.buf_w); + + data += 64; + len -= 64; + } + + if (len > 0) + memcpy (ctx->buf.buf + left, data, len); +} + +void +p_crypto_hash_sha2_256_finish (PHashSHA2_256 *ctx) +{ + puint32 high, low; + pint left, last; + + left = ctx->len_low & 0x3F; + last = (left < 56) ? (56 - left) : (120 - left); + + low = ctx->len_low << 3; + high = ctx->len_high << 3 + | ctx->len_low >> 29; + + if (last > 0) + p_crypto_hash_sha2_256_update (ctx, pp_crypto_hash_sha2_256_pad, (psize) last); + + ctx->buf.buf_w[14] = high; + ctx->buf.buf_w[15] = low; + + pp_crypto_hash_sha2_256_swap_bytes (ctx->buf.buf_w, 14); + pp_crypto_hash_sha2_256_process (ctx, ctx->buf.buf_w); + + pp_crypto_hash_sha2_256_swap_bytes (ctx->hash, ctx->is224 == FALSE ? 8 : 7); +} + +const puchar * +p_crypto_hash_sha2_256_digest (PHashSHA2_256 *ctx) +{ + return (const puchar *) ctx->hash; +} + +void +p_crypto_hash_sha2_256_free (PHashSHA2_256 *ctx) +{ + p_free (ctx); +} diff --git a/3rdparty/plibsys/src/pcryptohash-sha2-256.h b/3rdparty/plibsys/src/pcryptohash-sha2-256.h new file mode 100644 index 0000000..0255011 --- /dev/null +++ b/3rdparty/plibsys/src/pcryptohash-sha2-256.h @@ -0,0 +1,59 @@ +/* + * The MIT License + * + * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* SHA2-256 interface implementation for #PCryptoHash */ + +#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION) +# error "Header files shouldn't be included directly, consider using <plibsys.h> instead." +#endif + +#ifndef PLIBSYS_HEADER_PCRYPTOHASHSHA2_256_H +#define PLIBSYS_HEADER_PCRYPTOHASHSHA2_256_H + +#include "ptypes.h" +#include "pmacros.h" + +P_BEGIN_DECLS + +typedef struct PHashSHA2_256_ PHashSHA2_256; + +PHashSHA2_256 * p_crypto_hash_sha2_256_new (void); +void p_crypto_hash_sha2_256_update (PHashSHA2_256 *ctx, const puchar *data, psize len); +void p_crypto_hash_sha2_256_finish (PHashSHA2_256 *ctx); +const puchar * p_crypto_hash_sha2_256_digest (PHashSHA2_256 *ctx); +void p_crypto_hash_sha2_256_reset (PHashSHA2_256 *ctx); +void p_crypto_hash_sha2_256_free (PHashSHA2_256 *ctx); + +PHashSHA2_256 * p_crypto_hash_sha2_224_new (void); + +#define p_crypto_hash_sha2_224_update p_crypto_hash_sha2_256_update +#define p_crypto_hash_sha2_224_finish p_crypto_hash_sha2_256_finish +#define p_crypto_hash_sha2_224_digest p_crypto_hash_sha2_256_digest +#define p_crypto_hash_sha2_224_reset p_crypto_hash_sha2_256_reset +#define p_crypto_hash_sha2_224_free p_crypto_hash_sha2_256_free + +P_END_DECLS + +#endif /* PLIBSYS_HEADER_PCRYPTOHASHSHA2_256_H */ diff --git a/3rdparty/plibsys/src/pcryptohash-sha2-512.c b/3rdparty/plibsys/src/pcryptohash-sha2-512.c new file mode 100644 index 0000000..7864da3 --- /dev/null +++ b/3rdparty/plibsys/src/pcryptohash-sha2-512.c @@ -0,0 +1,300 @@ +/* + * The MIT License + * + * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include <string.h> +#include <stdlib.h> + +#include "pmem.h" +#include "pcryptohash-sha2-512.h" + +struct PHashSHA2_512_ { + union buf_ { + puchar buf[128]; + puint64 buf_w[16]; + } buf; + puint64 hash[8]; + + puint64 len_high; + puint64 len_low; + + pboolean is384; +}; + +static const puchar pp_crypto_hash_sha2_512_pad[128] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static const puint64 pp_crypto_hash_sha2_512_K[] = { + 0x428A2F98D728AE22ULL, 0x7137449123EF65CDULL, + 0xB5C0FBCFEC4D3B2FULL, 0xE9B5DBA58189DBBCULL, + 0x3956C25BF348B538ULL, 0x59F111F1B605D019ULL, + 0x923F82A4AF194F9BULL, 0xAB1C5ED5DA6D8118ULL, + 0xD807AA98A3030242ULL, 0x12835B0145706FBEULL, + 0x243185BE4EE4B28CULL, 0x550C7DC3D5FFB4E2ULL, + 0x72BE5D74F27B896FULL, 0x80DEB1FE3B1696B1ULL, + 0x9BDC06A725C71235ULL, 0xC19BF174CF692694ULL, + 0xE49B69C19EF14AD2ULL, 0xEFBE4786384F25E3ULL, + 0x0FC19DC68B8CD5B5ULL, 0x240CA1CC77AC9C65ULL, + 0x2DE92C6F592B0275ULL, 0x4A7484AA6EA6E483ULL, + 0x5CB0A9DCBD41FBD4ULL, 0x76F988DA831153B5ULL, + 0x983E5152EE66DFABULL, 0xA831C66D2DB43210ULL, + 0xB00327C898FB213FULL, 0xBF597FC7BEEF0EE4ULL, + 0xC6E00BF33DA88FC2ULL, 0xD5A79147930AA725ULL, + 0x06CA6351E003826FULL, 0x142929670A0E6E70ULL, + 0x27B70A8546D22FFCULL, 0x2E1B21385C26C926ULL, + 0x4D2C6DFC5AC42AEDULL, 0x53380D139D95B3DFULL, + 0x650A73548BAF63DEULL, 0x766A0ABB3C77B2A8ULL, + 0x81C2C92E47EDAEE6ULL, 0x92722C851482353BULL, + 0xA2BFE8A14CF10364ULL, 0xA81A664BBC423001ULL, + 0xC24B8B70D0F89791ULL, 0xC76C51A30654BE30ULL, + 0xD192E819D6EF5218ULL, 0xD69906245565A910ULL, + 0xF40E35855771202AULL, 0x106AA07032BBD1B8ULL, + 0x19A4C116B8D2D0C8ULL, 0x1E376C085141AB53ULL, + 0x2748774CDF8EEB99ULL, 0x34B0BCB5E19B48A8ULL, + 0x391C0CB3C5C95A63ULL, 0x4ED8AA4AE3418ACBULL, + 0x5B9CCA4F7763E373ULL, 0x682E6FF3D6B2B8A3ULL, + 0x748F82EE5DEFB2FCULL, 0x78A5636F43172F60ULL, + 0x84C87814A1F0AB72ULL, 0x8CC702081A6439ECULL, + 0x90BEFFFA23631E28ULL, 0xA4506CEBDE82BDE9ULL, + 0xBEF9A3F7B2C67915ULL, 0xC67178F2E372532BULL, + 0xCA273ECEEA26619CULL, 0xD186B8C721C0C207ULL, + 0xEADA7DD6CDE0EB1EULL, 0xF57D4F7FEE6ED178ULL, + 0x06F067AA72176FBAULL, 0x0A637DC5A2C898A6ULL, + 0x113F9804BEF90DAEULL, 0x1B710B35131C471BULL, + 0x28DB77F523047D84ULL, 0x32CAAB7B40C72493ULL, + 0x3C9EBE0A15C9BEBCULL, 0x431D67C49C100D4CULL, + 0x4CC5D4BECB3E42B6ULL, 0x597F299CFC657E2AULL, + 0x5FCB6FAB3AD6FAECULL, 0x6C44198C4A475817ULL +}; + +static void pp_crypto_hash_sha2_512_swap_bytes (puint64 *data, puint words); +static void pp_crypto_hash_sha2_512_process (PHashSHA2_512 *ctx, const puint64 data[16]); +static PHashSHA2_512 * pp_crypto_hash_sha2_512_new_internal (pboolean is384); + +#define P_SHA2_512_SHR(val, shift) ((val) >> (shift)) +#define P_SHA2_512_ROTR(val, shift) (P_SHA2_512_SHR(val, shift) | ((val) << (64 - (shift)))) + +#define P_SHA2_512_S0(x) (P_SHA2_512_ROTR (x, 1) ^ P_SHA2_512_ROTR (x, 8) ^ P_SHA2_512_SHR (x, 7)) +#define P_SHA2_512_S1(x) (P_SHA2_512_ROTR (x, 19) ^ P_SHA2_512_ROTR (x, 61) ^ P_SHA2_512_SHR (x, 6)) +#define P_SHA2_512_S2(x) (P_SHA2_512_ROTR (x, 28) ^ P_SHA2_512_ROTR (x, 34) ^ P_SHA2_512_ROTR (x, 39)) +#define P_SHA2_512_S3(x) (P_SHA2_512_ROTR (x, 14) ^ P_SHA2_512_ROTR (x, 18) ^ P_SHA2_512_ROTR (x, 41)) + +#define P_SHA2_512_F0(x, y, z) ((x & y) | (z & (x | y))) +#define P_SHA2_512_F1(x, y, z) (z ^ (x & (y ^ z))) + +#define P_SHA2_512_P(a, b, c, d, e, f, g, h, x, K) \ +{ \ + tmp_sum1 = h + P_SHA2_512_S3 (e) + P_SHA2_512_F1 (e, f, g) + K + x; \ + tmp_sum2 = P_SHA2_512_S2 (a) + P_SHA2_512_F0 (a, b, c); \ + d += tmp_sum1; \ + h = tmp_sum1 + tmp_sum2; \ +} + +static void +pp_crypto_hash_sha2_512_swap_bytes (puint64 *data, + puint words) +{ +#ifdef PLIBSYS_IS_BIGENDIAN + P_UNUSED (data); + P_UNUSED (words); +#else + while (words-- > 0) { + *data = PUINT64_TO_BE (*data); + ++data; + } +#endif +} + +static void +pp_crypto_hash_sha2_512_process (PHashSHA2_512 *ctx, + const puint64 data[16]) +{ + puint64 tmp_sum1, tmp_sum2; + puint64 W[80]; + puint64 A[8]; + puint i; + + for (i = 0; i < 8; ++i) + A[i] = ctx->hash[i]; + + memcpy (W, data, 128); + + for (i = 16; i < 80; ++i) + W[i] = P_SHA2_512_S1 (W[i - 2]) + W[i - 7] + P_SHA2_512_S0 (W[i - 15]) + W[i - 16]; + + for (i = 0; i < 80; i += 8) { + P_SHA2_512_P (A[0], A[1], A[2], A[3], A[4], A[5], A[6], A[7], W[i + 0], pp_crypto_hash_sha2_512_K[i + 0]); + P_SHA2_512_P (A[7], A[0], A[1], A[2], A[3], A[4], A[5], A[6], W[i + 1], pp_crypto_hash_sha2_512_K[i + 1]); + P_SHA2_512_P (A[6], A[7], A[0], A[1], A[2], A[3], A[4], A[5], W[i + 2], pp_crypto_hash_sha2_512_K[i + 2]); + P_SHA2_512_P (A[5], A[6], A[7], A[0], A[1], A[2], A[3], A[4], W[i + 3], pp_crypto_hash_sha2_512_K[i + 3]); + P_SHA2_512_P (A[4], A[5], A[6], A[7], A[0], A[1], A[2], A[3], W[i + 4], pp_crypto_hash_sha2_512_K[i + 4]); + P_SHA2_512_P (A[3], A[4], A[5], A[6], A[7], A[0], A[1], A[2], W[i + 5], pp_crypto_hash_sha2_512_K[i + 5]); + P_SHA2_512_P (A[2], A[3], A[4], A[5], A[6], A[7], A[0], A[1], W[i + 6], pp_crypto_hash_sha2_512_K[i + 6]); + P_SHA2_512_P (A[1], A[2], A[3], A[4], A[5], A[6], A[7], A[0], W[i + 7], pp_crypto_hash_sha2_512_K[i + 7]); + } + + for (i = 0; i < 8; ++i) + ctx->hash[i] += A[i]; +} + +static PHashSHA2_512 * +pp_crypto_hash_sha2_512_new_internal (pboolean is384) +{ + PHashSHA2_512 *ret; + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PHashSHA2_512))) == NULL)) + return NULL; + + ret->is384 = is384; + + p_crypto_hash_sha2_512_reset (ret); + + return ret; +} + +void +p_crypto_hash_sha2_512_reset (PHashSHA2_512 *ctx) +{ + memset (ctx->buf.buf, 0, 128); + + ctx->len_low = 0; + ctx->len_high = 0; + + if (ctx->is384 == FALSE) { + /* SHA2-512 */ + ctx->hash[0] = 0x6A09E667F3BCC908ULL; + ctx->hash[1] = 0xBB67AE8584CAA73BULL; + ctx->hash[2] = 0x3C6EF372FE94F82BULL; + ctx->hash[3] = 0xA54FF53A5F1D36F1ULL; + ctx->hash[4] = 0x510E527FADE682D1ULL; + ctx->hash[5] = 0x9B05688C2B3E6C1FULL; + ctx->hash[6] = 0x1F83D9ABFB41BD6BULL; + ctx->hash[7] = 0x5BE0CD19137E2179ULL; + } else { + /* SHA2-384 */ + ctx->hash[0] = 0xCBBB9D5DC1059ED8ULL; + ctx->hash[1] = 0x629A292A367CD507ULL; + ctx->hash[2] = 0x9159015A3070DD17ULL; + ctx->hash[3] = 0x152FECD8F70E5939ULL; + ctx->hash[4] = 0x67332667FFC00B31ULL; + ctx->hash[5] = 0x8EB44A8768581511ULL; + ctx->hash[6] = 0xDB0C2E0D64F98FA7ULL; + ctx->hash[7] = 0x47B5481DBEFA4FA4ULL; + } +} + +PHashSHA2_512 * +p_crypto_hash_sha2_512_new (void) +{ + return pp_crypto_hash_sha2_512_new_internal (FALSE); +} + +PHashSHA2_512 * +p_crypto_hash_sha2_384_new (void) +{ + return pp_crypto_hash_sha2_512_new_internal (TRUE); +} + +void +p_crypto_hash_sha2_512_update (PHashSHA2_512 *ctx, + const puchar *data, + psize len) +{ + puint32 left, to_fill; + + left = (puint32) (ctx->len_low & 0x7F); + to_fill = 128 - left; + + ctx->len_low += (puint64) len; + + if (ctx->len_low < (puint64) len) + ++ctx->len_high; + + if (left && (puint64) len >= to_fill) { + memcpy (ctx->buf.buf + left, data, to_fill); + pp_crypto_hash_sha2_512_swap_bytes (ctx->buf.buf_w, 16); + pp_crypto_hash_sha2_512_process (ctx, ctx->buf.buf_w); + + data += to_fill; + len -= to_fill; + left = 0; + } + + while (len >= 128) { + memcpy (ctx->buf.buf, data, 128); + pp_crypto_hash_sha2_512_swap_bytes (ctx->buf.buf_w, 16); + pp_crypto_hash_sha2_512_process (ctx, ctx->buf.buf_w); + + data += 128; + len -= 128; + } + + if (len > 0) + memcpy (ctx->buf.buf + left, data, len); +} + +void +p_crypto_hash_sha2_512_finish (PHashSHA2_512 *ctx) +{ + puint64 high, low; + pint left, last; + + left = (pint) (ctx->len_low & 0x7F); + last = (left < 112) ? (112 - left) : (240 - left); + + low = ctx->len_low << 3; + high = ctx->len_high << 3 + | ctx->len_low >> 61; + + if (last > 0) + p_crypto_hash_sha2_512_update (ctx, pp_crypto_hash_sha2_512_pad, (psize) last); + + ctx->buf.buf_w[14] = high; + ctx->buf.buf_w[15] = low; + + pp_crypto_hash_sha2_512_swap_bytes (ctx->buf.buf_w, 14); + pp_crypto_hash_sha2_512_process (ctx, ctx->buf.buf_w); + + pp_crypto_hash_sha2_512_swap_bytes (ctx->hash, ctx->is384 == FALSE ? 8 : 6); +} + +const puchar * +p_crypto_hash_sha2_512_digest (PHashSHA2_512 *ctx) +{ + return (const puchar *) ctx->hash; +} + +void +p_crypto_hash_sha2_512_free (PHashSHA2_512 *ctx) +{ + p_free (ctx); +} diff --git a/3rdparty/plibsys/src/pcryptohash-sha2-512.h b/3rdparty/plibsys/src/pcryptohash-sha2-512.h new file mode 100644 index 0000000..ce0c2f7 --- /dev/null +++ b/3rdparty/plibsys/src/pcryptohash-sha2-512.h @@ -0,0 +1,59 @@ +/* + * The MIT License + * + * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* SHA2-512 interface implementation for #PCryptoHash */ + +#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION) +# error "Header files shouldn't be included directly, consider using <plibsys.h> instead." +#endif + +#ifndef PLIBSYS_HEADER_PCRYPTOHASHSHA2_512_H +#define PLIBSYS_HEADER_PCRYPTOHASHSHA2_512_H + +#include "ptypes.h" +#include "pmacros.h" + +P_BEGIN_DECLS + +typedef struct PHashSHA2_512_ PHashSHA2_512; + +PHashSHA2_512 * p_crypto_hash_sha2_512_new (void); +void p_crypto_hash_sha2_512_update (PHashSHA2_512 *ctx, const puchar *data, psize len); +void p_crypto_hash_sha2_512_finish (PHashSHA2_512 *ctx); +const puchar * p_crypto_hash_sha2_512_digest (PHashSHA2_512 *ctx); +void p_crypto_hash_sha2_512_reset (PHashSHA2_512 *ctx); +void p_crypto_hash_sha2_512_free (PHashSHA2_512 *ctx); + +PHashSHA2_512 * p_crypto_hash_sha2_384_new (void); + +#define p_crypto_hash_sha2_384_update p_crypto_hash_sha2_512_update +#define p_crypto_hash_sha2_384_finish p_crypto_hash_sha2_512_finish +#define p_crypto_hash_sha2_384_digest p_crypto_hash_sha2_512_digest +#define p_crypto_hash_sha2_384_reset p_crypto_hash_sha2_512_reset +#define p_crypto_hash_sha2_384_free p_crypto_hash_sha2_512_free + +P_END_DECLS + +#endif /* PLIBSYS_HEADER_PCRYPTOHASHSHA2_512_H */ diff --git a/3rdparty/plibsys/src/pcryptohash-sha3.c b/3rdparty/plibsys/src/pcryptohash-sha3.c new file mode 100644 index 0000000..c86545e --- /dev/null +++ b/3rdparty/plibsys/src/pcryptohash-sha3.c @@ -0,0 +1,297 @@ +/* + * The MIT License + * + * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include <string.h> +#include <stdlib.h> + +#include "pmem.h" +#include "pcryptohash-sha3.h" + +struct PHashSHA3_ { + union buf_ { + puchar buf[200]; + puint64 buf_w[25]; + } buf; + puint64 hash[25]; + + puint32 len; + puint32 block_size; +}; + +static const puint64 pp_crypto_hash_sha3_K[] = { + 0x0000000000000001ULL, 0x0000000000008082ULL, + 0x800000000000808AULL, 0x8000000080008000ULL, + 0x000000000000808BULL, 0x0000000080000001ULL, + 0x8000000080008081ULL, 0x8000000000008009ULL, + 0x000000000000008AULL, 0x0000000000000088ULL, + 0x0000000080008009ULL, 0x000000008000000AULL, + 0x000000008000808BULL, 0x800000000000008BULL, + 0x8000000000008089ULL, 0x8000000000008003ULL, + 0x8000000000008002ULL, 0x8000000000000080ULL, + 0x000000000000800AULL, 0x800000008000000AULL, + 0x8000000080008081ULL, 0x8000000000008080ULL, + 0x0000000080000001ULL, 0x8000000080008008ULL +}; + +static void pp_crypto_hash_sha3_swap_bytes (puint64 *data, puint words); +static void pp_crypto_hash_sha3_keccak_theta (PHashSHA3 *ctx); +static void pp_crypto_hash_sha3_keccak_rho_pi (PHashSHA3 *ctx); +static void pp_crypto_hash_sha3_keccak_chi (PHashSHA3 *ctx); +static void pp_crypto_hash_sha3_keccak_permutate (PHashSHA3 *ctx); +static void pp_crypto_hash_sha3_process (PHashSHA3 *ctx, const puint64 *data); +static PHashSHA3 * pp_crypto_hash_sha3_new_internal (puint bits); + +#define P_SHA3_SHL(val, shift) ((val) << (shift)) +#define P_SHA3_ROTL(val, shift) (P_SHA3_SHL(val, shift) | ((val) >> (64 - (shift)))) + +static void +pp_crypto_hash_sha3_swap_bytes (puint64 *data, + puint words) +{ +#ifndef PLIBSYS_IS_BIGENDIAN + P_UNUSED (data); + P_UNUSED (words); +#else + while (words-- > 0) { + *data = PUINT64_TO_LE (*data); + ++data; + } +#endif +} + +/* Theta step (see [Keccak Reference, Section 2.3.2]) */ +static void +pp_crypto_hash_sha3_keccak_theta (PHashSHA3 *ctx) +{ + puint i; + puint64 C[5], D[5]; + + /* Compute the parity of the columns */ + for (i = 0; i < 5; ++i) + C[i] = ctx->hash[i] ^ ctx->hash[i + 5] ^ ctx->hash[i + 10] ^ ctx->hash[i + 15] ^ ctx->hash[i + 20]; + + /* Compute the theta effect for a given column */ + D[0] = P_SHA3_ROTL (C[1], 1) ^ C[4]; + D[1] = P_SHA3_ROTL (C[2], 1) ^ C[0]; + D[2] = P_SHA3_ROTL (C[3], 1) ^ C[1]; + D[3] = P_SHA3_ROTL (C[4], 1) ^ C[2]; + D[4] = P_SHA3_ROTL (C[0], 1) ^ C[3]; + + /* Add the theta effect to the whole column */ + for (i = 0; i < 5; ++i) { + ctx->hash[i] ^= D[i]; + ctx->hash[i + 5] ^= D[i]; + ctx->hash[i + 10] ^= D[i]; + ctx->hash[i + 15] ^= D[i]; + ctx->hash[i + 20] ^= D[i]; + } +} + +/* Rho and pi steps (see [Keccak Reference, Sections 2.3.3 and 2.3.4]) */ +static void +pp_crypto_hash_sha3_keccak_rho_pi (PHashSHA3 *ctx) +{ + puint64 tmp_A; + + /* Unroll the loop over ((0 1)(2 3))^t * (1 0) for 0 ≤ t ≤ 23 */ + tmp_A = ctx->hash[1]; + ctx->hash[1] = P_SHA3_ROTL (ctx->hash[6], 44); + ctx->hash[6] = P_SHA3_ROTL (ctx->hash[9], 20); + ctx->hash[9] = P_SHA3_ROTL (ctx->hash[22], 61); + ctx->hash[22] = P_SHA3_ROTL (ctx->hash[14], 39); + ctx->hash[14] = P_SHA3_ROTL (ctx->hash[20], 18); + ctx->hash[20] = P_SHA3_ROTL (ctx->hash[2], 62); + ctx->hash[2] = P_SHA3_ROTL (ctx->hash[12], 43); + ctx->hash[12] = P_SHA3_ROTL (ctx->hash[13], 25); + ctx->hash[13] = P_SHA3_ROTL (ctx->hash[19], 8); + ctx->hash[19] = P_SHA3_ROTL (ctx->hash[23], 56); + ctx->hash[23] = P_SHA3_ROTL (ctx->hash[15], 41); + ctx->hash[15] = P_SHA3_ROTL (ctx->hash[4], 27); + ctx->hash[4] = P_SHA3_ROTL (ctx->hash[24], 14); + ctx->hash[24] = P_SHA3_ROTL (ctx->hash[21], 2); + ctx->hash[21] = P_SHA3_ROTL (ctx->hash[8], 55); + ctx->hash[8] = P_SHA3_ROTL (ctx->hash[16], 45); + ctx->hash[16] = P_SHA3_ROTL (ctx->hash[5], 36); + ctx->hash[5] = P_SHA3_ROTL (ctx->hash[3], 28); + ctx->hash[3] = P_SHA3_ROTL (ctx->hash[18], 21); + ctx->hash[18] = P_SHA3_ROTL (ctx->hash[17], 15); + ctx->hash[17] = P_SHA3_ROTL (ctx->hash[11], 10); + ctx->hash[11] = P_SHA3_ROTL (ctx->hash[7], 6); + ctx->hash[7] = P_SHA3_ROTL (ctx->hash[10], 3); + ctx->hash[10] = P_SHA3_ROTL (tmp_A, 1); +} + +/* Chi step (see [Keccak Reference, Section 2.3.1]) */ +static void +pp_crypto_hash_sha3_keccak_chi (PHashSHA3 *ctx) +{ + puint i; + puint64 tmp_A1, tmp_A2; + + for (i = 0; i < 25; i += 5) { + tmp_A1 = ctx->hash[i + 0]; + tmp_A2 = ctx->hash[i + 1]; + + ctx->hash[i + 0] ^= ~tmp_A2 & ctx->hash[i + 2]; + ctx->hash[i + 1] ^= ~ctx->hash[i + 2] & ctx->hash[i + 3]; + ctx->hash[i + 2] ^= ~ctx->hash[i + 3] & ctx->hash[i + 4]; + ctx->hash[i + 3] ^= ~ctx->hash[i + 4] & tmp_A1; + ctx->hash[i + 4] ^= ~tmp_A1 & tmp_A2; + } +} + +static void +pp_crypto_hash_sha3_keccak_permutate (PHashSHA3 *ctx) +{ + puint i; + + for (i = 0; i < 24; ++i) { + pp_crypto_hash_sha3_keccak_theta (ctx); + pp_crypto_hash_sha3_keccak_rho_pi (ctx); + pp_crypto_hash_sha3_keccak_chi (ctx); + + /* Iota step (see [Keccak Reference, Section 2.3.5]) */ + ctx->hash[0] ^= pp_crypto_hash_sha3_K[i]; + } +} + +static void +pp_crypto_hash_sha3_process (PHashSHA3 *ctx, + const puint64 *data) +{ + puint i; + puint qwords = ctx->block_size / 8; + + for (i = 0; i < qwords; ++i) + ctx->hash[i] ^= data[i]; + + /* Make the Keccak permutation */ + pp_crypto_hash_sha3_keccak_permutate (ctx); +} + +static PHashSHA3 * +pp_crypto_hash_sha3_new_internal (puint bits) +{ + PHashSHA3 *ret; + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PHashSHA3))) == NULL)) + return NULL; + + ret->block_size = (1600 - bits * 2) / 8; + + return ret; +} + +void +p_crypto_hash_sha3_reset (PHashSHA3 *ctx) +{ + memset (ctx->buf.buf, 0, 200); + memset (ctx->hash, 0, sizeof (ctx->hash)); + + ctx->len = 0; +} + +PHashSHA3 * +p_crypto_hash_sha3_224_new (void) +{ + return pp_crypto_hash_sha3_new_internal (224); +} + +PHashSHA3 * +p_crypto_hash_sha3_256_new (void) +{ + return pp_crypto_hash_sha3_new_internal (256); +} + +PHashSHA3 * +p_crypto_hash_sha3_384_new (void) +{ + return pp_crypto_hash_sha3_new_internal (384); +} + +PHashSHA3 * +p_crypto_hash_sha3_512_new (void) +{ + return pp_crypto_hash_sha3_new_internal (512); +} + +void +p_crypto_hash_sha3_update (PHashSHA3 *ctx, + const puchar *data, + psize len) +{ + puint32 left, to_fill; + + left = ctx->len; + to_fill = ctx->block_size - left; + ctx->len = (puint32) (((psize) ctx->len + len) % (psize) ctx->block_size); + + if (left && (puint64) len >= to_fill) { + memcpy (ctx->buf.buf + left, data, to_fill); + pp_crypto_hash_sha3_swap_bytes (ctx->buf.buf_w, ctx->block_size >> 3); + pp_crypto_hash_sha3_process (ctx, ctx->buf.buf_w); + + data += to_fill; + len -= to_fill; + left = 0; + } + + while (len >= ctx->block_size) { + memcpy (ctx->buf.buf, data, ctx->block_size); + pp_crypto_hash_sha3_swap_bytes (ctx->buf.buf_w, ctx->block_size >> 3); + pp_crypto_hash_sha3_process (ctx, ctx->buf.buf_w); + + data += ctx->block_size; + len -= ctx->block_size; + } + + if (len > 0) + memcpy (ctx->buf.buf + left, data, len); +} + +void +p_crypto_hash_sha3_finish (PHashSHA3 *ctx) +{ + memset (ctx->buf.buf + ctx->len, 0, ctx->block_size - ctx->len); + ctx->buf.buf[ctx->len] |= 0x06; + ctx->buf.buf[ctx->block_size - 1] |= 0x80; + + pp_crypto_hash_sha3_swap_bytes (ctx->buf.buf_w, ctx->block_size >> 3); + pp_crypto_hash_sha3_process (ctx, ctx->buf.buf_w); + + pp_crypto_hash_sha3_swap_bytes (ctx->hash, (100 - (ctx->block_size >> 2)) >> 3); +} + +const puchar * +p_crypto_hash_sha3_digest (PHashSHA3 *ctx) +{ + return (const puchar *) ctx->hash; +} + +void +p_crypto_hash_sha3_free (PHashSHA3 *ctx) +{ + p_free (ctx); +} diff --git a/3rdparty/plibsys/src/pcryptohash-sha3.h b/3rdparty/plibsys/src/pcryptohash-sha3.h new file mode 100644 index 0000000..ad64e70 --- /dev/null +++ b/3rdparty/plibsys/src/pcryptohash-sha3.h @@ -0,0 +1,79 @@ +/* + * The MIT License + * + * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* SHA-3 (Keccak) interface implementation for #PCryptoHash */ + +#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION) +# error "Header files shouldn't be included directly, consider using <plibsys.h> instead." +#endif + +#ifndef PLIBSYS_HEADER_PCRYPTOHASHSHA3_H +#define PLIBSYS_HEADER_PCRYPTOHASHSHA3_H + +#include "ptypes.h" +#include "pmacros.h" + +P_BEGIN_DECLS + +typedef struct PHashSHA3_ PHashSHA3; + +void p_crypto_hash_sha3_update (PHashSHA3 *ctx, const puchar *data, psize len); +void p_crypto_hash_sha3_finish (PHashSHA3 *ctx); +const puchar * p_crypto_hash_sha3_digest (PHashSHA3 *ctx); +void p_crypto_hash_sha3_reset (PHashSHA3 *ctx); +void p_crypto_hash_sha3_free (PHashSHA3 *ctx); + +PHashSHA3 * p_crypto_hash_sha3_224_new (void); +PHashSHA3 * p_crypto_hash_sha3_256_new (void); +PHashSHA3 * p_crypto_hash_sha3_384_new (void); +PHashSHA3 * p_crypto_hash_sha3_512_new (void); + +#define p_crypto_hash_sha3_224_update p_crypto_hash_sha3_update +#define p_crypto_hash_sha3_224_finish p_crypto_hash_sha3_finish +#define p_crypto_hash_sha3_224_digest p_crypto_hash_sha3_digest +#define p_crypto_hash_sha3_224_reset p_crypto_hash_sha3_reset +#define p_crypto_hash_sha3_224_free p_crypto_hash_sha3_free + +#define p_crypto_hash_sha3_256_update p_crypto_hash_sha3_update +#define p_crypto_hash_sha3_256_finish p_crypto_hash_sha3_finish +#define p_crypto_hash_sha3_256_digest p_crypto_hash_sha3_digest +#define p_crypto_hash_sha3_256_reset p_crypto_hash_sha3_reset +#define p_crypto_hash_sha3_256_free p_crypto_hash_sha3_free + +#define p_crypto_hash_sha3_384_update p_crypto_hash_sha3_update +#define p_crypto_hash_sha3_384_finish p_crypto_hash_sha3_finish +#define p_crypto_hash_sha3_384_digest p_crypto_hash_sha3_digest +#define p_crypto_hash_sha3_384_reset p_crypto_hash_sha3_reset +#define p_crypto_hash_sha3_384_free p_crypto_hash_sha3_free + +#define p_crypto_hash_sha3_512_update p_crypto_hash_sha3_update +#define p_crypto_hash_sha3_512_finish p_crypto_hash_sha3_finish +#define p_crypto_hash_sha3_512_digest p_crypto_hash_sha3_digest +#define p_crypto_hash_sha3_512_reset p_crypto_hash_sha3_reset +#define p_crypto_hash_sha3_512_free p_crypto_hash_sha3_free + +P_END_DECLS + +#endif /* PLIBSYS_HEADER_PCRYPTOHASHSHA3_H */ diff --git a/3rdparty/plibsys/src/pcryptohash.c b/3rdparty/plibsys/src/pcryptohash.c new file mode 100644 index 0000000..2ccb6a6 --- /dev/null +++ b/3rdparty/plibsys/src/pcryptohash.c @@ -0,0 +1,250 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2017 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pmem.h" +#include "pcryptohash.h" +#include "pcryptohash-gost3411.h" +#include "pcryptohash-md5.h" +#include "pcryptohash-sha1.h" +#include "pcryptohash-sha2-256.h" +#include "pcryptohash-sha2-512.h" +#include "pcryptohash-sha3.h" + +#include <string.h> + +#define P_HASH_FUNCS(ctx, type) \ + ctx->create = (void * (*) (void)) p_crypto_hash_##type##_new; \ + ctx->update = (void (*) (void *, const puchar *, psize)) p_crypto_hash_##type##_update; \ + ctx->finish = (void (*) (void *)) p_crypto_hash_##type##_finish; \ + ctx->digest = (const puchar * (*) (void *)) p_crypto_hash_##type##_digest; \ + ctx->reset = (void (*) (void *)) p_crypto_hash_##type##_reset; \ + ctx->free = (void (*) (void *)) p_crypto_hash_##type##_free; + +struct PCryptoHash_ { + PCryptoHashType type; + ppointer context; + puint hash_len; + pboolean closed; + ppointer (*create) (void); + void (*update) (void *hash, const puchar *data, psize len); + void (*finish) (void *hash); + const puchar * (*digest) (void *hash); + void (*reset) (void *hash); + void (*free) (void *hash); +}; + +static pchar pp_crypto_hash_hex_str[]= "0123456789abcdef"; + +static void +pp_crypto_hash_digest_to_hex (const puchar *digest, puint len, pchar *out); + +static void +pp_crypto_hash_digest_to_hex (const puchar *digest, puint len, pchar *out) +{ + puint i; + + for (i = 0; i < len; ++i) { + *(out + (i << 1) ) = pp_crypto_hash_hex_str[(digest[i] >> 4) & 0x0F]; + *(out + (i << 1) + 1) = pp_crypto_hash_hex_str[(digest[i] ) & 0x0F]; + } +} + +P_LIB_API PCryptoHash * +p_crypto_hash_new (PCryptoHashType type) +{ + PCryptoHash *ret; + + if (P_UNLIKELY (!(type >= P_CRYPTO_HASH_TYPE_MD5 && type <= P_CRYPTO_HASH_TYPE_GOST))) + return NULL; + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PCryptoHash))) == NULL)) { + P_ERROR ("PCryptoHash::p_crypto_hash_new: failed to allocate memory"); + return NULL; + } + + switch (type) { + case P_CRYPTO_HASH_TYPE_MD5: + P_HASH_FUNCS (ret, md5); + ret->hash_len = 16; + break; + case P_CRYPTO_HASH_TYPE_SHA1: + P_HASH_FUNCS (ret, sha1); + ret->hash_len = 20; + break; + case P_CRYPTO_HASH_TYPE_SHA2_224: + P_HASH_FUNCS (ret, sha2_224); + ret->hash_len = 28; + break; + case P_CRYPTO_HASH_TYPE_SHA2_256: + P_HASH_FUNCS (ret, sha2_256); + ret->hash_len = 32; + break; + case P_CRYPTO_HASH_TYPE_SHA2_384: + P_HASH_FUNCS (ret, sha2_384); + ret->hash_len = 48; + break; + case P_CRYPTO_HASH_TYPE_SHA2_512: + P_HASH_FUNCS (ret, sha2_512); + ret->hash_len = 64; + break; + case P_CRYPTO_HASH_TYPE_SHA3_224: + P_HASH_FUNCS (ret, sha3_224); + ret->hash_len = 28; + break; + case P_CRYPTO_HASH_TYPE_SHA3_256: + P_HASH_FUNCS (ret, sha3_256); + ret->hash_len = 32; + break; + case P_CRYPTO_HASH_TYPE_SHA3_384: + P_HASH_FUNCS (ret, sha3_384); + ret->hash_len = 48; + break; + case P_CRYPTO_HASH_TYPE_SHA3_512: + P_HASH_FUNCS (ret, sha3_512); + ret->hash_len = 64; + break; + case P_CRYPTO_HASH_TYPE_GOST: + P_HASH_FUNCS (ret, gost3411); + ret->hash_len = 32; + break; + } + + ret->type = type; + ret->closed = FALSE; + + if (P_UNLIKELY ((ret->context = ret->create ()) == NULL)) { + p_free (ret); + return NULL; + } + + return ret; +} + +P_LIB_API void +p_crypto_hash_update (PCryptoHash *hash, const puchar *data, psize len) +{ + if (P_UNLIKELY (hash == NULL || data == NULL || len == 0)) + return; + + if (P_UNLIKELY (hash->closed)) + return; + + hash->update (hash->context, data, len); +} + +P_LIB_API void +p_crypto_hash_reset (PCryptoHash *hash) +{ + if (P_UNLIKELY (hash == NULL)) + return; + + hash->reset (hash->context); + hash->closed = FALSE; +} + +P_LIB_API pchar * +p_crypto_hash_get_string (PCryptoHash *hash) +{ + pchar *ret; + const puchar *digest; + + if (P_UNLIKELY (hash == NULL)) + return NULL; + + if (!hash->closed) { + hash->finish (hash->context); + hash->closed = TRUE; + } + + if (P_UNLIKELY ((digest = hash->digest (hash->context)) == NULL)) + return NULL; + + if (P_UNLIKELY ((ret = p_malloc0 (hash->hash_len * 2 + 1)) == NULL)) + return NULL; + + pp_crypto_hash_digest_to_hex (digest, hash->hash_len, ret); + + return ret; +} + +P_LIB_API void +p_crypto_hash_get_digest (PCryptoHash *hash, puchar *buf, psize *len) +{ + const puchar *digest; + + if (P_UNLIKELY (len == NULL)) + return; + + if (P_UNLIKELY (hash == NULL || buf == NULL)) { + *len = 0; + return; + } + + if (P_UNLIKELY (hash->hash_len > *len)) { + *len = 0; + return; + } + + if (!hash->closed) { + hash->finish (hash->context); + hash->closed = TRUE; + } + + if (P_UNLIKELY ((digest = hash->digest (hash->context)) == NULL)) { + *len = 0; + return; + } + + memcpy (buf, digest, hash->hash_len); + *len = hash->hash_len; +} + +P_LIB_API pssize +p_crypto_hash_get_length (const PCryptoHash *hash) +{ + if (P_UNLIKELY (hash == NULL)) + return 0; + + return hash->hash_len; +} + +P_LIB_API PCryptoHashType +p_crypto_hash_get_type (const PCryptoHash *hash) +{ + if (P_UNLIKELY (hash == NULL)) + return (PCryptoHashType) -1; + + return hash->type; +} + +P_LIB_API void +p_crypto_hash_free (PCryptoHash *hash) +{ + if (P_UNLIKELY (hash == NULL)) + return; + + hash->free (hash->context); + p_free (hash); +} diff --git a/3rdparty/plibsys/src/pcryptohash.h b/3rdparty/plibsys/src/pcryptohash.h new file mode 100644 index 0000000..47a8a6c --- /dev/null +++ b/3rdparty/plibsys/src/pcryptohash.h @@ -0,0 +1,188 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * @file pcryptohash.h + * @brief Cryptographic hash function + * @author Alexander Saprykin + * + * A cryptographic hash function is an algorithm which performs a transformation + * of the income data to a hash value. + * + * One of the main requirements to all of the cryptographic hashing algorithms + * is that any (even a considerably small) change in the input data must lead to + * notable changes in the result hash value. It is the so called avalanche + * effect. It helps to avoid collisions (the same hash value for different + * input arrays). + * + * The cryptographic hash function is designed to be a one-way so you couldn't + * revert the output hash value to the input data back. The length of the + * resulting hash is a constant value depending on the algorithm being used. + * + * A cryptographic hash works with the incoming data using fixed length blocks + * so it is possible to feed as many data as required. + * + * The cryptographic hash module supports the following hash functions: + * - MD5; + * - SHA-1; + * - SHA-2/224; + * - SHA-2/256; + * - SHA-2/384; + * - SHA-2/512; + * - SHA-3/224; + * - SHA-3/256; + * - SHA-3/384; + * - SHA-3/512; + * - GOST (R 34.11-94). + * + * Use p_crypto_hash_new() to initialize a new hash context with one of the + * mentioned above types. Data for hashing can be added in several chunks using + * the p_crypto_hash_update() routine. You can add more chunks as long as the + * hash context is open. + * + * The hash context becomes close in two cases: p_crypto_hash_get_string() or + * p_crypto_hash_get_digest() was called. After that you can only get a hash in + * a hexidemical string or in a raw representation. + * + * A hashing algorithm couldn't be changed after the context initialization. + */ + +#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION) +# error "Header files shouldn't be included directly, consider using <plibsys.h> instead." +#endif + +#ifndef PLIBSYS_HEADER_PCRYPTOHASH_H +#define PLIBSYS_HEADER_PCRYPTOHASH_H + +#include <pmacros.h> +#include <ptypes.h> + +P_BEGIN_DECLS + +/** Opaque data structure for handling a cryptographic hash context. */ +typedef struct PCryptoHash_ PCryptoHash; + +/** Cryptographic hash function types for #PCryptoHash. */ +typedef enum PCryptoHashType_ { + P_CRYPTO_HASH_TYPE_MD5 = 0, /**< MD5 hash function. @since 0.0.1 */ + P_CRYPTO_HASH_TYPE_SHA1 = 1, /**< SHA-1 hash function. @since 0.0.1 */ + P_CRYPTO_HASH_TYPE_SHA2_224 = 2, /**< SHA-2/224 hash function. @since 0.0.2 */ + P_CRYPTO_HASH_TYPE_SHA2_256 = 3, /**< SHA-2/256 hash function. @since 0.0.2 */ + P_CRYPTO_HASH_TYPE_SHA2_384 = 4, /**< SHA-2/384 hash function. @since 0.0.2 */ + P_CRYPTO_HASH_TYPE_SHA2_512 = 5, /**< SHA-2/512 hash function. @since 0.0.2 */ + P_CRYPTO_HASH_TYPE_SHA3_224 = 6, /**< SHA-2/224 hash function. @since 0.0.2 */ + P_CRYPTO_HASH_TYPE_SHA3_256 = 7, /**< SHA-2/256 hash function. @since 0.0.2 */ + P_CRYPTO_HASH_TYPE_SHA3_384 = 8, /**< SHA-2/384 hash function. @since 0.0.2 */ + P_CRYPTO_HASH_TYPE_SHA3_512 = 9, /**< SHA-3/512 hash function. @since 0.0.2 */ + P_CRYPTO_HASH_TYPE_GOST = 10 /**< GOST (R 34.11-94) hash function. @since 0.0.1 */ +} PCryptoHashType; + +/** + * @brief Initializes a new #PCryptoHash context. + * @param type Hash function type to use, can't be changed later. + * @return Newly initialized #PCryptoHash context in case of success, NULL + * otherwise. + * @since 0.0.1 + */ +P_LIB_API PCryptoHash * p_crypto_hash_new (PCryptoHashType type); + +/** + * @brief Adds a new chunk of data for hashing. + * @param hash #PCryptoHash context to add @a data to. + * @param data Data to add for hashing. + * @param len Data length, in bytes. + * @note After calling p_crypto_hash_get_string() or p_crypto_hash_get_digest() + * the hash couldn't be updated anymore as it becomes close. + * @since 0.0.1 + */ +P_LIB_API void p_crypto_hash_update (PCryptoHash *hash, + const puchar *data, + psize len); + +/** + * @brief Resets a hash state. + * @param hash #PCryptoHash context to reset. + * @since 0.0.1 + * + * After a reset the hash context becomes open for updating, but all previously + * added data will be lost. A hash function type couldn't be changed during or + * after the resets. + */ +P_LIB_API void p_crypto_hash_reset (PCryptoHash *hash); + +/** + * @brief Gets a hash in a hexidemical representation. + * @param hash #PCryptoHash context to get a string from. + * @return NULL-terminated string with the hexidemical representation of a hash + * state in case of success, NULL otherwise. The string should be freed with + * p_free() after using it. + * @note Before returning the string the hash context will be closed for further + * updates. + * @since 0.0.1 + */ +P_LIB_API pchar * p_crypto_hash_get_string (PCryptoHash *hash); + +/** + * @brief Gets a hash in a raw representation. + * @param hash #PCryptoHash context to get a digest from. + * @param buf Buffer to store the digest with the hash raw representation. + * @param[in,out] len Size of @a buf when calling, count of written bytes + * after. + * @note Before getting the raw digest the hash context will be closed for + * further updates. + * @since 0.0.1 + */ +P_LIB_API void p_crypto_hash_get_digest (PCryptoHash *hash, + puchar *buf, + psize *len); + +/** + * @brief Gets a hash digest length depending on its type. + * @param hash #PCryptoHash context to get the length for. + * @return Length (in bytes) of the given hash depending on its type in case of + * success, -1 otherwise. + * @note This length doesn't match a string hash representation. + * @since 0.0.1 + */ +P_LIB_API pssize p_crypto_hash_get_length (const PCryptoHash *hash); + +/** + * @brief Gets a hash function type. + * @param hash #PCryptoHash context to get the type for. + * @return Hash function type used in the given context. + * @since 0.0.1 + */ +P_LIB_API PCryptoHashType p_crypto_hash_get_type (const PCryptoHash *hash); + +/** + * @brief Frees a previously initialized hash context. + * @param hash #PCryptoHash context to free. + * @since 0.0.1 + */ +P_LIB_API void p_crypto_hash_free (PCryptoHash *hash); + +P_END_DECLS + +#endif /* PLIBSYS_HEADER_PCRYPTOHASH_H */ diff --git a/3rdparty/plibsys/src/pdir-none.c b/3rdparty/plibsys/src/pdir-none.c new file mode 100644 index 0000000..eaaa8b9 --- /dev/null +++ b/3rdparty/plibsys/src/pdir-none.c @@ -0,0 +1,124 @@ +/* + * The MIT License + * + * Copyright (C) 2015-2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pdir.h" + +#include <stdlib.h> + +struct PDir_ { + pint hdl; +}; + +P_LIB_API PDir * +p_dir_new (const pchar *path, + PError **error) +{ + P_UNUSED (path); + + p_error_set_error_p (error, + (pint) P_ERROR_IO_NOT_IMPLEMENTED, + 0, + "No directory implementation"); + + return NULL; +} + +P_LIB_API pboolean +p_dir_create (const pchar *path, + pint mode, + PError **error) +{ + P_UNUSED (path); + P_UNUSED (mode); + + p_error_set_error_p (error, + (pint) P_ERROR_IO_NOT_IMPLEMENTED, + 0, + "No directory implementation"); + + return FALSE; +} + +P_LIB_API pboolean +p_dir_remove (const pchar *path, + PError **error) +{ + P_UNUSED (path); + + p_error_set_error_p (error, + (pint) P_ERROR_IO_NOT_IMPLEMENTED, + 0, + "No directory implementation"); + + return FALSE; +} + +P_LIB_API pboolean +p_dir_is_exists (const pchar *path) +{ + P_UNUSED (path); + return FALSE; +} + +P_LIB_API pchar * +p_dir_get_path (const PDir *dir) +{ + P_UNUSED (dir); + return NULL; +} + +P_LIB_API PDirEntry * +p_dir_get_next_entry (PDir *dir, + PError **error) +{ + P_UNUSED (dir); + + p_error_set_error_p (error, + (pint) P_ERROR_IO_NOT_IMPLEMENTED, + 0, + "No directory implementation"); + + return NULL; +} + +P_LIB_API pboolean +p_dir_rewind (PDir *dir, + PError **error) +{ + P_UNUSED (dir); + + p_error_set_error_p (error, + (pint) P_ERROR_IO_NOT_IMPLEMENTED, + 0, + "No directory implementation"); + + return FALSE; +} + +P_LIB_API void +p_dir_free (PDir *dir) +{ + P_UNUSED (dir); +} diff --git a/3rdparty/plibsys/src/pdir-os2.c b/3rdparty/plibsys/src/pdir-os2.c new file mode 100644 index 0000000..8869329 --- /dev/null +++ b/3rdparty/plibsys/src/pdir-os2.c @@ -0,0 +1,381 @@ +/* + * The MIT License + * + * Copyright (C) 2017 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pdir.h" +#include "perror.h" +#include "pmem.h" +#include "pstring.h" +#include "perror-private.h" + +#define INCL_DOSFILEMGR +#define INCL_DOSERRORS +#include <os2.h> + +#include <stdlib.h> +#include <string.h> + +struct PDir_ { + FILEFINDBUF3 find_data; + HDIR search_handle; + pboolean cached; + pchar path[CCHMAXPATH]; + pchar *orig_path; +}; + +P_LIB_API PDir * +p_dir_new (const pchar *path, + PError **error) +{ + PDir *ret; + pchar *pathp; + pchar *adj_path; + pint path_len; + APIRET ulrc; + ULONG find_count; + + if (P_UNLIKELY (path == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return NULL; + } + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PDir))) == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_NO_RESOURCES, + 0, + "Failed to allocate memory for directory structure"); + return NULL; + } + + adj_path = NULL; + path_len = strlen (path); + + if (P_UNLIKELY (path[path_len - 1] == '\\' || path[path_len - 1] == '/') && path_len > 1) { + while ((path[path_len - 1] == '\\' || path[path_len - 1] == '/') && path_len > 1) + --path_len; + + if (P_UNLIKELY ((adj_path = p_malloc0 (path_len + 1)) == NULL)) { + p_free (ret); + p_error_set_error_p (error, + (pint) P_ERROR_IO_NO_RESOURCES, + 0, + "Failed to allocate memory for directory path"); + return NULL; + } + + memcpy (adj_path, path, path_len); + + adj_path[path_len] = '\0'; + ret->orig_path = p_strdup (path); + path = (const pchar *) adj_path; + } + + ret->search_handle = HDIR_CREATE; + + ulrc = DosQueryPathInfo ((PSZ) path, FIL_QUERYFULLNAME, ret->path, sizeof (ret->path) - 2); + + if (P_UNLIKELY (ulrc != NO_ERROR)) { + p_error_set_error_p (error, + (pint) p_error_get_io_from_system ((pint) ulrc), + (pint) ulrc, + "Failed to call DosQueryPathInfo() to get directory path"); + + if (P_UNLIKELY (adj_path != NULL)) { + p_free (adj_path); + p_free (ret->orig_path); + } + + p_free (ret); + return NULL; + } + + /* Append the search pattern "\\*\0" to the directory name */ + pathp = strchr (ret->path, '\0'); + + if (ret->path < pathp && *(pathp - 1) != '\\' && *(pathp - 1) != ':') + *pathp++ = '\\'; + + *pathp++ = '*'; + *pathp = '\0'; + + find_count = 1; + + /* Open directory stream and retrieve the first entry */ + ulrc = DosFindFirst (ret->path, + &ret->search_handle, + FILE_NORMAL | FILE_DIRECTORY, + &ret->find_data, + sizeof (ret->find_data), + &find_count, + FIL_STANDARD); + + if (P_UNLIKELY (ulrc != NO_ERROR && ulrc != ERROR_NO_MORE_FILES)) { + p_error_set_error_p (error, + (pint) p_error_get_io_from_system ((pint) ulrc), + (pint) ulrc, + "Failed to call DosFindFirst() to open directory stream"); + + if (P_UNLIKELY (adj_path != NULL)) { + p_free (adj_path); + p_free (ret->orig_path); + } + + p_free (ret); + return NULL; + } + + ret->cached = TRUE; + + if (P_UNLIKELY (adj_path != NULL)) + p_free (adj_path); + else + ret->orig_path = p_strdup (path); + + return ret; +} + +P_LIB_API pboolean +p_dir_create (const pchar *path, + pint mode, + PError **error) +{ + APIRET ulrc; + + P_UNUSED (mode); + + if (P_UNLIKELY (path == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return FALSE; + } + + if (p_dir_is_exists (path)) + return TRUE; + + if (P_UNLIKELY ((ulrc = DosCreateDir ((PSZ) path, NULL)) != NO_ERROR)) { + p_error_set_error_p (error, + (pint) p_error_get_io_from_system ((pint) ulrc), + (pint) ulrc, + "Failed to call DosCreateDir() to create directory"); + return FALSE; + } else + return TRUE; +} + +P_LIB_API pboolean +p_dir_remove (const pchar *path, + PError **error) +{ + APIRET ulrc; + + if (P_UNLIKELY (path == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return FALSE; + } + + if (!p_dir_is_exists (path)) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_NOT_EXISTS, + 0, + "Specified directory doesn't exist"); + return FALSE; + } + + if (P_UNLIKELY ((ulrc = DosDeleteDir ((PSZ) path)) != NO_ERROR)) { + p_error_set_error_p (error, + (pint) p_error_get_io_from_system ((pint) ulrc), + (pint) ulrc, + "Failed to call DosDeleteDir() to remove directory"); + return FALSE; + } else + return TRUE; +} + +P_LIB_API pboolean +p_dir_is_exists (const pchar *path) +{ + FILESTATUS3 status; + + if (P_UNLIKELY (path == NULL)) + return FALSE; + + if (DosQueryPathInfo ((PSZ) path, FIL_STANDARD, (PVOID) &status, sizeof (status)) != NO_ERROR) + return FALSE; + + return (status.attrFile & FILE_DIRECTORY) != 0; +} + +P_LIB_API pchar * +p_dir_get_path (const PDir *dir) +{ + if (P_UNLIKELY (dir == NULL)) + return NULL; + + return p_strdup (dir->orig_path); +} + +P_LIB_API PDirEntry * +p_dir_get_next_entry (PDir *dir, + PError **error) +{ + PDirEntry *ret; + APIRET ulrc; + ULONG find_count; + + if (P_UNLIKELY (dir == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return NULL; + } + + if (dir->cached == TRUE) { + dir->cached = FALSE; + + /* Opened directory is empty */ + if (P_UNLIKELY (dir->search_handle == HDIR_CREATE)) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_NO_MORE, + (pint) ERROR_NO_MORE_FILES, + "Directory is empty to get the next entry"); + return NULL; + } + } else { + if (P_UNLIKELY (dir->search_handle == HDIR_CREATE)) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_INVALID_ARGUMENT, + 0, + "Not a valid (or closed) directory stream"); + return NULL; + } + + find_count = 1; + + ulrc = DosFindNext (dir->search_handle, + (PVOID) &dir->find_data, + sizeof (dir->find_data), + &find_count); + + if (P_UNLIKELY (ulrc != NO_ERROR)) { + p_error_set_error_p (error, + (pint) p_error_get_io_from_system ((pint) ulrc), + (pint) ulrc, + "Failed to call DosFindNext() to read directory stream"); + DosFindClose (dir->search_handle); + dir->search_handle = HDIR_CREATE; + return NULL; + } + } + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PDirEntry))) == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_NO_RESOURCES, + 0, + "Failed to allocate memory for directory entry"); + return NULL; + } + + ret->name = p_strdup (dir->find_data.achName); + + if ((dir->find_data.attrFile & FILE_DIRECTORY) != 0) + ret->type = P_DIR_ENTRY_TYPE_DIR; + else + ret->type = P_DIR_ENTRY_TYPE_FILE; + + return ret; +} + +P_LIB_API pboolean +p_dir_rewind (PDir *dir, + PError **error) +{ + APIRET ulrc; + ULONG find_count; + + if (P_UNLIKELY (dir == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return FALSE; + } + + if (dir->search_handle != HDIR_CREATE) { + if (P_UNLIKELY ((ulrc = DosFindClose (dir->search_handle)) != NO_ERROR)) { + p_error_set_error_p (error, + (pint) p_error_get_io_from_system ((pint) ulrc), + (pint) ulrc, + "Failed to call DosFindClose() to close directory stream"); + return FALSE; + } + + dir->search_handle = HDIR_CREATE; + } + + find_count = 1; + + ulrc = DosFindFirst (dir->path, + &dir->search_handle, + FILE_NORMAL | FILE_DIRECTORY, + &dir->find_data, + sizeof (dir->find_data), + &find_count, + FIL_STANDARD); + + if (P_UNLIKELY (ulrc != NO_ERROR && ulrc != ERROR_NO_MORE_FILES)) { + p_error_set_error_p (error, + (pint) p_error_get_io_from_system ((pint) ulrc), + (pint) ulrc, + "Failed to call DosFindFirst() to open directory stream"); + dir->cached = FALSE; + return FALSE; + } else { + dir->cached = TRUE; + return TRUE; + } +} + +P_LIB_API void +p_dir_free (PDir *dir) +{ + if (dir == NULL) + return; + + if (P_LIKELY (dir->search_handle != HDIR_CREATE)) { + if (P_UNLIKELY (DosFindClose (dir->search_handle) != NO_ERROR)) + P_ERROR ("PDir::p_dir_free: DosFindClose() failed"); + } + + p_free (dir->orig_path); + p_free (dir); +} diff --git a/3rdparty/plibsys/src/pdir-posix.c b/3rdparty/plibsys/src/pdir-posix.c new file mode 100644 index 0000000..22b7863 --- /dev/null +++ b/3rdparty/plibsys/src/pdir-posix.c @@ -0,0 +1,378 @@ +/* + * The MIT License + * + * Copyright (C) 2015-2017 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pdir.h" +#include "perror.h" +#include "pfile.h" +#include "pmem.h" +#include "pstring.h" +#include "perror-private.h" + +#include <stdlib.h> +#include <string.h> +#include <dirent.h> +#include <unistd.h> +#include <limits.h> +#include <sys/stat.h> +#include <sys/types.h> + +#if defined (__GLIBC__) && (__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 24) +# define P_DIR_NON_REENTRANT 1 +# elif defined (P_OS_SOLARIS) || defined (P_OS_QNX6) || defined (P_OS_UNIXWARE) || \ + defined (P_OS_SCO) || defined (P_OS_IRIX) || defined (P_OS_HAIKU) +# define P_DIR_NEED_BUF_ALLOC 1 +#endif + +#ifdef P_DIR_NEED_BUF_ALLOC +# if defined (P_OS_SCO) +# define P_DIR_NEED_SIMPLE_R 1 +# endif +#else +# if defined (P_OS_BEOS) || defined (P_OS_AMIGA) +# define P_DIR_NON_REENTRANT 1 +# endif +#endif + +struct PDir_ { + DIR * dir; + struct dirent *dir_result; + pchar *path; + pchar *orig_path; +}; + +P_LIB_API PDir * +p_dir_new (const pchar *path, + PError **error) +{ + PDir *ret; + DIR *dir; + pchar *pathp; + + if (P_UNLIKELY (path == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return NULL; + } + + if (P_UNLIKELY ((dir = opendir (path)) == NULL)) { + p_error_set_error_p (error, + (pint) p_error_get_last_io (), + p_error_get_last_system (), + "Failed to call opendir() to open directory stream"); + return NULL; + } + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PDir))) == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_NO_RESOURCES, + 0, + "Failed to allocate memory for directory structure"); + closedir (dir); + return NULL; + } + + ret->dir = dir; + ret->path = p_strdup (path); + ret->orig_path = p_strdup (path); + + pathp = ret->path + strlen (ret->path) - 1; + + if (*pathp == '/' || *pathp == '\\') + *pathp = '\0'; + + return ret; +} + +P_LIB_API pboolean +p_dir_create (const pchar *path, + pint mode, + PError **error) +{ + if (P_UNLIKELY (path == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return FALSE; + } + + if (p_dir_is_exists (path)) + return TRUE; + + if (P_UNLIKELY (mkdir (path, (mode_t) mode) != 0)) { + p_error_set_error_p (error, + (pint) p_error_get_last_io (), + p_error_get_last_system (), + "Failed to call mkdir() to create directory"); + return FALSE; + } else + return TRUE; +} + +P_LIB_API pboolean +p_dir_remove (const pchar *path, + PError **error) +{ + if (P_UNLIKELY (path == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return FALSE; + } + + if (!p_dir_is_exists (path)) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_NOT_EXISTS, + 0, + "Specified directory doesn't exist"); + return FALSE; + } + + if (P_UNLIKELY (rmdir (path) != 0)) { + p_error_set_error_p (error, + (pint) p_error_get_last_io (), + p_error_get_last_system (), + "Failed to call rmdir() to remove directory"); + return FALSE; + } else + return TRUE; +} + +P_LIB_API pboolean +p_dir_is_exists (const pchar *path) +{ + struct stat sb; + + if (P_UNLIKELY (path == NULL)) + return FALSE; + + return (stat (path, &sb) == 0 && S_ISDIR (sb.st_mode)) ? TRUE : FALSE; +} + +P_LIB_API pchar * +p_dir_get_path (const PDir *dir) +{ + if (P_UNLIKELY (dir == NULL)) + return NULL; + + return p_strdup (dir->orig_path); +} + +P_LIB_API PDirEntry * +p_dir_get_next_entry (PDir *dir, + PError **error) +{ + PDirEntry *ret; +#ifdef P_DIR_NEED_BUF_ALLOC + struct dirent *dirent_st; +#elif !defined (P_DIR_NON_REENTRANT) + struct dirent dirent_st; +#endif + struct stat sb; + pchar *entry_path; + psize path_len; +#ifdef P_DIR_NEED_BUF_ALLOC + pint name_max; +#endif + + if (P_UNLIKELY (dir == NULL || dir->dir == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return NULL; + } + +#ifdef P_DIR_NEED_BUF_ALLOC +# if defined (P_OS_SOLARIS) + name_max = (pint) (FILENAME_MAX); +# elif defined (P_OS_SCO) || defined (P_OS_IRIX) + name_max = (pint) pathconf (dir->orig_path, _PC_NAME_MAX); + + if (name_max == -1) { + if (p_error_get_last_system () == 0) + name_max = _POSIX_PATH_MAX; + else { + p_error_set_error_p (error, + (pint) P_ERROR_IO_FAILED, + 0, + "Failed to get NAME_MAX using pathconf()"); + return NULL; + } + } +# elif defined (P_OS_QNX6) || defined (P_OS_UNIXWARE) || defined (P_OS_HAIKU) + name_max = (pint) (NAME_MAX); +# endif + + if (P_UNLIKELY ((dirent_st = p_malloc0 (sizeof (struct dirent) + name_max + 1)) == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_NO_RESOURCES, + 0, + "Failed to allocate memory for internal directory entry"); + return NULL; + } + +# ifdef P_DIR_NEED_SIMPLE_R + p_error_set_last_system (0); + + if ((dir->dir_result = readdir_r (dir->dir, dirent_st)) == NULL) { + if (P_UNLIKELY (p_error_get_last_system () != 0)) { + p_error_set_error_p (error, + (pint) p_error_get_last_io (), + p_error_get_last_system (), + "Failed to call readdir_r() to read directory stream"); + p_free (dirent_st); + return NULL; + } + } +# else + if (P_UNLIKELY (readdir_r (dir->dir, dirent_st, &dir->dir_result) != 0)) { + p_error_set_error_p (error, + (pint) p_error_get_last_io (), + p_error_get_last_system (), + "Failed to call readdir_r() to read directory stream"); + p_free (dirent_st); + return NULL; + } +# endif +#else +# ifdef P_DIR_NON_REENTRANT + p_error_set_last_system (0); + + if ((dir->dir_result = readdir (dir->dir)) == NULL) { + if (P_UNLIKELY (p_error_get_last_system () != 0)) { + p_error_set_error_p (error, + (pint) p_error_get_last_io (), + p_error_get_last_system (), + "Failed to call readdir() to read directory stream"); + return NULL; + } + } +# else + if (P_UNLIKELY (readdir_r (dir->dir, &dirent_st, &dir->dir_result) != 0)) { + p_error_set_error_p (error, + (pint) p_error_get_last_io (), + p_error_get_last_system (), + "Failed to call readdir_r() to read directory stream"); + return NULL; + } +# endif +#endif + + if (dir->dir_result == NULL) { +#ifdef P_DIR_NEED_BUF_ALLOC + p_free (dirent_st); +#endif + return NULL; + } + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PDirEntry))) == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_NO_RESOURCES, + 0, + "Failed to allocate memory for directory entry"); +#ifdef P_DIR_NEED_BUF_ALLOC + p_free (dirent_st); +#endif + return NULL; + } + +#ifdef P_DIR_NEED_BUF_ALLOC + ret->name = p_strdup (dirent_st->d_name); + p_free (dirent_st); +#else +# ifdef P_DIR_NON_REENTRANT + ret->name = p_strdup (dir->dir_result->d_name); +# else + ret->name = p_strdup (dirent_st.d_name); +# endif +#endif + + path_len = strlen (dir->path); + + if (P_UNLIKELY ((entry_path = p_malloc0 (path_len + strlen (ret->name) + 2)) == NULL)) { + P_WARNING ("PDir::p_dir_get_next_entry: failed to allocate memory for stat()"); + ret->type = P_DIR_ENTRY_TYPE_OTHER; + return ret; + } + + strcat (entry_path, dir->path); + *(entry_path + path_len) = '/'; + strcat (entry_path + path_len + 1, ret->name); + + if (P_UNLIKELY (stat (entry_path, &sb) != 0)) { + P_WARNING ("PDir::p_dir_get_next_entry: stat() failed"); + ret->type = P_DIR_ENTRY_TYPE_OTHER; + p_free (entry_path); + return ret; + } + + p_free (entry_path); + + if (S_ISDIR (sb.st_mode)) + ret->type = P_DIR_ENTRY_TYPE_DIR; + else if (S_ISREG (sb.st_mode)) + ret->type = P_DIR_ENTRY_TYPE_FILE; + else + ret->type = P_DIR_ENTRY_TYPE_OTHER; + + return ret; +} + +P_LIB_API pboolean +p_dir_rewind (PDir *dir, + PError **error) +{ + if (P_UNLIKELY (dir == NULL || dir->dir == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return FALSE; + } + + rewinddir (dir->dir); + + return TRUE; +} + +P_LIB_API void +p_dir_free (PDir *dir) +{ + if (P_UNLIKELY (dir == NULL)) + return; + + if (P_LIKELY (dir->dir != NULL)) { + if (P_UNLIKELY (closedir (dir->dir) != 0)) + P_ERROR ("PDir::p_dir_free: closedir() failed"); + } + + p_free (dir->path); + p_free (dir->orig_path); + p_free (dir); +} diff --git a/3rdparty/plibsys/src/pdir-win.c b/3rdparty/plibsys/src/pdir-win.c new file mode 100644 index 0000000..8c6df15 --- /dev/null +++ b/3rdparty/plibsys/src/pdir-win.c @@ -0,0 +1,291 @@ +/* + * The MIT License + * + * Copyright (C) 2015-2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pdir.h" +#include "perror.h" +#include "pmem.h" +#include "pstring.h" +#include "perror-private.h" + +#include <stdlib.h> +#include <string.h> + +struct PDir_ { + WIN32_FIND_DATAA find_data; + HANDLE search_handle; + pboolean cached; + pchar path[MAX_PATH + 3]; + pchar *orig_path; +}; + +P_LIB_API PDir * +p_dir_new (const pchar *path, + PError **error) +{ + PDir *ret; + pchar *pathp; + + if (P_UNLIKELY (path == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return NULL; + } + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PDir))) == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_NO_RESOURCES, + 0, + "Failed to allocate memory for directory structure"); + return NULL; + } + + if (P_UNLIKELY (!GetFullPathNameA (path, MAX_PATH, ret->path, NULL))) { + p_error_set_error_p (error, + (pint) p_error_get_last_io (), + p_error_get_last_system (), + "Failed to call GetFullPathNameA() to get directory path"); + p_free (ret); + return NULL; + } + + /* Append the search pattern "\\*\0" to the directory name */ + pathp = strchr (ret->path, '\0'); + + if (ret->path < pathp && *(pathp - 1) != '\\' && *(pathp - 1) != ':') + *pathp++ = '\\'; + + *pathp++ = '*'; + *pathp = '\0'; + + /* Open directory stream and retrieve the first entry */ + ret->search_handle = FindFirstFileA (ret->path, &ret->find_data); + + if (P_UNLIKELY (ret->search_handle == INVALID_HANDLE_VALUE)) { + p_error_set_error_p (error, + (pint) p_error_get_last_io (), + p_error_get_last_system (), + "Failed to call FindFirstFileA() to open directory stream"); + p_free (ret); + return NULL; + } + + ret->cached = TRUE; + ret->orig_path = p_strdup (path); + + return ret; +} + +P_LIB_API pboolean +p_dir_create (const pchar *path, + pint mode, + PError **error) +{ + P_UNUSED (mode); + + if (P_UNLIKELY (path == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return FALSE; + } + + if (p_dir_is_exists (path)) + return TRUE; + + if (P_UNLIKELY (CreateDirectoryA (path, NULL) == 0)) { + p_error_set_error_p (error, + (pint) p_error_get_last_io (), + p_error_get_last_system (), + "Failed to call CreateDirectoryA() to create directory"); + return FALSE; + } else + return TRUE; +} + +P_LIB_API pboolean +p_dir_remove (const pchar *path, + PError **error) +{ + if (P_UNLIKELY (path == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return FALSE; + } + + if (!p_dir_is_exists (path)) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_NOT_EXISTS, + 0, + "Specified directory doesn't exist"); + return FALSE; + } + + if (P_UNLIKELY (RemoveDirectoryA (path) == 0)) { + p_error_set_error_p (error, + (pint) p_error_get_last_io (), + p_error_get_last_system (), + "Failed to call RemoveDirectoryA() to remove directory"); + return FALSE; + } else + return TRUE; +} + +P_LIB_API pboolean +p_dir_is_exists (const pchar *path) +{ + DWORD dwAttrs; + + if (P_UNLIKELY (path == NULL)) + return FALSE; + + dwAttrs = GetFileAttributesA (path); + + return (dwAttrs != INVALID_FILE_ATTRIBUTES) && (dwAttrs & FILE_ATTRIBUTE_DIRECTORY); +} + +P_LIB_API pchar * +p_dir_get_path (const PDir *dir) +{ + if (P_UNLIKELY (dir == NULL)) + return NULL; + + return p_strdup (dir->orig_path); +} + +P_LIB_API PDirEntry * +p_dir_get_next_entry (PDir *dir, + PError **error) +{ + PDirEntry *ret; + DWORD dwAttrs; + + if (P_UNLIKELY (dir == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return NULL; + } + + if (dir->cached == TRUE) + dir->cached = FALSE; + else { + if (P_UNLIKELY (dir->search_handle == INVALID_HANDLE_VALUE)) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_INVALID_ARGUMENT, + 0, + "Not a valid (or closed) directory stream"); + return NULL; + } + + if (P_UNLIKELY (!FindNextFileA (dir->search_handle, &dir->find_data))) { + p_error_set_error_p (error, + (pint) p_error_get_last_io (), + p_error_get_last_system (), + "Failed to call FindNextFileA() to read directory stream"); + FindClose (dir->search_handle); + dir->search_handle = INVALID_HANDLE_VALUE; + return NULL; + } + } + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PDirEntry))) == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_NO_RESOURCES, + 0, + "Failed to allocate memory for directory entry"); + return NULL; + } + + ret->name = p_strdup (dir->find_data.cFileName); + + dwAttrs = dir->find_data.dwFileAttributes; + + if (dwAttrs & FILE_ATTRIBUTE_DIRECTORY) + ret->type = P_DIR_ENTRY_TYPE_DIR; + else if (dwAttrs & FILE_ATTRIBUTE_DEVICE) + ret->type = P_DIR_ENTRY_TYPE_OTHER; + else + ret->type = P_DIR_ENTRY_TYPE_FILE; + + return ret; +} + +P_LIB_API pboolean +p_dir_rewind (PDir *dir, + PError **error) +{ + if (P_UNLIKELY (dir == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return FALSE; + } + + if (dir->search_handle != INVALID_HANDLE_VALUE) { + if (P_UNLIKELY (FindClose (dir->search_handle) == 0)) { + p_error_set_error_p (error, + (pint) p_error_get_last_io (), + p_error_get_last_system (), + "Failed to call FindClose() to close directory stream"); + return FALSE; + } + } + + dir->search_handle = FindFirstFileA (dir->path, &dir->find_data); + + if (P_UNLIKELY (dir->search_handle == INVALID_HANDLE_VALUE)) { + p_error_set_error_p (error, + (pint) p_error_get_last_io (), + p_error_get_last_system (), + "Failed to call FindFirstFileA() to open directory stream"); + dir->cached = FALSE; + return FALSE; + } else { + dir->cached = TRUE; + return TRUE; + } +} + +P_LIB_API void +p_dir_free (PDir *dir) +{ + if (dir == NULL) + return; + + if (P_LIKELY (dir->search_handle != INVALID_HANDLE_VALUE)) { + if (P_UNLIKELY (!FindClose (dir->search_handle))) + P_ERROR ("PDir::p_dir_free: FindClose() failed"); + } + + p_free (dir->orig_path); + p_free (dir); +} diff --git a/3rdparty/plibsys/src/pdir.c b/3rdparty/plibsys/src/pdir.c new file mode 100644 index 0000000..4166cf7 --- /dev/null +++ b/3rdparty/plibsys/src/pdir.c @@ -0,0 +1,37 @@ +/* + * The MIT License + * + * Copyright (C) 2015-2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pmem.h" +#include "pdir.h" + +P_LIB_API void +p_dir_entry_free (PDirEntry *entry) +{ + if (P_UNLIKELY (entry == NULL)) + return; + + p_free (entry->name); + p_free (entry); +} diff --git a/3rdparty/plibsys/src/pdir.h b/3rdparty/plibsys/src/pdir.h new file mode 100644 index 0000000..9438a34 --- /dev/null +++ b/3rdparty/plibsys/src/pdir.h @@ -0,0 +1,177 @@ +/* + * The MIT License + * + * Copyright (C) 2015-2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * @file pdir.h + * @brief Filesystem interface + * @author Alexander Saprykin + * + * A traditional filesystem can be presented as a combination of directories and + * files within a defined hierarchy. A directory contains the so called entries: + * files and other directories. #PDir allows to iterate through these entries + * without reading their contents, thus building a filesystem hierarchy tree. + * + * Think of this module as an interface to the well-known `dirent` API. + * + * First you need to open a directory for iterating through its content entries + * using p_dir_new(). After that every next entry inside the directory can be + * read with the p_dir_get_next_entry() call until it returns NULL (though it's + * better to check an error code to be sure no error occurred). + * + * Also some directory manipulation routines are provided to create, remove and + * check existance. + */ + +#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION) +# error "Header files shouldn't be included directly, consider using <plibsys.h> instead." +#endif + +#ifndef PLIBSYS_HEADER_PDIR_H +#define PLIBSYS_HEADER_PDIR_H + +#include <pmacros.h> +#include <ptypes.h> +#include <perror.h> + +P_BEGIN_DECLS + +/** Directory opaque data structure. */ +typedef struct PDir_ PDir; + +/** Directory entry types. */ +typedef enum PDirEntryType_ { + P_DIR_ENTRY_TYPE_DIR = 1, /**< Directory. */ + P_DIR_ENTRY_TYPE_FILE = 2, /**< File. */ + P_DIR_ENTRY_TYPE_OTHER = 3 /**< Other. */ +} PDirEntryType; + +/** Structure with directory entry information. */ +typedef struct PDirEntry_ { + char *name; /**< Name. */ + PDirEntryType type; /**< Type. */ +} PDirEntry; + +/** + * @brief Creates a new #PDir object. + * @param path Directory path. + * @return Pointer to a newly created #PDir object in case of success, NULL + * otherwise. + * @param[out] error Error report object, NULL to ignore. + * @since 0.0.1 + * @note If you want to create a new directory on a filesystem, use + * p_dir_create() instead. + */ +P_LIB_API PDir * p_dir_new (const pchar *path, + PError **error); + +/** + * @brief Creates a new directory on a filesystem. + * @param path Directory path. + * @param mode Directory permissions to use, ignored on Windows. + * @param[out] error Error report object, NULL to ignore. + * @return TRUE in case of success, FALSE otherwise. + * @since 0.0.1 + * @note Call returns TRUE if the directory @a path is already exists. + * @note On OpenVMS operating system it creates intermediate directories as + * well. + */ +P_LIB_API pboolean p_dir_create (const pchar *path, + pint mode, + PError **error); + +/** + * @brief Removes an empty directory. + * @param path Directory path to remove. + * @param[out] error Error report object, NULL to ignore. + * @return TRUE in case of success, FALSE otherwise. + * @since 0.0.1 + * + * The directory @a path should be empty to be removed successfully. + */ +P_LIB_API pboolean p_dir_remove (const pchar *path, + PError **error); + +/** + * @brief Checks whether a directory exists or not. + * @param path Directory path. + * @return TRUE in case of success, FALSE otherwise. + * @since 0.0.1 + */ +P_LIB_API pboolean p_dir_is_exists (const pchar *path); + +/** + * @brief Gets the original directory path used to create a #PDir object. + * @param dir #PDir object to retrieve the path from. + * @return The directory path in case of success, NULL otherwise. + * @since 0.0.1 + * + * Caller takes ownership of the returned string. Use p_free() to free memory + * after usage. + */ +P_LIB_API pchar * p_dir_get_path (const PDir *dir); + +/** + * @brief Gets the next directory entry info. + * @param dir Directory to get the next entry from. + * @param[out] error Error report object, NULL to ignore. + * @return Info for the next entry in case of success, NULL otherwise. + * @since 0.0.1 + * + * Caller takes ownership of the returned object. Use p_dir_entry_free() to free + * memory of the directory entry after usage. + * + * An error is set only if it is occurred. You should check the @a error object + * for #P_ERROR_IO_NO_MORE code. + */ +P_LIB_API PDirEntry * p_dir_get_next_entry (PDir *dir, + PError **error); + +/** + * @brief Resets a directory entry pointer. + * @param dir Directory to reset the entry pointer. + * @param[out] error Error report object, NULL to ignore. + * @return TRUE in case of success, FALSE otherwise. + * @since 0.0.1 + */ +P_LIB_API pboolean p_dir_rewind (PDir *dir, + PError **error); + +/** + * @brief Frees #PDirEntry object. + * @param entry #PDirEntry to free. + * @since 0.0.1 + */ +P_LIB_API void p_dir_entry_free (PDirEntry *entry); + +/** + * @brief Frees #PDir object. + * @param dir #PDir to free. + * @since 0.0.1 + */ +P_LIB_API void p_dir_free (PDir *dir); + +P_END_DECLS + +#endif /* PLIBSYS_HEADER_PDIR_H */ diff --git a/3rdparty/plibsys/src/perror-private.h b/3rdparty/plibsys/src/perror-private.h new file mode 100644 index 0000000..b108583 --- /dev/null +++ b/3rdparty/plibsys/src/perror-private.h @@ -0,0 +1,67 @@ +/* + * The MIT License + * + * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION) +# error "Header files shouldn't be included directly, consider using <plibsys.h> instead." +#endif + +#ifndef PLIBSYS_HEADER_PERROR_PRIVATE_H +#define PLIBSYS_HEADER_PERROR_PRIVATE_H + +#include "pmacros.h" +#include "ptypes.h" +#include "perrortypes.h" + +P_BEGIN_DECLS + +/** + * @brief Gets an IO error code from a system error code. + * @param err_code System error code. + * @return IO error code. + */ +PErrorIO p_error_get_io_from_system (pint err_code); + +/** + * @brief Gets an IO error code from the last call result. + * @return IO error code. + */ +PErrorIO p_error_get_last_io (void); + +/** + * @brief Gets an IPC error code from a system error code + * @param err_code System error code. + * @return IPC error code. + */ +PErrorIPC p_error_get_ipc_from_system (pint err_code); + +/** + * @brief Gets an IPC error code from the last call result. + * @return IPC error code. + */ +PErrorIPC p_error_get_last_ipc (void); + +P_END_DECLS + +#endif /* PLIBSYS_HEADER_PERROR_PRIVATE_H */ diff --git a/3rdparty/plibsys/src/perror.c b/3rdparty/plibsys/src/perror.c new file mode 100644 index 0000000..c26dfa1 --- /dev/null +++ b/3rdparty/plibsys/src/perror.c @@ -0,0 +1,878 @@ +/* + * The MIT License + * + * Copyright (C) 2016-2018 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "perror.h" +#include "pmem.h" +#include "pstring.h" +#include "perror-private.h" + +#ifndef P_OS_WIN +# if defined (P_OS_OS2) +# define INCL_DOSERRORS +# include <os2.h> +# include <sys/socket.h> +# endif +# include <errno.h> +#endif + +struct PError_ { + pint code; + pint native_code; + pchar *message; +}; + +PErrorIO +p_error_get_io_from_system (pint err_code) +{ + switch (err_code) { + case 0: + return P_ERROR_IO_NONE; +#if defined (P_OS_WIN) +# ifdef WSAEADDRINUSE + case WSAEADDRINUSE: + return P_ERROR_IO_ADDRESS_IN_USE; +# endif +# ifdef WSAEWOULDBLOCK + case WSAEWOULDBLOCK: + return P_ERROR_IO_WOULD_BLOCK; +# endif +# ifdef WSAEACCES + case WSAEACCES: + return P_ERROR_IO_ACCESS_DENIED; +# endif +# ifdef WSA_INVALID_HANDLE + case WSA_INVALID_HANDLE: + return P_ERROR_IO_INVALID_ARGUMENT; +# endif +# ifdef WSA_INVALID_PARAMETER + case WSA_INVALID_PARAMETER: + return P_ERROR_IO_INVALID_ARGUMENT; +# endif +# ifdef WSAEBADF + case WSAEBADF: + return P_ERROR_IO_INVALID_ARGUMENT; +# endif +# ifdef WSAENOTSOCK + case WSAENOTSOCK: + return P_ERROR_IO_INVALID_ARGUMENT; +# endif +# ifdef WSAEINVAL + case WSAEINVAL: + return P_ERROR_IO_INVALID_ARGUMENT; +# endif +# ifdef WSAESOCKTNOSUPPORT + case WSAESOCKTNOSUPPORT: + return P_ERROR_IO_NOT_SUPPORTED; +# endif +# ifdef WSAEOPNOTSUPP + case WSAEOPNOTSUPP: + return P_ERROR_IO_NOT_SUPPORTED; +# endif +# ifdef WSAEPFNOSUPPORT + case WSAEPFNOSUPPORT: + return P_ERROR_IO_NOT_SUPPORTED; +# endif +# ifdef WSAEAFNOSUPPORT + case WSAEAFNOSUPPORT: + return P_ERROR_IO_NOT_SUPPORTED; +# endif +# ifdef WSAEPROTONOSUPPORT + case WSAEPROTONOSUPPORT: + return P_ERROR_IO_NOT_SUPPORTED; +# endif +# ifdef WSAECANCELLED + case WSAECANCELLED: + return P_ERROR_IO_ABORTED; +# endif +# ifdef ERROR_ALREADY_EXISTS + case ERROR_ALREADY_EXISTS: + return P_ERROR_IO_EXISTS; +# endif +# ifdef ERROR_FILE_NOT_FOUND + case ERROR_FILE_NOT_FOUND: + return P_ERROR_IO_NOT_EXISTS; +# endif +# ifdef ERROR_NO_MORE_FILES + case ERROR_NO_MORE_FILES: + return P_ERROR_IO_NO_MORE; +# endif +# ifdef ERROR_ACCESS_DENIED + case ERROR_ACCESS_DENIED: + return P_ERROR_IO_ACCESS_DENIED; +# endif +# ifdef ERROR_OUTOFMEMORY + case ERROR_OUTOFMEMORY: + return P_ERROR_IO_NO_RESOURCES; +# endif +# ifdef ERROR_NOT_ENOUGH_MEMORY + case ERROR_NOT_ENOUGH_MEMORY: + return P_ERROR_IO_NO_RESOURCES; +# endif +# ifdef ERROR_INVALID_HANDLE +# if !defined(WSA_INVALID_HANDLE) || (ERROR_INVALID_HANDLE != WSA_INVALID_HANDLE) + case ERROR_INVALID_HANDLE: + return P_ERROR_IO_INVALID_ARGUMENT; +# endif +# endif +# ifdef ERROR_INVALID_PARAMETER +# if !defined(WSA_INVALID_PARAMETER) || (ERROR_INVALID_PARAMETER != WSA_INVALID_PARAMETER) + case ERROR_INVALID_PARAMETER: + return P_ERROR_IO_INVALID_ARGUMENT; +# endif +# endif +# ifdef ERROR_NOT_SUPPORTED + case ERROR_NOT_SUPPORTED: + return P_ERROR_IO_NOT_SUPPORTED; +# endif +#elif defined (P_OS_OS2) +# ifdef ERROR_FILE_NOT_FOUND + case ERROR_FILE_NOT_FOUND: + return P_ERROR_IO_NOT_EXISTS; +# endif +# ifdef ERROR_PATH_NOT_FOUND + case ERROR_PATH_NOT_FOUND: + return P_ERROR_IO_NOT_EXISTS; +# endif +# ifdef ERROR_TOO_MANY_OPEN_FILES + case ERROR_TOO_MANY_OPEN_FILES: + return P_ERROR_IO_NO_RESOURCES; +# endif +# ifdef ERROR_NOT_ENOUGH_MEMORY + case ERROR_NOT_ENOUGH_MEMORY: + return P_ERROR_IO_NO_RESOURCES; +# endif +# ifdef ERROR_ACCESS_DENIED + case ERROR_ACCESS_DENIED: + return P_ERROR_IO_ACCESS_DENIED; +# endif +# ifdef ERROR_INVALID_HANDLE + case ERROR_INVALID_HANDLE: + return P_ERROR_IO_INVALID_ARGUMENT; +# endif +# ifdef ERROR_INVALID_PARAMETER + case ERROR_INVALID_PARAMETER: + return P_ERROR_IO_INVALID_ARGUMENT; +# endif +# ifdef ERROR_INVALID_ADDRESS + case ERROR_INVALID_ADDRESS: + return P_ERROR_IO_INVALID_ARGUMENT; +# endif +# ifdef ERROR_NO_MORE_FILES + case ERROR_NO_MORE_FILES: + return P_ERROR_IO_NO_MORE; +# endif +# ifdef ERROR_NOT_SUPPORTED + case ERROR_NOT_SUPPORTED: + return P_ERROR_IO_NOT_SUPPORTED; +# endif +# ifdef ERROR_FILE_EXISTS + case ERROR_FILE_EXISTS: + return P_ERROR_IO_EXISTS; +# endif +#else /* !P_OS_WIN && !P_OS_OS2 */ +# ifdef EACCES + case EACCES: + return P_ERROR_IO_ACCESS_DENIED; +# endif + +# ifdef EPERM + case EPERM: + return P_ERROR_IO_ACCESS_DENIED; +# endif + +# ifdef ENOMEM + case ENOMEM: + return P_ERROR_IO_NO_RESOURCES; +# endif + +# ifdef ENOSR + case ENOSR: + return P_ERROR_IO_NO_RESOURCES; +# endif + +# ifdef ENOBUFS + case ENOBUFS: + return P_ERROR_IO_NO_RESOURCES; +# endif + +# ifdef ENFILE + case ENFILE: + return P_ERROR_IO_NO_RESOURCES; +# endif + +# ifdef ENOSPC + case ENOSPC: + return P_ERROR_IO_NO_RESOURCES; +# endif + +# ifdef EMFILE + case EMFILE: + return P_ERROR_IO_NO_RESOURCES; +# endif + +# ifdef EINVAL + case EINVAL: + return P_ERROR_IO_INVALID_ARGUMENT; +# endif + +# ifdef EBADF + case EBADF: + return P_ERROR_IO_INVALID_ARGUMENT; +# endif + +# ifdef ENOTSOCK + case ENOTSOCK: + return P_ERROR_IO_INVALID_ARGUMENT; +# endif + +# ifdef EFAULT + case EFAULT: + return P_ERROR_IO_INVALID_ARGUMENT; +# endif + +# ifdef EPROTOTYPE + case EPROTOTYPE: + return P_ERROR_IO_INVALID_ARGUMENT; +# endif + + /* On Linux these errors can have same codes */ +# if defined(ENOTSUP) && (!defined(EOPNOTSUPP) || ENOTSUP != EOPNOTSUPP) + case ENOTSUP: + return P_ERROR_IO_NOT_SUPPORTED; +# endif + +# ifdef ENOPROTOOPT + case ENOPROTOOPT: + return P_ERROR_IO_NOT_SUPPORTED; +# endif + +# ifdef EPROTONOSUPPORT + case EPROTONOSUPPORT: + return P_ERROR_IO_NOT_SUPPORTED; +# endif + +# ifdef EAFNOSUPPORT + case EAFNOSUPPORT: + return P_ERROR_IO_NOT_SUPPORTED; +# endif + +# ifdef EOPNOTSUPP + case EOPNOTSUPP: + return P_ERROR_IO_NOT_SUPPORTED; +# endif + +# ifdef EADDRNOTAVAIL + case EADDRNOTAVAIL: + return P_ERROR_IO_NOT_AVAILABLE; +# endif + +# ifdef ENETUNREACH + case ENETUNREACH: + return P_ERROR_IO_NOT_AVAILABLE; +# endif + +# ifdef ENETDOWN + case ENETDOWN: + return P_ERROR_IO_NOT_AVAILABLE; +# endif + +# ifdef EHOSTDOWN + case EHOSTDOWN: + return P_ERROR_IO_NOT_AVAILABLE; +# endif + +# ifdef ENONET + case ENONET: + return P_ERROR_IO_NOT_AVAILABLE; +# endif + +# ifdef EHOSTUNREACH + case EHOSTUNREACH: + return P_ERROR_IO_NOT_AVAILABLE; +# endif + +# ifdef EINPROGRESS + case EINPROGRESS: + return P_ERROR_IO_IN_PROGRESS; +# endif + +# ifdef EALREADY + case EALREADY: + return P_ERROR_IO_IN_PROGRESS; +# endif + +# ifdef EISCONN + case EISCONN: + return P_ERROR_IO_CONNECTED; +# endif + +# ifdef ECONNREFUSED + case ECONNREFUSED: + return P_ERROR_IO_CONNECTION_REFUSED; +# endif + +# ifdef ENOTCONN + case ENOTCONN: + return P_ERROR_IO_NOT_CONNECTED; +# endif + +# ifdef ECONNABORTED + case ECONNABORTED: + return P_ERROR_IO_ABORTED; +# endif + +# ifdef EADDRINUSE + case EADDRINUSE: + return P_ERROR_IO_ADDRESS_IN_USE; +# endif + +# ifdef ETIMEDOUT + case ETIMEDOUT: + return P_ERROR_IO_TIMED_OUT; +# endif + +# ifdef EDQUOT + case EDQUOT: + return P_ERROR_IO_QUOTA; +# endif + +# ifdef EISDIR + case EISDIR: + return P_ERROR_IO_IS_DIRECTORY; +# endif + +# ifdef ENOTDIR + case ENOTDIR: + return P_ERROR_IO_NOT_DIRECTORY; +# endif + +# ifdef EEXIST + case EEXIST: + return P_ERROR_IO_EXISTS; +# endif + +# ifdef ENOENT + case ENOENT: + return P_ERROR_IO_NOT_EXISTS; +# endif + +# ifdef ENAMETOOLONG + case ENAMETOOLONG: + return P_ERROR_IO_NAMETOOLONG; +# endif + +# ifdef ENOSYS + case ENOSYS: + return P_ERROR_IO_NOT_IMPLEMENTED; +# endif + + /* Some magic to deal with EWOULDBLOCK and EAGAIN. + * Apparently on HP-UX these are actually defined to different values, + * but on Linux, for example, they are the same. */ +# if defined(EWOULDBLOCK) && defined(EAGAIN) && EWOULDBLOCK == EAGAIN + /* We have both and they are the same: only emit one case. */ + case EAGAIN: + return P_ERROR_IO_WOULD_BLOCK; +# else + /* Else: consider each of them separately. This handles both the + * case of having only one and the case where they are different values. */ +# ifdef EAGAIN + case EAGAIN: + return P_ERROR_IO_WOULD_BLOCK; +# endif + +# ifdef EWOULDBLOCK + case EWOULDBLOCK: + return P_ERROR_IO_WOULD_BLOCK; +# endif +# endif +#endif /* !P_OS_WIN */ + default: + return P_ERROR_IO_FAILED; + } +} + +PErrorIO +p_error_get_last_io (void) +{ + return p_error_get_io_from_system (p_error_get_last_system ()); +} + +PErrorIPC +p_error_get_ipc_from_system (pint err_code) +{ + switch (err_code) { + case 0: + return P_ERROR_IPC_NONE; +#ifdef P_OS_WIN +# ifdef ERROR_ALREADY_EXISTS + case ERROR_ALREADY_EXISTS: + return P_ERROR_IPC_EXISTS; +# endif +# ifdef ERROR_SEM_OWNER_DIED + case ERROR_SEM_OWNER_DIED: + return P_ERROR_IPC_NOT_EXISTS; +# endif +# ifdef ERROR_SEM_NOT_FOUND + case ERROR_SEM_NOT_FOUND: + return P_ERROR_IPC_NOT_EXISTS; +# endif +# ifdef ERROR_SEM_USER_LIMIT + case ERROR_SEM_USER_LIMIT: + return P_ERROR_IPC_NO_RESOURCES; +# endif +# ifdef ERROR_TOO_MANY_SEMAPHORES + case ERROR_TOO_MANY_SEMAPHORES: + return P_ERROR_IPC_NO_RESOURCES; +# endif +# ifdef ERROR_ACCESS_DENIED + case ERROR_ACCESS_DENIED: + return P_ERROR_IPC_ACCESS; +# endif +# ifdef ERROR_EXCL_SEM_ALREADY_OWNED + case ERROR_EXCL_SEM_ALREADY_OWNED: + return P_ERROR_IPC_ACCESS; +# endif +# ifdef ERROR_TOO_MANY_SEM_REQUESTS + case ERROR_TOO_MANY_SEM_REQUESTS: + return P_ERROR_IPC_NO_RESOURCES; +# endif +# ifdef ERROR_TOO_MANY_POSTS + case ERROR_TOO_MANY_POSTS: + return P_ERROR_IPC_NO_RESOURCES; +# endif +# ifdef ERROR_OUTOFMEMORY + case ERROR_OUTOFMEMORY: + return P_ERROR_IPC_NO_RESOURCES; +# endif +# ifdef ERROR_NOT_ENOUGH_MEMORY + case ERROR_NOT_ENOUGH_MEMORY: + return P_ERROR_IPC_NO_RESOURCES; +# endif +# ifdef ERROR_INVALID_HANDLE + case ERROR_INVALID_HANDLE: + return P_ERROR_IPC_INVALID_ARGUMENT; +# endif +# ifdef ERROR_INVALID_PARAMETER + case ERROR_INVALID_PARAMETER: + return P_ERROR_IPC_INVALID_ARGUMENT; +# endif +# ifdef ERROR_NOT_SUPPORTED + case ERROR_NOT_SUPPORTED: + return P_ERROR_IPC_NOT_IMPLEMENTED; +# endif +#elif defined (P_OS_OS2) +# ifdef ERROR_NOT_ENOUGH_MEMORY + case ERROR_NOT_ENOUGH_MEMORY: + return P_ERROR_IPC_NO_RESOURCES; +# endif +# ifdef ERROR_INVALID_PARAMETER + case ERROR_INVALID_PARAMETER: + return P_ERROR_IPC_INVALID_ARGUMENT; +# endif +# ifdef ERROR_INVALID_NAME + case ERROR_INVALID_NAME: + return P_ERROR_IPC_INVALID_ARGUMENT; +# endif +# ifdef ERROR_INVALID_HANDLE + case ERROR_INVALID_HANDLE: + return P_ERROR_IPC_INVALID_ARGUMENT; +# endif +# ifdef ERROR_FILE_NOT_FOUND + case ERROR_FILE_NOT_FOUND: + return P_ERROR_IPC_NOT_EXISTS; +# endif +# ifdef ERROR_INVALID_ADDRESS + case ERROR_INVALID_ADDRESS: + return P_ERROR_IPC_INVALID_ARGUMENT; +# endif +# ifdef ERROR_GEN_FAILURE + case ERROR_GEN_FAILURE: + return P_ERROR_IPC_FAILED; +# endif +# ifdef ERROR_LOCKED + case ERROR_LOCKED: + return P_ERROR_IPC_ACCESS; +# endif +# ifdef ERROR_DUPLICATE_NAME + case ERROR_DUPLICATE_NAME: + return P_ERROR_IPC_EXISTS; +# endif +# ifdef ERROR_TOO_MANY_HANDLES + case ERROR_TOO_MANY_HANDLES: + return P_ERROR_IPC_NO_RESOURCES; +# endif +# ifdef ERROR_TOO_MANY_OPENS + case ERROR_TOO_MANY_OPENS: + return P_ERROR_IPC_NO_RESOURCES; +# endif +# ifdef ERROR_TOO_MANY_SEM_REQUESTS + case ERROR_TOO_MANY_SEM_REQUESTS: + return P_ERROR_IPC_NO_RESOURCES; +# endif +# ifdef ERROR_SEM_OWNER_DIED + case ERROR_SEM_OWNER_DIED: + return P_ERROR_IPC_NOT_EXISTS; +# endif +# ifdef ERROR_NOT_OWNER + case ERROR_NOT_OWNER: + return P_ERROR_IPC_ACCESS; +# endif +# ifdef ERROR_SEM_NOT_FOUND + case ERROR_SEM_NOT_FOUND: + return P_ERROR_IPC_NOT_EXISTS; +# endif +#else /* !P_OS_WINDOWS && !P_OS_OS2 */ +# ifdef EACCES + case EACCES: + return P_ERROR_IPC_ACCESS; +# endif + +# ifdef EPERM + case EPERM: + return P_ERROR_IPC_ACCESS; +# endif + +# ifdef EEXIST + case EEXIST: + return P_ERROR_IPC_EXISTS; +# endif + +# ifdef E2BIG + case E2BIG: + return P_ERROR_IPC_INVALID_ARGUMENT; +# endif + +# ifdef EFAULT + case EFAULT: + return P_ERROR_IPC_INVALID_ARGUMENT; +# endif + +# ifdef EFBIG + case EFBIG: + return P_ERROR_IPC_INVALID_ARGUMENT; +# endif + +# ifdef EINVAL + case EINVAL: + return P_ERROR_IPC_INVALID_ARGUMENT; +# endif + +# ifdef ELOOP + case ELOOP: + return P_ERROR_IPC_INVALID_ARGUMENT; +# endif + +# ifdef ERANGE + case ERANGE: + return P_ERROR_IPC_INVALID_ARGUMENT; +# endif + +# ifdef ENOMEM + case ENOMEM: + return P_ERROR_IPC_NO_RESOURCES; +# endif + +# ifdef EMFILE + case EMFILE: + return P_ERROR_IPC_NO_RESOURCES; +# endif + +# ifdef ENFILE + case ENFILE: + return P_ERROR_IPC_NO_RESOURCES; +# endif + +# ifdef ENOSPC + case ENOSPC: + return P_ERROR_IPC_NO_RESOURCES; +# endif + +# ifdef EIDRM + case EIDRM: + return P_ERROR_IPC_NOT_EXISTS; +# endif + +# ifdef ENOENT + case ENOENT: + return P_ERROR_IPC_NOT_EXISTS; +# endif + +# ifdef EOVERFLOW + case EOVERFLOW: + return P_ERROR_IPC_OVERFLOW; +# endif + +# ifdef ENOSYS + case ENOSYS: + return P_ERROR_IPC_NOT_IMPLEMENTED; +# endif + +# ifdef EDEADLK + case EDEADLK: + return P_ERROR_IPC_DEADLOCK; +# endif + +# ifdef ENAMETOOLONG + case ENAMETOOLONG: + return P_ERROR_IPC_NAMETOOLONG; +# endif +#endif /* !P_OS_WIN */ + default: + return P_ERROR_IPC_FAILED; + } +} + +PErrorIPC +p_error_get_last_ipc (void) +{ + return p_error_get_ipc_from_system (p_error_get_last_system ()); +} + +P_LIB_API PError * +p_error_new (void) +{ + PError *ret; + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PError))) == NULL)) + return NULL; + + return ret; +} + +P_LIB_API PError * +p_error_new_literal (pint code, + pint native_code, + const pchar *message) +{ + PError *ret; + + if (P_UNLIKELY ((ret = p_error_new ()) == NULL)) + return NULL; + + ret->code = code; + ret->native_code = native_code; + ret->message = p_strdup (message); + + return ret; +} + +P_LIB_API const pchar * +p_error_get_message (PError *error) +{ + if (P_UNLIKELY (error == NULL)) + return NULL; + + return error->message; +} + +P_LIB_API pint +p_error_get_code (PError *error) +{ + if (P_UNLIKELY (error == NULL)) + return 0; + + return error->code; +} + +P_LIB_API pint +p_error_get_native_code (PError *error) +{ + if (P_UNLIKELY (error == NULL)) + return 0; + + return error->native_code; +} + +P_LIB_API PErrorDomain +p_error_get_domain (PError *error) +{ + if (P_UNLIKELY (error == NULL)) + return P_ERROR_DOMAIN_NONE; + + if (error->code >= (pint) P_ERROR_DOMAIN_IPC) + return P_ERROR_DOMAIN_IPC; + else if (error->code >= (pint) P_ERROR_DOMAIN_IO) + return P_ERROR_DOMAIN_IO; + else + return P_ERROR_DOMAIN_NONE; +} + +P_LIB_API PError * +p_error_copy (PError *error) +{ + PError *ret; + + if (P_UNLIKELY (error == NULL)) + return NULL; + + if (P_UNLIKELY ((ret = p_error_new_literal (error->code, + error->native_code, + error->message)) == NULL)) + return NULL; + + return ret; +} + +P_LIB_API void +p_error_set_error (PError *error, + pint code, + pint native_code, + const pchar *message) +{ + if (P_UNLIKELY (error == NULL)) + return; + + if (error->message != NULL) + p_free (error->message); + + error->code = code; + error->native_code = native_code; + error->message = p_strdup (message); +} + +P_LIB_API void +p_error_set_error_p (PError **error, + pint code, + pint native_code, + const pchar *message) +{ + if (error == NULL || *error != NULL) + return; + + *error = p_error_new_literal (code, native_code, message); +} + +P_LIB_API void +p_error_set_code (PError *error, + pint code) +{ + if (P_UNLIKELY (error == NULL)) + return; + + error->code = code; +} + +P_LIB_API void +p_error_set_native_code (PError *error, + pint native_code) +{ + if (P_UNLIKELY (error == NULL)) + return; + + error->native_code = native_code; +} + +P_LIB_API void +p_error_set_message (PError *error, + const pchar *message) +{ + if (P_UNLIKELY (error == NULL)) + return; + + if (error->message != NULL) + p_free (error->message); + + error->message = p_strdup (message); +} + +P_LIB_API void +p_error_clear (PError *error) +{ + if (P_UNLIKELY (error == NULL)) + return; + + if (error->message != NULL) + p_free (error->message); + + error->message = NULL; + error->code = 0; + error->native_code = 0; +} + +P_LIB_API void +p_error_free (PError *error) +{ + if (P_UNLIKELY (error == NULL)) + return; + + if (error->message != NULL) + p_free (error->message); + + p_free (error); +} + +P_LIB_API pint +p_error_get_last_system (void) +{ +#ifdef P_OS_WIN + return (pint) GetLastError (); +#else +# ifdef P_OS_VMS + pint error_code = errno; + + if (error_code == EVMSERR) + return vaxc$errno; + else + return error_code; +# else + return errno; +# endif +#endif +} + +P_LIB_API pint +p_error_get_last_net (void) +{ +#if defined (P_OS_WIN) + return WSAGetLastError (); +#elif defined (P_OS_OS2) + return sock_errno (); +#else + return p_error_get_last_system (); +#endif +} + +P_LIB_API void +p_error_set_last_system (pint code) +{ +#ifdef P_OS_WIN + SetLastError (code); +#else + errno = code; +#endif +} + +P_LIB_API void +p_error_set_last_net (pint code) +{ +#if defined (P_OS_WIN) + WSASetLastError (code); +#elif defined (P_OS_OS2) + P_UNUSED (code); +#else + errno = code; +#endif +} diff --git a/3rdparty/plibsys/src/perror.h b/3rdparty/plibsys/src/perror.h new file mode 100644 index 0000000..c08f53f --- /dev/null +++ b/3rdparty/plibsys/src/perror.h @@ -0,0 +1,249 @@ +/* + * The MIT License + * + * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * @file perror.h + * @brief Error report system + * @author Alexander Saprykin + * + * An error report system is used to notify a caller about fatal situations + * during the library API invocation. Usually the sequence is as following: + * @code + * PError *error = NULL; + * ... + * + * if (error != NULL) { + * ... + * p_error_free (error); + * } + * @endcode + * Note that you should not initialize a new #PError object before passing the + * pointer into an API call. Simply initialize it with zero and check the result + * after. Therefore you need to free memory if an error occurred. + * + * Most operating systems store the last error code of the most system calls in + * a thread-specific variable. Moreover, Windows stores the error code of the + * last socket related call in a separate variable. Use + * p_error_get_last_system() and p_error_set_last_system() to access the last + * system error code, p_error_get_last_net() and p_error_set_last_net() to + * access the last network error code. + */ + +#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION) +# error "Header files shouldn't be included directly, consider using <plibsys.h> instead." +#endif + +#ifndef PLIBSYS_HEADER_PERROR_H +#define PLIBSYS_HEADER_PERROR_H + +#include <pmacros.h> +#include <ptypes.h> +#include <perrortypes.h> + +P_BEGIN_DECLS + +/** Opaque data structure for an error object. */ +typedef struct PError_ PError; + +/** + * @brief Initializes a new empty #PError. + * @return Newly initialized #PError object in case of success, NULL otherwise. + * @since 0.0.1 + */ +P_LIB_API PError * p_error_new (void); + +/** + * @brief Initializes a new #PError with data. + * @param code Error code. + * @param native_code Native error code, leave 0 to ignore. + * @param message Error message. + * @return Newly initialized #PError object in case of success, NULL otherwise. + * @since 0.0.1 + */ +P_LIB_API PError * p_error_new_literal (pint code, + pint native_code, + const pchar *message); + +/** + * @brief Gets an error message. + * @param error #PError object to get the message from. + * @return Error message in case of success, NULL otherwise. + * @since 0.0.1 + */ +P_LIB_API const pchar * p_error_get_message (PError *error); + +/** + * @brief Gets an error code. + * @param error #PError object to get the code from. + * @return Error code in case of success, 0 otherwise. + * @since 0.0.1 + */ +P_LIB_API pint p_error_get_code (PError *error); + +/** + * @brief Gets a platform native error code, if any. + * @param error #PError object to get the native code from. + * @return Error code in case of success, 0 otherwise. + * @since 0.0.1 + * @note In some situations there can be no native code error, i.e. when an + * internal library call failed. Do not rely on this code. + */ +P_LIB_API pint p_error_get_native_code (PError *error); + +/** + * @brief Gets an error domain. + * @param error #PError object to get the domain from. + * @return Error domain in case of success, #P_ERROR_DOMAIN_NONE otherwise. + * @since 0.0.1 + */ +P_LIB_API PErrorDomain p_error_get_domain (PError *error); + +/** + * @brief Creates a copy of a #PError object. + * @param error #PError object to copy. + * @return Newly created #PError object in case of success, NULL otherwise. + * @since 0.0.1 + * @note The caller is responsible to free memory of the created object after + * usage. + */ +P_LIB_API PError * p_error_copy (PError *error); + +/** + * @brief Sets error data. + * @param error #PError object to set the data for. + * @param code Error code. + * @param native_code Native error code, leave 0 to ignore. + * @param message Error message. + * @since 0.0.1 + */ +P_LIB_API void p_error_set_error (PError *error, + pint code, + pint native_code, + const pchar *message); + +/** + * @brief Sets error data through a double pointer. + * @param error #PError object to set the data for. + * @param code Error code. + * @param native_code Native error code, leave 0 to ignore. + * @param message Error message. + * @since 0.0.1 + * + * If @a error is NULL it does nothing. If @a error is not NULL then @a *error + * should be NULL, otherwise it does nothing. It creates a #PError object, sets + * error data and assigns it to @a *error. The caller is responsible to free + * memory of the created object after usage. + */ +P_LIB_API void p_error_set_error_p (PError **error, + pint code, + pint native_code, + const pchar *message); + +/** + * @brief Sets an error code. + * @param error #PError object to set the code for. + * @param code Error code. + * @since 0.0.1 + */ +P_LIB_API void p_error_set_code (PError *error, + pint code); + +/** + * @brief Sets a platform native error code. + * @param error #PError object to set the native error code for. + * @param native_code Platform native error code. + * @since 0.0.1 + */ +P_LIB_API void p_error_set_native_code (PError *error, + pint native_code); + +/** + * @brief Sets an error message. + * @param error #PError object to set the message for. + * @param message Error message. + * @since 0.0.1 + */ +P_LIB_API void p_error_set_message (PError *error, + const pchar *message); + +/** + * @brief Clears error data. + * @param error #PError object to clear the data for. + * @since 0.0.1 + * @note Error code is reseted to 0. + */ +P_LIB_API void p_error_clear (PError *error); + +/** + * @brief Frees a previously initialized error object. + * @param error #PError object to free. + * @since 0.0.1 + */ +P_LIB_API void p_error_free (PError *error); + +/** + * @brief Gets the last system native error code. + * @return Last system native error code. + * @since 0.0.2 + * @sa p_error_get_last_net(), p_error_set_last_system(), + * p_error_set_last_net() + * @note If you want get an error code for socket-related calls, use + * p_error_get_last_net() instead. + */ + +P_LIB_API pint p_error_get_last_system (void); + +/** + * @brief Gets the last network native error code. + * @return Last network native error code. + * @since 0.0.2 + * @sa p_error_get_last_system(), p_error_set_last_net(), + * p_error_set_last_system() + */ +P_LIB_API pint p_error_get_last_net (void); + +/** + * @brief Sets the last system native error code. + * @param code Error code to set. + * @since 0.0.2 + * @sa p_error_set_last_net(), p_error_get_last_system(), + * p_error_get_last_net() + * @note If you want set an error code for socket-related calls, use + * p_error_set_last_net() instead. + */ +P_LIB_API void p_error_set_last_system (pint code); + +/** + * @brief Sets the last network native error code. + * @param code Error code to set. + * @since 0.0.2 + * @sa p_error_set_last_system(), p_error_get_last_net(), + * p_error_get_last_system() + */ +P_LIB_API void p_error_set_last_net (pint code); + +P_END_DECLS + +#endif /* PLIBSYS_HEADER_PERROR_H */ diff --git a/3rdparty/plibsys/src/perrortypes.h b/3rdparty/plibsys/src/perrortypes.h new file mode 100644 index 0000000..e935c8a --- /dev/null +++ b/3rdparty/plibsys/src/perrortypes.h @@ -0,0 +1,106 @@ +/* + * The MIT License + * + * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * @file perrortypes.h + * @brief Error data types + * @author Alexander Saprykin + * + * All error codes are splitted into the several domains. Every error should + * belong to one of the domains described in #PErrorDomain. Think of an error + * domain as a logical subsystem. + * + * Every error domain has its own enumeration with the list of possible error + * codes. System error codes are converted to specified domains using internal + * routines. + */ + +#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION) +# error "Header files shouldn't be included directly, consider using <plibsys.h> instead." +#endif + +#ifndef PLIBSYS_HEADER_PERRORTYPES_H +#define PLIBSYS_HEADER_PERRORTYPES_H + +#include <pmacros.h> + +P_BEGIN_DECLS + +/** Enum with error domains. */ +typedef enum PErrorDomain_ { + P_ERROR_DOMAIN_NONE = 0, /**< No domain was specified. */ + P_ERROR_DOMAIN_IO = 500, /**< Input/output domain. */ + P_ERROR_DOMAIN_IPC = 600 /**< Interprocess communication domain. */ +} PErrorDomain; + +/** Enum with IO errors. */ +typedef enum PErrorIO_ { + P_ERROR_IO_NONE = 500, /**< No error. */ + P_ERROR_IO_NO_RESOURCES = 501, /**< Operating system hasn't enough resources. */ + P_ERROR_IO_NOT_AVAILABLE = 502, /**< Resource isn't available. */ + P_ERROR_IO_ACCESS_DENIED = 503, /**< Access denied. */ + P_ERROR_IO_CONNECTED = 504, /**< Already connected. */ + P_ERROR_IO_IN_PROGRESS = 505, /**< Operation in progress. */ + P_ERROR_IO_ABORTED = 506, /**< Operation aborted. */ + P_ERROR_IO_INVALID_ARGUMENT = 507, /**< Invalid argument specified. */ + P_ERROR_IO_NOT_SUPPORTED = 508, /**< Operation not supported. */ + P_ERROR_IO_TIMED_OUT = 509, /**< Operation timed out. */ + P_ERROR_IO_WOULD_BLOCK = 510, /**< Operation cannot be completed immediatly. */ + P_ERROR_IO_ADDRESS_IN_USE = 511, /**< Address is already under usage. */ + P_ERROR_IO_CONNECTION_REFUSED = 512, /**< Connection refused. */ + P_ERROR_IO_NOT_CONNECTED = 513, /**< Connection required first. */ + P_ERROR_IO_QUOTA = 514, /**< User quota exceeded. */ + P_ERROR_IO_IS_DIRECTORY = 515, /**< Trying to open directory for writing. */ + P_ERROR_IO_NOT_DIRECTORY = 516, /**< Component of the path prefix is not a directory. */ + P_ERROR_IO_NAMETOOLONG = 517, /**< Specified name is too long. */ + P_ERROR_IO_EXISTS = 518, /**< Specified entry already exists. */ + P_ERROR_IO_NOT_EXISTS = 519, /**< Specified entry doesn't exist. */ + P_ERROR_IO_NO_MORE = 520, /**< No more data left. */ + P_ERROR_IO_NOT_IMPLEMENTED = 521, /**< Operation is not implemented. */ + P_ERROR_IO_FAILED = 522 /**< General error. */ +} PErrorIO; + +/** Enum with IPC errors */ +typedef enum PErrorIPC_ { + P_ERROR_IPC_NONE = 600, /**< No error. */ + P_ERROR_IPC_ACCESS = 601, /**< Not enough rights to access object or its key. */ + P_ERROR_IPC_EXISTS = 602, /**< Object already exists and no proper open flags + were specified. */ + P_ERROR_IPC_NOT_EXISTS = 603, /**< Object doesn't exist or was removed before, and + no proper create flags were specified. */ + P_ERROR_IPC_NO_RESOURCES = 604, /**< Not enough system resources or memory to perform + operation. */ + P_ERROR_IPC_OVERFLOW = 605, /**< Semaphore value overflow. */ + P_ERROR_IPC_NAMETOOLONG = 606, /**< Object name is too long. */ + P_ERROR_IPC_INVALID_ARGUMENT = 607, /**< Invalid argument (parameter) specified. */ + P_ERROR_IPC_NOT_IMPLEMENTED = 608, /**< Operation is not implemented (for example when + using some filesystems). */ + P_ERROR_IPC_DEADLOCK = 609, /**< Deadlock detected. */ + P_ERROR_IPC_FAILED = 610 /**< General error. */ +} PErrorIPC; + +P_END_DECLS + +#endif /* PLIBSYS_HEADER_PERRORTYPES_H */ diff --git a/3rdparty/plibsys/src/pfile.c b/3rdparty/plibsys/src/pfile.c new file mode 100644 index 0000000..905b2d7 --- /dev/null +++ b/3rdparty/plibsys/src/pfile.c @@ -0,0 +1,80 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "perror.h" +#include "pfile.h" +#include "perror-private.h" + +#ifndef P_OS_WIN +# include <unistd.h> +#endif + +P_LIB_API pboolean +p_file_is_exists (const pchar *file) +{ +#ifdef P_OS_WIN + DWORD attrs; +#endif + + if (P_UNLIKELY (file == NULL)) + return FALSE; + +#ifdef P_OS_WIN + attrs = GetFileAttributesA ((LPCSTR) file); + + return (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY) == 0); +#else + return access (file, F_OK) == 0; +#endif +} + +P_LIB_API pboolean +p_file_remove (const pchar *file, + PError **error) +{ + pboolean result; + + if (P_UNLIKELY (file == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return FALSE; + } + +#ifdef P_OS_WIN + result = (DeleteFileA ((LPCSTR) file) != 0); +#else + result = (unlink (file) == 0); +#endif + + if (P_UNLIKELY (!result)) + p_error_set_error_p (error, + (pint) p_error_get_last_io (), + p_error_get_last_system (), + "Failed to remove file"); + + return result; +} diff --git a/3rdparty/plibsys/src/pfile.h b/3rdparty/plibsys/src/pfile.h new file mode 100644 index 0000000..19748c9 --- /dev/null +++ b/3rdparty/plibsys/src/pfile.h @@ -0,0 +1,87 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * @file pfile.h + * @brief File operations + * @author Alexander Saprykin + * + * To check file existance use p_file_is_exists(). To remove an exisiting file + * use p_file_remove(). + * + * #P_DIR_SEPARATOR provides a platform independent directory separator symbol + * which you can use to form file or directory path. + */ + +#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION) +# error "Header files shouldn't be included directly, consider using <plibsys.h> instead." +#endif + +#ifndef PLIBSYS_HEADER_PFILE_H +#define PLIBSYS_HEADER_PFILE_H + +#include <pmacros.h> +#include <ptypes.h> +#include <perror.h> + +/** + * @def P_DIR_SEPARATOR + * @brief A directory separator. + */ +#if defined (P_OS_WIN) || defined (P_OS_OS2) +# define P_DIR_SEPARATOR "\\" +#else +# define P_DIR_SEPARATOR "/" +#endif + +P_BEGIN_DECLS + +/** + * @brief Checks whether a file exists or not. + * @param file File name to check. + * @return TRUE if the file exists, FALSE otherwise. + * @since 0.0.1 + * + * On Windows this call doesn't resolve symbolic links, while on UNIX systems + * does. + */ +P_LIB_API pboolean p_file_is_exists (const pchar *file); + +/** + * @brief Removes a file from the disk. + * @param file File name to remove. + * @param[out] error Error report object, NULL to ignore. + * @return TRUE if the file was successully removed, FALSE otherwise. + * @since 0.0.1 + * + * This call doesn't resolve symbolic links and remove a symbolic link if the + * given path points to it. + */ +P_LIB_API pboolean p_file_remove (const pchar *file, + PError **error); + +P_END_DECLS + +#endif /* PLIBSYS_HEADER_PFILE_H */ diff --git a/3rdparty/plibsys/src/phashtable.c b/3rdparty/plibsys/src/phashtable.c new file mode 100644 index 0000000..a9fc052 --- /dev/null +++ b/3rdparty/plibsys/src/phashtable.c @@ -0,0 +1,243 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2019 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* Hash table organized like this: table[hash key]->[list with values] + * Note: this implementation is not intended to use on huge loads */ + +#include "pmem.h" +#include "phashtable.h" + +#include <stdlib.h> + +typedef struct PHashTableNode_ PHashTableNode; + +struct PHashTableNode_ { + PHashTableNode *next; + ppointer key; + ppointer value; +}; + +struct PHashTable_ { + PHashTableNode **table; + psize size; +}; + +/* Size of unique hash keys in hash table */ +#define P_HASH_TABLE_SIZE 101 + +static puint pp_hash_table_calc_hash (pconstpointer pointer, psize modulo); +static PHashTableNode * pp_hash_table_find_node (const PHashTable *table, pconstpointer key, puint hash); + +static puint +pp_hash_table_calc_hash (pconstpointer pointer, psize modulo) +{ + /* As simple as we can :) */ + return (puint) (((psize) (P_POINTER_TO_INT (pointer) + 37)) % modulo); +} + +static PHashTableNode * +pp_hash_table_find_node (const PHashTable *table, pconstpointer key, puint hash) +{ + PHashTableNode *ret; + + for (ret = table->table[hash]; ret != NULL; ret = ret->next) + if (ret->key == key) + return ret; + + return NULL; +} + +P_LIB_API PHashTable * +p_hash_table_new (void) +{ + PHashTable *ret; + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PHashTable))) == NULL)) { + P_ERROR ("PHashTable::p_hash_table_new: failed(1) to allocate memory"); + return NULL; + } + + if (P_UNLIKELY ((ret->table = p_malloc0 (P_HASH_TABLE_SIZE * sizeof (PHashTableNode *))) == NULL)) { + P_ERROR ("PHashTable::p_hash_table_new: failed(2) to allocate memory"); + p_free (ret); + return NULL; + } + + ret->size = P_HASH_TABLE_SIZE; + + return ret; +} + +P_LIB_API void +p_hash_table_insert (PHashTable *table, ppointer key, ppointer value) +{ + PHashTableNode *node; + puint hash; + + if (P_UNLIKELY (table == NULL)) + return; + + hash = pp_hash_table_calc_hash (key, table->size); + + if ((node = pp_hash_table_find_node (table, key, hash)) == NULL) { + if (P_UNLIKELY ((node = p_malloc0 (sizeof (PHashTableNode))) == NULL)) { + P_ERROR ("PHashTable::p_hash_table_insert: failed to allocate memory"); + return; + } + + /* Insert a new node in front of others */ + node->key = key; + node->value = value; + node->next = table->table[hash]; + + table->table[hash] = node; + } else + node->value = value; +} + +P_LIB_API ppointer +p_hash_table_lookup (const PHashTable *table, pconstpointer key) +{ + PHashTableNode *node; + puint hash; + + if (P_UNLIKELY (table == NULL)) + return NULL; + + hash = pp_hash_table_calc_hash (key, table->size); + + return ((node = pp_hash_table_find_node (table, key, hash)) == NULL) ? (ppointer) (-1) : node->value; +} + +P_LIB_API PList * +p_hash_table_keys (const PHashTable *table) +{ + PList *ret = NULL; + PHashTableNode *node; + puint i; + + if (P_UNLIKELY (table == NULL)) + return NULL; + + for (i = 0; i < table->size; ++i) + for (node = table->table[i]; node != NULL; node = node->next) + ret = p_list_append (ret, node->key); + + return ret; +} + +P_LIB_API PList * +p_hash_table_values (const PHashTable *table) +{ + PList *ret = NULL; + PHashTableNode *node; + puint i; + + if (P_UNLIKELY (table == NULL)) + return NULL; + + for (i = 0; i < table->size; ++i) + for (node = table->table[i]; node != NULL; node = node->next) + ret = p_list_append (ret, node->value); + + return ret; +} + +P_LIB_API void +p_hash_table_free (PHashTable *table) +{ + PHashTableNode *node, *next_node; + puint i; + + if (P_UNLIKELY (table == NULL)) + return; + + for (i = 0; i < table->size; ++i) + for (node = table->table[i]; node != NULL; ) { + next_node = node->next; + p_free (node); + node = next_node; + } + + p_free (table->table); + p_free (table); +} + +P_LIB_API void +p_hash_table_remove (PHashTable *table, pconstpointer key) +{ + PHashTableNode *node, *prev_node; + puint hash; + + if (P_UNLIKELY (table == NULL)) + return; + + hash = pp_hash_table_calc_hash (key, table->size); + + if (pp_hash_table_find_node (table, key, hash) != NULL) { + node = table->table[hash]; + prev_node = NULL; + + while (node != NULL) { + if (node->key == key) { + if (prev_node == NULL) + table->table[hash] = node->next; + else + prev_node->next = node->next; + + p_free (node); + break; + } else { + prev_node = node; + node = node->next; + } + } + } +} + +P_LIB_API PList * +p_hash_table_lookup_by_value (const PHashTable *table, pconstpointer val, PCompareFunc func) +{ + PList *ret = NULL; + PHashTableNode *node; + puint i; + pboolean res; + + if (P_UNLIKELY (table == NULL)) + return NULL; + + for (i = 0; i < table->size; ++i) + for (node = table->table[i]; node != NULL; node = node->next) { + if (func == NULL) + res = (node->value == val); + else + res = (func (node->value, val) == 0); + + if (res) + ret = p_list_append (ret, node->key); + } + + return ret; +} diff --git a/3rdparty/plibsys/src/phashtable.h b/3rdparty/plibsys/src/phashtable.h new file mode 100644 index 0000000..702bbe7 --- /dev/null +++ b/3rdparty/plibsys/src/phashtable.h @@ -0,0 +1,167 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * @file phashtable.h + * @brief Hash table + * @author Alexander Saprykin + * + * A hash table is a data structure used to map keys to values. The hash table + * consists of several internal slots which hold a list of values. A hash + * function is used to compute an index in the array of the slots from a given + * key. The hash function itself is fast and it takes a constant time to compute + * the internal slot index. + * + * When the number of pairs in the hash table is small the lookup and insert + * (remove) operations are very fast and have average complexity O(1), because + * every slot holds almost the only one pair. As the number of internal slots is + * fixed, the increasing number of pairs will lead to degraded performance and + * the average complexity of the operations can drop to O(N) in the worst case. + * This is because the more pairs are inserted the more longer the list of + * values is placed in every slot. + * + * This is a simple hash table implementation which is not intended for heavy + * usage (several thousands), see #PTree if you need the best performance on + * large data sets. This implementation doesn't support multi-inserts when + * several values belong to the same key. + * + * Note that #PHashTable stores keys and values only as pointers, so you need + * to free used memory manually, p_hash_table_free() will not do it in any way. + * + * Integers (up to 32 bits) can be stored in pointers using #P_POINTER_TO_INT + * and #P_INT_TO_POINTER macros. + */ + +#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION) +# error "Header files shouldn't be included directly, consider using <plibsys.h> instead." +#endif + +#ifndef PLIBSYS_HEADER_PHASHTABLE_H +#define PLIBSYS_HEADER_PHASHTABLE_H + +#include <pmacros.h> +#include <ptypes.h> +#include <plist.h> + +P_BEGIN_DECLS + +/** Opaque data structure for a hash table. */ +typedef struct PHashTable_ PHashTable; + +/** + * @brief Initializes a new hash table. + * @return Pointer to a newly initialized #PHashTable structure in case of + * success, NULL otherwise. + * @since 0.0.1 + * @note Free with p_hash_table_free() after usage. + */ +P_LIB_API PHashTable * p_hash_table_new (void); + +/** + * @brief Inserts a new key-value pair into a hash table. + * @param table Initialized hash table. + * @param key Key to insert. + * @param value Value to insert. + * @since 0.0.1 + * + * This function only stores pointers, so you need to manually free pointed + * data after using the hash table. + */ +P_LIB_API void p_hash_table_insert (PHashTable *table, + ppointer key, + ppointer value); + +/** + * @brief Searches for a specifed key in the hash table. + * @param table Hash table to lookup in. + * @param key Key to lookup for. + * @return Value related to its key pair (can be NULL), (#ppointer) -1 if no + * value was found. + * @since 0.0.1 + */ +P_LIB_API ppointer p_hash_table_lookup (const PHashTable *table, + pconstpointer key); + +/** + * @brief Gives a list of all the stored keys in the hash table. + * @param table Hash table to collect the keys from. + * @return List of all the stored keys, the list can be empty if no keys were + * found. + * @since 0.0.1 + * @note You should manually free the returned list with p_list_free() after + * using it. + */ +P_LIB_API PList * p_hash_table_keys (const PHashTable *table); + +/** + * @brief Gives a list of all the stored values in the hash table. + * @param table Hash table to collect the values from. + * @return List of all the stored values, the list can be empty if no keys were + * found. + * @since 0.0.1 + * @note You should manually free the returned list with p_list_free() after + * using it. + */ +P_LIB_API PList * p_hash_table_values (const PHashTable *table); + +/** + * @brief Frees a previously initialized #PHashTable. + * @param table Hash table to free. + * @since 0.0.1 + */ +P_LIB_API void p_hash_table_free (PHashTable *table); + +/** + * @brief Removes @a key from a hash table. + * @param table Hash table to remove the key from. + * @param key Key to remove (if exists). + * @since 0.0.1 + */ +P_LIB_API void p_hash_table_remove (PHashTable *table, + pconstpointer key); + +/** + * @brief Searches for a specifed key in the hash table by its value. + * @param table Hash table to lookup in. + * @param val Value to lookup keys for. + * @param func Function to compare table's values with @a val, if NULL then + * values will be compared as pointers. + * @return List of the keys with @a val (can be NULL), NULL if no keys were + * found. + * @since 0.0.1 + * @note Caller is responsible to call p_list_free() on the returned list after + * usage. + * + * The compare function should return 0 if a value from the hash table (the + * first parameter) is accepted related to the given lookup value (the second + * parameter), and -1 or 1 otherwise. + */ +P_LIB_API PList * p_hash_table_lookup_by_value (const PHashTable *table, + pconstpointer val, + PCompareFunc func); + +P_END_DECLS + +#endif /* PLIBSYS_HEADER_PHASHTABLE_H */ diff --git a/3rdparty/plibsys/src/pinifile.c b/3rdparty/plibsys/src/pinifile.c new file mode 100644 index 0000000..d8361fc --- /dev/null +++ b/3rdparty/plibsys/src/pinifile.c @@ -0,0 +1,503 @@ +/* + * The MIT License + * + * Copyright (C) 2012-2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "perror.h" +#include "pinifile.h" +#include "plist.h" +#include "pmem.h" +#include "pstring.h" +#include "perror-private.h" + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> + +#define P_INI_FILE_MAX_LINE 1024 + +typedef struct PIniParameter_ { + pchar *name; + pchar *value; +} PIniParameter; + +typedef struct PIniSection_ { + pchar *name; + PList *keys; +} PIniSection; + +struct PIniFile_ { + pchar *path; + PList *sections; + pboolean is_parsed; +}; + +static PIniParameter * pp_ini_file_parameter_new (const pchar *name, const pchar *val); +static void pp_ini_file_parameter_free (PIniParameter *param); +static PIniSection * pp_ini_file_section_new (const pchar *name); +static void pp_ini_file_section_free (PIniSection *section); +static pchar * pp_ini_file_find_parameter (const PIniFile *file, const pchar *section, const pchar *key); + +static PIniParameter * +pp_ini_file_parameter_new (const pchar *name, + const pchar *val) +{ + PIniParameter *ret; + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PIniParameter))) == NULL)) + return NULL; + + if (P_UNLIKELY ((ret->name = p_strdup (name)) == NULL)) { + p_free (ret); + return NULL; + } + + if (P_UNLIKELY ((ret->value = p_strdup (val)) == NULL)) { + p_free (ret->name); + p_free (ret); + return NULL; + } + + return ret; +} + +static void +pp_ini_file_parameter_free (PIniParameter *param) +{ + p_free (param->name); + p_free (param->value); + p_free (param); +} + +static PIniSection * +pp_ini_file_section_new (const pchar *name) +{ + PIniSection *ret; + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PIniSection))) == NULL)) + return NULL; + + if (P_UNLIKELY ((ret->name = p_strdup (name)) == NULL)) { + p_free (ret); + return NULL; + } + + return ret; +} + +static void +pp_ini_file_section_free (PIniSection *section) +{ + p_list_foreach (section->keys, (PFunc) pp_ini_file_parameter_free, NULL); + p_list_free (section->keys); + p_free (section->name); + p_free (section); +} + +static pchar * +pp_ini_file_find_parameter (const PIniFile *file, const pchar *section, const pchar *key) +{ + PList *item; + + if (P_UNLIKELY (file == NULL || file->is_parsed == FALSE || section == NULL || key == NULL)) + return NULL; + + for (item = file->sections; item != NULL; item = item->next) + if (strcmp (((PIniSection *) item->data)->name, section) == 0) + break; + + if (item == NULL) + return NULL; + + for (item = ((PIniSection *) item->data)->keys; item != NULL; item = item->next) + if (strcmp (((PIniParameter *) item->data)->name, key) == 0) + return p_strdup (((PIniParameter *) item->data)->value); + + return NULL; +} + +P_LIB_API PIniFile * +p_ini_file_new (const pchar *path) +{ + PIniFile *ret; + + if (P_UNLIKELY (path == NULL)) + return NULL; + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PIniFile))) == NULL)) + return NULL; + + if (P_UNLIKELY ((ret->path = p_strdup (path)) == NULL)) { + p_free (ret); + return NULL; + } + + ret->is_parsed = FALSE; + + return ret; +} + +P_LIB_API void +p_ini_file_free (PIniFile *file) +{ + if (P_UNLIKELY (file == NULL)) + return; + + p_list_foreach (file->sections, (PFunc) pp_ini_file_section_free, NULL); + p_list_free (file->sections); + p_free (file->path); + p_free (file); +} + +P_LIB_API pboolean +p_ini_file_parse (PIniFile *file, + PError **error) +{ + PIniSection *section; + PIniParameter *param; + FILE *in_file; + pchar *dst_line, *tmp_str; + pchar src_line[P_INI_FILE_MAX_LINE + 1], + key[P_INI_FILE_MAX_LINE + 1], + value[P_INI_FILE_MAX_LINE + 1]; + pint bom_shift; + + if (P_UNLIKELY (file == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return FALSE; + } + + if (file->is_parsed) + return TRUE; + + if (P_UNLIKELY ((in_file = fopen (file->path, "r")) == NULL)) { + p_error_set_error_p (error, + (pint) p_error_get_last_io (), + p_error_get_last_system (), + "Failed to open file for reading"); + return FALSE; + } + + dst_line = NULL; + section = NULL; + param = NULL; + + memset (src_line, 0, sizeof (src_line)); + + while (fgets (src_line, sizeof (src_line), in_file) != NULL) { + /* UTF-8, UTF-16 and UTF-32 BOM detection */ + if ((puchar) src_line[0] == 0xEF && (puchar) src_line[1] == 0xBB && (puchar) src_line[2] == 0xBF) + bom_shift = 3; + else if (((puchar) src_line[0] == 0xFE && (puchar) src_line[1] == 0xFF) || + ((puchar) src_line[0] == 0xFF && (puchar) src_line[1] == 0xFE)) + bom_shift = 2; + else if ((puchar) src_line[0] == 0x00 && (puchar) src_line[1] == 0x00 && + (puchar) src_line[2] == 0xFE && (puchar) src_line[3] == 0xFF) + bom_shift = 4; + else if ((puchar) src_line[0] == 0xFF && (puchar) src_line[1] == 0xFE && + (puchar) src_line[2] == 0x00 && (puchar) src_line[3] == 0x00) + bom_shift = 4; + else + bom_shift = 0; + + dst_line = p_strchomp (src_line + bom_shift); + + if (dst_line == NULL) + continue; + + /* This should not happen */ + if (P_UNLIKELY (strlen (dst_line) > P_INI_FILE_MAX_LINE)) + dst_line[P_INI_FILE_MAX_LINE] = '\0'; + + if (dst_line[0] == '[' && dst_line[strlen (dst_line) - 1] == ']' && + sscanf (dst_line, "[%[^]]", key) == 1) { + /* New section found */ + if ((tmp_str = p_strchomp (key)) != NULL) { + /* This should not happen */ + if (P_UNLIKELY (strlen (tmp_str) > P_INI_FILE_MAX_LINE)) + tmp_str[P_INI_FILE_MAX_LINE] = '\0'; + + strcpy (key, tmp_str); + p_free (tmp_str); + + if (section != NULL) { + if (section->keys == NULL) + pp_ini_file_section_free (section); + else + file->sections = p_list_prepend (file->sections, section); + } + + section = pp_ini_file_section_new (key); + } + } else if (sscanf (dst_line, "%[^=] = \"%[^\"]\"", key, value) == 2 || + sscanf (dst_line, "%[^=] = '%[^\']'", key, value) == 2 || + sscanf (dst_line, "%[^=] = %[^;#]", key, value) == 2) { + /* New parameter found */ + if ((tmp_str = p_strchomp (key)) != NULL) { + /* This should not happen */ + if (P_UNLIKELY (strlen (tmp_str) > P_INI_FILE_MAX_LINE)) + tmp_str[P_INI_FILE_MAX_LINE] = '\0'; + + strcpy (key, tmp_str); + p_free (tmp_str); + + if ((tmp_str = p_strchomp (value)) != NULL) { + /* This should not happen */ + if (P_UNLIKELY (strlen (tmp_str) > P_INI_FILE_MAX_LINE)) + tmp_str[P_INI_FILE_MAX_LINE] = '\0'; + + strcpy (value, tmp_str); + p_free (tmp_str); + + if (strcmp (value, "\"\"") == 0 || (strcmp (value, "''") == 0)) + value[0] = '\0'; + + if (section != NULL && (param = pp_ini_file_parameter_new (key, value)) != NULL) + section->keys = p_list_prepend (section->keys, param); + } + } + } + + p_free (dst_line); + memset (src_line, 0, sizeof (src_line)); + } + + if (section != NULL) { + if (section->keys == NULL) + pp_ini_file_section_free (section); + else + file->sections = p_list_append (file->sections, section); + } + + if (P_UNLIKELY (fclose (in_file) != 0)) + P_WARNING ("PIniFile::p_ini_file_parse: fclose() failed"); + + file->is_parsed = TRUE; + + return TRUE; +} + +P_LIB_API pboolean +p_ini_file_is_parsed (const PIniFile *file) +{ + if (P_UNLIKELY (file == NULL)) + return FALSE; + + return file->is_parsed; +} + +P_LIB_API PList * +p_ini_file_sections (const PIniFile *file) +{ + PList *ret; + PList *sec; + + if (P_UNLIKELY (file == NULL || file->is_parsed == FALSE)) + return NULL; + + ret = NULL; + + for (sec = file->sections; sec != NULL; sec = sec->next) + ret = p_list_prepend (ret, p_strdup (((PIniSection *) sec->data)->name)); + + return ret; +} + +P_LIB_API PList * +p_ini_file_keys (const PIniFile *file, + const pchar *section) +{ + PList *ret; + PList *item; + + if (P_UNLIKELY (file == NULL || file->is_parsed == FALSE || section == NULL)) + return NULL; + + ret = NULL; + + for (item = file->sections; item != NULL; item = item->next) + if (strcmp (((PIniSection *) item->data)->name, section) == 0) + break; + + if (item == NULL) + return NULL; + + for (item = ((PIniSection *) item->data)->keys; item != NULL; item = item->next) + ret = p_list_prepend (ret, p_strdup (((PIniParameter *) item->data)->name)); + + return ret; +} + +P_LIB_API pboolean +p_ini_file_is_key_exists (const PIniFile *file, + const pchar *section, + const pchar *key) +{ + PList *item; + + if (P_UNLIKELY (file == NULL || file->is_parsed == FALSE || section == NULL || key == NULL)) + return FALSE; + + for (item = file->sections; item != NULL; item = item->next) + if (strcmp (((PIniSection *) item->data)->name, section) == 0) + break; + + if (item == NULL) + return FALSE; + + for (item = ((PIniSection *) item->data)->keys; item != NULL; item = item->next) + if (strcmp (((PIniParameter *) item->data)->name, key) == 0) + return TRUE; + + return FALSE; +} + +P_LIB_API pchar * +p_ini_file_parameter_string (const PIniFile *file, + const pchar *section, + const pchar *key, + const pchar *default_val) +{ + pchar *val; + + if ((val = pp_ini_file_find_parameter (file, section, key)) == NULL) + return p_strdup (default_val); + + return val; +} + +P_LIB_API pint +p_ini_file_parameter_int (const PIniFile *file, + const pchar *section, + const pchar *key, + pint default_val) +{ + pchar *val; + pint ret; + + if ((val = pp_ini_file_find_parameter (file, section, key)) == NULL) + return default_val; + + ret = atoi (val); + p_free (val); + + return ret; +} + +P_LIB_API double +p_ini_file_parameter_double (const PIniFile *file, + const pchar *section, + const pchar *key, + double default_val) +{ + pchar *val; + pdouble ret; + + if ((val = pp_ini_file_find_parameter (file, section, key)) == NULL) + return default_val; + + ret = p_strtod (val); + p_free (val); + + return ret; +} + +P_LIB_API pboolean +p_ini_file_parameter_boolean (const PIniFile *file, + const pchar *section, + const pchar *key, + pboolean default_val) +{ + pchar *val; + pboolean ret; + + if ((val = pp_ini_file_find_parameter (file, section, key)) == NULL) + return default_val; + + if (strcmp (val, "true") == 0 || strcmp (val, "TRUE") == 0) + ret = TRUE; + else if (strcmp (val, "false") == 0 || strcmp (val, "FALSE") == 0) + ret = FALSE; + else if (atoi (val) > 0) + ret = TRUE; + else + ret = FALSE; + + p_free (val); + + return ret; +} + +P_LIB_API PList * +p_ini_file_parameter_list (const PIniFile *file, + const pchar *section, + const pchar *key) +{ + PList *ret = NULL; + pchar *val, *str; + pchar buf[P_INI_FILE_MAX_LINE + 1]; + psize len, buf_cnt; + + if ((val = pp_ini_file_find_parameter (file, section, key)) == NULL) + return NULL; + + len = strlen (val); + + if (len < 3 || val[0] != '{' || val[len - 1] != '}') { + p_free (val); + return NULL; + } + + /* Skip first brace '{' symbol */ + str = val + 1; + buf[0] = '\0'; + buf_cnt = 0; + + while (*str && *str != '}') { + if (!isspace (* ((const puchar *) str))) + buf[buf_cnt++] = *str; + else { + buf[buf_cnt] = '\0'; + + if (buf_cnt > 0) + ret = p_list_append (ret, p_strdup (buf)); + + buf_cnt = 0; + } + + ++str; + } + + if (buf_cnt > 0) { + buf[buf_cnt] = '\0'; + ret = p_list_append (ret, p_strdup (buf)); + } + + p_free (val); + + return ret; +} diff --git a/3rdparty/plibsys/src/pinifile.h b/3rdparty/plibsys/src/pinifile.h new file mode 100644 index 0000000..b2a2e3d --- /dev/null +++ b/3rdparty/plibsys/src/pinifile.h @@ -0,0 +1,261 @@ +/* + * The MIT License + * + * Copyright (C) 2012-2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * @file pinifile.h + * @brief INI file parser + * @author Alexander Saprykin + * + * An INI file is usually used for storing configuration information. It + * consists of sections: every section starts with a line containing the name in + * square brackets (i.e. [section_name]). After that line all the following + * parameters will belong to that section until another section begins. + * + * Each section has a list of key-value pairs. Empty sections are not permitted + * (they will be skipped). Every key-value pair is represented with a line in + * the `key = value` format. If a section has several values with the same key + * the last one will be used. A value is parsed by the first in-order '=' + * symbol. All the following '=' occurrences belong to the value. + * + * All symbols after '#' and ';' (even at the line ending) are the comments and + * wouldn't be read. If you want to use them in values take the value inside the + * "" or '' symbols. A section name line is not allowed to use the comment + * symbols after the section name in the square brackets itself. + * + * Integer values can be written in the usual form. + * + * Floating point values can be written in any commonly used notation (i.e. with + * the decimal point, in the exponential form using the 'e' character). The only + * valid decimal point symbol is the '.'. There is no locale dependency on the + * decimal point. + * + * Boolean values can be written in the form of 'true/false' or 'TRUE/FALSE', or + * simply '0/1'. + * + * Any value can be interpreted as a string at any moment. Actually all the + * values are stored internally as strings. + * + * A list of values can be stored between the '{}' symbols separated with + * spaces. The list only supports string values, so you should convert them to + * numbers manually. The list doesn't support strings with spaces - such strings + * will be splitted. + * + * To parse a file, create #PIniFile with p_ini_file_new() and then parse it + * with the p_ini_file_parse() routine. + * + * #PIniFile handles (skips) UTF-8/16/32 BOM characters (marks). + * + * Example of the INI file contents: + * @code + * [numeric_section] + * numeric_value_1 = 1234 # One type of the comment + * numeric_value_2 = 123 ; Comment is allowed here + * + * [floating_section] + * float_value_1 = 123.3e10 + * float_value_2 = 123.19 + * + * [boolean_section] + * boolean_value_1 = TRUE + * boolean_value_2 = 0 + * boolean_value_3 = false + * + * [string_section] + * string_value_1 = "Test string" + * string_value_2 = 'Another test string' + * + * [list_section] + * list_value = {123 val 7654} + * @endcode + */ + +#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION) +# error "Header files shouldn't be included directly, consider using <plibsys.h> instead." +#endif + +#ifndef PLIBSYS_HEADER_PINIFILE_H +#define PLIBSYS_HEADER_PINIFILE_H + +#include <pmacros.h> +#include <ptypes.h> +#include <plist.h> +#include <perror.h> + +P_BEGIN_DECLS + +/** INI file opaque data structure. */ +typedef struct PIniFile_ PIniFile; + +/** + * @brief Creates a new #PIniFile for parsing. + * @param path Path to a file to parse. + * @return Newly allocated #PIniFile in case of success, NULL otherwise. + * @since 0.0.1 + */ +P_LIB_API PIniFile * p_ini_file_new (const pchar *path); + +/** + * @brief Frees memory and allocated resources of #PIniFile. + * @param file #PIniFile to free. + * @since 0.0.1 + */ +P_LIB_API void p_ini_file_free (PIniFile *file); + +/** + * @brief Parses given #PIniFile. + * @param file #PIniFile file to parse. + * @param[out] error Error report object, NULL to ignore. + * @return TRUE in case of success, FALSE otherwise. + * @since 0.0.1 + */ +P_LIB_API pboolean p_ini_file_parse (PIniFile *file, + PError **error); + +/** + * @brief Checks whether #PIniFile was already parsed or not. + * @param file #PIniFile to check. + * @return TRUE if the file was already parsed, FALSE otherwise. + * @since 0.0.1 + */ +P_LIB_API pboolean p_ini_file_is_parsed (const PIniFile *file); + +/** + * @brief Gets all the sections from a given file. + * @param file #PIniFile to get the sections from. The @a file should be parsed + * before. + * @return #PList of section names. + * @since 0.0.1 + * @note It's a caller responsibility to p_free() each returned string and to + * free the returned list with p_list_free(). + */ +P_LIB_API PList * p_ini_file_sections (const PIniFile *file); + +/** + * @brief Gets all the keys from a given section. + * @param file #PIniFile to get the keys from. The @a file should be parsed + * before. + * @param section Section name to get the keys from. + * @return #PList of key names. + * @since 0.0.1 + * @note It's a caller responsibility to p_free() each returned string and to + * free the returned list with p_list_free(). + */ +P_LIB_API PList * p_ini_file_keys (const PIniFile *file, + const pchar *section); + +/** + * @brief Checks whether a key exists. + * @param file #PIniFile to check in. The @a file should be parsed before. + * @param section Section to check the key in. + * @param key Key to check. + * @return TRUE if @a key exists, FALSE otherwise. + * @since 0.0.1 + */ +P_LIB_API pboolean p_ini_file_is_key_exists (const PIniFile *file, + const pchar *section, + const pchar *key); + +/** + * @brief Gets specified parameter's value as a string. + * @param file #PIniFile to get the value from. The @a file should be parsed + * before. + * @param section Section to get the value from. + * @param key Key to get the value from. + * @param default_val Default value to return if no specified key exists. + * @return Key's value in case of success, @a default_value otherwise. + * @since 0.0.1 + * @note It's a caller responsibility to p_free() the returned string after + * usage. + */ +P_LIB_API pchar * p_ini_file_parameter_string (const PIniFile *file, + const pchar *section, + const pchar *key, + const pchar *default_val); + +/** + * @brief Gets specified parameter's value as an integer. + * @param file #PIniFile to get the value from. The @a file should be parsed + * before. + * @param section Section to get the value from. + * @param key Key to get the value from. + * @param default_val Default value to return if no specified key exists. + * @return Key's value in case of success, @a default_value otherwise. + * @since 0.0.1 + */ +P_LIB_API pint p_ini_file_parameter_int (const PIniFile *file, + const pchar *section, + const pchar *key, + pint default_val); + +/** + * @brief Gets specified parameter's value as a floating point. + * @param file #PIniFile to get the value from. The @a file should be parsed + * before. + * @param section Section to get the value from. + * @param key Key to get the value from. + * @param default_val Default value to return if no specified key exists. + * @return Key's value in case of success, @a default_value otherwise. + * @since 0.0.1 + */ +P_LIB_API double p_ini_file_parameter_double (const PIniFile *file, + const pchar *section, + const pchar *key, + double default_val); +/** + * @brief Gets specified parameter's value as a boolean. + * @param file #PIniFile to get the value from. The @a file should be parsed + * before. + * @param section Section to get the value from. + * @param key Key to get the value from. + * @param default_val Default value to return if no specified key exists. + * @return Key's value in case of success, @a default_value otherwise. + * @since 0.0.1 + */ + +P_LIB_API pboolean p_ini_file_parameter_boolean (const PIniFile *file, + const pchar *section, + const pchar *key, + pboolean default_val); + +/** + * @brief Gets specified parameter's value as a list of strings separated with + * the spaces or tabs. + * @param file #PIniFile to get the value from. The @a file should be parsed + * before. + * @param section Section to get the value from. + * @param key Key to get the value from. + * @return #PList of strings. NULL will be returned if no parameter with the + * given name exists. + * @since 0.0.1 + * @note It's a caller responsibility to p_free() each returned string and to + * free the returned list with p_list_free(). + */ +P_LIB_API PList * p_ini_file_parameter_list (const PIniFile *file, + const pchar *section, + const pchar *key); + +P_END_DECLS + +#endif /* PLIBSYS_HEADER_PINIFILE_H */ diff --git a/3rdparty/plibsys/src/pipc-private.h b/3rdparty/plibsys/src/pipc-private.h new file mode 100644 index 0000000..312d378 --- /dev/null +++ b/3rdparty/plibsys/src/pipc-private.h @@ -0,0 +1,76 @@ +/* + * The MIT License + * + * Copyright (C) 2016-2017 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION) +# error "Header files shouldn't be included directly, consider using <plibsys.h> instead." +#endif + +#ifndef PLIBSYS_HEADER_PIPC_PRIVATE_H +#define PLIBSYS_HEADER_PIPC_PRIVATE_H + +#include "pmacros.h" +#include "ptypes.h" + +P_BEGIN_DECLS + +#if !defined (P_OS_WIN) && !defined (P_OS_OS2) +/** + * @brief Gets a temporary directory on UNIX systems. + * @return Temporary directory. + */ +pchar * p_ipc_unix_get_temp_dir (void); + +/* Create file for System V IPC, if needed + * Returns: -1 = error, 0 = file successfully created, 1 = file already exists */ +/** + * @brief Creates a file for System V IPC usage. + * @param file_name File name to create. + * @return -1 in case of error, 0 if all was OK, and 1 if the file already + * exists. + */ +pint p_ipc_unix_create_key_file (const pchar *file_name); + +/** + * @brief Wrapps the ftok() UNIX call for a unique IPC key. + * @param file_name File name for ftok() call. + * @return Key in case of success, -1 otherwise. + */ +pint p_ipc_unix_get_ftok_key (const pchar *file_name); +#endif /* !P_OS_WIN && !P_OS_OS2 */ + +/** + * @brief Generates a platform independent key for IPC usage, an object name for + * Windows and a file name to use with ftok () for UNIX-like systems. + * @param name Object name. + * @param posix TRUE if the key will be used for the POSIX IPC calls, otherwise + * FALSE. This parameter is not used on the Windows platform. + * @return Platform independent key for IPC usage. + */ +pchar * p_ipc_get_platform_key (const pchar *name, + pboolean posix); + +P_END_DECLS + +#endif /* PLIBSYS_HEADER_PIPC_PRIVATE_H */ diff --git a/3rdparty/plibsys/src/pipc.c b/3rdparty/plibsys/src/pipc.c new file mode 100644 index 0000000..60093ee --- /dev/null +++ b/3rdparty/plibsys/src/pipc.c @@ -0,0 +1,178 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2017 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pmem.h" +#include "pcryptohash.h" +#include "pstring.h" +#include "psysclose-private.h" + +#include <stdlib.h> +#include <string.h> + +#if !defined (P_OS_WIN) && !defined (P_OS_OS2) && !defined (P_OS_AMIGA) +# include <unistd.h> +# include <errno.h> +# include <fcntl.h> +# include <sys/stat.h> +# include <sys/types.h> +# include <sys/ipc.h> +#endif + +#if !defined (P_OS_WIN) && !defined (P_OS_OS2) && !defined (P_OS_AMIGA) +pchar * +p_ipc_unix_get_temp_dir (void) +{ + pchar *str, *ret; + psize len; + +#ifdef P_tmpdir + if (strlen (P_tmpdir) > 0) + str = p_strdup (P_tmpdir); + else + return p_strdup ("/tmp/"); +#else + const pchar *tmp_env; + + tmp_env = getenv ("TMPDIR"); + + if (tmp_env != NULL) + str = p_strdup (tmp_env); + else + return p_strdup ("/tmp/"); +#endif /* P_tmpdir */ + + /* Now we need to ensure that we have only the one trailing slash */ + len = strlen (str); + while (*(str + --len) == '/') + ; + *(str + ++len) = '\0'; + + /* len + / + zero symbol */ + if (P_UNLIKELY ((ret = p_malloc0 (len + 2)) == NULL)) { + p_free (str); + return NULL; + } + + strcpy (ret, str); + strcat (ret, "/"); + + return ret; +} + +/* Create file for System V IPC, if needed + * Returns: -1 = error, 0 = file successfully created, 1 = file already exists */ +pint +p_ipc_unix_create_key_file (const pchar *file_name) +{ + pint fd; + + if (P_UNLIKELY (file_name == NULL)) + return -1; + + if ((fd = open (file_name, O_CREAT | O_EXCL | O_RDONLY, 0640)) == -1) + /* file already exists */ + return (errno == EEXIST) ? 1 : -1; + else + return p_sys_close (fd); +} + +pint +p_ipc_unix_get_ftok_key (const pchar *file_name) +{ + struct stat st_info; + + if (P_UNLIKELY (file_name == NULL)) + return -1; + + if (P_UNLIKELY (stat (file_name, &st_info) == -1)) + return -1; + + return ftok (file_name, 'P'); +} +#endif /* !P_OS_WIN && !P_OS_OS2 && !P_OS_AMIGA */ + +/* Returns a platform-independent key for IPC usage, object name for Windows and + * a file name to use with ftok () for UNIX-like systems */ +pchar * +p_ipc_get_platform_key (const pchar *name, pboolean posix) +{ + PCryptoHash *sha1; + pchar *hash_str; + +#if defined (P_OS_WIN) || defined (P_OS_OS2) || defined (P_OS_AMIGA) + P_UNUSED (posix); +#else + pchar *path_name, *tmp_path; +#endif + + if (P_UNLIKELY (name == NULL)) + return NULL; + + if (P_UNLIKELY ((sha1 = p_crypto_hash_new (P_CRYPTO_HASH_TYPE_SHA1)) == NULL)) + return NULL; + + p_crypto_hash_update (sha1, (const puchar *) name, strlen (name)); + + hash_str = p_crypto_hash_get_string (sha1); + p_crypto_hash_free (sha1); + + if (P_UNLIKELY (hash_str == NULL)) + return NULL; + +#if defined (P_OS_WIN) || defined (P_OS_OS2) || defined (P_OS_AMIGA) + return hash_str; +#else + if (posix) { + /* POSIX semaphores which are named kinda like '/semname'. + * Some implementations of POSIX semaphores has restriction for + * the name as of max 14 characters, best to use this limit */ + if (P_UNLIKELY ((path_name = p_malloc0 (15)) == NULL)) { + p_free (hash_str); + return NULL; + } + + strcpy (path_name, "/"); + strncat (path_name, hash_str, 13); + } else { + tmp_path = p_ipc_unix_get_temp_dir (); + + /* tmp dir + filename + zero symbol */ + path_name = p_malloc0 (strlen (tmp_path) + strlen (hash_str) + 1); + + if (P_UNLIKELY ((path_name) == NULL)) { + p_free (tmp_path); + p_free (hash_str); + return NULL; + } + + strcpy (path_name, tmp_path); + strcat (path_name, hash_str); + p_free (tmp_path); + } + + p_free (hash_str); + return path_name; +#endif +} diff --git a/3rdparty/plibsys/src/plibraryloader-amiga.c b/3rdparty/plibsys/src/plibraryloader-amiga.c new file mode 100644 index 0000000..463e350 --- /dev/null +++ b/3rdparty/plibsys/src/plibraryloader-amiga.c @@ -0,0 +1,583 @@ +/* + * The MIT License + * + * Copyright (C) 2017-2018 Alexander Saprykin <saprykin.spb@gmail.com> + * Copyright (C) 2002-2015 by Olaf Barthel <obarthel (at) gmx.net> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Path conversion code was taken and adopted from clib2 project: + * https://github.com/adtools/clib2 + * + * This part of code is distributed under the BSD-3-Clause license: + * + * 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. + * + * - Neither the name of Olaf Barthel nor the names of 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 OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "perror.h" +#include "pfile.h" +#include "plibraryloader.h" +#include "pmem.h" +#include "pstring.h" + +#include <string.h> + +#include <proto/dos.h> +#include <proto/elf.h> + +#if defined (__CLIB2__) +# include <dos.h> +#elif defined (__NEWLIB__) +# include <amiga_platform.h> +#endif + +typedef APTR plibrary_handle; + +struct PLibraryLoader_ { + plibrary_handle handle; + Elf32_Error last_error; +}; + +static Elf32_Handle pp_library_loader_elf_root = NULL; + +static void pp_library_loader_clean_handle (plibrary_handle handle); +static pint pp_library_loader_translate_path (const pchar *in, pchar *out, psize out_len); + +/* + * The following patterns must translate properly: + * + * foo/ + * /// + * foo//bar + * foo//bar//baz + * ./foo + * ././foo + * foo/./baz + * foo/./bar/./baz + * foo/./././bar + * foo/. + * /. + * /tmp + * /tmp/foo + * /dev/null + * /dev/null/foo + * /dev/nullX + * /foo + * /foo/ + * /foo/bar + * /foo/bar/baz + * foo/../bar + * foo/bar/../../baz + * foo/bar/.. + * ../foo + * ../../foo + * . + * .. + */ + +static pint +pp_library_loader_translate_path (const pchar *in, pchar *out, psize out_len) +{ + pchar volume_name[MAXPATHLEN]; + psize len, volume_name_len; + pint i, j; + + len = strlen (in); + + if (out_len < MAXPATHLEN || len > MAXPATHLEN) + return -1; + + strcpy (out, in); + + /* Just copy is path is already an Amiga one */ + + if (strchr (in, ':') != NULL) + return 0; + + in = out; + + /* Strip neighbouring slashes: ('foo//bar' -> 'foo/bar'). + * The "//" pattern in a Unix file name is apparently harmless, + * but on AmigaDOS it has a very definite meaning. */ + + if (len > 2) { + pboolean have_double_slash = FALSE; + + for (i = 0; i < len - 1; ++i) { + if (in[i] == '/' && in[i + 1] == '/') { + have_double_slash = TRUE; + break; + } + } + + if (have_double_slash) { + pboolean have_slash; + pchar c; + + have_slash = FALSE; + + for (i = j = 0; i < len; ++i) { + c = in[i]; + + if (c == '/') { + if (!have_slash) + out[j++] = c; + + have_slash = TRUE; + } else { + out[j++] = c; + have_slash = FALSE; + } + } + + len = j; + out[len] = '\0'; + } + } + + /* Strip trailing slashes ('foo/' -> 'foo'). A leading '/' must + * be preserved, though ('///' -> '/'). */ + + if(len > 1) { + psize num_trailing_slashes = 0; + + while ((num_trailing_slashes < len - 1) && (in[len - (num_trailing_slashes + 1)] == '/')) + num_trailing_slashes++; + + if (num_trailing_slashes > 0) { + len -= num_trailing_slashes; + out[len] = '\0'; + } + } + + /* Ditch all leading './' ('./foo' -> 'foo'). */ + + while (len > 2 && out[0] == '.' && out[1] == '/') { + len -= 2; + memmove (out, &out[2], len); + out[len] = '\0'; + } + + /* Ditch all embedded '/./' ('foo/./bar' -> 'foo/bar', 'foo/././bar' -> 'foo/bar'). */ + + if (len > 2) { + pboolean have_slash_dot_slash = FALSE; + + for (i = j = 0; i < len - 2; ++i) { + if(in[i] == '/' && in[i + 1] == '.' && in[i + 2] == '/') { + have_slash_dot_slash = TRUE; + break; + } + } + + if (have_slash_dot_slash) { + for (i = j = 0; i < len; ++i) { + while (i < len - 2 && in[i] == '/' && in[i + 1] == '.' && in[i + 2] == '/') + i += 2; + + if (i < len) + out[j++] = in[i]; + } + + len = j; + out[len] = '\0'; + } + } + + /* Special case: the path name may end with "/." signifying that the + * directory itself is requested ('foo/.' -> 'foo'). */ + + if (len >= 2 && strncmp (&in[len - 2], "/.", 2) == 0) { + /* If it's just '/.' then it's really '/'. */ + if (len == 2) { + strcpy (out, "/"); + len = 1; + } else { + len -= 2; + out[len] = '\0'; + } + } + + /* Check for absolute path. */ + + if (in[0] == '/') { + /* OK, so this is an absolute path. We begin by checking + * for a few special cases, the first being a reference + * to "/tmp". */ + if ((strncmp (in, "/tmp", 4) == 0) && (in[4] == '/' || len == 4)) { + if (in[4] == '/') { + /* Convert "/tmp/foo" to "T:foo". */ + memmove (&out[2], &in[5], len - 5); + memmove (out, "T:", 2); + + len -= 3; + } else { + /* Convert "/tmp" to "T:". */ + strcpy (out, "T:"); + + len = 2; + } + + out[len] = '\0'; + } else if ((strncmp (in, "/dev/null", 9)) == 0 && (len == 9 || in[9] == '/')) { + strcpy (out, "NIL:"); + len = 4; + } else { + psize path_name_start = 0; + volume_name_len = 0; + + /* Find out how long the first component of the absolute path is. */ + for (i = 1; i <= len; ++i) { + if (i == len || in[i] == '/') { + volume_name_len = i - 1; + + /* Is there anything following the path name? */ + if (i < len) + path_name_start = i + 1; + + break; + } + } + + /* Copy the first component and attach a colon. "/foo" becomes "foo:". */ + memmove (out, &in[1], volume_name_len); + out[volume_name_len++] = ':'; + + /* Now add the finishing touches. "/foo/bar" finally + * becomes "foo:bar" and "/foo" becomes "foo:". */ + if (path_name_start > 0) { + memmove (&out[volume_name_len], &in[path_name_start], len - path_name_start); + + len--; + } + + out[len] = '\0'; + } + } + + /* Extract and remove the volume name from the path. We + * are going to need it later. */ + + volume_name_len = 0; + + for (i = 0; i < len; ++i) { + if (in[i] == ':') { + /* Look for extra colon characters embedded in the name + * (as in "foo/bar:baz") which really don't belong here. */ + for (j = 0 ; j < i ; j++) { + if(in[j] == '/') + return -1; + } + + volume_name_len = i + 1; + len -= volume_name_len; + + memmove (volume_name, in, volume_name_len); + memmove (out, &out[volume_name_len], len); + + out[len] = '\0'; + + break; + } + } + + /* Look for extra colon characters embedded in the name + * (as in "foo:bar:baz") which really don't belong here. */ + + for (i = 0; i < len; ++i) { + if (in[i] == ':') + return -1; + } + + /* Now parse the path name and replace all embedded '..' with + * the AmigaDOS counterparts ('foo/../bar' -> 'foo//bar'). */ + + if (len > 3) { + pboolean have_slash_dot_dot_slash = FALSE; + + for (i = j = 0; i < len - 3; ++i) { + if (in[i] == '/' && in[i + 1] == '.' && in[i + 2] == '.' && in[i + 3] == '/') { + have_slash_dot_dot_slash = TRUE; + break; + } + } + + if (have_slash_dot_dot_slash) { + pboolean have_before = FALSE; + + for (i = j = 0; i < len; ++i) { + if(i < len - 3 && in[i] == '/' && in[i + 1] == '.' && in[i + 2] == '.' && in[i + 3] == '/') { + out[j++] = in[i]; + + if (have_before) + out[j++] = '/'; + + i += 2; + + have_before = TRUE; + } else { + have_before = FALSE; + out[j++] = in[i]; + } + } + + len = j; + out[len] = '\0'; + } + } + + /* Translate a trailing '/..' to '//' */ + + if (len >= 3 && strncmp (&in[len - 3], "/..", 3) == 0) { + len -= 2; + out[len++] = '/'; + out[len] = '\0'; + } + + /* Translate a leading '../' ('../foo' -> '/foo') */ + + if (len >= 3 && strncmp (in, "../", 3) == 0) { + memmove (out, &in[2], len - 2); + + len -= 2; + out[len] = '\0'; + } + + /* Translate the '..' ('..' -> '/') */ + + if (len == 2 && strncmp (in, "..", 2) == 0) { + strcpy (out, "/"); + + len = 1; + } + + /* Translate the '.' ('.' -> '') */ + + if (len == 1 && in[0] == '.') { + strcpy (out, ""); + + len = 0; + } + + /* Now put it all together again. */ + + if(volume_name_len > 0) { + memmove (&out[volume_name_len], in, len); + memmove (out, volume_name, volume_name_len); + + len += volume_name_len; + out[len] = '\0'; + } + + return 0; +} + +static void +pp_library_loader_clean_handle (plibrary_handle handle) +{ + IElf->DLClose (pp_library_loader_elf_root, handle); +} + +P_LIB_API PLibraryLoader * +p_library_loader_new (const pchar *path) +{ + PLibraryLoader *loader = NULL; + plibrary_handle handle = NULL; + pchar path_buffer[MAXPATHLEN]; + + if (!p_file_is_exists (path)) + return NULL; + + if (pp_library_loader_elf_root == NULL) { + P_ERROR ("PLibraryLoader::p_library_loader_new: shared library subsystem is not initialized"); + return NULL; + } + + if (strlen (path) >= MAXPATHLEN) { + P_ERROR ("PLibraryLoader::p_library_loader_new: too long file path or name"); + return NULL; + } + + if (pp_library_loader_translate_path (path, path_buffer, MAXPATHLEN) != 0) { + P_ERROR ("PLibraryLoader::p_library_loader_new: failed to convert to UNIX path"); + return NULL; + } + + path = path_buffer; + + handle = IElf->DLOpen (pp_library_loader_elf_root, (CONST_STRPTR) path, ELF32_RTLD_LOCAL); + + if (P_UNLIKELY (handle == NULL)) { + P_ERROR ("PLibraryLoader::p_library_loader_new: DLOpen() failed"); + return NULL; + } + + if (P_UNLIKELY ((loader = p_malloc0 (sizeof (PLibraryLoader))) == NULL)) { + P_ERROR ("PLibraryLoader::p_library_loader_new: failed to allocate memory"); + pp_library_loader_clean_handle (handle); + return NULL; + } + + loader->handle = handle; + loader->last_error = ELF32_NO_ERROR; + + return loader; +} + +P_LIB_API PFuncAddr +p_library_loader_get_symbol (PLibraryLoader *loader, const pchar *sym) +{ + APTR func_addr = NULL; + + if (P_UNLIKELY (loader == NULL || sym == NULL || loader->handle == NULL)) + return NULL; + + if (pp_library_loader_elf_root == NULL) { + P_ERROR ("PLibraryLoader::p_library_loader_new: shared library subsystem is not initialized"); + return NULL; + } + + loader->last_error = IElf->DLSym (pp_library_loader_elf_root, + loader->handle, + (CONST_STRPTR) sym, + &func_addr); + + return (PFuncAddr) func_addr; +} + +P_LIB_API void +p_library_loader_free (PLibraryLoader *loader) +{ + if (P_UNLIKELY (loader == NULL)) + return; + + pp_library_loader_clean_handle (loader->handle); + + p_free (loader); +} + +P_LIB_API pchar * +p_library_loader_get_last_error (PLibraryLoader *loader) +{ + if (P_UNLIKELY (loader == NULL)) + return NULL; + + switch (loader->last_error) { + case ELF32_NO_ERROR: + return NULL; + case ELF32_OUT_OF_MEMORY: + return p_strdup ("Out of memory"); + case ELF32_INVALID_HANDLE: + return p_strdup ("Invalid resource handler"); + case ELF32_NO_MORE_RELOCS: + return p_strdup ("No more relocations left"); + case ELF32_SECTION_NOT_LOADED: + return p_strdup ("Section not loaded"); + case ELF32_UNKNOWN_RELOC: + return p_strdup ("Unknown relocation"); + case ELF32_READ_ERROR: + return p_strdup ("Read error"); + case ELF32_INVALID_SDA_BASE: + return p_strdup ("Invalid SDA base"); + case ELF32_SYMBOL_NOT_FOUND: + return p_strdup ("Symbol not found"); + case ELF32_INVALID_NAME: + return p_strdup ("Invalid procedure name"); + case ELF32_REQUIRED_OBJECT_MISSING: + return p_strdup ("Required object is missing"); + default: + return p_strdup ("Unknown error"); + } +} + +P_LIB_API pboolean +p_library_loader_is_ref_counted (void) +{ + return TRUE; +} + +void +p_library_loader_init (void) +{ + BPTR segment_list; + Elf32_Handle elf_handle; + + if (pp_library_loader_elf_root != NULL) + return; + + segment_list = IDOS->GetProcSegList (NULL, GPSLF_RUN); + + if (P_UNLIKELY (segment_list == 0)) { + P_ERROR ("PLibraryLoader::p_library_loader_init: GetProcSegList() failed"); + return; + } + + if (P_UNLIKELY (IDOS->GetSegListInfoTags (segment_list, + GSLI_ElfHandle, + &elf_handle, + TAG_DONE) != 1)) { + P_ERROR ("PLibraryLoader::p_library_loader_init: GetSegListInfoTags() failed"); + return; + } + + if (P_UNLIKELY (elf_handle == NULL)) { + P_ERROR ("PLibraryLoader::p_library_loader_init: failed to finf proper GSLI_ElfHandle"); + return; + } + + pp_library_loader_elf_root = IElf->OpenElfTags (OET_ElfHandle, elf_handle, TAG_DONE); + + if (P_UNLIKELY (pp_library_loader_elf_root == NULL)) { + P_ERROR ("PLibraryLoader::p_library_loader_init: OpenElfTags() failed"); + return; + } +} + +void +p_library_loader_shutdown (void) +{ + if (pp_library_loader_elf_root == NULL) + return; + + IElf->CloseElfTags (pp_library_loader_elf_root, CET_ReClose, TRUE, TAG_DONE); + + pp_library_loader_elf_root = NULL; +} diff --git a/3rdparty/plibsys/src/plibraryloader-beos.c b/3rdparty/plibsys/src/plibraryloader-beos.c new file mode 100644 index 0000000..403f7f4 --- /dev/null +++ b/3rdparty/plibsys/src/plibraryloader-beos.c @@ -0,0 +1,142 @@ +/* + * The MIT License + * + * Copyright (C) 2016-2017 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "perror.h" +#include "pfile.h" +#include "plibraryloader.h" +#include "pmem.h" +#include "pstring.h" + +#include <be/kernel/image.h> + +typedef image_id plibrary_handle; + +struct PLibraryLoader_ { + plibrary_handle handle; + status_t last_status; +}; + +static void pp_library_loader_clean_handle (plibrary_handle handle); + +static void +pp_library_loader_clean_handle (plibrary_handle handle) +{ + if (P_UNLIKELY (unload_add_on (handle) != B_OK)) + P_ERROR ("PLibraryLoader::pp_library_loader_clean_handle: unload_add_on() failed"); +} + +P_LIB_API PLibraryLoader * +p_library_loader_new (const pchar *path) +{ + PLibraryLoader *loader = NULL; + plibrary_handle handle; + + if (!p_file_is_exists (path)) + return NULL; + + if (P_UNLIKELY ((handle = load_add_on (path)) == B_ERROR)) { + P_ERROR ("PLibraryLoader::p_library_loader_new: load_add_on() failed"); + return NULL; + } + + if (P_UNLIKELY ((loader = p_malloc0 (sizeof (PLibraryLoader))) == NULL)) { + P_ERROR ("PLibraryLoader::p_library_loader_new: failed to allocate memory"); + pp_library_loader_clean_handle (handle); + return NULL; + } + + loader->handle = handle; + loader->last_status = B_OK; + + return loader; +} + +P_LIB_API PFuncAddr +p_library_loader_get_symbol (PLibraryLoader *loader, const pchar *sym) +{ + ppointer location = NULL; + status_t status; + + if (P_UNLIKELY (loader == NULL || sym == NULL)) + return NULL; + + if (P_UNLIKELY ((status = get_image_symbol (loader->handle, + (pchar *) sym, + B_SYMBOL_TYPE_ANY, + &location)) != B_OK)) { + P_ERROR ("PLibraryLoader::p_library_loader_get_symbol: get_image_symbol() failed"); + loader->last_status = status; + return NULL; + } + + loader->last_status = B_OK; + + return (PFuncAddr) location; +} + +P_LIB_API void +p_library_loader_free (PLibraryLoader *loader) +{ + if (P_UNLIKELY (loader == NULL)) + return; + + pp_library_loader_clean_handle (loader->handle); + + p_free (loader); +} + +P_LIB_API pchar * +p_library_loader_get_last_error (PLibraryLoader *loader) +{ + if (loader == NULL) + return NULL; + + switch (loader->last_status) { + case B_OK: + return NULL; + case B_BAD_IMAGE_ID: + return p_strdup ("Image handler doesn't identify an existing image"); + case B_BAD_INDEX: + return p_strdup ("Invalid symbol index"); + default: + return p_strdup ("Unknown error"); + } +} + +P_LIB_API pboolean +p_library_loader_is_ref_counted (void) +{ + return TRUE; +} + +void +p_library_loader_init (void) +{ +} + +void +p_library_loader_shutdown (void) +{ +} diff --git a/3rdparty/plibsys/src/plibraryloader-none.c b/3rdparty/plibsys/src/plibraryloader-none.c new file mode 100644 index 0000000..aab6d87 --- /dev/null +++ b/3rdparty/plibsys/src/plibraryloader-none.c @@ -0,0 +1,75 @@ +/* + * The MIT License + * + * Copyright (C) 2016-2017 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "plibraryloader.h" + +P_LIB_API PLibraryLoader * +p_library_loader_new (const pchar *path) +{ + P_ERROR ("PLibraryLoader::p_library_loader_new: not implemented"); + return NULL; +} + +P_LIB_API PFuncAddr +p_library_loader_get_symbol (PLibraryLoader *loader, const pchar *sym) +{ + P_UNUSED (loader); + P_UNUSED (sym); + + P_ERROR ("PLibraryLoader::p_library_loader_get_symbol: not implemented"); + return NULL; +} + +P_LIB_API void +p_library_loader_free (PLibraryLoader *loader) +{ + P_UNUSED (loader); + P_ERROR ("PLibraryLoader::p_library_loader_free: not implemented"); +} + +P_LIB_API pchar * +p_library_loader_get_last_error (PLibraryLoader *loader) +{ + P_UNUSED (loader); + + P_ERROR ("PLibraryLoader::p_library_loader_get_last_error: not implemented"); + return NULL; +} + +P_LIB_API pboolean +p_library_loader_is_ref_counted (void) +{ + return FALSE; +} + +void +p_library_loader_init (void) +{ +} + +void +p_library_loader_shutdown (void) +{ +} diff --git a/3rdparty/plibsys/src/plibraryloader-os2.c b/3rdparty/plibsys/src/plibraryloader-os2.c new file mode 100644 index 0000000..0f6c95d --- /dev/null +++ b/3rdparty/plibsys/src/plibraryloader-os2.c @@ -0,0 +1,155 @@ +/* + * The MIT License + * + * Copyright (C) 2017 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "perror.h" +#include "pfile.h" +#include "plibraryloader.h" +#include "pmem.h" +#include "pstring.h" + +#define INCL_DOSMODULEMGR +#define INCL_DOSERRORS +#include <os2.h> + +typedef HMODULE plibrary_handle; + +struct PLibraryLoader_ { + plibrary_handle handle; + APIRET last_error; +}; + +static void pp_library_loader_clean_handle (plibrary_handle handle); + +static void +pp_library_loader_clean_handle (plibrary_handle handle) +{ + APIRET ulrc; + + while ((ulrc = DosFreeModule (handle)) == ERROR_INTERRUPT) + ; + + if (P_UNLIKELY (ulrc != NO_ERROR)) + P_ERROR ("PLibraryLoader::pp_library_loader_clean_handle: DosFreeModule() failed"); +} + +P_LIB_API PLibraryLoader * +p_library_loader_new (const pchar *path) +{ + PLibraryLoader *loader = NULL; + plibrary_handle handle = NULLHANDLE; + UCHAR load_err[256]; + APIRET ulrc; + + + if (!p_file_is_exists (path)) + return NULL; + + while ((ulrc = DosLoadModule ((PSZ) load_err, + sizeof (load_err), + (PSZ) path, + (PHMODULE) &handle)) == ERROR_INTERRUPT) + ; + + if (P_UNLIKELY (ulrc != NO_ERROR)) { + P_ERROR ("PLibraryLoader::p_library_loader_new: DosLoadModule() failed"); + return NULL; + } + + if (P_UNLIKELY ((loader = p_malloc0 (sizeof (PLibraryLoader))) == NULL)) { + P_ERROR ("PLibraryLoader::p_library_loader_new: failed to allocate memory"); + pp_library_loader_clean_handle (handle); + return NULL; + } + + loader->handle = handle; + loader->last_error = NO_ERROR; + + return loader; +} + +P_LIB_API PFuncAddr +p_library_loader_get_symbol (PLibraryLoader *loader, const pchar *sym) +{ + PFN func_addr = NULL; + APIRET ulrc; + + if (P_UNLIKELY (loader == NULL || sym == NULL || loader->handle == NULL)) + return NULL; + + if (P_UNLIKELY ((ulrc = DosQueryProcAddr (loader->handle, 0, (PSZ) sym, &func_addr)) != NO_ERROR)) { + P_ERROR ("PLibraryLoader::p_library_loader_get_symbol: DosQueryProcAddr() failed"); + loader->last_error = ulrc; + return NULL; + } + + loader->last_error = NO_ERROR; + + return (PFuncAddr) func_addr; +} + +P_LIB_API void +p_library_loader_free (PLibraryLoader *loader) +{ + if (P_UNLIKELY (loader == NULL)) + return; + + pp_library_loader_clean_handle (loader->handle); + + p_free (loader); +} + +P_LIB_API pchar * +p_library_loader_get_last_error (PLibraryLoader *loader) +{ + if (loader == NULL) + return NULL; + + switch (loader->last_error) { + case NO_ERROR: + return NULL; + case ERROR_INVALID_HANDLE: + return p_strdup ("Invalid resource handler"); + case ERROR_INVALID_NAME: + return p_strdup ("Invalid procedure name"); + default: + return p_strdup ("Unknown error"); + } +} + +P_LIB_API pboolean +p_library_loader_is_ref_counted (void) +{ + return TRUE; +} + +void +p_library_loader_init (void) +{ +} + +void +p_library_loader_shutdown (void) +{ +} diff --git a/3rdparty/plibsys/src/plibraryloader-posix.c b/3rdparty/plibsys/src/plibraryloader-posix.c new file mode 100644 index 0000000..75ef8ed --- /dev/null +++ b/3rdparty/plibsys/src/plibraryloader-posix.c @@ -0,0 +1,148 @@ +/* + * The MIT License + * + * Copyright (C) 2015-2017 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "perror.h" +#include "pfile.h" +#include "plibraryloader.h" +#include "pmem.h" +#include "pstring.h" + +#include <dlfcn.h> + +/* FreeBSD may cause a segfault: https://reviews.freebsd.org/D5112, + * DragonFlyBSD as well, so we need to check a file size before calling dlopen() + */ +#if defined (P_OS_FREEBSD) || defined (P_OS_DRAGONFLY) +# include <unistd.h> +# include <sys/types.h> +# include <sys/stat.h> +#endif + +typedef ppointer plibrary_handle; + +struct PLibraryLoader_ { + plibrary_handle handle; +}; + +static void pp_library_loader_clean_handle (plibrary_handle handle); + +static void +pp_library_loader_clean_handle (plibrary_handle handle) +{ + if (P_UNLIKELY (dlclose (handle) != 0)) + P_ERROR ("PLibraryLoader::pp_library_loader_clean_handle: dlclose() failed"); +} + +P_LIB_API PLibraryLoader * +p_library_loader_new (const pchar *path) +{ + PLibraryLoader *loader = NULL; + plibrary_handle handle; +#if defined (P_OS_FREEBSD) || defined (P_OS_DRAGONFLY) + struct stat stat_buf; +#endif + + if (!p_file_is_exists (path)) + return NULL; + +#if defined (P_OS_FREEBSD) || defined (P_OS_DRAGONFLY) + if (P_UNLIKELY (stat (path, &stat_buf) != 0)) { + P_ERROR ("PLibraryLoader::p_library_loader_new: stat() failed"); + return NULL; + } + + if (P_UNLIKELY (stat_buf.st_size == 0)) { + P_ERROR ("PLibraryLoader::p_library_loader_new: unable to handle zero-size file"); + return NULL; + } +#endif + + if (P_UNLIKELY ((handle = dlopen (path, RTLD_NOW)) == NULL)) { + P_ERROR ("PLibraryLoader::p_library_loader_new: dlopen() failed"); + return NULL; + } + + if (P_UNLIKELY ((loader = p_malloc0 (sizeof (PLibraryLoader))) == NULL)) { + P_ERROR ("PLibraryLoader::p_library_loader_new: failed to allocate memory"); + pp_library_loader_clean_handle (handle); + return NULL; + } + + loader->handle = handle; + + return loader; +} + +P_LIB_API PFuncAddr +p_library_loader_get_symbol (PLibraryLoader *loader, const pchar *sym) +{ + if (P_UNLIKELY (loader == NULL || sym == NULL || loader->handle == NULL)) + return NULL; + + return (PFuncAddr) dlsym (loader->handle, sym); +} + +P_LIB_API void +p_library_loader_free (PLibraryLoader *loader) +{ + if (P_UNLIKELY (loader == NULL)) + return; + + pp_library_loader_clean_handle (loader->handle); + + p_free (loader); +} + +P_LIB_API pchar * +p_library_loader_get_last_error (PLibraryLoader *loader) +{ + pchar *res = NULL; + pchar *msg; + + P_UNUSED (loader); + + msg = dlerror (); + + if (msg != NULL) + res = p_strdup (msg); + + return res; +} + +P_LIB_API pboolean +p_library_loader_is_ref_counted (void) +{ + return TRUE; +} + +void +p_library_loader_init (void) +{ +} + +void +p_library_loader_shutdown (void) +{ +} diff --git a/3rdparty/plibsys/src/plibraryloader-shl.c b/3rdparty/plibsys/src/plibraryloader-shl.c new file mode 100644 index 0000000..8129697 --- /dev/null +++ b/3rdparty/plibsys/src/plibraryloader-shl.c @@ -0,0 +1,140 @@ +/* + * The MIT License + * + * Copyright (C) 2017 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "perror.h" +#include "pfile.h" +#include "plibraryloader.h" +#include "pmem.h" +#include "pstring.h" + +#include <dl.h> +#include <errno.h> +#include <string.h> + +typedef shl_t plibrary_handle; + +struct PLibraryLoader_ { + plibrary_handle handle; + int last_error; +}; + +static void pp_library_loader_clean_handle (plibrary_handle handle); + +static void +pp_library_loader_clean_handle (plibrary_handle handle) +{ + if (P_UNLIKELY (shl_unload (handle) != 0)) + P_ERROR ("PLibraryLoader::pp_library_loader_clean_handle: shl_unload() failed"); +} + +P_LIB_API PLibraryLoader * +p_library_loader_new (const pchar *path) +{ + PLibraryLoader *loader = NULL; + plibrary_handle handle; + + if (!p_file_is_exists (path)) + return NULL; + + if (P_UNLIKELY ((handle = shl_load (path, BIND_IMMEDIATE | BIND_NONFATAL | DYNAMIC_PATH, 0)) == NULL)) { + P_ERROR ("PLibraryLoader::p_library_loader_new: shl_load() failed"); + return NULL; + } + + if (P_UNLIKELY ((loader = p_malloc0 (sizeof (PLibraryLoader))) == NULL)) { + P_ERROR ("PLibraryLoader::p_library_loader_new: failed to allocate memory"); + pp_library_loader_clean_handle (handle); + return NULL; + } + + loader->handle = handle; + loader->last_error = 0; + + return loader; +} + +P_LIB_API PFuncAddr +p_library_loader_get_symbol (PLibraryLoader *loader, const pchar *sym) +{ + PFuncAddr func_addr = NULL; + + if (P_UNLIKELY (loader == NULL || sym == NULL || loader->handle == NULL)) + return NULL; + + if (P_UNLIKELY (shl_findsym (&loader->handle, sym, TYPE_UNDEFINED, (ppointer) &func_addr) != 0)) { + P_ERROR ("PLibraryLoader::p_library_loader_get_symbol: shl_findsym() failed"); + loader->last_error = (errno == 0 ? -1 : errno); + return NULL; + } + + loader->last_error = 0; + + return func_addr; +} + +P_LIB_API void +p_library_loader_free (PLibraryLoader *loader) +{ + if (P_UNLIKELY (loader == NULL)) + return; + + pp_library_loader_clean_handle (loader->handle); + + p_free (loader); +} + +P_LIB_API pchar * +p_library_loader_get_last_error (PLibraryLoader *loader) +{ + if (loader == NULL) + return NULL; + + if (loader->last_error == 0) + return NULL; + else if (loader->last_error == -1) + return p_strdup ("Failed to find a symbol"); + else + return p_strdup (strerror (loader->last_error)); +} + +P_LIB_API pboolean +p_library_loader_is_ref_counted (void) +{ +#if defined (P_OS_HPUX) && defined (P_CPU_HPPA) && (PLIBSYS_SIZEOF_VOID_P == 4) + return FALSE; +#else + return TRUE; +#endif +} + +void +p_library_loader_init (void) +{ +} + +void +p_library_loader_shutdown (void) +{ +} diff --git a/3rdparty/plibsys/src/plibraryloader-win.c b/3rdparty/plibsys/src/plibraryloader-win.c new file mode 100644 index 0000000..80c5aba --- /dev/null +++ b/3rdparty/plibsys/src/plibraryloader-win.c @@ -0,0 +1,140 @@ +/* + * The MIT License + * + * Copyright (C) 2015-2017 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "perror.h" +#include "pfile.h" +#include "plibraryloader.h" +#include "pmem.h" +#include "pstring.h" + +typedef HINSTANCE plibrary_handle; + +struct PLibraryLoader_ { + plibrary_handle handle; +}; + +static void pp_library_loader_clean_handle (plibrary_handle handle); + +static void +pp_library_loader_clean_handle (plibrary_handle handle) +{ + if (P_UNLIKELY (!FreeLibrary (handle))) + P_ERROR ("PLibraryLoader::pp_library_loader_clean_handle: FreeLibrary() failed"); +} + +P_LIB_API PLibraryLoader * +p_library_loader_new (const pchar *path) +{ + PLibraryLoader *loader = NULL; + plibrary_handle handle; + + if (!p_file_is_exists (path)) + return NULL; + + if (P_UNLIKELY ((handle = LoadLibraryA (path)) == NULL)) { + P_ERROR ("PLibraryLoader::p_library_loader_new: LoadLibraryA() failed"); + return NULL; + } + + if (P_UNLIKELY ((loader = p_malloc0 (sizeof (PLibraryLoader))) == NULL)) { + P_ERROR ("PLibraryLoader::p_library_loader_new: failed to allocate memory"); + pp_library_loader_clean_handle (handle); + return NULL; + } + + loader->handle = handle; + + return loader; +} + +P_LIB_API PFuncAddr +p_library_loader_get_symbol (PLibraryLoader *loader, const pchar *sym) +{ + PFuncAddr ret_sym = NULL; + + if (P_UNLIKELY (loader == NULL || sym == NULL || loader->handle == NULL)) + return NULL; + + ret_sym = (PFuncAddr) GetProcAddress (loader->handle, sym); + + return ret_sym; +} + +P_LIB_API void +p_library_loader_free (PLibraryLoader *loader) +{ + if (P_UNLIKELY (loader == NULL)) + return; + + pp_library_loader_clean_handle (loader->handle); + + p_free (loader); +} + +P_LIB_API pchar * +p_library_loader_get_last_error (PLibraryLoader *loader) +{ + pchar *res = NULL; + DWORD err_code; + LPVOID msg_buf; + + P_UNUSED (loader); + + err_code = p_error_get_last_system (); + + if (err_code == 0) + return NULL; + + if (P_LIKELY (FormatMessageA (FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + err_code, + MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPSTR) &msg_buf, + 0, + NULL) != 0)) { + res = p_strdup ((pchar *) msg_buf); + LocalFree (msg_buf); + } + + return res; +} + +P_LIB_API pboolean +p_library_loader_is_ref_counted (void) +{ + return TRUE; +} + +void +p_library_loader_init (void) +{ +} + +void +p_library_loader_shutdown (void) +{ +} diff --git a/3rdparty/plibsys/src/plibraryloader.h b/3rdparty/plibsys/src/plibraryloader.h new file mode 100644 index 0000000..d44bef2 --- /dev/null +++ b/3rdparty/plibsys/src/plibraryloader.h @@ -0,0 +1,155 @@ +/* + * The MIT License + * + * Copyright (C) 2015-2017 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * @file plibraryloader.h + * @brief Shared library loader + * @author Alexander Saprykin + * + * All modern operating systems support dynamic loadable objects. Such objects + * are compiled with special flags and can be loaded by other programs and + * libraries later at the runtime. These loadable objects often called as the + * shared libraries, though some platforms even allow to treat the program + * binary as a loadable object, too. + * + * When the program is linked with a shared library its dependency would be + * resolved by the operating system automatically before starting the program. + * But in some circumstances you may need to load a shared library object + * explicitly (i.e. implementing a plugin subsystem, checking for API + * availability). + * + * All functions and variables which a shared library is exporting are called + * symbols. Usually only the exported symbols are available from outside the + * shared library. Actually all those symbols represent a library API. + * + * Use p_library_loader_new() to load a shared library and + * p_library_loader_get_symbol() to retrieve a pointer to a symbol within it. + * Close the library after usage with p_library_loader_free(). + * + * Please note the following platform specific differences: + * + * - HP-UX doesn't support loading libraries containing TLS and built with + * static TLS model. The same rule applies to any library used as dependency. + * HP-UX on 32-bit PA-RISC systems doesn't support reference counting for loaded + * libraries when using shl_* family of functions (always removes all library + * references on unload). + * + * - On OpenVMS only shareable images (linked with /SHAREABLE) can be used for + * dynamic symbol resolving. Usually they have .EXE extension. + * + * - BeOS supports dynamic loading for add-ons only. It is also possible to + * load the same library several times independently (not like a traditional + * shared library). + */ + +#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION) +# error "Header files shouldn't be included directly, consider using <plibsys.h> instead." +#endif + +#ifndef PLIBSYS_HEADER_PLIBRARYLOADER_H +#define PLIBSYS_HEADER_PLIBRARYLOADER_H + +#include <pmacros.h> +#include <ptypes.h> + +P_BEGIN_DECLS + +/** Opaque data structure to handle a shared library. */ +typedef struct PLibraryLoader_ PLibraryLoader; + +/** Pointer to a function address. */ +typedef void (*PFuncAddr) (void); + +/** + * @brief Loads a shared library. + * @param path Path to the shared library file. + * @return Pointer to #PLibraryLoader in case of success, NULL otherwise. + * @since 0.0.1 + * + * If you are loading the already loaded shared library, an operating system + * increments corresponding reference count and decrements it after freeing + * #PLibraryLoader, thus the shared library would be unloaded from the address + * space only when the counter becomes zero. + */ +P_LIB_API PLibraryLoader * p_library_loader_new (const pchar *path); + +/** + * @brief Gets a pointer to a symbol in the loaded shared library. + * @param loader Pointer to the loaded shared library handle. + * @param sym Name of the symbol. + * @return Pointer to the symbol in case of success, NULL otherwise. + * @since 0.0.1 + * + * Since the symbol may have a NULL value, the returned NULL value from this + * call actually doesn't mean the failed result. You can additionally check the + * error result using p_library_loader_get_last_error(). + */ +P_LIB_API PFuncAddr p_library_loader_get_symbol (PLibraryLoader *loader, + const pchar *sym); + +/** + * @brief Frees memory and allocated resources of #PLibraryLoader. + * @param loader #PLibraryLoader object to free. + * @since 0.0.1 + */ +P_LIB_API void p_library_loader_free (PLibraryLoader *loader); + +/** + * @brief Gets the last occurred error. + * @param loader #PLibraryLoader object to get error for. + * @return Human readable error string in case of success, NULL otherwise. + * @since 0.0.1 + * @version 0.0.3 @p loader parameter was added. + * @note Caller takes ownership of the returned string. + * + * The NULL result may indicate that no error was occurred since the last call. + * + * Different operating systems have different behavior on error indicating. + * Some systems reset an error status before the call, some do not. Some + * systems write the successful call result (usually zero) to the error status, + * thus resetting an error from the previous call. + * + * Some operating systems may return last error even if library handler was not + * created. In that case try to pass NULL value as a parameter. + */ +P_LIB_API pchar * p_library_loader_get_last_error (PLibraryLoader *loader); + +/** + * @brief Checks whether library loading subsystem uses reference counting. + * @return TRUE in case of success, FALSE otherwise. + * @since 0.0.3 + * + * When reference counting is supported, the same shared library can be opened + * several times, but it would be completely unloaded from the memory only when + * the last reference to it is removed. + * + * @note For now, only HP-UX on 32-bit PA-RISC systems with shl_* model doesn't + * support reference counting. + */ +P_LIB_API pboolean p_library_loader_is_ref_counted (void); + +P_END_DECLS + +#endif /* PLIBSYS_HEADER_PLIBRARYLOADER_H */ diff --git a/3rdparty/plibsys/src/plibsys-private.h b/3rdparty/plibsys/src/plibsys-private.h new file mode 100644 index 0000000..75a9507 --- /dev/null +++ b/3rdparty/plibsys/src/plibsys-private.h @@ -0,0 +1,86 @@ +/* + * The MIT License + * + * Copyright (C) 2013-2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION) +# error "Header files shouldn't be included directly, consider using <plibsys.h> instead." +#endif + +#ifndef PLIBSYS_HEADER_PLIBSYS_PRIVATE_H +#define PLIBSYS_HEADER_PLIBSYS_PRIVATE_H + +#include "pmacros.h" +#include "ptypes.h" + +P_BEGIN_DECLS + +#ifndef PLIBSYS_HAS_SOCKLEN_T +# ifdef P_OS_VMS +typedef unsigned int socklen_t; +# else +typedef int socklen_t; +# endif +#endif + +#ifndef PLIBSYS_HAS_SOCKADDR_STORAGE +/* According to RFC 2553 */ +# define _PLIBSYS_SS_MAXSIZE 128 +# define _PLIBSYS_SS_ALIGNSIZE (sizeof (pint64)) + +# ifdef PLIBSYS_SOCKADDR_HAS_SA_LEN +# define _PLIBSYS_SS_PAD1SIZE (_PLIBSYS_SS_ALIGNSIZE - (sizeof (puchar) + sizeof (puchar))) +# else +# define _PLIBSYS_SS_PAD1SIZE (_PLIBSYS_SS_ALIGNSIZE - sizeof (puchar)) +# endif + +# define _PLIBSYS_SS_PAD2SIZE (_PLIBSYS_SS_MAXSIZE - (sizeof (puchar) + _PLIBSYS_SS_PAD1SIZE + _PLIBSYS_SS_ALIGNSIZE)) + +struct sockaddr_storage { +# ifdef PLIBSYS_SOCKADDR_HAS_SA_LEN + puchar ss_len; +# endif +# ifdef PLIBSYS_SIZEOF_SAFAMILY_T +# if (PLIBSYS_SIZEOF_SAFAMILY_T == 1) + puchar ss_family; +# elif (PLIBSYS_SIZEOF_SAFAMILY_T == 2) + pushort ss_family; +# else + puint ss_family; +# endif +# else +# ifdef PLIBSYS_SOCKADDR_HAS_SA_LEN + puchar ss_family; +# else + pushort ss_family; +# endif +# endif + pchar __ss_pad1[_PLIBSYS_SS_PAD1SIZE]; + pint64 __ss_align; + pchar __ss_pad2[_PLIBSYS_SS_PAD2SIZE]; +}; +#endif + +P_END_DECLS + +#endif /* PLIBSYS_HEADER_PLIBSYS_PRIVATE_H */ diff --git a/3rdparty/plibsys/src/plibsys.h b/3rdparty/plibsys/src/plibsys.h new file mode 100644 index 0000000..c322747 --- /dev/null +++ b/3rdparty/plibsys/src/plibsys.h @@ -0,0 +1,64 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2017 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef PLIBSYS_HEADER_PLIBSYS_H +#define PLIBSYS_HEADER_PLIBSYS_H + +#define PLIBSYS_H_INSIDE + +#include "plibsysconfig.h" +#include "patomic.h" +#include "pcondvariable.h" +#include "pcryptohash.h" +#include "pdir.h" +#include "perror.h" +#include "pfile.h" +#include "phashtable.h" +#include "pinifile.h" +#include "plibraryloader.h" +#include "plist.h" +#include "pmacros.h" +#include "pmacroscompiler.h" +#include "pmacroscpu.h" +#include "pmacrosos.h" +#include "pmain.h" +#include "pmem.h" +#include "pmutex.h" +#include "pprocess.h" +#include "prwlock.h" +#include "psemaphore.h" +#include "pshm.h" +#include "pshmbuffer.h" +#include "psocket.h" +#include "psocketaddress.h" +#include "pspinlock.h" +#include "pstdarg.h" +#include "pstring.h" +#include "ptimeprofiler.h" +#include "ptree.h" +#include "ptypes.h" +#include "puthread.h" + +#endif /* PLIBSYS_HEADER_PLIBSYS_H */ diff --git a/3rdparty/plibsys/src/plibsysconfig.h.in b/3rdparty/plibsys/src/plibsysconfig.h.in new file mode 100644 index 0000000..db06020 --- /dev/null +++ b/3rdparty/plibsys/src/plibsysconfig.h.in @@ -0,0 +1,76 @@ +#ifndef PLIBSYS_HEADER_PLIBSYSCONFIG_H +#define PLIBSYS_HEADER_PLIBSYSCONFIG_H + +#define PLIBSYS_VERSION_MAJOR @PLIBSYS_VERSION_MAJOR@ +#define PLIBSYS_VERSION_MINOR @PLIBSYS_VERSION_MINOR@ +#define PLIBSYS_VERSION_PATCH @PLIBSYS_VERSION_PATCH@ +#define PLIBSYS_VERSION_STR "@PLIBSYS_VERSION@" +#define PLIBSYS_VERSION @PLIBSYS_VERSION_NUM@ + +#cmakedefine PLIBSYS_NEED_WINDOWS_H +#cmakedefine PLIBSYS_NEED_FLOAT_H +#cmakedefine PLIBSYS_NEED_LIMITS_H +#cmakedefine PLIBSYS_NEED_VALUES_H +#cmakedefine PLIBSYS_NEED_PTHREAD_NP_H +#cmakedefine PLIBSYS_IS_BIGENDIAN +#cmakedefine PLIBSYS_SIZEOF_SAFAMILY_T @PLIBSYS_SIZEOF_SAFAMILY_T@ +#cmakedefine PLIBSYS_VA_COPY @PLIBSYS_VA_COPY@ + +#define PLIBSYS_NTDDI_VERSION_FROM_WIN32_WINNT2(ver) ver##0000 +#define PLIBSYS_NTDDI_VERSION_FROM_WIN32_WINNT(ver) PLIBSYS_NTDDI_VERSION_FROM_WIN32_WINNT2(ver) + +#ifdef PLIBSYS_NEED_WINDOWS_H +# ifndef _WIN32_WINNT +# define _WIN32_WINNT 0x501 +# endif +# ifndef NTDDI_VERSION +# define NTDDI_VERSION PLIBSYS_NTDDI_VERSION_FROM_WIN32_WINNT(_WIN32_WINNT) +# endif +# include <winsock2.h> +# include <ws2tcpip.h> +# include <windows.h> +#endif + +#include <pmacros.h> + +#ifdef PLIBSYS_NEED_FLOAT_H +# include <float.h> +#endif + +#ifdef PLIBSYS_NEED_LIMITS_H +# include <limits.h> +#endif + +#ifdef PLIBSYS_NEED_VALUES_H +# include <values.h> +#endif + +P_BEGIN_DECLS + +#define P_MINFLOAT @PLIBSYS_FLOAT_MIN@ +#define P_MAXFLOAT @PLIBSYS_FLOAT_MAX@ +#define P_MINDOUBLE @PLIBSYS_DOUBLE_MIN@ +#define P_MAXDOUBLE @PLIBSYS_DOUBLE_MAX@ +#define P_MINSHORT @PLIBSYS_SHORT_MIN@ +#define P_MAXSHORT @PLIBSYS_SHORT_MAX@ +#define P_MAXUSHORT @PLIBSYS_USHORT_MAX@ +#define P_MININT @PLIBSYS_INT_MIN@ +#define P_MAXINT @PLIBSYS_INT_MAX@ +#define P_MAXUINT @PLIBSYS_UINT_MAX@ +#define P_MINLONG @PLIBSYS_LONG_MIN@ +#define P_MAXLONG @PLIBSYS_LONG_MAX@ +#define P_MAXULONG @PLIBSYS_ULONG_MAX@ + +@PLIBSYS_SIZEOF_VOID_P_CODE@ +@PLIBSYS_SIZEOF_SIZE_T_CODE@ +@PLIBSYS_SIZEOF_LONG_CODE@ + +#ifdef PLIBSYS_IS_BIGENDIAN +# define P_BYTE_ORDER P_BIG_ENDIAN +#else +# define P_BYTE_ORDER P_LITTLE_ENDIAN +#endif + +P_END_DECLS + +#endif /* PLIBSYS_HEADER_PLIBSYSCONFIG_H */ diff --git a/3rdparty/plibsys/src/plist.c b/3rdparty/plibsys/src/plist.c new file mode 100644 index 0000000..ccb5dee --- /dev/null +++ b/3rdparty/plibsys/src/plist.c @@ -0,0 +1,174 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pmem.h" +#include "plist.h" + +#include <stdlib.h> + +P_LIB_API PList * +p_list_append (PList *list, ppointer data) +{ + PList *item, *cur; + + if (P_UNLIKELY ((item = p_malloc0 (sizeof (PList))) == NULL)) { + P_ERROR ("PList::p_list_append: failed to allocate memory"); + return list; + } + + item->data = data; + + /* List is empty */ + if (P_UNLIKELY (list == NULL)) + return item; + + for (cur = list; cur->next != NULL; cur = cur->next) + ; + cur->next = item; + + return list; +} + +P_LIB_API PList * +p_list_remove (PList *list, ppointer data) +{ + PList *cur, *prev, *head; + + if (P_UNLIKELY (list == NULL)) + return NULL; + + for (head = list, prev = NULL, cur = list; cur != NULL; prev = cur, cur = cur->next) { + if (cur->data == data) { + if (prev == NULL) + head = cur->next; + else + prev->next = cur->next; + + p_free (cur); + + break; + } + } + + return head; +} + +P_LIB_API void +p_list_foreach (PList *list, PFunc func, ppointer user_data) +{ + PList *cur; + + if (P_UNLIKELY (list == NULL || func == NULL)) + return; + + for (cur = list; cur != NULL; cur = cur->next) + func (cur->data, user_data); +} + +P_LIB_API void +p_list_free (PList *list) +{ + PList *cur, *next; + + if (P_UNLIKELY (list == NULL)) + return; + + for (next = cur = list; cur != NULL && next != NULL; cur = next) { + next = cur->next; + p_free (cur); + } +} + +P_LIB_API PList * +p_list_last (PList *list) +{ + PList *cur; + + if (P_UNLIKELY (list == NULL)) + return NULL; + + for (cur = list; cur->next != NULL; cur = cur->next) + ; + + return cur; +} + +P_LIB_API psize +p_list_length (const PList *list) +{ + const PList *cur; + psize ret; + + if (P_UNLIKELY (list == NULL)) + return 0; + + for (cur = list, ret = 1; cur->next != NULL; cur = cur->next, ++ret) + ; + + return ret; +} + +P_LIB_API PList * +p_list_prepend (PList *list, ppointer data) +{ + PList *item; + + if (P_UNLIKELY ((item = p_malloc0 (sizeof (PList))) == NULL)) { + P_ERROR ("PList::p_list_prepend: failed to allocate memory"); + return list; + } + + item->data = data; + + /* List is empty */ + if (P_UNLIKELY (list == NULL)) + return item; + + item->next = list; + + return item; +} + +P_LIB_API PList * +p_list_reverse (PList *list) +{ + PList *prev, *cur, *tmp; + + if (P_UNLIKELY (list == NULL)) + return NULL; + + prev = list; + cur = list->next; + prev->next = NULL; + + while (cur != NULL) { + tmp = cur->next; + cur->next = prev; + prev = cur; + cur = tmp; + } + + return prev; +} diff --git a/3rdparty/plibsys/src/plist.h b/3rdparty/plibsys/src/plist.h new file mode 100644 index 0000000..e72ba0c --- /dev/null +++ b/3rdparty/plibsys/src/plist.h @@ -0,0 +1,199 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * @file plist.h + * @brief Singly linked list + * @author Alexander Saprykin + * + * A singly linked list is a data structure consists of the nodes which + * represent a sequence. Each node contains a data pointer and a pointer to the + * next node. Every node has a link only to the next node, hence list is a + * singly linked (in a single direction). + * + * As the singly linked list is a linear collection of the nodes with the + * sequential access, it has an O(N) average complexity for appending, removing + * and searching operations. Prepending a node takes O(1) constant time. Thus it + * is not intended for heavy usage, please refer to #PHashTable or #PTree if you + * are working with large data sets. + * + * Before the first usage you must initialize a #PList variable to NULL. After + * that you can use the p_list_append(), p_list_prepend(), p_list_remove() and + * p_list_reverse() routines to update that variable: + * @code + * PList *list; + * ppointer data; + * + * list = NULL; + * data = my_obj_new (); + * + * list = p_list_append (list, data); + * @endcode + * #PList stores only the pointers to the data, so you must free used memory + * manually, p_list_free() only frees list's internal memory, not the data it + * stores the pointers for. The best approach to free used memory is the + * p_list_foreach() routine: + * @code + * PList *list; + * ... + * p_list_foreach (list, (PFunc) my_free_func, my_data); + * p_list_free (list); + * @endcode + * Also you can use #P_INT_TO_POINTER and #P_POINTER_TO_INT macros to store + * integers (up to 32-bit) without allocating memory for them: + * @code + * PList *list; + * pint a; + * + * list = p_list_append (list, P_INT_TO_POINTER (12)); + * a = P_POINTER_TO_INT (list->data); + * @endcode + * #PList can store several nodes with the same pointer value, but + * p_list_remove() will remove only the first matching node. + * + * If you need to add large amount of nodes at once it is better to prepend them + * and then reverse the list. + */ + +#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION) +# error "Header files shouldn't be included directly, consider using <plibsys.h> instead." +#endif + +#ifndef PLIBSYS_HEADER_PLIST_H +#define PLIBSYS_HEADER_PLIST_H + +#include <pmacros.h> +#include <ptypes.h> + +P_BEGIN_DECLS + +/** Typedef for a list node. */ +typedef struct PList_ PList; + +/** Node for a singly linked list. */ +struct PList_ { + ppointer data; /**< Pointer to the node data. */ + PList *next; /**< Next list node. */ +}; + +/** + * @brief Appends data to a list. + * @param list #PList for appending the data. + * @param data Data to append. + * @return Pointer to the updated list in case of success, @a list otherwise. + * @since 0.0.1 + * + * Before appending the first node to the list, @a list argument must be + * initialized with NULL. Otherwise behavior is unpredictable. + */ +P_LIB_API PList * p_list_append (PList *list, + ppointer data) P_GNUC_WARN_UNUSED_RESULT; + +/** + * @brief Removes data from a list. + * @param list List to remove the data from. + * @param data Data to remove. + * @return Pointer to the updated list in case of success, @a list otherwise. + * @since 0.0.1 + * + * It searches for the first matching occurrence in the @a list and removes + * that node. Note that it removes only the pointer from the @a list, not the + * data it pointers to, so you need to free the data manually. + */ +P_LIB_API PList * p_list_remove (PList *list, + ppointer data) P_GNUC_WARN_UNUSED_RESULT; + +/** + * @brief Calls a specified function for each list node. + * @param list List to go through. + * @param func Pointer for the callback function. + * @param user_data User defined data, may be NULL. + * @since 0.0.1 + * + * This function goes through the whole @a list and calls @a func for each node. + * The @a func will receive pointer to the node's data and @a user_data. You can + * use it to free the data: + * @code + * p_list_foreach (list, (PFunc) free, NULL); + * p_list_free (list); + * @endcode + */ +P_LIB_API void p_list_foreach (PList *list, + PFunc func, + ppointer user_data); + +/** + * @brief Frees list memory. + * @param list List to free. + * @since 0.0.1 + * + * This function frees only the list's internal memory, not the data in the + * pointers stored in the nodes. Don't forget to free all the data stored in the + * list manually. + */ +P_LIB_API void p_list_free (PList *list); + +/** + * @brief Gets the last node from the list. + * @param list List to get the node from. + * @return Pointer to the last @a list node, NULL if the @a list is empty. + * @since 0.0.1 + */ +P_LIB_API PList * p_list_last (PList *list); + +/** + * @brief Gets the number of list nodes. + * @param list List to count nodes in. + * @return Number of nodes in the @a list. + * @since 0.0.1 + * @note This function will iterate through the whole @a list, so don't use it + * in condition of the for-loop or in the code which is repeated a lot of times. + */ +P_LIB_API psize p_list_length (const PList *list); + +/** + * @brief Prepends data to a list. + * @param list #PList for prepending the data. + * @param data Data to prepend. + * @return Pointer to the updated list in case of success, @a list otherwise. + * @since 0.0.1 + * + * Before prepending the first node to the list, @a list argument must be + * initialized with NULL. Otherwise behavior is unpredictable. + */ +P_LIB_API PList * p_list_prepend (PList *list, + ppointer data) P_GNUC_WARN_UNUSED_RESULT; + +/** + * @brief Reverses the list order. + * @param list #PList to reverse the order. + * @return Pointer to the top of the reversed list. + * @since 0.0.1 + */ +P_LIB_API PList * p_list_reverse (PList *list) P_GNUC_WARN_UNUSED_RESULT; + +P_END_DECLS + +#endif /* PLIBSYS_HEADER_PLIST_H */ diff --git a/3rdparty/plibsys/src/pmacros.h b/3rdparty/plibsys/src/pmacros.h new file mode 100644 index 0000000..7751337 --- /dev/null +++ b/3rdparty/plibsys/src/pmacros.h @@ -0,0 +1,302 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2017 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * @file pmacros.h + * @brief Miscellaneous macros + * @author Alexander Saprykin + * + * All the macros are completely independent of any other platform-specific + * headers, thus gurantee to work with any compiler under any operating system + * in the same way as they are used within the library. + * + * This family of macros provides various additional capabilities (compiler + * hints, attributes, version, etc.). + */ + +#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION) +# error "Header files shouldn't be included directly, consider using <plibsys.h> instead." +#endif + +#ifndef PLIBSYS_HEADER_PMACROS_H +#define PLIBSYS_HEADER_PMACROS_H + +#include <pmacroscompiler.h> +#include <pmacroscpu.h> +#include <pmacrosos.h> + +#include <stdio.h> + +/* For Clang */ +#ifndef __has_attribute +# define __has_attribute(x) 0 +#endif + +#ifndef __has_builtin +# define __has_builtin(x) 0 +#endif + +/** + * @def P_GNUC_WARN_UNUSED_RESULT + * @brief Gives a warning if the result returned from a function is not being + * used. + * @since 0.0.1 + */ + +#if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) || \ + __has_attribute(warn_unused_result) +# define P_GNUC_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) +#else +# define P_GNUC_WARN_UNUSED_RESULT +#endif + +/** + * @def P_LIB_INTERNAL_API + * @brief Marks a symbol (variable, function) as local. + * @since 0.0.4 + * + * Local symbols are not exported during the linkage and are not available from + * the outside of the module they are defined in. Use it for internal API. + * + * @note Some compilers allow to put this attribute at the beginning of the + * symbol declaration, and some also at the end of the declaration. Thus it is + * better to put it in the beginning for more portability. + */ + +/** + * @def P_LIB_GLOBAL_API + * @brief Marks a symbol (variable, function) as global. + * @since 0.0.4 + * + * Global symbols are exported during the linkage and are available from the + * outside of the module they are defined in. Use it for public API. + * + * @note Some compilers allow to put this attribute at the beginning of the + * symbol declaration, and some also at the end of the declaration. Thus it is + * better to put it in the beginning for more portability. + */ + +/* + * Oracle Solaris Studio since version 12 has visibility attribute for C + * compiler, and since version 12.2 for C++ compiler, or since version 8.0 + * specific __global attribute which is the same. + * IBM XL C has support for visibility attributes since version 13.1. + * HP C/aC++ has support for visibility attributes since version A.06.15. + */ + +#if defined(P_CC_MSVC) || defined(P_CC_BORLAND) || defined(P_CC_WATCOM) || \ + defined(P_OS_OS2) || (defined(P_OS_BEOS) && !defined(P_CC_GNU)) || \ + (defined(P_OS_WIN) && defined(P_CC_PGI)) || \ + ((defined(P_OS_WIN) || defined(P_OS_CYGWIN) || defined(P_OS_MSYS)) && defined(P_CC_GNU)) +# define P_LIB_GLOBAL_API __declspec(dllexport) +# define P_LIB_INTERNAL_API +#elif ((__GNUC__ >= 4) && !defined(P_OS_SOLARIS) && !defined(P_OS_HPUX) && !defined(P_OS_AIX)) || \ + (defined(P_CC_SUN) && __SUNPRO_C >= 0x590) || \ + (defined(P_CC_SUN) && __SUNPRO_CC >= 0x5110) || \ + (defined(P_CC_XLC) && __xlC__ >= 0x0D01) || \ + (defined(P_CC_HP) && __HP_aCC >= 0x061500) || \ + (defined(P_CC_HP) && __HP_cc >= 0x061500) || \ + __has_attribute(visibility) +# define P_LIB_GLOBAL_API __attribute__ ((visibility ("default"))) +# define P_LIB_INTERNAL_API __attribute__ ((visibility ("hidden"))) +#elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x550) +# define P_LIB_GLOBAL_API __global +# define P_LIB_INTERNAL_API __hidden +#else +# define P_LIB_GLOBAL_API +# define P_LIB_INTERNAL_API +#endif + +/** + * @def P_LIB_API + * @brief Exports a symbol from a shared library. + * @since 0.0.1 + */ + +#define P_LIB_API P_LIB_GLOBAL_API + +/* Oracle Solaris Studio at least since 12.2 has ((noreturn)) attribute */ + +/** + * @def P_NO_RETURN + * @brief Notifies a compiler that a function will never return a value (i.e. + * due to the abort () call). + * @since 0.0.1 + */ + +#if defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +# define P_NO_RETURN _Noreturn +#elif defined(P_CC_MSVC) || (defined(P_CC_BORLAND) && __BORLANDC__ >= 0x0550) +# define P_NO_RETURN __declspec(noreturn) +#elif __has_attribute(noreturn) || \ + defined(P_CC_GNU) || \ + (defined(P_CC_SUN) && __SUNPRO_C >= 0x5110) || \ + (defined(P_CC_SUN) && __SUNPRO_CC >= 0x5110) +# define P_NO_RETURN __attribute__((noreturn)) +#else +# define P_NO_RETURN +#endif + +/** + * @def P_LIKELY + * @brief Hints a compiler that a condition is likely to be true so it can + * perform code optimizations. + * @since 0.0.1 + */ + +/** + * @def P_UNLIKELY + * @brief Hints a compiler that a condition is likely to be false so it can + * perform code optimizations. + * @since 0.0.1 + */ + +#if (defined(P_CC_GNU) && (__GNUC__ > 2 && __GNUC_MINOR__ > 0)) || \ + (defined(P_CC_INTEL) && __INTEL_COMPILER >= 800) || \ + (defined(P_CC_XLC) && __xlC__ >= 0x0900) || \ + __has_builtin(__builtin_expect) +# define P_LIKELY(x) __builtin_expect(!!(x), 1) +# define P_UNLIKELY(x) __builtin_expect(!!(x), 0) +#else +# define P_LIKELY(x) (x) +# define P_UNLIKELY(x) (x) +#endif + +/** + * @def P_UNUSED + * @brief Macro to by-pass a compiler warning on unused variables. + * @since 0.0.1 + */ +#define P_UNUSED(a) ((void) a) + +/** + * @def P_WARNING + * @brief Prints a warning message. + * @param msg Message to print. + * @since 0.0.1 + */ +#define P_WARNING(msg) printf ("** Warning: %s **\n", msg) + +/** + * @def P_ERROR + * @brief Prints an error message. + * @param msg Message to print. + * @since 0.0.1 + */ +#define P_ERROR(msg) printf ("** Error: %s **\n", msg) + +/** + * @def P_DEBUG + * @brief Prints a debug message. + * @param msg Message to print. + * @since 0.0.1 + */ +#define P_DEBUG(msg) printf ("** Debug: %s **\n", msg) + +#ifdef DOXYGEN +# define PLIBSYS_VERSION_MAJOR +# define PLIBSYS_VERSION_MINOR +# define PLIBSYS_VERSION_PATCH +# define PLIBSYS_VERSION_STR +# define PLIBSYS_VERSION +#endif + +/** + * @def PLIBSYS_VERSION_MAJOR + * @brief Library major version number. + * @since 0.0.1 + * @note This is the version against which the application is compiled. + */ + +/** + * @def PLIBSYS_VERSION_MINOR + * @brief Library minor version number. + * @since 0.0.1 + * @note This is the version against which the application is compiled. + */ + +/** + * @def PLIBSYS_VERSION_PATCH + * @brief Library patch version number. + * @since 0.0.1 + * @note This is the version against which the application is compiled. + */ + +/** + * @def PLIBSYS_VERSION_STR + * @brief Library full version in the string form, i.e. "0.0.1". + * @since 0.0.1 + * @note This is the version against which the application is compiled. + * @sa p_libsys_version() + */ + +/** + * @def PLIBSYS_VERSION + * @brief Library full version in the form 0xMMNNPP (MM = major, NN = minor, + * PP = patch), i.e. 0x000001. + * @since 0.0.1 + * @note This is the version against which the application is compiled. + * @sa p_libsys_version() + */ + +/** + * @def PLIBSYS_VERSION_CHECK + * @brief Makes a library version number which can be used to check the library + * version against which the application is compiled. + * @param major Major version number to check. + * @param minor Minor version number to check. + * @param patch Minor version number to check. + * @since 0.0.1 + * @sa p_libsys_version() + * + * @code + * if (PLIBSYS_VERSION >= PLIBSYS_VERSION_CHECK (0, 0, 1)) + * ... + * @endcode + */ +#define PLIBSYS_VERSION_CHECK(major, minor, patch) ((major << 16) | (minor << 8) | (patch)) + +/** + * @def P_BEGIN_DECLS + * @brief Starts .h file declarations to be exported as C functions. + * @since 0.0.1 + */ + +/** + * @def P_END_DECLS + * @brief Closes .h file declarations to be exported as C functions, should be + * always used after #P_BEGIN_DECLS. + * @since 0.0.1 + */ + +#ifdef __cplusplus +# define P_BEGIN_DECLS extern "C" { +# define P_END_DECLS } +#else +# define P_BEGIN_DECLS +# define P_END_DECLS +#endif + +#endif /* PLIBSYS_HEADER_PMACROS_H */ diff --git a/3rdparty/plibsys/src/pmacroscompiler.h b/3rdparty/plibsys/src/pmacroscompiler.h new file mode 100644 index 0000000..cbe4cc0 --- /dev/null +++ b/3rdparty/plibsys/src/pmacroscompiler.h @@ -0,0 +1,253 @@ +/* + * The MIT License + * + * Copyright (C) 2017 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * @file pmacroscompiler.h + * @brief Compiler detection macros + * @author Alexander Saprykin + * + * All the macros are completely independent of any other platform-specific + * headers, thus gurantee to work with any compiler under any operating system + * in the same way as they are used within the library. + * + * This family of macros provides compiler detection and defines one or several + * of P_CC_x macros. + */ + +#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION) +# error "Header files shouldn't be included directly, consider using <plibsys.h> instead." +#endif + +#ifndef PLIBSYS_HEADER_PMACROSCOMPILER_H +#define PLIBSYS_HEADER_PMACROSCOMPILER_H + +/* + * List of supported compilers (P_CC_x): + * + * MSVC - Microsoft Visual C/C++ + * GNU - GNU C/C++ + * MINGW - MinGW C/C++ + * INTEL - Intel C/C++ + * CLANG - LLVM Clang C/C++ + * SUN - Sun WorkShop/Studio C/C++ + * XLC - IBM XL C/C++ + * HP - HP C/aC++ + * DEC - DEC C/C++ + * MIPS - MIPSpro C/C++ + * USLC - SCO OUDK and UDK C/C++ + * WATCOM - Watcom C/C++ + * BORLAND - Borland C/C++ + * PGI - Portland Group C/C++ + * CRAY - CRAY C/C++ + */ + +/** + * @def P_CC_MSVC + * @brief Microsoft Visual C/C++ compiler. + * @since 0.0.1 + */ + +/** + * @def P_CC_GNU + * @brief GNU C/C++ compiler. + * @since 0.0.1 + */ + +/** + * @def P_CC_MINGW + * @brief MinGW C/C++ compiler. + * @since 0.0.1 + */ + +/** + * @def P_CC_INTEL + * @brief Intel C/C++ compiler. + * @since 0.0.1 + */ + +/** + * @def P_CC_CLANG + * @brief LLVM Clang C/C++ compiler. + * @since 0.0.1 + */ + +/** + * @def P_CC_SUN + * @brief Sun WorkShop/Studio C/C++ compiler. + * @since 0.0.1 + */ + +/** + * @def P_CC_XLC + * @brief IBM XL C/C++ compiler. + * @since 0.0.1 + */ + +/** + * @def P_CC_HP + * @brief HP C/aC++ compiler. + * @since 0.0.1 + */ + +/** + * @def P_CC_DEC + * @brief DEC C/C++ compiler. + * @since 0.0.2 + */ + +/** + * @def P_CC_MIPS + * @brief MIPSpro C/C++ compiler. + * @since 0.0.1 + */ + +/** + * @def P_CC_USLC + * @brief SCO OUDK and UDK C/C++ compiler. + * @since 0.0.1 + */ + +/** + * @def P_CC_WATCOM + * @brief Watcom C/C++ compiler. + * @since 0.0.1 + */ + +/** + * @def P_CC_BORLAND + * @brief Borland C/C++ compiler. + * @since 0.0.1 + */ + +/** + * @def P_CC_PGI + * @brief Portland Group C/C++ compiler. + * @since 0.0.3 + */ + +/** + * @def P_CC_CRAY + * @brief Cray C/C++ compiler. + * @since 0.0.4 + */ + +#if defined(_MSC_VER) +# define P_CC_MSVC +# if defined(__INTEL_COMPILER) +# define P_CC_INTEL +# endif +# if defined(__clang__) +# define P_CC_CLANG +# endif +#elif defined(__GNUC__) +# define P_CC_GNU +# if defined(__MINGW32__) +# define P_CC_MINGW +# endif +# if defined(__INTEL_COMPILER) +# define P_CC_INTEL +# endif +# if defined(__clang__) +# define P_CC_CLANG +# endif +# if defined(_CRAYC) +# define P_CC_CRAY +# endif +#elif defined(__SUNPRO_C) || defined(__SUNPRO_CC) +# define P_CC_SUN +#elif defined(__xlc__) || defined(__xlC__) +# define P_CC_XLC +#elif defined(__HP_cc) || defined(__HP_aCC) +# define P_CC_HP +#elif defined (__DECC) || defined(__DECCXX) +# define P_CC_DEC +#elif (defined(__sgi) || defined(sgi)) && \ + (defined(_COMPILER_VERSION) || defined(_SGI_COMPILER_VERSION)) +# define P_CC_MIPS +#elif defined(__USLC__) && defined(__SCO_VERSION__) +# define P_CC_USLC +#elif defined(__WATCOMC__) +# define P_CC_WATCOM +#elif defined(__BORLANDC__) +# define P_CC_BORLAND +#elif defined(__INTEL_COMPILER) +# define P_CC_INTEL +#elif defined(__PGI) +# define P_CC_PGI +#elif defined(_CRAYC) +# define P_CC_CRAY +#endif + +/* We need this to generate full Doxygen documentation */ + +#ifdef DOXYGEN +# ifndef P_CC_MSVC +# define P_CC_MSVC +# endif +# ifndef P_CC_GNU +# define P_CC_GNU +# endif +# ifndef P_CC_MINGW +# define P_CC_MINGW +# endif +# ifndef P_CC_INTEL +# define P_CC_INTEL +# endif +# ifndef P_CC_CLANG +# define P_CC_CLANG +# endif +# ifndef P_CC_SUN +# define P_CC_SUN +# endif +# ifndef P_CC_XLC +# define P_CC_XLC +# endif +# ifndef P_CC_HP +# define P_CC_HP +# endif +# ifndef P_CC_DEC +# define P_CC_DEC +# endif +# ifndef P_CC_MIPS +# define P_CC_MIPS +# endif +# ifndef P_CC_USLC +# define P_CC_USLC +# endif +# ifndef P_CC_WATCOM +# define P_CC_WATCOM +# endif +# ifndef P_CC_BORLAND +# define P_CC_BORLAND +# endif +# ifndef P_CC_PGI +# define P_CC_PGI +# endif +# ifndef P_CC_CRAY +# define P_CC_CRAY +# endif +#endif + +#endif /* PLIBSYS_HEADER_PMACROSCOMPILER_H */ diff --git a/3rdparty/plibsys/src/pmacroscpu.h b/3rdparty/plibsys/src/pmacroscpu.h new file mode 100644 index 0000000..d1b0cbb --- /dev/null +++ b/3rdparty/plibsys/src/pmacroscpu.h @@ -0,0 +1,627 @@ +/* + * The MIT License + * + * Copyright (C) 2017 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * @file pmacroscpu.h + * @brief CPU detection macros + * @author Alexander Saprykin + * + * All the macros are completely independent of any other platform-specific + * headers, thus gurantee to work with any compiler under any operating system + * in the same way as they are used within the library. + * + * This family of macros provides CPU detection and defines one or several of + * P_CPU_x macros. + */ + +#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION) +# error "Header files shouldn't be included directly, consider using <plibsys.h> instead." +#endif + +#ifndef PLIBSYS_HEADER_PMACROSCPU_H +#define PLIBSYS_HEADER_PMACROSCPU_H + +/* + * List of supported CPU architectures (P_CPU_x): + * + * ALPHA - Alpha + * ARM - ARM architecture revision: + * v2, v3, v4, v5, v6, v7, v8 + * ARM_32 - ARM 32-bit + * ARM_64 - ARM 64-bit + * ARM_V2 - ARMv2 instruction set + * ARM_V3 - ARMv3 instruction set + * ARM_V4 - ARMv4 instruction set + * ARM_V5 - ARMv5 instruction set + * ARM_V6 - ARMv6 instruction set + * ARM_V7 - ARMv7 instruction set + * ARM_V8 - ARMv8 instruction set + * X86 - x86 architecture revision: + * 3, 4, 5, 6 (Intel P6 or better) + * X86_32 - x86 32-bit + * X86_64 - x86 64-bit + * IA64 - Intel Itanium (IA-64) + * MIPS - MIPS + * MIPS_I - MIPS I + * MIPS_II - MIPS II + * MIPS_III - MIPS III + * MIPS_IV - MIPS IV + * MIPS_32 - MIPS32 + * MIPS_64 - MIPS64 + * POWER - PowerPC + * POWER_32 - PowerPC 32-bit + * POWER_64 - PowerPC 64-bit + * SPARC - Sparc + * SPARC_V8 - Sparc V8 + * SPARC_V9 - Sparc V9 + * HPPA - HPPA-RISC + * HPPA_32 - HPPA-RISC 32-bit + * HPPA_64 - HPPA-RISC 64-bit + */ + +/** + * @def P_CPU_ALPHA + * @brief DEC Alpha architecture. + * @since 0.0.3 + */ + +/** + * @def P_CPU_ARM + * @brief ARM architecture. + * @since 0.0.3 + * + * This macro is defined for any ARM target. It contains an architecture + * revision number. One of the revision specific macros (P_CPU_ARM_Vx) is also + * defined, as well as #P_CPU_ARM_32 or #P_CPU_ARM_64. + */ + +/** + * @def P_CPU_ARM_32 + * @brief ARM 32-bit architecture. + * @since 0.0.3 + * + * This macro is defined for ARM 32-bit target. One of the revision specific + * macros (P_CPU_ARM_Vx) is also defined, as well as #P_CPU_ARM. + */ + +/** + * @def P_CPU_ARM_64 + * @brief ARM 64-bit architecture. + * @since 0.0.3 + * + * This macro is defined for ARM 64-bit target. One of the revision specific + * macros (P_CPU_ARM_Vx) is also defined, as well as #P_CPU_ARM. + */ + +/** + * @def P_CPU_ARM_V2 + * @brief ARMv2 architecture revision. + * @since 0.0.3 + * + * This macro is defined for ARMv2 target. #P_CPU_ARM_32 and #P_CPU_ARM macros + * are also defined. + */ + +/** + * @def P_CPU_ARM_V3 + * @brief ARMv3 architecture revision. + * @since 0.0.3 + * + * This macro is defined for ARMv3 target. #P_CPU_ARM_32 and #P_CPU_ARM macros + * are also defined. + */ + +/** + * @def P_CPU_ARM_V4 + * @brief ARMv4 architecture revision. + * @since 0.0.3 + * + * This macro is defined for ARMv4 target. #P_CPU_ARM_32 and #P_CPU_ARM macros + * are also defined. + */ + +/** + * @def P_CPU_ARM_V5 + * @brief ARMv5 architecture revision. + * @since 0.0.3 + * + * This macro is defined for ARMv5 target. #P_CPU_ARM_32 and #P_CPU_ARM macros + * are also defined. + */ + +/** + * @def P_CPU_ARM_V6 + * @brief ARMv6 architecture revision. + * @since 0.0.3 + * + * This macro is defined for ARMv6 target. #P_CPU_ARM_32 and #P_CPU_ARM macros + * are also defined. + */ + +/** + * @def P_CPU_ARM_V7 + * @brief ARMv7 architecture revision. + * @since 0.0.3 + * + * This macro is defined for ARMv7 target. #P_CPU_ARM_32 and #P_CPU_ARM macros + * are also defined. + */ + +/** + * @def P_CPU_ARM_V8 + * @brief ARMv8 architecture revision. + * @since 0.0.3 + * + * This macro is defined for ARMv8 target. #P_CPU_ARM_32 or #P_CPU_ARM_64 macro + * is defined, as well as #P_CPU_ARM. + */ + +/** + * @def P_CPU_X86 + * @brief Intel x86 architecture. + * @since 0.0.3 + * + * This macro is defined for any x86 target. It contains an architecture + * revision number (3 for i386 and lower, 4 for i486, 5 for i586, 6 for i686 and + * better). One of the architecture specific macros (P_CPU_X86_xx) is also + * defined. + */ + +/** + * @def P_CPU_X86_32 + * @brief Intel x86 32-bit architecture. + * @since 0.0.3 + * + * This macro is defined for x86 32-bit target. #P_CPU_X86 macro is also + * defined. + */ + +/** + * @def P_CPU_X86_64 + * @brief Intel x86 64-bit architecture. + * @since 0.0.3 + * + * This macro is defined for x86 64-bit target. #P_CPU_X86 macro is also + * defined. + */ + +/** + * @def P_CPU_IA64 + * @brief Intel Itanium (IA-64) architecture. + * @since 0.0.3 + * + * This macro is defined for Intel Itanium (IA-64) target. + */ + +/** + * @def P_CPU_MIPS + * @brief MIPS architecture. + * @since 0.0.3 + * + * This macro is defined for any MIPS target. Some other specific macros + * (P_CPU_MIPS_xx) for different MIPS ISAs may be defined. + */ + +/** + * @def P_CPU_MIPS_I + * @brief MIPS I ISA. + * @since 0.0.3 + * + * This macro is defined for MIPS I target. #P_CPU_MIPS is also defined, as well + * as probably some other ISA macros (P_CPU_MIPS_xx). + */ + +/** + * @def P_CPU_MIPS_II + * @brief MIPS II ISA. + * @since 0.0.3 + * + * This macro is defined for MIPS II target. #P_CPU_MIPS and #P_CPU_MIPS_I are + * also defined, as well as probably some other ISA macros (P_CPU_MIPS_xx). + */ + +/** + * @def P_CPU_MIPS_III + * @brief MIPS III ISA. + * @since 0.0.3 + * + * This macro is defined for MIPS III target. #P_CPU_MIPS, #P_CPU_MIPS_I and + * #P_CPU_MIPS_II are also defined, as well as probably some other ISA macros + * (P_CPU_MIPS_xx). + */ + +/** + * @def P_CPU_MIPS_IV + * @brief MIPS IV ISA. + * @since 0.0.3 + * + * This macro is defined for MIPS IV target. #P_CPU_MIPS, #P_CPU_MIPS_I, + * #P_CPU_MIPS_II and #P_CPU_MIPS_III are also defined, as well as probably some + * other ISA macros (P_CPU_MIPS_xx). + */ + +/** + * @def P_CPU_MIPS_32 + * @brief MIPS32 ISA. + * @since 0.0.3 + * + * This macro is defined for MIPS32 target. #P_CPU_MIPS, #P_CPU_MIPS_I and + * #P_CPU_MIPS_II. + */ + +/** + * @def P_CPU_MIPS_64 + * @brief MIPS64 ISA. + * @since 0.0.3 + * + * This macro is defined for MIPS64 target. #P_CPU_MIPS, #P_CPU_MIPS_I, + * #P_CPU_MIPS_II, #P_CPU_MIPS_III, #P_CPU_MIPS_IV and are also defined. + */ + +/** + * @def P_CPU_POWER + * @brief PowerPC architecture. + * @since 0.0.3 + * + * This macro is defined for any PowerPC target. One of the architecture + * specific macros (P_CPU_POWER_xx) is also defined. + */ + +/** + * @def P_CPU_POWER_32 + * @brief PowerPC 32-bit architecture. + * @since 0.0.3 + * + * This macro is defined for PowerPC 32-bit target. #P_CPU_POWER macro is also + * defined. + */ + +/** + * @def P_CPU_POWER_64 + * @brief PowerPC 64-bit architecture. + * @since 0.0.3 + * + * This macro is defined for PowerPC 64-bit target. #P_CPU_POWER macro is also + * defined. + */ + +/** + * @def P_CPU_SPARC + * @brief Sun SPARC architecture. + * @since 0.0.3 + * + * This macro is defined for any SPARC target. One of the architecture + * specific macros (P_CPU_SPARC_xx) is also may be defined. + */ + +/** + * @def P_CPU_SPARC_V8 + * @brief Sun SPARC V8 architecture. + * @since 0.0.3 + * + * This macro is defined for SPARC V8 target. #P_CPU_SPARC macro is also + * defined. + */ + +/** + * @def P_CPU_SPARC_V9 + * @brief Sun SPARC V9 architecture. + * @since 0.0.3 + * + * This macro is defined for SPARC V9 target. #P_CPU_SPARC macro is also + * defined. + */ + +/** + * @def P_CPU_HPPA + * @brief HP PA-RISC architecture. + * @since 0.0.3 + * + * This macro is defined for any PA-RISC target. One of the architecture + * specific macros (P_CPU_HPPA_xx) is also defined. + */ + +/** + * @def P_CPU_HPPA_32 + * @brief HP PA-RISC 32-bit (1.0, 1.1) architecture. + * @since 0.0.3 + * + * This macro is defined for PA-RISC 32-bit target. #P_CPU_HPPA macro is also + * defined. + */ + +/** + * @def P_CPU_HPPA_64 + * @brief HP PA-RISC 64-bit (2.0) architecture. + * @since 0.0.3 + * + * This macro is defined for PA-RISC 64-bit target. #P_CPU_HPPA macro is also + * defined. + */ + +#if defined(__alpha__) || defined(__alpha) || defined(_M_ALPHA) +# define P_CPU_ALPHA +#elif defined(__arm__) || defined(__TARGET_ARCH_ARM) || defined(_ARM) || \ + defined(_M_ARM) || defined(_M_ARM_64) || defined(__arm) || defined(__aarch64__) || \ + defined(__ARM64__) +# if defined(__aarch64__) || defined(_M_ARM64) || defined(__ARM64__) +# define P_CPU_ARM_64 +# else +# define P_CPU_ARM_32 +# endif +# if defined(__ARM_ARCH) && __ARM_ARCH > 1 +# define P_CPU_ARM __ARM_ARCH +# elif defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM > 1 +# define P_CPU_ARM __TARGET_ARCH_ARM +# elif defined(_M_ARM) && _M_ARM > 1 +# define P_CPU_ARM _M_ARM +# elif defined(__ARM64_ARCH_8__) || \ + defined(__ARM_ARCH_8__) || \ + defined(__ARM_ARCH_8A__) || \ + defined(__aarch64__) || \ + defined(__ARMv8__) || \ + defined(__ARMv8_A__) || \ + defined(_M_ARM_64) || \\ + defined(__CORE_CORTEXAV8__) +# define P_CPU_ARM 8 +# elif defined(__ARM_ARCH_7__) || \ + defined(__ARM_ARCH_7A__) || \ + defined(__ARM_ARCH_7R__) || \ + defined(__ARM_ARCH_7M__) || \ + defined(__ARM_ARCH_7S__) || \ + defined(_ARM_ARCH_7) || \ + defined(__CORE_CORTEXA__) +# define P_CPU_ARM 7 +# elif defined(__ARM_ARCH_6__) || \ + defined(__ARM_ARCH_6J__) || \ + defined(__ARM_ARCH_6T2__) || \ + defined(__ARM_ARCH_6Z__) || \ + defined(__ARM_ARCH_6K__) || \ + defined(__ARM_ARCH_6ZK__) || \ + defined(__ARM_ARCH_6M__) +# define P_CPU_ARM 6 +# elif defined(__ARM_ARCH_5__) || \ + defined(__ARM_ARCH_5E__) || \ + defined(__ARM_ARCH_5T__) || \ + defined(__ARM_ARCH_5TE__) || \ + defined(__ARM_ARCH_5TEJ__) +# define P_CPU_ARM 5 +# elif defined(__ARM_ARCH_4__) || \ + defined(__ARM_ARCH_4T__) +# define P_CPU_ARM 4 +# elif defined(__ARM_ARCH_3__) || \ + defined(__ARM_ARCH_3M__) +# define P_CPU_ARM 3 +# elif defined(__ARM_ARCH_2__) +# define P_CPU_ARM 2 +# else +# define P_CPU_ARM 0 +# endif +# if P_CPU_ARM == 8 +# define P_CPU_ARM_V8 +# elif P_CPU_ARM == 7 +# define P_CPU_ARM_V7 +# elif P_CPU_ARM == 6 +# define P_CPU_ARM_V6 +# elif P_CPU_ARM == 5 +# define P_CPU_ARM_V5 +# elif P_CPU_ARM == 4 +# define P_CPU_ARM_V4 +# elif P_CPU_ARM == 3 +# define P_CPU_ARM_V3 +# elif P_CPU_ARM == 2 +# define P_CPU_ARM_V2 +# else +# error "ARM architecture is uknown or too old" +# endif +#elif defined(__i386__) || defined(__i386) || defined(_M_IX86) +# define P_CPU_X86_32 +# if defined(_M_IX86) +# if (_M_IX86 >= 300 &&_M_IX86 <= 600) +# define P_CPU_X86 (_M_IX86 / 100) +# else +# define P_CPU_X86 6 +# endif +# elif defined(__i686__) || defined(__athlon__) || defined(__SSE__) || defined(__pentiumpro__) +# define P_CPU_X86 6 +# elif defined(__i586__) || defined(__k6__) || defined(__pentium__) +# define P_CPU_X86 5 +# elif defined(__i486__) || defined(__80486__) +# define P_CPU_X86 4 +# else +# define P_CPU_X86 3 +# endif +#elif defined(__x86_64__) || defined(__x86_64) || \ + defined(__amd64__) || defined(__amd64) || \ + defined(_M_X64) || defined(_M_AMD64) +# define P_CPU_X86_64 +# define P_CPU_X86 6 +#elif defined(__ia64__) || defined(__ia64) || defined(_M_IA64) +# define P_CPU_IA64 +#elif defined(__mips__) || defined(__mips) || defined(_M_MRX000) +# define P_CPU_MIPS +# if defined(_M_MRX000) +# if (_M_MRX000 >= 10000) +# define P_CPU_MIPS_IV +# else +# define P_CPU_MIPS_III +# endif +# endif +# if defined(_MIPS_ARCH_MIPS64) || (defined(__mips) && __mips - 0 >= 64) || \ + (defined(_MIPS_ISA) && defined(_MIPS_ISA_MIPS64) && __MIPS_ISA - 0 >= _MIPS_ISA_MIPS64) +# define P_CPU_MIPS_64 +# elif defined(_MIPS_ARCH_MIPS32) || (defined(__mips) && __mips - 0 >= 32) || \ + (defined(_MIPS_ISA) && defined(_MIPS_ISA_MIPS32) && __MIPS_ISA - 0 >= _MIPS_ISA_MIPS32) +# define P_CPU_MIPS_32 +# elif defined(_MIPS_ARCH_MIPS4) || (defined(__mips) && __mips - 0 >= 4) || \ + (defined(_MIPS_ISA) && defined(_MIPS_ISA_MIPS4) && __MIPS_ISA - 0 >= _MIPS_ISA_MIPS4) +# define P_CPU_MIPS_IV +# elif defined(_MIPS_ARCH_MIPS3) || (defined(__mips) && __mips - 0 >= 3) || \ + (defined(_MIPS_ISA)&& defined(_MIPS_ISA_MIPS3) && __MIPS_ISA - 0 >= _MIPS_ISA_MIPS3) +# define P_CPU_MIPS_III +# elif defined(_MIPS_ARCH_MIPS2) || (defined(__mips) && __mips - 0 >= 2) || \ + (defined(_MIPS_ISA) && defined(_MIPS_ISA_MIPS2) && __MIPS_ISA - 0 >= _MIPS_ISA_MIPS2) +# define P_CPU_MIPS_II +# elif defined(_MIPS_ARCH_MIPS1) || (defined(__mips) && __mips - 0 >= 1) || \ + (defined(_MIPS_ISA) && defined(_MIPS_ISA_MIPS1) && __MIPS_ISA - 0 >= _MIPS_ISA_MIPS1) +# define P_CPU_MIPS_I +# endif +# if defined(P_CPU_MIPS_64) +# define P_CPU_MIPS_IV +# endif +# if defined(P_CPU_MIPS_IV) +# define P_CPU_MIPS_III +# endif +# if defined(P_CPU_MIPS_32) || defined(P_CPU_MIPS_III) +# define P_CPU_MIPS_II +# endif +# if defined(P_CPU_MIPS_II) +# define P_CPU_MIPS_I +# endif +#elif defined(__powerpc__) || defined(__powerpc) || defined(__ppc__) || defined(__ppc) || \ + defined(_ARCH_PPC) || defined(_ARCH_PWR) || defined(_ARCH_COM) || \ + defined(_M_PPC) || defined(_M_MPPC) +# define P_CPU_POWER +# if defined(__powerpc64__) || defined(__powerpc64) || defined(__ppc64__) || defined(__ppc64) || \ + defined(__64BIT__) || defined(__LP64__) || defined(_LP64) +# define P_CPU_POWER_64 +# else +# define P_CPU_POWER_32 +# endif +#elif defined(__sparc__) || defined(__sparc) +# define P_CPU_SPARC +# if defined(__sparc_v9__) || defined(__sparcv9) +# define P_CPU_SPARC_V9 +# elif defined(__sparc_v8__) || defined(__sparcv8) +# define P_CPU_SPARC_V8 +# endif +#elif defined(__hppa__) || defined(__hppa) +# define P_CPU_HPPA +# if defined(_PA_RISC2_0) || defined(__RISC2_0__) || defined(__HPPA20__) || defined(__PA8000__) +# define P_CPU_HPPA_64 +# else +# define P_CPU_HPPA_32 +# endif +#endif + +/* We need this to generate full Doxygen documentation */ + +#ifdef DOXYGEN +# ifndef P_CPU_ALPHA +# define P_CPU_ALPHA +# endif +# ifndef P_CPU_ARM +# define P_CPU_ARM +# endif +# ifndef P_CPU_ARM_32 +# define P_CPU_ARM_32 +# endif +# ifndef P_CPU_ARM_64 +# define P_CPU_ARM_64 +# endif +# ifndef P_CPU_ARM_V2 +# define P_CPU_ARM_V2 +# endif +# ifndef P_CPU_ARM_V3 +# define P_CPU_ARM_V3 +# endif +# ifndef P_CPU_ARM_V4 +# define P_CPU_ARM_V4 +# endif +# ifndef P_CPU_ARM_V5 +# define P_CPU_ARM_V5 +# endif +# ifndef P_CPU_ARM_V6 +# define P_CPU_ARM_V6 +# endif +# ifndef P_CPU_ARM_V7 +# define P_CPU_ARM_V7 +# endif +# ifndef P_CPU_ARM_V8 +# define P_CPU_ARM_V8 +# endif +# ifndef P_CPU_X86 +# define P_CPU_X86 +# endif +# ifndef P_CPU_X86_32 +# define P_CPU_X86_32 +# endif +# ifndef P_CPU_X86_64 +# define P_CPU_X86_64 +# endif +# ifndef P_CPU_IA64 +# define P_CPU_IA64 +# endif +# ifndef P_CPU_MIPS +# define P_CPU_MIPS +# endif +# ifndef P_CPU_MIPS_I +# define P_CPU_MIPS_I +# endif +# ifndef P_CPU_MIPS_II +# define P_CPU_MIPS_II +# endif +# ifndef P_CPU_MIPS_III +# define P_CPU_MIPS_III +# endif +# ifndef P_CPU_MIPS_IV +# define P_CPU_MIPS_IV +# endif +# ifndef P_CPU_MIPS_32 +# define P_CPU_MIPS_32 +# endif +# ifndef P_CPU_MIPS_64 +# define P_CPU_MIPS_64 +# endif +# ifndef P_CPU_POWER +# define P_CPU_POWER +# endif +# ifndef P_CPU_POWER_32 +# define P_CPU_POWER_32 +# endif +# ifndef P_CPU_POWER_64 +# define P_CPU_POWER_64 +# endif +# ifndef P_CPU_SPARC +# define P_CPU_SPARC +# endif +# ifndef P_CPU_SPARC_V8 +# define P_CPU_SPARC_V8 +# endif +# ifndef P_CPU_SPARC_V9 +# define P_CPU_SPARC_V9 +# endif +# ifndef P_CPU_HPPA +# define P_CPU_HPPA +# endif +# ifndef P_CPU_HPPA_32 +# define P_CPU_HPPA_32 +# endif +# ifndef P_CPU_HPPA_64 +# define P_CPU_HPPA_64 +# endif +#endif + +#endif /* PLIBSYS_HEADER_PMACROSCPU_H */ diff --git a/3rdparty/plibsys/src/pmacrosos.h b/3rdparty/plibsys/src/pmacrosos.h new file mode 100644 index 0000000..c14a27a --- /dev/null +++ b/3rdparty/plibsys/src/pmacrosos.h @@ -0,0 +1,500 @@ +/* + * The MIT License + * + * Copyright (C) 2017-2018 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * @file pmacrosos.h + * @brief OS detection macros + * @author Alexander Saprykin + * + * All the macros are completely independent of any other platform-specific + * headers, thus gurantee to work with any compiler under any operating system + * in the same way as they are used within the library. + * + * This family of macros provides OS detection and defines one or several of + * P_OS_x macros. + */ + +#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION) +# error "Header files shouldn't be included directly, consider using <plibsys.h> instead." +#endif + +#ifndef PLIBSYS_HEADER_PMACROSOS_H +#define PLIBSYS_HEADER_PMACROSOS_H + +/* + * List of supported operating systems (P_OS_x): + * + * DARWIN - Any Darwin based system + * DARWIN32 - Any 32-bit Darwin based system + * DARWIN64 - Any 64-bit Darwin based system + * BSD4 - Any BSD 4.x based system + * FREEBSD - FreeBSD + * DRAGONFLY - DragonFlyBSD + * NETBSD - NetBSD + * OPENBSD - OpenBSD + * AIX - IBM AIX + * HPUX - HP-UX + * TRU64 - Tru64 + * SOLARIS - Sun (Oracle) Solaris + * QNX - QNX 4.x + * QNX6 - QNX Neutrino 6.x + * BB10 - BlackBerry 10 + * SCO - SCO OpenServer 5/6 + * UNIXWARE - UnixWare 7 + * IRIX - SGI IRIX + * HAIKU - Haiku + * SYLLABLE - Syllable + * BEOS - BeOS + * OS2 - OS/2 + * VMS - OpenVMS + * AMIGA - AmigaOS + * UNIX - Any UNIX BSD/SYSV based system + * LINUX - Linux + * MAC9 - Mac OS 9 (Classic) + * MAC - Any macOS + * MAC32 - 32-bit macOS + * MAC64 - 64-bit macOS + * CYGWIN - Cygwin + * MSYS - MSYS + * WIN - 32-bit Windows + * WIN64 - 64-bit Windows + * ANDROID - Android + */ + +/** + * @def P_OS_DARWIN + * @brief Darwin based operating system (i.e. macOS). + * @since 0.0.1 + */ + +/** + * @def P_OS_DARWIN32 + * @brief Darwin based 32-bit operating system. + * @since 0.0.1 + */ + +/** + * @def P_OS_DARWIN64 + * @brief Darwin based 64-bit operating system. + * @since 0.0.1 + */ + +/** + * @def P_OS_BSD4 + * @brief BSD 4.x based operating system. + * @since 0.0.1 + */ + +/** + * @def P_OS_FREEBSD + * @brief FreeBSD operating system. + * @since 0.0.1 + */ + +/** + * @def P_OS_DRAGONFLY + * @brief DragonFlyBSD operating system. + * @since 0.0.1 + */ + +/** + * @def P_OS_NETBSD + * @brief NetBSD operating system. + * @since 0.0.1 + */ + +/** + * @def P_OS_OPENBSD + * @brief OpenBSD operating system. + * @since 0.0.1 + */ + +/** + * @def P_OS_AIX + * @brief IBM AIX operating system. + * @since 0.0.1 + */ + +/** + * @def P_OS_HPUX + * @brief HP-UX operating system. + * @since 0.0.1 + */ + +/** + * @def P_OS_TRU64 + * @brief Tru64 operating system. + * @since 0.0.2 + */ + +/** + * @def P_OS_SOLARIS + * @brief Sun (Oracle) Solaris operating system. + * @since 0.0.1 + */ + +/** + * @def P_OS_QNX + * @brief QNX 4.x operating system. + * @since 0.0.1 + */ + +/** + * @def P_OS_QNX6 + * @brief QNX Neutrino 6.x operating system. + * @since 0.0.1 + */ + +/** + * @def P_OS_BB10 + * @brief BlackBerry 10 operating system. + * @since 0.0.4 + */ + +/** + * @def P_OS_SCO + * @brief SCO OpenServer operating system. + * @since 0.0.1 + */ + +/** + * @def P_OS_UNIXWARE + * @brief UnixWare 7 operating system. + * @since 0.0.1 + */ + +/** + * @def P_OS_IRIX + * @brief SGI's IRIX operating system. + * @since 0.0.1 + */ + +/** + * @def P_OS_HAIKU + * @brief Haiku operating system. + * @since 0.0.1 + */ + +/** + * @def P_OS_SYLLABLE + * @brief Syllable operating system. + * @since 0.0.2 + */ + +/** + * @def P_OS_BEOS + * @brief BeOS operating system. + * @since 0.0.3 + */ + +/** + * @def P_OS_OS2 + * @brief OS/2 operating system. + * @since 0.0.3 + */ + +/** + * @def P_OS_VMS + * @brief OpenVMS operating system. + * @since 0.0.1 + */ + +/** + * @def P_OS_AMIGA + * @brief AmigaOS operating system. + * @since 0.0.4 + */ + +/** + * @def P_OS_UNIX + * @brief UNIX based operating system. + * @since 0.0.1 + */ + +/** + * @def P_OS_LINUX + * @brief Linux based operating system. + * @since 0.0.1 + */ + +/** + * @def P_OS_MAC9 + * @brief Apple's Mac OS 9 operating system. + * @since 0.0.1 + */ + +/** + * @def P_OS_MAC + * @brief Apple's macOS operating system. + * @since 0.0.1 + */ + +/** + * @def P_OS_MAC32 + * @brief Apple's macOS 32-bit operating system. + * @since 0.0.1 + */ + +/** + * @def P_OS_MAC64 + * @brief Apple's macOS 64-bit operating system. + * @since 0.0.1 + */ + +/** + * @def P_OS_CYGWIN + * @brief Microsoft Windows POSIX runtime environment. + * @since 0.0.1 + */ + +/** + * @def P_OS_MSYS + * @brief Microsoft Windows POSIX development environment. + * @since 0.0.1 + */ + +/** + * @def P_OS_WIN + * @brief Microsoft Windows 32-bit operating system. + * @since 0.0.1 + */ + +/** + * @def P_OS_WIN64 + * @brief Microsoft Windows 64-bit operating system. + * @since 0.0.1 + */ + +/** + * @def P_OS_ANDROID + * @brief Google Android + * @since 0.0.4 + */ + +#if defined(__APPLE__) && (defined(__GNUC__) || defined(__xlC__) || defined(__xlc__)) +# define P_OS_DARWIN +# define P_OS_BSD4 +# ifdef __LP64__ +# define P_OS_DARWIN64 +# else +# define P_OS_DARWIN32 +# endif +# elif defined(Macintosh) || defined(macintosh) +# define P_OS_MAC9 +#elif defined(__MSYS__) +# define P_OS_MSYS +#elif defined(__CYGWIN__) +# define P_OS_CYGWIN +#elif defined(_WIN64) || defined(_M_X64) || defined(_M_AMD64) +# define P_OS_WIN64 +#elif defined(__WIN32__) || defined(_WIN32) || defined(WIN32) +# define P_OS_WIN +#elif defined(__ANDROID__) +# define P_OS_ANDROID +# define P_OS_LINUX +#elif defined(__linux) || defined(__linux__) +# define P_OS_LINUX +#elif defined(__FreeBSD__) +# define P_OS_FREEBSD +# define P_OS_BSD4 +#elif defined(__DragonFly__) +# define P_OS_DRAGONFLY +# define P_OS_BSD4 +#elif defined(__NetBSD__) +# define P_OS_NETBSD +# define P_OS_BSD4 +#elif defined(__OpenBSD__) +# define P_OS_OPENBSD +# define P_OS_BSD4 +#elif defined(_AIX) +# define P_OS_AIX +#elif defined(hpux) || defined(__hpux) +# define P_OS_HPUX +#elif defined(__osf__) || defined(__osf) +# define P_OS_TRU64 +#elif defined(__sun) || defined(sun) +# define P_OS_SOLARIS +#elif defined(__QNXNTO__) +# ifdef __BLACKBERRY10__ +# define P_OS_BB10 +# else +# define P_OS_QNX6 +# endif +#elif defined(__QNX__) +# define P_OS_QNX +#elif defined(_SCO_DS) +# define P_OS_SCO +#elif defined(__USLC__) || defined(__UNIXWARE__) +# define P_OS_UNIXWARE +#elif defined(__svr4__) && defined(i386) +# define P_OS_UNIXWARE +#elif defined(__sgi) || defined(sgi) +# define P_OS_IRIX +#elif defined(__HAIKU__) +# define P_OS_HAIKU +#elif defined(__SYLLABLE__) +# define P_OS_SYLLABLE +#elif defined(__BEOS__) +# define P_OS_BEOS +#elif defined(__OS2__) +# define P_OS_OS2 +#elif defined(VMS) || defined(__VMS) +# define P_OS_VMS +#elif defined(AMIGA) || defined(__amigaos__) +# define P_OS_AMIGA +#endif + +#ifdef P_OS_WIN64 +# define P_OS_WIN +#endif + +#if defined(P_OS_DARWIN) +# define P_OS_MAC +# if defined(P_OS_DARWIN64) +# define P_OS_MAC64 +# elif defined(P_OS_DARWIN32) +# define P_OS_MAC32 +# endif +#endif + +#if defined(P_OS_WIN) || defined(P_OS_MAC9) || defined(P_OS_HAIKU) || \ + defined(P_OS_BEOS) || defined(P_OS_OS2) || defined(P_OS_VMS) || \ + defined(P_OS_AMIGA) +# undef P_OS_UNIX +#elif !defined(P_OS_UNIX) +# define P_OS_UNIX +#endif + +/* We need this to generate full Doxygen documentation */ + +#ifdef DOXYGEN +# ifndef P_OS_DARWIN +# define P_OS_DARWIN +# endif +# ifndef P_OS_DARWIN32 +# define P_OS_DARWIN32 +# endif +# ifndef P_OS_DARWIN64 +# define P_OS_DARWIN64 +# endif +# ifndef P_OS_BSD4 +# define P_OS_BSD4 +# endif +# ifndef P_OS_FREEBSD +# define P_OS_FREEBSD +# endif +# ifndef P_OS_DRAGONFLY +# define P_OS_DRAGONFLY +# endif +# ifndef P_OS_NETBSD +# define P_OS_NETBSD +# endif +# ifndef P_OS_OPENBSD +# define P_OS_OPENBSD +# endif +# ifndef P_OS_AIX +# define P_OS_AIX +# endif +# ifndef P_OS_HPUX +# define P_OS_HPUX +# endif +# ifndef P_OS_TRU64 +# define P_OS_TRU64 +# endif +# ifndef P_OS_SOLARIS +# define P_OS_SOLARIS +# endif +# ifndef P_OS_QNX +# define P_OS_QNX +# endif +# ifndef P_OS_QNX6 +# define P_OS_QNX6 +# endif +# ifndef P_OS_BB10 +# define P_OS_BB10 +# endif +# ifndef P_OS_SCO +# define P_OS_SCO +# endif +# ifndef P_OS_UNIXWARE +# define P_OS_UNIXWARE +# endif +# ifndef P_OS_IRIX +# define P_OS_IRIX +# endif +# ifndef P_OS_HAIKU +# define P_OS_HAIKU +# endif +# ifndef P_OS_SYLLABLE +# define P_OS_SYLLABLE +# endif +# ifndef P_OS_BEOS +# define P_OS_BEOS +# endif +# ifndef P_OS_OS2 +# define P_OS_OS2 +# endif +# ifndef P_OS_VMS +# define P_OS_VMS +# endif +# ifndef P_OS_AMIGA +# define P_OS_AMIGA +# endif +# ifndef P_OS_UNIX +# define P_OS_UNIX +# endif +# ifndef P_OS_LINUX +# define P_OS_LINUX +# endif +# ifndef P_OS_MAC9 +# define P_OS_MAC9 +# endif +# ifndef P_OS_MAC +# define P_OS_MAC +# endif +# ifndef P_OS_MAC32 +# define P_OS_MAC32 +# endif +# ifndef P_OS_MAC64 +# define P_OS_MAC64 +# endif +# ifndef P_OS_CYGWIN +# define P_OS_CYGWIN +# endif +# ifndef P_OS_MSYS +# define P_OS_MSYS +# endif +# ifndef P_OS_WIN +# define P_OS_WIN +# endif +# ifndef P_OS_WIN64 +# define P_OS_WIN64 +# endif +# ifndef P_OS_ANDROID +# define P_OS_ANDROID +# endif +#endif + +#endif /* PLIBSYS_HEADER_PMACROSOS_H */ diff --git a/3rdparty/plibsys/src/pmain.c b/3rdparty/plibsys/src/pmain.c new file mode 100644 index 0000000..ad40dfb --- /dev/null +++ b/3rdparty/plibsys/src/pmain.c @@ -0,0 +1,132 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pmem.h" +#include "pmain.h" + +extern void p_mem_init (void); +extern void p_mem_shutdown (void); +extern void p_atomic_thread_init (void); +extern void p_atomic_thread_shutdown (void); +extern void p_socket_init_once (void); +extern void p_socket_close_once (void); +extern void p_uthread_init (void); +extern void p_uthread_shutdown (void); +extern void p_cond_variable_init (void); +extern void p_cond_variable_shutdown (void); +extern void p_rwlock_init (void); +extern void p_rwlock_shutdown (void); +extern void p_time_profiler_init (void); +extern void p_time_profiler_shutdown (void); +extern void p_library_loader_init (void); +extern void p_library_loader_shutdown (void); + +static pboolean pp_plibsys_inited = FALSE; +static pchar pp_plibsys_version[] = PLIBSYS_VERSION_STR; + +P_LIB_API void +p_libsys_init (void) +{ + if (P_UNLIKELY (pp_plibsys_inited == TRUE)) + return; + + pp_plibsys_inited = TRUE; + + p_mem_init (); + p_atomic_thread_init (); + p_socket_init_once (); + p_uthread_init (); + p_cond_variable_init (); + p_rwlock_init (); + p_time_profiler_init (); + p_library_loader_init (); +} + +P_LIB_API void +p_libsys_init_full (const PMemVTable *vtable) +{ + if (p_mem_set_vtable (vtable) == FALSE) + P_ERROR ("MAIN::p_libsys_init_full: failed to initialize memory table"); + + p_libsys_init (); +} + +P_LIB_API void +p_libsys_shutdown (void) +{ + if (P_UNLIKELY (pp_plibsys_inited == FALSE)) + return; + + pp_plibsys_inited = FALSE; + + p_library_loader_init (); + p_time_profiler_shutdown (); + p_rwlock_shutdown (); + p_cond_variable_shutdown (); + p_uthread_shutdown (); + p_socket_close_once (); + p_atomic_thread_shutdown (); + p_mem_shutdown (); +} + +P_LIB_API const pchar * +p_libsys_version (void) +{ + return (const pchar *) pp_plibsys_version; +} + +#ifdef P_OS_WIN +extern void p_uthread_win32_thread_detach (void); + +BOOL WINAPI DllMain (HINSTANCE hinstDLL, + DWORD fdwReason, + LPVOID lpvReserved); + +BOOL WINAPI +DllMain (HINSTANCE hinstDLL, + DWORD fdwReason, + LPVOID lpvReserved) +{ + P_UNUSED (hinstDLL); + P_UNUSED (lpvReserved); + + switch (fdwReason) { + case DLL_PROCESS_ATTACH: + break; + + case DLL_THREAD_DETACH: + p_uthread_win32_thread_detach (); + break; + + case DLL_PROCESS_DETACH: + break; + + default: + ; + } + + return TRUE; +} +#endif diff --git a/3rdparty/plibsys/src/pmain.h b/3rdparty/plibsys/src/pmain.h new file mode 100644 index 0000000..eff615d --- /dev/null +++ b/3rdparty/plibsys/src/pmain.h @@ -0,0 +1,209 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2017 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * @file pmain.h + * @brief Library initialization + * @author Alexander Saprykin + * + * Before using the library you must to initialize it properly. Use + * p_libsys_init() to initialize the library. Please note that you need to call + * it only once, not in every thread. This call is not MT-safe (because it also + * initializes the threading subsystem itself), so it is best to place it in the + * program's main thread, when the program starts. + * + * The only difference between p_libsys_init() and p_libsys_init_full() is that + * the latter one allows to setup memory management routines before doing any + * internal library call. This way you can ensure to use provided memory + * management everywhere (even for library initialization). + * + * When you do not need the library anymore release used resourses with the + * p_libsys_shutdown() routine. You should only call it once, too. This call is + * not MT-safe (because it also deinitializes the threading subsystem itself), + * so it is best to place it in the program's main thread, when the program + * finishes. + * + * It is not recommended to call the initialization and deinitialization + * routines on Windows in the DllMain() call because it may require libraries + * other than kernel32.dll. + */ + +/** + * @mainpage + * Basic + * - @link + * pmain.h Library initialization + * @endlink + * - @link + * ptypes.h Data types + * @endlink + * - @link + * pmacroscpu.h CPU detection macros + * @endlink + * - @link + * pmacrosos.h OS detection macros + * @endlink + * - @link + * pmacroscompiler.h Compiler detection macros + * @endlink + * - @link + * pmacros.h Miscellaneous macros + * @endlink + * - @link + * pstring.h Strings + * @endlink + * + * System + * - @link + * pmem.h Memory management + * @endlink + * - @link + * pprocess.h Process + * @endlink + * - @link + * plibraryloader.h Shared library loader + * @endlink + * - @link + * ptimeprofiler.h Time profiler + * @endlink + * - @link + * perror.h Errors + * @endlink + * + * Data structures + * - @link + * plist.h Singly linked list + * @endlink + * - @link + * phashtable.h Hash table + * @endlink + * - @link + * pcryptohash.h Cryptographic hash + * @endlink + * - @link + * ptree.h Binary search tree + * @endlink + * + * Multithreading + * - @link + * puthread.h Thread + * @endlink + * - @link + * pmutex.h Mutex + * @endlink + * - @link + * pcondvariable.h Condition variable + * @endlink + * - @link + * prwlock.h Read-write lock + * @endlink + * - @link + * pspinlock.h Spinlock + * @endlink + * - @link + * patomic.h Atomic operations + * @endlink + * + * Interprocess communication + * - @link + * psemaphore.h Semaphore + * @endlink + * - @link + * pshm.h Shared memory + * @endlink + * - @link + * pshmbuffer.h Shared memory buffer + * @endlink + * + * Networking + * - @link + * psocketaddress.h Socket address + * @endlink + * - @link + * psocket.h Socket + * @endlink + * + * File and directories + * - @link + * pfile.h Files + * @endlink + * - @link + * pdir.h Directories + * @endlink + * - @link + * pinifile.h INI file parser + * @endlink + * + * Stack + * - @link + * pstdarg.h Variable number of arguments + * @endlink + */ + +#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION) +# error "Header files shouldn't be included directly, consider using <plibsys.h> instead." +#endif + +#ifndef PLIBSYS_HEADER_PMAIN_H +#define PLIBSYS_HEADER_PMAIN_H + +#include <pmacros.h> +#include <pmem.h> + +P_BEGIN_DECLS + +/** + * @brief Initializes library resources. + * @since 0.0.1 + */ +P_LIB_API void p_libsys_init (void); + +/** + * @brief Initializes library resources along with the memory table. + * @param vtable Memory management table. + * @since 0.0.1 + */ +P_LIB_API void p_libsys_init_full (const PMemVTable *vtable); + +/** + * @brief Frees library resources. You should stop using any of the library + * routines after calling this one. + * @since 0.0.1 + */ +P_LIB_API void p_libsys_shutdown (void); + +/** + * @brief Gets the library version against which it was compiled at run-time. + * @return Library version. + * @since 0.0.1 + * @note This version may differ from the version the application was compiled + * against. + * @sa #PLIBSYS_VERSION, #PLIBSYS_VERSION_STR, #PLIBSYS_VERSION_CHECK + */ +P_LIB_API const pchar * p_libsys_version (void); + +P_END_DECLS + +#endif /* PLIBSYS_HEADER_PMAIN_H */ diff --git a/3rdparty/plibsys/src/pmem.c b/3rdparty/plibsys/src/pmem.c new file mode 100644 index 0000000..04701fa --- /dev/null +++ b/3rdparty/plibsys/src/pmem.c @@ -0,0 +1,357 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2017 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include <string.h> +#include <stdlib.h> + +#include "perror.h" +#include "pmem.h" +#include "perror-private.h" +#include "psysclose-private.h" + +#ifndef P_OS_WIN +# if defined (P_OS_BEOS) +# include <be/kernel/OS.h> +# elif defined (P_OS_OS2) +# define INCL_DOSMEMMGR +# define INCL_DOSERRORS +# include <os2.h> +# elif !defined (P_OS_AMIGA) +# include <unistd.h> +# include <sys/types.h> +# include <sys/mman.h> +# include <sys/stat.h> +# include <fcntl.h> +# endif +#endif + +static pboolean p_mem_table_inited = FALSE; +static PMemVTable p_mem_table; + +void +p_mem_init (void) +{ + if (P_UNLIKELY (p_mem_table_inited == TRUE)) + return; + + p_mem_restore_vtable (); +} + +void +p_mem_shutdown (void) +{ + if (P_UNLIKELY (!p_mem_table_inited)) + return; + + p_mem_table.malloc = NULL; + p_mem_table.realloc = NULL; + p_mem_table.free = NULL; + + p_mem_table_inited = FALSE; +} + +P_LIB_API ppointer +p_malloc (psize n_bytes) +{ + if (P_LIKELY (n_bytes > 0)) + return p_mem_table.malloc (n_bytes); + else + return NULL; +} + +P_LIB_API ppointer +p_malloc0 (psize n_bytes) +{ + ppointer ret; + + if (P_LIKELY (n_bytes > 0)) { + if (P_UNLIKELY ((ret = p_mem_table.malloc (n_bytes)) == NULL)) + return NULL; + + memset (ret, 0, n_bytes); + return ret; + } else + return NULL; +} + +P_LIB_API ppointer +p_realloc (ppointer mem, psize n_bytes) +{ + if (P_UNLIKELY (n_bytes == 0)) + return NULL; + + if (P_UNLIKELY (mem == NULL)) + return p_mem_table.malloc (n_bytes); + else + return p_mem_table.realloc (mem, n_bytes); +} + +P_LIB_API void +p_free (ppointer mem) +{ + if (P_LIKELY (mem != NULL)) + p_mem_table.free (mem); +} + +P_LIB_API pboolean +p_mem_set_vtable (const PMemVTable *table) +{ + if (P_UNLIKELY (table == NULL)) + return FALSE; + + if (P_UNLIKELY (table->free == NULL || table->malloc == NULL || table->realloc == NULL)) + return FALSE; + + p_mem_table.malloc = table->malloc; + p_mem_table.realloc = table->realloc; + p_mem_table.free = table->free; + + p_mem_table_inited = TRUE; + + return TRUE; +} + +P_LIB_API void +p_mem_restore_vtable (void) +{ + p_mem_table.malloc = (ppointer (*)(psize)) malloc; + p_mem_table.realloc = (ppointer (*)(ppointer, psize)) realloc; + p_mem_table.free = (void (*)(ppointer)) free; + + p_mem_table_inited = TRUE; +} + +P_LIB_API ppointer +p_mem_mmap (psize n_bytes, + PError **error) +{ + ppointer addr; +#if defined (P_OS_WIN) + HANDLE hdl; +#elif defined (P_OS_BEOS) + area_id area; +#elif defined (P_OS_OS2) + APIRET ulrc; +#elif !defined (P_OS_AMIGA) + int fd; + int map_flags = MAP_PRIVATE; +#endif + + if (P_UNLIKELY (n_bytes == 0)) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return NULL; + } + +#if defined (P_OS_WIN) + if (P_UNLIKELY ((hdl = CreateFileMappingA (INVALID_HANDLE_VALUE, + NULL, + PAGE_READWRITE, + 0, + (DWORD) n_bytes, + NULL)) == NULL)) { + p_error_set_error_p (error, + (pint) p_error_get_last_io (), + p_error_get_last_system (), + "Failed to call CreateFileMapping() to create file mapping"); + return NULL; + } + + if (P_UNLIKELY ((addr = MapViewOfFile (hdl, + FILE_MAP_READ | FILE_MAP_WRITE, + 0, + 0, + n_bytes)) == NULL)) { + p_error_set_error_p (error, + (pint) p_error_get_last_io (), + p_error_get_last_system (), + "Failed to call MapViewOfFile() to map file view"); + CloseHandle (hdl); + return NULL; + } + + if (P_UNLIKELY (!CloseHandle (hdl))) { + p_error_set_error_p (error, + (pint) p_error_get_last_io (), + p_error_get_last_system (), + "Failed to call CloseHandle() to close file mapping"); + UnmapViewOfFile (addr); + return NULL; + } +#elif defined (P_OS_BEOS) + if (P_LIKELY ((n_bytes % B_PAGE_SIZE)) != 0) + n_bytes = (n_bytes / B_PAGE_SIZE + 1) * B_PAGE_SIZE; + + area = create_area ("", &addr, B_ANY_ADDRESS, n_bytes, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA); + + if (P_UNLIKELY (area < B_NO_ERROR)) { + p_error_set_error_p (error, + (pint) p_error_get_last_io (), + p_error_get_last_system (), + "Failed to call create_area() to create memory area"); + return NULL; + } +#elif defined (P_OS_OS2) + if (P_UNLIKELY ((ulrc = DosAllocMem ((PPVOID) &addr, + (ULONG) n_bytes, + PAG_READ | PAG_WRITE | PAG_COMMIT | + OBJ_ANY)) != NO_ERROR)) { + /* Try to remove OBJ_ANY */ + if (P_UNLIKELY ((ulrc = DosAllocMem ((PPVOID) &addr, + (ULONG) n_bytes, + PAG_READ | PAG_WRITE)) != NO_ERROR)) { + p_error_set_error_p (error, + (pint) p_error_get_io_from_system ((pint) ulrc), + ulrc, + "Failed to call DosAllocMemory() to alocate memory"); + return NULL; + } + } +#elif defined (P_OS_AMIGA) + addr = malloc (n_bytes); + + if (P_UNLIKELY (addr == NULL)) { + p_error_set_error_p (error, + (pint) p_error_get_last_io (), + p_error_get_last_system (), + "Failed to allocate system memory"); + return NULL; + } +#else +# if !defined (PLIBSYS_MMAP_HAS_MAP_ANONYMOUS) && !defined (PLIBSYS_MMAP_HAS_MAP_ANON) + if (P_UNLIKELY ((fd = open ("/dev/zero", O_RDWR | O_EXCL, 0754)) == -1)) { + p_error_set_error_p (error, + (pint) p_error_get_last_io (), + p_error_get_last_system (), + "Failed to open /dev/zero for file mapping"); + return NULL; + } +# else + fd = -1; +# endif + +# ifdef PLIBSYS_MMAP_HAS_MAP_ANONYMOUS + map_flags |= MAP_ANONYMOUS; +# elif defined (PLIBSYS_MMAP_HAS_MAP_ANON) + map_flags |= MAP_ANON; +# endif + + if (P_UNLIKELY ((addr = mmap (NULL, + n_bytes, + PROT_READ | PROT_WRITE, + map_flags, + fd, + 0)) == (void *) -1)) { + p_error_set_error_p (error, + (pint) p_error_get_last_io (), + p_error_get_last_system (), + "Failed to call mmap() to create file mapping"); +# if !defined (PLIBSYS_MMAP_HAS_MAP_ANONYMOUS) && !defined (PLIBSYS_MMAP_HAS_MAP_ANON) + if (P_UNLIKELY (p_sys_close (fd) != 0)) + P_WARNING ("PMem::p_mem_mmap: failed to close file descriptor to /dev/zero"); +# endif + return NULL; + } + +# if !defined (PLIBSYS_MMAP_HAS_MAP_ANONYMOUS) && !defined (PLIBSYS_MMAP_HAS_MAP_ANON) + if (P_UNLIKELY (p_sys_close (fd) != 0)) { + p_error_set_error_p (error, + (pint) p_error_get_last_io (), + p_error_get_last_system (), + "Failed to close /dev/zero handle"); + munmap (addr, n_bytes); + return NULL; + } +# endif +#endif + + return addr; +} + +P_LIB_API pboolean +p_mem_munmap (ppointer mem, + psize n_bytes, + PError **error) +{ +#if defined (P_OS_BEOS) + area_id area; +#elif defined (P_OS_OS2) + APIRET ulrc; +#elif defined (P_OS_AMIGA) + P_UNUSED (n_bytes); + P_UNUSED (error); +#endif + + if (P_UNLIKELY (mem == NULL || n_bytes == 0)) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return FALSE; + } + +#if defined (P_OS_WIN) + if (P_UNLIKELY (UnmapViewOfFile (mem) == 0)) { + p_error_set_error_p (error, + (pint) p_error_get_last_io (), + p_error_get_last_system (), + "Failed to call UnmapViewOfFile() to remove file mapping"); +#elif defined (P_OS_BEOS) + if (P_UNLIKELY ((area = area_for (mem)) == B_ERROR)) { + p_error_set_error_p (error, + (pint) p_error_get_last_io (), + p_error_get_last_system (), + "Failed to call area_for() to find allocated memory area"); + return FALSE; + } + + if (P_UNLIKELY ((delete_area (area)) != B_OK)) { + p_error_set_error_p (error, + (pint) p_error_get_last_io (), + p_error_get_last_system (), + "Failed to call delete_area() to remove memory area"); +#elif defined (P_OS_OS2) + if (P_UNLIKELY ((ulrc = DosFreeMem ((PVOID) mem)) != NO_ERROR)) { + p_error_set_error_p (error, + (pint) p_error_get_io_from_system ((pint) ulrc), + ulrc, + "Failed to call DosFreeMem() to free memory"); +#elif defined (P_OS_AMIGA) + free (mem); + + if (P_UNLIKELY (FALSE)) { +#else + if (P_UNLIKELY (munmap (mem, n_bytes) != 0)) { + p_error_set_error_p (error, + (pint) p_error_get_last_io (), + p_error_get_last_system (), + "Failed to call munmap() to remove file mapping"); +#endif + return FALSE; + } else + return TRUE; +} diff --git a/3rdparty/plibsys/src/pmem.h b/3rdparty/plibsys/src/pmem.h new file mode 100644 index 0000000..3643942 --- /dev/null +++ b/3rdparty/plibsys/src/pmem.h @@ -0,0 +1,191 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2017 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * @file pmem.h + * @brief Memory management + * @author Alexander Saprykin + * + * Usually the system routines for memory management are used: malloc(), + * realloc(), free() and so on. But it is highly encouraged to use a more + * general approach: p_malloc(), p_malloc0(), p_realloc() and p_free() family of + * memory management routines. It gives you several advantages: + * - automatical checking of all input parameters for the NULL values; + * - ability to use a custom memory allocator. + * You can also mix these two families of calls, but it is not recommended. + * + * By default p_* routines are mapped to system calls, thus only NULL-checking + * is additionally performed. If you want to use the custom memory allocator, + * then fill in #PMemVTable structure and pass it to the p_mem_set_vtable(). To + * restore system calls back use p_mem_restore_vtable(). + * + * Be careful when using the custom memory allocator: all memory chunks + * allocated with the custom allocator must be freed with the same allocator. If + * the custom allocator was installed after the library initialization call + * p_libsys_init() then you must to restore the original allocator before + * calling p_libsys_shutdown(). + * + * Use p_mem_mmap() to allocate system memory using memory mapping and + * p_mem_munmap() to release the mapped memory. This type of allocated memory + * is not backed physically (does not consume any physical storage) by operating + * system. It means that every memory page within the allocated region will be + * committed to physical backend only when you first touch it. Until that + * untouched pages will be reserved for future usage. It can be useful when + * dealing with large memory blocks which should be filled with data on demand, + * i.e. custom memory allocator can request a large block first, and then it + * allocates chunks of memory within the block upon request. + * + * @note OS/2 supports non-backed memory pages allocation, but in a specific + * way: an exception handler to control access to uncommitted pages must be + * allocated on the stack of each thread before using the mapped memory. To + * unify the behaviour, on OS/2 all memory mapped allocations are already + * committed to the backing storage. + */ + +#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION) +# error "Header files shouldn't be included directly, consider using <plibsys.h> instead." +#endif + +#ifndef PLIBSYS_HEADER_PMEM_H +#define PLIBSYS_HEADER_PMEM_H + +#include <ptypes.h> +#include <pmacros.h> +#include <perror.h> + +P_BEGIN_DECLS + +/** Memory management table. */ +typedef struct PMemVTable_ { + ppointer (*malloc) (psize n_bytes); /**< malloc() implementation. */ + ppointer (*realloc) (ppointer mem, + psize n_bytes); /**< realloc() implementation. */ + void (*free) (ppointer mem); /**< free() implementation. */ +} PMemVTable; + +/** + * @brief Allocates a memory block for the specified number of bytes. + * @param n_bytes Size of the memory block in bytes. + * @return Pointer to a newly allocated memory block in case of success, NULL + * otherwise. + * @since 0.0.1 + */ +P_LIB_API ppointer p_malloc (psize n_bytes); + +/** + * @brief Allocates a memory block for the specified number of bytes and fills + * it with zeros. + * @param n_bytes Size of the memory block in bytes. + * @return Pointer to a newly allocated memory block filled with zeros in case + * of success, NULL otherwise. + * @since 0.0.1 + */ +P_LIB_API ppointer p_malloc0 (psize n_bytes); + +/** + * @brief Changes the memory block size. + * @param mem Pointer to the memory block. + * @param n_bytes New size for @a mem block. + * @return Pointer to a newlly allocated memory block in case of success (if + * @a mem is NULL then it acts like p_malloc()), NULL otherwise. + * @since 0.0.1 + */ +P_LIB_API ppointer p_realloc (ppointer mem, + psize n_bytes); + +/** + * @brief Frees a memory block by its pointer. + * @param mem Pointer to the memory block to free. + * @since 0.0.1 + * + * You should only call this function for the pointers which were obtained using + * the p_malloc(), p_malloc0() and p_realloc() routines, otherwise behavior is + * unpredictable. + * + * Checks the pointer for the NULL value. + */ +P_LIB_API void p_free (ppointer mem); + +/** + * @brief Sets custom memory management routines. + * @param table Table of the memory routines to use. + * @return TRUE if the table was accepted, FALSE otherwise. + * @note All members of @a table must be non-NULL. + * @note This call is not thread-safe. + * @warning Do not forget to set the original memory management routines before + * calling p_libsys_shutdown() if you have used p_mem_set_vtable() after the + * library initialization. + * @since 0.0.1 + * + * In most cases you do not need to use this function. Use it only when you know + * what are you doing! + */ +P_LIB_API pboolean p_mem_set_vtable (const PMemVTable *table); + +/** + * @brief Restores system memory management routines. + * @note This call is not thread-safe. + * @since 0.0.1 + * + * The following system routines are restored: malloc(), free(), realloc(). + */ +P_LIB_API void p_mem_restore_vtable (void); + +/** + * @brief Gets a memory mapped block from the system. + * @param n_bytes Size of the memory block in bytes. + * @param[out] error Error report object, NULL to ignore. + * @return Pointer to the allocated memory block in case of success, NULL + * otherwise. + * @since 0.0.1 + * + * Note that some systems can allocate memory only in chunks of the page size, + * so if @a n_bytes is less than the page size it will try to allocate a chunk + * of memory equal to the page size instead. + * + * On most systems returned memory is mapped to the null or swap device. + * + * @warning On OS/2 returned memory is mapped to physical storage and can be + * swapped. + */ +P_LIB_API ppointer p_mem_mmap (psize n_bytes, + PError **error); + +/** + * @brief Unmaps memory back to the system. + * @param mem Pointer to a memory block previously allocated using the + * p_mem_mmap() call. + * @param n_bytes Size of the memory block in bytes. + * @param[out] error Error report object, NULL to ignore. + * @return TRUE in case of success, FALSE otherwise. + * @since 0.0.1 + */ +P_LIB_API pboolean p_mem_munmap (ppointer mem, + psize n_bytes, + PError **error); + +P_END_DECLS + +#endif /* PLIBSYS_HEADER_PMEM_H */ diff --git a/3rdparty/plibsys/src/pmutex-amiga.c b/3rdparty/plibsys/src/pmutex-amiga.c new file mode 100644 index 0000000..0c1216a --- /dev/null +++ b/3rdparty/plibsys/src/pmutex-amiga.c @@ -0,0 +1,101 @@ +/* + * The MIT License + * + * Copyright (C) 2017 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pmem.h" +#include "pmutex.h" + +#include <stdlib.h> + +#include <exec/exectags.h> +#include <proto/exec.h> + +typedef APTR mutex_hdl; + +struct PMutex_ { + mutex_hdl hdl; +}; + +P_LIB_API PMutex * +p_mutex_new (void) +{ + PMutex *ret; + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PMutex))) == NULL)) { + P_ERROR ("PMutex::p_mutex_new: failed to allocate memory"); + return NULL; + } + + ret->hdl = IExec->AllocSysObjectTags (ASOT_MUTEX, ASOMUTEX_Recursive, TRUE, TAG_END); + + if (P_UNLIKELY (ret->hdl == NULL)) { + P_ERROR ("PMutex::p_mutex_new: AllocSysObjectTags() failed"); + p_free (ret); + return NULL; + } + + return ret; +} + +P_LIB_API pboolean +p_mutex_lock (PMutex *mutex) +{ + if (P_UNLIKELY (mutex == NULL)) + return FALSE; + + IExec->MutexObtain (mutex->hdl); + + return TRUE; +} + +P_LIB_API pboolean +p_mutex_trylock (PMutex *mutex) +{ + if (P_UNLIKELY (mutex == NULL)) + return FALSE; + + return IExec->MutexAttempt (mutex->hdl); +} + +P_LIB_API pboolean +p_mutex_unlock (PMutex *mutex) +{ + if (P_UNLIKELY (mutex == NULL)) + return FALSE; + + IExec->MutexRelease (mutex->hdl); + + return TRUE; +} + +P_LIB_API void +p_mutex_free (PMutex *mutex) +{ + if (P_UNLIKELY (mutex == NULL)) + return; + + IExec->FreeSysObject (ASOT_MUTEX, mutex->hdl); + + p_free (mutex); +} diff --git a/3rdparty/plibsys/src/pmutex-atheos.c b/3rdparty/plibsys/src/pmutex-atheos.c new file mode 100644 index 0000000..352e5c5 --- /dev/null +++ b/3rdparty/plibsys/src/pmutex-atheos.c @@ -0,0 +1,111 @@ +/* + * The MIT License + * + * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pmem.h" +#include "pmutex.h" + +#include <stdlib.h> +#include <errno.h> + +#include <atheos/semaphore.h> + +typedef sem_id mutex_hdl; + +struct PMutex_ { + mutex_hdl hdl; +}; + +P_LIB_API PMutex * +p_mutex_new (void) +{ + PMutex *ret; + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PMutex))) == NULL)) { + P_ERROR ("PMutex::p_mutex_new: failed to allocate memory"); + return NULL; + } + + if (P_UNLIKELY ((ret->hdl = create_semaphore ("", 1, 0)) < 0)) { + P_ERROR ("PMutex::p_mutex_new: create_semaphore() failed"); + p_free (ret); + return NULL; + } + + return ret; +} + +P_LIB_API pboolean +p_mutex_lock (PMutex *mutex) +{ + status_t ret_status; + + if (P_UNLIKELY (mutex == NULL)) + return FALSE; + + while ((ret_status = lock_semaphore (mutex->hdl)) == EINTR) + ; + + if (P_LIKELY (ret_status == 0)) + return TRUE; + else { + P_ERROR ("PMutex::p_mutex_lock: lock_semaphore() failed"); + return FALSE; + } +} + +P_LIB_API pboolean +p_mutex_trylock (PMutex *mutex) +{ + if (P_UNLIKELY (mutex == NULL)) + return FALSE; + + return (lock_semaphore_x (mutex->hdl, 1, 0, 0)) == 0 ? TRUE : FALSE; +} + +P_LIB_API pboolean +p_mutex_unlock (PMutex *mutex) +{ + if (P_UNLIKELY (mutex == NULL)) + return FALSE; + + if (P_LIKELY (unlock_semaphore (mutex->hdl) == 0)) + return TRUE; + else { + P_ERROR ("PMutex::p_mutex_unlock: unlock_semaphore() failed"); + return FALSE; + } +} + +P_LIB_API void +p_mutex_free (PMutex *mutex) +{ + if (P_UNLIKELY (mutex == NULL)) + return; + + if (P_UNLIKELY (delete_semaphore (mutex->hdl) != 0)) + P_ERROR ("PMutex::p_mutex_free: delete_semaphore() failed"); + + p_free (mutex); +} diff --git a/3rdparty/plibsys/src/pmutex-beos.c b/3rdparty/plibsys/src/pmutex-beos.c new file mode 100644 index 0000000..c16bf8b --- /dev/null +++ b/3rdparty/plibsys/src/pmutex-beos.c @@ -0,0 +1,110 @@ +/* + * The MIT License + * + * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pmem.h" +#include "pmutex.h" + +#include <stdlib.h> + +#include <kernel/OS.h> + +typedef sem_id mutex_hdl; + +struct PMutex_ { + mutex_hdl hdl; +}; + +P_LIB_API PMutex * +p_mutex_new (void) +{ + PMutex *ret; + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PMutex))) == NULL)) { + P_ERROR ("PMutex::p_mutex_new: failed to allocate memory"); + return NULL; + } + + if (P_UNLIKELY ((ret->hdl = create_sem (1, "")) < B_OK)) { + P_ERROR ("PMutex::p_mutex_new: create_sem() failed"); + p_free (ret); + return NULL; + } + + return ret; +} + +P_LIB_API pboolean +p_mutex_lock (PMutex *mutex) +{ + status_t ret_status; + + if (P_UNLIKELY (mutex == NULL)) + return FALSE; + + while ((ret_status = acquire_sem (mutex->hdl)) == B_INTERRUPTED) + ; + + if (P_LIKELY (ret_status == B_NO_ERROR)) + return TRUE; + else { + P_ERROR ("PMutex::p_mutex_lock: acquire_sem() failed"); + return FALSE; + } +} + +P_LIB_API pboolean +p_mutex_trylock (PMutex *mutex) +{ + if (P_UNLIKELY (mutex == NULL)) + return FALSE; + + return (acquire_sem_etc (mutex->hdl, 1, B_RELATIVE_TIMEOUT, 0)) == B_NO_ERROR ? TRUE : FALSE; +} + +P_LIB_API pboolean +p_mutex_unlock (PMutex *mutex) +{ + if (P_UNLIKELY (mutex == NULL)) + return FALSE; + + if (P_LIKELY (release_sem (mutex->hdl) == B_NO_ERROR)) + return TRUE; + else { + P_ERROR ("PMutex::p_mutex_unlock: release_sem() failed"); + return FALSE; + } +} + +P_LIB_API void +p_mutex_free (PMutex *mutex) +{ + if (P_UNLIKELY (mutex == NULL)) + return; + + if (P_UNLIKELY (delete_sem (mutex->hdl) != B_NO_ERROR)) + P_ERROR ("PMutex::p_mutex_free: delete_sem() failed"); + + p_free (mutex); +} diff --git a/3rdparty/plibsys/src/pmutex-none.c b/3rdparty/plibsys/src/pmutex-none.c new file mode 100644 index 0000000..1bc9b50 --- /dev/null +++ b/3rdparty/plibsys/src/pmutex-none.c @@ -0,0 +1,66 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pmutex.h" + +#include <stdlib.h> + +struct PMutex_ { + pint hdl; +}; + +P_LIB_API PMutex * +p_mutex_new (void) +{ + return NULL; +} + +P_LIB_API pboolean +p_mutex_lock (PMutex *mutex) +{ + return FALSE; +} + +P_LIB_API pboolean +p_mutex_trylock (PMutex *mutex) +{ + P_UNUSED (mutex); + + return FALSE; +} + +P_LIB_API pboolean +p_mutex_unlock (PMutex *mutex) +{ + P_UNUSED (mutex); + + return FALSE; +} + +P_LIB_API void +p_mutex_free (PMutex *mutex) +{ + P_UNUSED (mutex); +} diff --git a/3rdparty/plibsys/src/pmutex-os2.c b/3rdparty/plibsys/src/pmutex-os2.c new file mode 100644 index 0000000..1040044 --- /dev/null +++ b/3rdparty/plibsys/src/pmutex-os2.c @@ -0,0 +1,112 @@ +/* + * The MIT License + * + * Copyright (C) 2017 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pmem.h" +#include "pmutex.h" + +#include <stdlib.h> + +#define INCL_DOSSEMAPHORES +#define INCL_DOSERRORS +#include <os2.h> + +typedef HMTX mutex_hdl; + +struct PMutex_ { + mutex_hdl hdl; +}; + +P_LIB_API PMutex * +p_mutex_new (void) +{ + PMutex *ret; + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PMutex))) == NULL)) { + P_ERROR ("PMutex::p_mutex_new: failed to allocate memory"); + return NULL; + } + + if (P_UNLIKELY (DosCreateMutexSem (NULL, (PHMTX) &ret->hdl, 0, FALSE) != NO_ERROR)) { + P_ERROR ("PMutex::p_mutex_new: DosCreateMutexSem() failed"); + p_free (ret); + return NULL; + } + + return ret; +} + +P_LIB_API pboolean +p_mutex_lock (PMutex *mutex) +{ + APIRET ulrc; + + if (P_UNLIKELY (mutex == NULL)) + return FALSE; + + while ((ulrc = DosRequestMutexSem (mutex->hdl, SEM_INDEFINITE_WAIT)) == ERROR_INTERRUPT) + ; + + if (P_LIKELY (ulrc == NO_ERROR)) + return TRUE; + else { + P_ERROR ("PMutex::p_mutex_lock: DosRequestMutexSem() failed"); + return FALSE; + } +} + +P_LIB_API pboolean +p_mutex_trylock (PMutex *mutex) +{ + if (P_UNLIKELY (mutex == NULL)) + return FALSE; + + return (DosRequestMutexSem (mutex->hdl, SEM_IMMEDIATE_RETURN)) == NO_ERROR ? TRUE : FALSE; +} + +P_LIB_API pboolean +p_mutex_unlock (PMutex *mutex) +{ + if (P_UNLIKELY (mutex == NULL)) + return FALSE; + + if (P_LIKELY (DosReleaseMutexSem (mutex->hdl) == NO_ERROR)) + return TRUE; + else { + P_ERROR ("PMutex::p_mutex_unlock: DosReleaseMutexSem() failed"); + return FALSE; + } +} + +P_LIB_API void +p_mutex_free (PMutex *mutex) +{ + if (P_UNLIKELY (mutex == NULL)) + return; + + if (P_UNLIKELY (DosCloseMutexSem (mutex->hdl) != NO_ERROR)) + P_ERROR ("PMutex::p_mutex_free: DosCloseMutexSem() failed"); + + p_free (mutex); +} diff --git a/3rdparty/plibsys/src/pmutex-posix.c b/3rdparty/plibsys/src/pmutex-posix.c new file mode 100644 index 0000000..4c471cb --- /dev/null +++ b/3rdparty/plibsys/src/pmutex-posix.c @@ -0,0 +1,104 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pmem.h" +#include "pmutex.h" + +#include <stdlib.h> +#include <pthread.h> + +typedef pthread_mutex_t mutex_hdl; + +struct PMutex_ { + mutex_hdl hdl; +}; + +P_LIB_API PMutex * +p_mutex_new (void) +{ + PMutex *ret; + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PMutex))) == NULL)) { + P_ERROR ("PMutex::p_mutex_new: failed to allocate memory"); + return NULL; + } + + if (P_UNLIKELY (pthread_mutex_init (&ret->hdl, NULL) != 0)) { + P_ERROR ("PMutex::p_mutex_new: pthread_mutex_init() failed"); + p_free (ret); + return NULL; + } + + return ret; +} + +P_LIB_API pboolean +p_mutex_lock (PMutex *mutex) +{ + if (P_UNLIKELY (mutex == NULL)) + return FALSE; + + if (P_LIKELY (pthread_mutex_lock (&mutex->hdl) == 0)) + return TRUE; + else { + P_ERROR ("PMutex::p_mutex_lock: pthread_mutex_lock() failed"); + return FALSE; + } +} + +P_LIB_API pboolean +p_mutex_trylock (PMutex *mutex) +{ + if (P_UNLIKELY (mutex == NULL)) + return FALSE; + + return (pthread_mutex_trylock (&mutex->hdl) == 0) ? TRUE : FALSE; +} + +P_LIB_API pboolean +p_mutex_unlock (PMutex *mutex) +{ + if (P_UNLIKELY (mutex == NULL)) + return FALSE; + + if (P_LIKELY (pthread_mutex_unlock (&mutex->hdl) == 0)) + return TRUE; + else { + P_ERROR ("PMutex::p_mutex_unlock: pthread_mutex_unlock() failed"); + return FALSE; + } +} + +P_LIB_API void +p_mutex_free (PMutex *mutex) +{ + if (P_UNLIKELY (mutex == NULL)) + return; + + if (P_UNLIKELY (pthread_mutex_destroy (&mutex->hdl) != 0)) + P_ERROR ("PMutex::p_mutex_free: pthread_mutex_destroy() failed"); + + p_free (mutex); +} diff --git a/3rdparty/plibsys/src/pmutex-solaris.c b/3rdparty/plibsys/src/pmutex-solaris.c new file mode 100644 index 0000000..bb7adda --- /dev/null +++ b/3rdparty/plibsys/src/pmutex-solaris.c @@ -0,0 +1,105 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pmem.h" +#include "pmutex.h" + +#include <stdlib.h> +#include <synch.h> +#include <thread.h> + +typedef mutex_t mutex_hdl; + +struct PMutex_ { + mutex_hdl hdl; +}; + +P_LIB_API PMutex * +p_mutex_new (void) +{ + PMutex *ret; + + if ((P_UNLIKELY (ret = p_malloc0 (sizeof (PMutex))) == NULL)) { + P_ERROR ("PMutex::p_mutex_new: failed to allocate memory"); + return NULL; + } + + if (P_UNLIKELY (mutex_init (&ret->hdl, USYNC_THREAD, NULL) != 0)) { + P_ERROR ("PMutex::p_mutex_new: mutex_init() failed"); + p_free (ret); + return NULL; + } + + return ret; +} + +P_LIB_API pboolean +p_mutex_lock (PMutex *mutex) +{ + if (P_UNLIKELY (mutex == NULL)) + return FALSE; + + if (P_LIKELY (mutex_lock (&mutex->hdl) == 0)) + return TRUE; + else { + P_ERROR ("PMutex::p_mutex_lock: mutex_lock() failed"); + return FALSE; + } +} + +P_LIB_API pboolean +p_mutex_trylock (PMutex *mutex) +{ + if (P_UNLIKELY (mutex == NULL)) + return FALSE; + + return (mutex_trylock (&mutex->hdl) == 0) ? TRUE : FALSE; +} + +P_LIB_API pboolean +p_mutex_unlock (PMutex *mutex) +{ + if (P_UNLIKELY (mutex == NULL)) + return FALSE; + + if (P_LIKELY (mutex_unlock (&mutex->hdl) == 0)) + return TRUE; + else { + P_ERROR ("PMutex::p_mutex_unlock: mutex_unlock() failed"); + return FALSE; + } +} + +P_LIB_API void +p_mutex_free (PMutex *mutex) +{ + if (P_UNLIKELY (mutex == NULL)) + return; + + if (P_UNLIKELY (mutex_destroy (&mutex->hdl) != 0)) + P_ERROR ("PMutex::p_mutex_unlock: mutex_destroy() failed"); + + p_free (mutex); +} diff --git a/3rdparty/plibsys/src/pmutex-win.c b/3rdparty/plibsys/src/pmutex-win.c new file mode 100644 index 0000000..2a801b6 --- /dev/null +++ b/3rdparty/plibsys/src/pmutex-win.c @@ -0,0 +1,92 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pmem.h" +#include "pmutex.h" + +#include <stdlib.h> + +typedef CRITICAL_SECTION mutex_hdl; + +struct PMutex_ { + mutex_hdl hdl; +}; + +P_LIB_API PMutex * +p_mutex_new (void) +{ + PMutex *ret; + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PMutex))) == NULL)) { + P_ERROR ("PMutex::p_mutex_new: failed to allocate memory"); + return NULL; + } + + InitializeCriticalSection (&ret->hdl); + + return ret; +} + +P_LIB_API pboolean +p_mutex_lock (PMutex *mutex) +{ + if (P_UNLIKELY (mutex == NULL)) + return FALSE; + + EnterCriticalSection (&mutex->hdl); + + return TRUE; +} + +P_LIB_API pboolean +p_mutex_trylock (PMutex *mutex) +{ + if (P_UNLIKELY (mutex == NULL)) + return FALSE; + + return TryEnterCriticalSection (&mutex->hdl) != 0 ? TRUE : FALSE; +} + +P_LIB_API pboolean +p_mutex_unlock (PMutex *mutex) +{ + if (P_UNLIKELY (mutex == NULL)) + return FALSE; + + LeaveCriticalSection (&mutex->hdl); + + return TRUE; +} + +P_LIB_API void +p_mutex_free (PMutex *mutex) +{ + if (P_UNLIKELY (mutex == NULL)) + return; + + DeleteCriticalSection (&mutex->hdl); + + p_free (mutex); +} diff --git a/3rdparty/plibsys/src/pmutex.h b/3rdparty/plibsys/src/pmutex.h new file mode 100644 index 0000000..449a652 --- /dev/null +++ b/3rdparty/plibsys/src/pmutex.h @@ -0,0 +1,136 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2017 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * @file pmutex.h + * @brief Mutex routines + * @author Alexander Saprykin + * + * A mutex is a mutual exclusive (hence mutex) synchronization primitive which + * allows access to a critical section only to one of the concurrently running + * threads. It is used to protected shared data structures from concurrent + * modifications which could lead to unpredictable behavior. + * + * When entering a critical section a thread must call p_mutex_lock() to get a + * lock. If another thread is already holding the lock all other threads will + * be suspended until the lock is released with p_mutex_unlock(). After + * releasing the lock one of the waiting threads is resumed to continue + * execution. On most systems it is not specified whether a mutex waiting queue + * is fair (FIFO) or not. + * + * The typical mutex usage: + * @code + * p_mutex_lock (mutex); + * + * ... code in critical section ... + * + * p_mutex_unlock (mutex); + * @endcode + * You can also think of the mutex as a binary semaphore. + * + * It is implementation dependent whether recursive locking or non-locked mutex + * unlocking is allowed, but such actions can lead to unpredictable behavior. + * Do not rely on such behavior in cross-platform applications. + * + * This is the thread scoped mutex implementation. You could not share this + * mutex outside the process adress space, but you can share it between the + * threads of the same process. + */ + +#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION) +# error "Header files shouldn't be included directly, consider using <plibsys.h> instead." +#endif + +#ifndef PLIBSYS_HEADER_PMUTEX_H +#define PLIBSYS_HEADER_PMUTEX_H + +#include <pmacros.h> +#include <ptypes.h> + +P_BEGIN_DECLS + +/** Mutex opaque data structure. */ +typedef struct PMutex_ PMutex; + +/** + * @brief Creates a new #PMutex object. + * @return Pointer to a newly created #PMutex object. + * @since 0.0.1 + */ +P_LIB_API PMutex * p_mutex_new (void); + +/** + * @brief Locks a mutex. + * @param mutex #PMutex to lock. + * @return TRUE in case of success, FALSE otherwise. + * @since 0.0.1 + * @warning Do not lock the mutex recursively - it may lead to an application + * deadlock (implementation dependent). + * + * Forces the calling thread to sleep until @a mutex becomes available for + * locking. + */ +P_LIB_API pboolean p_mutex_lock (PMutex *mutex); + +/** + * @brief Tries to lock a mutex immediately. + * @param mutex #PMutex to lock. + * @return TRUE in case of success, FALSE otherwise. + * @since 0.0.1 + * @warning Do not lock the mutex recursively - it may lead to an application + * deadlock (implementation dependent). + * + * Tries to lock @a mutex and returns immediately if it is not available for + * locking. + */ +P_LIB_API pboolean p_mutex_trylock (PMutex *mutex); + +/** + * @brief Releases a locked mutex. + * @param mutex #PMutex to release. + * @return TRUE in case of success, FALSE otherwise. + * @since 0.0.1 + * @warning Do not use this function on non-locked mutexes - behavior may be + * unpredictable. + * + * If @a mutex was previously locked then it becomes unlocked. + * + * It's implementation dependent whether only the same thread can lock and + * unlock the same mutex. + */ +P_LIB_API pboolean p_mutex_unlock (PMutex *mutex); + +/** + * @brief Frees #PMutex object. + * @param mutex #PMutex to free. + * @since 0.0.1 + * @warning It doesn't unlock @a mutex before freeing memory, so you should do + * it manually. + */ +P_LIB_API void p_mutex_free (PMutex *mutex); + +P_END_DECLS + +#endif /* PLIBSYS_HEADER_PMUTEX_H */ diff --git a/3rdparty/plibsys/src/pprocess.c b/3rdparty/plibsys/src/pprocess.c new file mode 100644 index 0000000..d509230 --- /dev/null +++ b/3rdparty/plibsys/src/pprocess.c @@ -0,0 +1,61 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pprocess.h" + +#ifndef P_OS_WIN +# include <sys/types.h> +# include <signal.h> +# include <unistd.h> +#endif + +P_LIB_API puint32 +p_process_get_current_pid (void) +{ +#ifdef P_OS_WIN + return (puint32) GetCurrentProcessId (); +#else + return (puint32) getpid (); +#endif +} + +P_LIB_API pboolean +p_process_is_running (puint32 pid) +{ +#ifdef P_OS_WIN + HANDLE proc; + DWORD ret; + + if ((proc = OpenProcess (SYNCHRONIZE, FALSE, pid)) == NULL) + return FALSE; + + ret = WaitForSingleObject (proc, 0); + CloseHandle (proc); + + return ret == WAIT_TIMEOUT; +#else + return kill ((pid_t) pid, 0) == 0; +#endif +} diff --git a/3rdparty/plibsys/src/pprocess.h b/3rdparty/plibsys/src/pprocess.h new file mode 100644 index 0000000..272bbb5 --- /dev/null +++ b/3rdparty/plibsys/src/pprocess.h @@ -0,0 +1,69 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * @file pprocess.h + * @brief Process information + * @author Alexander Saprykin + * + * A process is an executing unit in an operating system with its own address + * space. Every process can be identified with a unique identifier called PID. + * To get a PID of the currently running process call + * p_process_get_current_pid(). To check whether a process with a given PID is + * running up use p_process_is_running(). + */ + +#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION) +# error "Header files shouldn't be included directly, consider using <plibsys.h> instead." +#endif + +#ifndef PLIBSYS_HEADER_PPROCESS_H +#define PLIBSYS_HEADER_PPROCESS_H + +#include <pmacros.h> +#include <ptypes.h> + +P_BEGIN_DECLS + +/** + * @brief Gets a PID of the calling process. + * @return PID of the calling process. + * @since 0.0.1 + */ +P_LIB_API puint32 p_process_get_current_pid (void); + +/** + * @brief Checks whether a process with a given PID is running or not. + * @param pid PID to check for. + * @return TRUE if the process with the given PID exists and is running up, + * FALSE otherwise. + * @since 0.0.1 + */ +P_LIB_API pboolean p_process_is_running (puint32 pid); + +P_END_DECLS + +#endif /* PLIBSYS_HEADER_PPROCESS_H */ + diff --git a/3rdparty/plibsys/src/prwlock-general.c b/3rdparty/plibsys/src/prwlock-general.c new file mode 100644 index 0000000..5d79ed1 --- /dev/null +++ b/3rdparty/plibsys/src/prwlock-general.c @@ -0,0 +1,326 @@ +/* + * The MIT License + * + * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pmem.h" +#include "pmutex.h" +#include "pcondvariable.h" +#include "prwlock.h" + +#include <stdlib.h> + +#define P_RWLOCK_SET_READERS(lock, readers) (((lock) & (~0x00007FFF)) | (readers)) +#define P_RWLOCK_READER_COUNT(lock) ((lock) & 0x00007FFF) +#define P_RWLOCK_SET_WRITERS(lock, writers) (((lock) & (~0x3FFF8000)) | ((writers) << 15)) +#define P_RWLOCK_WRITER_COUNT(lock) (((lock) & 0x3FFF8000) >> 15) + +struct PRWLock_ { + PMutex *mutex; + PCondVariable *read_cv; + PCondVariable *write_cv; + puint32 active_threads; + puint32 waiting_threads; +}; + +P_LIB_API PRWLock * +p_rwlock_new (void) +{ + PRWLock *ret; + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PRWLock))) == NULL)) { + P_ERROR ("PRWLock::p_rwlock_new: failed to allocate memory"); + return NULL; + } + + if (P_UNLIKELY ((ret->mutex = p_mutex_new ()) == NULL)) { + P_ERROR ("PRWLock::p_rwlock_new: failed to allocate mutex"); + p_free (ret); + } + + if (P_UNLIKELY ((ret->read_cv = p_cond_variable_new ()) == NULL)) { + P_ERROR ("PRWLock::p_rwlock_new: failed to allocate condition variable for read"); + p_mutex_free (ret->mutex); + p_free (ret); + } + + if (P_UNLIKELY ((ret->write_cv = p_cond_variable_new ()) == NULL)) { + P_ERROR ("PRWLock::p_rwlock_new: failed to allocate condition variable for write"); + p_cond_variable_free (ret->read_cv); + p_mutex_free (ret->mutex); + p_free (ret); + } + + return ret; +} + +P_LIB_API pboolean +p_rwlock_reader_lock (PRWLock *lock) +{ + pboolean wait_ok; + + if (P_UNLIKELY (lock == NULL)) + return FALSE; + + if (P_UNLIKELY (p_mutex_lock (lock->mutex) == FALSE)) { + P_ERROR ("PRWLock::p_rwlock_reader_lock: p_mutex_lock() failed"); + return FALSE; + } + + wait_ok = TRUE; + + if (P_RWLOCK_WRITER_COUNT (lock->active_threads)) { + lock->waiting_threads = P_RWLOCK_SET_READERS (lock->waiting_threads, + P_RWLOCK_READER_COUNT (lock->waiting_threads) + 1); + + while (P_RWLOCK_WRITER_COUNT (lock->active_threads)) { + wait_ok = p_cond_variable_wait (lock->read_cv, lock->mutex); + + if (P_UNLIKELY (wait_ok == FALSE)) { + P_ERROR ("PRWLock::p_rwlock_reader_lock: p_cond_variable_wait() failed"); + break; + } + } + + lock->waiting_threads = P_RWLOCK_SET_READERS (lock->waiting_threads, + P_RWLOCK_READER_COUNT (lock->waiting_threads) - 1); + } + + if (P_LIKELY (wait_ok == TRUE)) + lock->active_threads = P_RWLOCK_SET_READERS (lock->active_threads, + P_RWLOCK_READER_COUNT (lock->active_threads) + 1); + + if (P_UNLIKELY (p_mutex_unlock (lock->mutex) == FALSE)) { + P_ERROR ("PRWLock::p_rwlock_reader_lock: p_mutex_unlock() failed"); + return FALSE; + } + + return wait_ok; +} + +P_LIB_API pboolean +p_rwlock_reader_trylock (PRWLock *lock) +{ + if (P_UNLIKELY (lock == NULL)) + return FALSE; + + if (P_UNLIKELY (p_mutex_lock (lock->mutex) == FALSE)) { + P_ERROR ("PRWLock::p_rwlock_reader_trylock: p_mutex_lock() failed"); + return FALSE; + } + + if (P_RWLOCK_WRITER_COUNT (lock->active_threads)) { + if (P_UNLIKELY (p_mutex_unlock (lock->mutex) == FALSE)) + P_ERROR ("PRWLock::p_rwlock_reader_trylock: p_mutex_unlock() failed(1)"); + + return FALSE; + } + + lock->active_threads = P_RWLOCK_SET_READERS (lock->active_threads, + P_RWLOCK_READER_COUNT (lock->active_threads) + 1); + + if (P_UNLIKELY (p_mutex_unlock (lock->mutex) == FALSE)) { + P_ERROR ("PRWLock::p_rwlock_reader_trylock: p_mutex_unlock() failed(2)"); + return FALSE; + } + + return TRUE; +} + +P_LIB_API pboolean +p_rwlock_reader_unlock (PRWLock *lock) +{ + puint32 reader_count; + pboolean signal_ok; + + if (P_UNLIKELY (lock == NULL)) + return FALSE; + + if (P_UNLIKELY (p_mutex_lock (lock->mutex) == FALSE)) { + P_ERROR ("PRWLock::p_rwlock_reader_unlock: p_mutex_lock() failed"); + return FALSE; + } + + reader_count = P_RWLOCK_READER_COUNT (lock->active_threads); + + if (P_UNLIKELY (reader_count == 0)) { + if (P_UNLIKELY (p_mutex_unlock (lock->mutex) == FALSE)) + P_ERROR ("PRWLock::p_rwlock_reader_unlock: p_mutex_unlock() failed(1)"); + + return TRUE; + } + + lock->active_threads = P_RWLOCK_SET_READERS (lock->active_threads, reader_count - 1); + + signal_ok = TRUE; + + if (reader_count == 1 && P_RWLOCK_WRITER_COUNT (lock->waiting_threads)) + signal_ok = p_cond_variable_signal (lock->write_cv); + + if (P_UNLIKELY (signal_ok == FALSE)) + P_ERROR ("PRWLock::p_rwlock_reader_unlock: p_cond_variable_signal() failed"); + + if (P_UNLIKELY (p_mutex_unlock (lock->mutex) == FALSE)) { + P_ERROR ("PRWLock::p_rwlock_reader_unlock: p_mutex_unlock() failed(2)"); + return FALSE; + } + + return signal_ok; +} + +P_LIB_API pboolean +p_rwlock_writer_lock (PRWLock *lock) +{ + pboolean wait_ok; + + if (P_UNLIKELY (lock == NULL)) + return FALSE; + + if (P_UNLIKELY (p_mutex_lock (lock->mutex) == FALSE)) { + P_ERROR ("PRWLock::p_rwlock_writer_lock: p_mutex_lock() failed"); + return FALSE; + } + + wait_ok = TRUE; + + if (lock->active_threads) { + lock->waiting_threads = P_RWLOCK_SET_WRITERS (lock->waiting_threads, + P_RWLOCK_WRITER_COUNT (lock->waiting_threads) + 1); + + while (lock->active_threads) { + wait_ok = p_cond_variable_wait (lock->write_cv, lock->mutex); + + if (P_UNLIKELY (wait_ok == FALSE)) { + P_ERROR ("PRWLock::p_rwlock_writer_lock: p_cond_variable_wait() failed"); + break; + } + } + + lock->waiting_threads = P_RWLOCK_SET_WRITERS (lock->waiting_threads, + P_RWLOCK_WRITER_COUNT (lock->waiting_threads) - 1); + } + + if (P_LIKELY (wait_ok == TRUE)) + lock->active_threads = P_RWLOCK_SET_WRITERS (lock->active_threads, 1); + + if (P_UNLIKELY (p_mutex_unlock (lock->mutex) == FALSE)) { + P_ERROR ("PRWLock::p_rwlock_writer_lock: p_mutex_unlock() failed"); + return FALSE; + } + + return wait_ok; +} + +P_LIB_API pboolean +p_rwlock_writer_trylock (PRWLock *lock) +{ + if (P_UNLIKELY (lock == NULL)) + return FALSE; + + if (P_UNLIKELY (p_mutex_lock (lock->mutex) == FALSE)) { + P_ERROR ("PRWLock::p_rwlock_writer_trylock: p_mutex_lock() failed"); + return FALSE; + } + + if (lock->active_threads) { + if (P_UNLIKELY (p_mutex_unlock (lock->mutex) == FALSE)) + P_ERROR ("PRWLock::p_rwlock_writer_trylock: p_mutex_unlock() failed(1)"); + + return FALSE; + } + + lock->active_threads = P_RWLOCK_SET_WRITERS (lock->active_threads, 1); + + if (P_UNLIKELY (p_mutex_unlock (lock->mutex) == FALSE)) { + P_ERROR ("PRWLock::p_rwlock_writer_trylock: p_mutex_unlock() failed(2)"); + return FALSE; + } + + return TRUE; +} + +P_LIB_API pboolean +p_rwlock_writer_unlock (PRWLock *lock) +{ + pboolean signal_ok; + + if (P_UNLIKELY (lock == NULL)) + return FALSE; + + if (P_UNLIKELY (p_mutex_lock (lock->mutex) == FALSE)) { + P_ERROR ("PRWLock::p_rwlock_writer_unlock: p_mutex_lock() failed"); + return FALSE; + } + + lock->active_threads = P_RWLOCK_SET_WRITERS (lock->active_threads, 0); + + signal_ok = TRUE; + + if (P_RWLOCK_WRITER_COUNT (lock->waiting_threads)) { + if (P_UNLIKELY (p_cond_variable_signal (lock->write_cv) == FALSE)) { + P_ERROR ("PRWLock::p_rwlock_writer_unlock: p_cond_variable_signal() failed"); + signal_ok = FALSE; + } + } else if (P_RWLOCK_READER_COUNT (lock->waiting_threads)) { + if (P_UNLIKELY (p_cond_variable_broadcast (lock->read_cv) == FALSE)) { + P_ERROR ("PRWLock::p_rwlock_writer_unlock: p_cond_variable_broadcast() failed"); + signal_ok = FALSE; + } + } + + if (P_UNLIKELY (p_mutex_unlock (lock->mutex) == FALSE)) { + P_ERROR ("PRWLock::p_rwlock_writer_unlock: p_mutex_unlock() failed"); + return FALSE; + } + + return signal_ok; +} + +P_LIB_API void +p_rwlock_free (PRWLock *lock) +{ + if (P_UNLIKELY (lock == NULL)) + return; + + if (P_UNLIKELY (lock->active_threads)) + P_WARNING ("PRWLock::p_rwlock_free: destroying while active threads are present"); + + if (P_UNLIKELY (lock->waiting_threads)) + P_WARNING ("PRWLock::p_rwlock_free: destroying while waiting threads are present"); + + p_mutex_free (lock->mutex); + p_cond_variable_free (lock->read_cv); + p_cond_variable_free (lock->write_cv); + + p_free (lock); +} + +void +p_rwlock_init (void) +{ +} + +void +p_rwlock_shutdown (void) +{ +} diff --git a/3rdparty/plibsys/src/prwlock-none.c b/3rdparty/plibsys/src/prwlock-none.c new file mode 100644 index 0000000..53fdb76 --- /dev/null +++ b/3rdparty/plibsys/src/prwlock-none.c @@ -0,0 +1,103 @@ +/* + * The MIT License + * + * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "prwlock.h" + +#include <stdlib.h> + +struct PRWLock_ { + pint hdl; +}; + +P_LIB_API PRWLock * +p_rwlock_new (void) +{ + return NULL; +} + +P_LIB_API pboolean +p_rwlock_reader_lock (PRWLock *lock) +{ + P_UNUSED (lock); + + return FALSE; +} + +P_LIB_API pboolean +p_rwlock_reader_trylock (PRWLock *lock) +{ + P_UNUSED (lock); + + return FALSE; +} + +P_LIB_API pboolean +p_rwlock_reader_unlock (PRWLock *lock) +{ + P_UNUSED (lock); + + return FALSE; +} + +P_LIB_API pboolean +p_rwlock_writer_lock (PRWLock *lock) +{ + P_UNUSED (lock); + + return FALSE; +} + +P_LIB_API pboolean +p_rwlock_writer_trylock (PRWLock *lock) +{ + P_UNUSED (lock); + + return FALSE; +} + +P_LIB_API pboolean +p_rwlock_writer_unlock (PRWLock *lock) +{ + P_UNUSED (lock); + + return FALSE; +} + +P_LIB_API void +p_rwlock_free (PRWLock *lock) +{ + P_UNUSED (lock); +} + +void +p_rwlock_init (void) +{ +} + +void +p_rwlock_shutdown (void) +{ +} + diff --git a/3rdparty/plibsys/src/prwlock-posix.c b/3rdparty/plibsys/src/prwlock-posix.c new file mode 100644 index 0000000..817de8c --- /dev/null +++ b/3rdparty/plibsys/src/prwlock-posix.c @@ -0,0 +1,151 @@ +/* + * The MIT License + * + * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pmem.h" +#include "prwlock.h" + +#include <stdlib.h> +#include <pthread.h> + +typedef pthread_rwlock_t rwlock_hdl; + +struct PRWLock_ { + rwlock_hdl hdl; +}; + +static pboolean pp_rwlock_unlock_any (PRWLock *lock); + +static pboolean +pp_rwlock_unlock_any (PRWLock *lock) +{ + if (P_UNLIKELY (lock == NULL)) + return FALSE; + + if (P_LIKELY (pthread_rwlock_unlock (&lock->hdl) == 0)) + return TRUE; + else { + P_ERROR ("PRWLock::pp_rwlock_unlock_any: pthread_rwlock_unlock() failed"); + return FALSE; + } +} + +P_LIB_API PRWLock * +p_rwlock_new (void) +{ + PRWLock *ret; + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PRWLock))) == NULL)) { + P_ERROR ("PRWLock::p_rwlock_new: failed to allocate memory"); + return NULL; + } + + if (P_UNLIKELY (pthread_rwlock_init (&ret->hdl, NULL) != 0)) { + P_ERROR ("PRWLock::p_rwlock_new: pthread_rwlock_init() failed"); + p_free (ret); + return NULL; + } + + return ret; +} + +P_LIB_API pboolean +p_rwlock_reader_lock (PRWLock *lock) +{ + if (P_UNLIKELY (lock == NULL)) + return FALSE; + + if (P_UNLIKELY (pthread_rwlock_rdlock (&lock->hdl) == 0)) + return TRUE; + else { + P_ERROR ("PRWLock::p_rwlock_reader_lock: pthread_rwlock_rdlock() failed"); + return FALSE; + } +} + +P_LIB_API pboolean +p_rwlock_reader_trylock (PRWLock *lock) +{ + if (P_UNLIKELY (lock == NULL)) + return FALSE; + + return (pthread_rwlock_tryrdlock (&lock->hdl) == 0) ? TRUE : FALSE; +} + +P_LIB_API pboolean +p_rwlock_reader_unlock (PRWLock *lock) +{ + return pp_rwlock_unlock_any (lock); +} + +P_LIB_API pboolean +p_rwlock_writer_lock (PRWLock *lock) +{ + if (P_UNLIKELY (lock == NULL)) + return FALSE; + + if (P_UNLIKELY (pthread_rwlock_wrlock (&lock->hdl) == 0)) + return TRUE; + else { + P_ERROR ("PRWLock::p_rwlock_writer_lock: pthread_rwlock_wrlock() failed"); + return FALSE; + } +} + +P_LIB_API pboolean +p_rwlock_writer_trylock (PRWLock *lock) +{ + if (P_UNLIKELY (lock == NULL)) + return FALSE; + + return (pthread_rwlock_trywrlock (&lock->hdl) == 0) ? TRUE : FALSE; +} + +P_LIB_API pboolean +p_rwlock_writer_unlock (PRWLock *lock) +{ + return pp_rwlock_unlock_any (lock); +} + +P_LIB_API void +p_rwlock_free (PRWLock *lock) +{ + if (P_UNLIKELY (lock == NULL)) + return; + + if (P_UNLIKELY (pthread_rwlock_destroy (&lock->hdl) != 0)) + P_ERROR ("PRWLock::p_rwlock_free: pthread_rwlock_destroy() failed"); + + p_free (lock); +} + +void +p_rwlock_init (void) +{ +} + +void +p_rwlock_shutdown (void) +{ +} diff --git a/3rdparty/plibsys/src/prwlock-solaris.c b/3rdparty/plibsys/src/prwlock-solaris.c new file mode 100644 index 0000000..c29d345 --- /dev/null +++ b/3rdparty/plibsys/src/prwlock-solaris.c @@ -0,0 +1,151 @@ +/* + * The MIT License + * + * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pmem.h" +#include "prwlock.h" + +#include <stdlib.h> +#include <thread.h> + +typedef rwlock_t rwlock_hdl; + +struct PRWLock_ { + rwlock_hdl hdl; +}; + +static pboolean pp_rwlock_unlock_any (PRWLock *lock); + +static pboolean +pp_rwlock_unlock_any (PRWLock *lock) +{ + if (P_UNLIKELY (lock == NULL)) + return FALSE; + + if (P_LIKELY (rw_unlock (&lock->hdl) == 0)) + return TRUE; + else { + P_ERROR ("PRWLock::pp_rwlock_unlock_any: rw_unlock() failed"); + return FALSE; + } +} + +P_LIB_API PRWLock * +p_rwlock_new (void) +{ + PRWLock *ret; + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PRWLock))) == NULL)) { + P_ERROR ("PRWLock::p_rwlock_new: failed to allocate memory"); + return NULL; + } + + if (P_UNLIKELY (rwlock_init (&ret->hdl, USYNC_THREAD, NULL) != 0)) { + P_ERROR ("PRWLock::p_rwlock_new: rwlock_init() failed"); + p_free (ret); + return NULL; + } + + return ret; +} + +P_LIB_API pboolean +p_rwlock_reader_lock (PRWLock *lock) +{ + if (P_UNLIKELY (lock == NULL)) + return FALSE; + + if (P_UNLIKELY (rw_rdlock (&lock->hdl) == 0)) + return TRUE; + else { + P_ERROR ("PRWLock::p_rwlock_reader_lock: rw_rdlock() failed"); + return FALSE; + } +} + +P_LIB_API pboolean +p_rwlock_reader_trylock (PRWLock *lock) +{ + if (P_UNLIKELY (lock == NULL)) + return FALSE; + + return (rw_tryrdlock (&lock->hdl) == 0) ? TRUE : FALSE; +} + +P_LIB_API pboolean +p_rwlock_reader_unlock (PRWLock *lock) +{ + return pp_rwlock_unlock_any (lock); +} + +P_LIB_API pboolean +p_rwlock_writer_lock (PRWLock *lock) +{ + if (P_UNLIKELY (lock == NULL)) + return FALSE; + + if (P_UNLIKELY (rw_wrlock (&lock->hdl) == 0)) + return TRUE; + else { + P_ERROR ("PRWLock::p_rwlock_writer_lock: rw_wrlock() failed"); + return FALSE; + } +} + +P_LIB_API pboolean +p_rwlock_writer_trylock (PRWLock *lock) +{ + if (P_UNLIKELY (lock == NULL)) + return FALSE; + + return (rw_trywrlock (&lock->hdl) == 0) ? TRUE : FALSE; +} + +P_LIB_API pboolean +p_rwlock_writer_unlock (PRWLock *lock) +{ + return pp_rwlock_unlock_any (lock); +} + +P_LIB_API void +p_rwlock_free (PRWLock *lock) +{ + if (P_UNLIKELY (lock == NULL)) + return; + + if (P_UNLIKELY (rwlock_destroy (&lock->hdl) != 0)) + P_ERROR ("PRWLock::p_rwlock_free: rwlock_destroy() failed"); + + p_free (lock); +} + +void +p_rwlock_init (void) +{ +} + +void +p_rwlock_shutdown (void) +{ +} diff --git a/3rdparty/plibsys/src/prwlock-win.c b/3rdparty/plibsys/src/prwlock-win.c new file mode 100644 index 0000000..3064223 --- /dev/null +++ b/3rdparty/plibsys/src/prwlock-win.c @@ -0,0 +1,548 @@ +/* + * The MIT License + * + * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* More emulation variants: https://github.com/neosmart/RWLock */ + +#include "pmem.h" +#include "patomic.h" +#include "puthread.h" +#include "prwlock.h" + +#include <stdlib.h> + +#define P_RWLOCK_XP_MAX_SPIN 4000 +#define P_RWLOCK_XP_IS_CLEAR(lock) (((lock) & 0x40007FFF) == 0) +#define P_RWLOCK_XP_IS_WRITER(lock) (((lock) & 0x40000000) != 0) +#define P_RWLOCK_XP_SET_WRITER(lock) ((lock) | 0x40000000) +#define P_RWLOCK_XP_UNSET_WRITER(lock) ((lock) & (~0x40000000)) +#define P_RWLOCK_XP_SET_READERS(lock, readers) (((lock) & (~0x00007FFF)) | (readers)) +#define P_RWLOCK_XP_READER_COUNT(lock) ((lock) & 0x00007FFF) +#define P_RWLOCK_XP_SET_WAITING(lock, waiting) (((lock) & (~0x3FFF8000)) | ((waiting) << 15)) +#define P_RWLOCK_XP_WAITING_COUNT(lock) (((lock) & 0x3FFF8000) >> 15) + +typedef VOID (WINAPI *InitializeSRWLockFunc) (ppointer lock); +typedef VOID (WINAPI *AcquireSRWLockExclusiveFunc) (ppointer lock); +typedef BOOLEAN (WINAPI *TryAcquireSRWLockExclusiveFunc) (ppointer lock); +typedef VOID (WINAPI *ReleaseSRWLockExclusiveFunc) (ppointer lock); +typedef VOID (WINAPI *AcquireSRWLockSharedFunc) (ppointer lock); +typedef BOOLEAN (WINAPI *TryAcquireSRWLockSharedFunc) (ppointer lock); +typedef VOID (WINAPI *ReleaseSRWLockSharedFunc) (ppointer lock); + +typedef pboolean (* PWin32LockInit) (PRWLock *lock); +typedef void (* PWin32LockClose) (PRWLock *lock); +typedef pboolean (* PWin32LockStartRead) (PRWLock *lock); +typedef pboolean (* PWin32LockStartReadTry) (PRWLock *lock); +typedef pboolean (* PWin32LockEndRead) (PRWLock *lock); +typedef pboolean (* PWin32LockStartWrite) (PRWLock *lock); +typedef pboolean (* PWin32LockStartWriteTry) (PRWLock *lock); +typedef pboolean (* PWin32LockEndWrite) (PRWLock *lock); + +static PWin32LockInit pp_rwlock_init_func = NULL; +static PWin32LockClose pp_rwlock_close_func = NULL; +static PWin32LockStartRead pp_rwlock_start_read_func = NULL; +static PWin32LockStartReadTry pp_rwlock_start_read_try_func = NULL; +static PWin32LockEndRead pp_rwlock_end_read_func = NULL; +static PWin32LockStartWrite pp_rwlock_start_write_func = NULL; +static PWin32LockStartWriteTry pp_rwlock_start_write_try_func = NULL; +static PWin32LockEndWrite pp_rwlock_end_write_func = NULL; + +typedef struct PRWLockVistaTable_ { + InitializeSRWLockFunc rwl_init; + AcquireSRWLockExclusiveFunc rwl_excl_lock; + TryAcquireSRWLockExclusiveFunc rwl_excl_lock_try; + ReleaseSRWLockExclusiveFunc rwl_excl_rel; + AcquireSRWLockSharedFunc rwl_shr_lock; + TryAcquireSRWLockSharedFunc rwl_shr_lock_try; + ReleaseSRWLockSharedFunc rwl_shr_rel; +} PRWLockVistaTable; + +typedef struct PRWLockXP_ { + volatile puint32 lock; + HANDLE event; +} PRWLockXP; + +struct PRWLock_ { + ppointer lock; +}; + +static PRWLockVistaTable pp_rwlock_vista_table = {NULL, NULL, NULL, NULL, + NULL, NULL, NULL}; + +/* SRWLock routines */ +static pboolean pp_rwlock_init_vista (PRWLock *lock); +static void pp_rwlock_close_vista (PRWLock *lock); +static pboolean pp_rwlock_start_read_vista (PRWLock *lock); +static pboolean pp_rwlock_start_read_try_vista (PRWLock *lock); +static pboolean pp_rwlock_end_read_vista (PRWLock *lock); +static pboolean pp_rwlock_start_write_vista (PRWLock *lock); +static pboolean pp_rwlock_start_write_try_vista (PRWLock *lock); +static pboolean pp_rwlock_end_write_vista (PRWLock *lock); + +/* Windows XP emulation routines */ +static pboolean pp_rwlock_init_xp (PRWLock *lock); +static void pp_rwlock_close_xp (PRWLock *lock); +static pboolean pp_rwlock_start_read_xp (PRWLock *lock); +static pboolean pp_rwlock_start_read_try_xp (PRWLock *lock); +static pboolean pp_rwlock_end_read_xp (PRWLock *lock); +static pboolean pp_rwlock_start_write_xp (PRWLock *lock); +static pboolean pp_rwlock_start_write_try_xp (PRWLock *lock); +static pboolean pp_rwlock_end_write_xp (PRWLock *lock); + +/* SRWLock routines */ + +static pboolean +pp_rwlock_init_vista (PRWLock *lock) +{ + pp_rwlock_vista_table.rwl_init (lock); + + return TRUE; +} + +static void +pp_rwlock_close_vista (PRWLock *lock) +{ + P_UNUSED (lock); +} + +static pboolean +pp_rwlock_start_read_vista (PRWLock *lock) +{ + pp_rwlock_vista_table.rwl_shr_lock (lock); + + return TRUE; +} + +static pboolean +pp_rwlock_start_read_try_vista (PRWLock *lock) +{ + return pp_rwlock_vista_table.rwl_shr_lock_try (lock) != 0 ? TRUE : FALSE; +} + +static pboolean +pp_rwlock_end_read_vista (PRWLock *lock) +{ + pp_rwlock_vista_table.rwl_shr_rel (lock); + + return TRUE; +} + +static pboolean +pp_rwlock_start_write_vista (PRWLock *lock) +{ + pp_rwlock_vista_table.rwl_excl_lock (lock); + + return TRUE; +} + +static pboolean +pp_rwlock_start_write_try_vista (PRWLock *lock) +{ + return pp_rwlock_vista_table.rwl_excl_lock_try (lock) != 0 ? TRUE : FALSE; +} + +static pboolean +pp_rwlock_end_write_vista (PRWLock *lock) +{ + pp_rwlock_vista_table.rwl_excl_rel (lock); + + return TRUE; +} + +/* Windows XP emulation routines */ + +static pboolean +pp_rwlock_init_xp (PRWLock *lock) +{ + PRWLockXP *rwl_xp; + + if ((lock->lock = p_malloc0 (sizeof (PRWLockXP))) == NULL) { + P_ERROR ("PRWLock::pp_rwlock_init_xp: failed to allocate memory"); + return FALSE; + } + + rwl_xp = ((PRWLockXP *) lock->lock); + + rwl_xp->lock = 0; + rwl_xp->event = CreateEventA (NULL, FALSE, FALSE, NULL); + + if (P_UNLIKELY (rwl_xp->event == NULL)) { + P_ERROR ("PRWLock::pp_rwlock_init_xp: CreateEventA() failed"); + p_free (lock->lock); + lock->lock = NULL; + return FALSE; + } + + return TRUE; +} + +static void +pp_rwlock_close_xp (PRWLock *lock) +{ + CloseHandle (((PRWLockXP *) lock->lock)->event); + p_free (lock->lock); +} + +static pboolean +pp_rwlock_start_read_xp (PRWLock *lock) +{ + PRWLockXP *rwl_xp = ((PRWLockXP *) lock->lock); + int i; + puint32 tmp_lock; + puint32 counter; + + for (i = 0; ; ++i) { + tmp_lock = (puint32) p_atomic_int_get ((const volatile pint *) &rwl_xp->lock); + + if (!P_RWLOCK_XP_IS_WRITER (tmp_lock)) { + counter = P_RWLOCK_XP_SET_READERS (tmp_lock, P_RWLOCK_XP_READER_COUNT (tmp_lock) + 1); + + if (p_atomic_int_compare_and_exchange ((volatile pint *) &rwl_xp->lock, + (pint) tmp_lock, + (pint) counter) == TRUE) + return TRUE; + else + continue; + } else { + if (P_LIKELY (i < P_RWLOCK_XP_MAX_SPIN)) { + p_uthread_yield (); + continue; + } + + counter = P_RWLOCK_XP_SET_WAITING (tmp_lock, P_RWLOCK_XP_WAITING_COUNT (tmp_lock) + 1); + + if (p_atomic_int_compare_and_exchange ((volatile pint *) &rwl_xp->lock, + (pint) tmp_lock, + (pint) counter) != TRUE) + continue; + + i = 0; + + if (P_UNLIKELY (WaitForSingleObject (rwl_xp->event, INFINITE) != WAIT_OBJECT_0)) + P_WARNING ("PRWLock::pp_rwlock_start_read_xp: WaitForSingleObject() failed, go ahead"); + + do { + tmp_lock = p_atomic_int_get ((const volatile pint *) &rwl_xp->lock); + counter = P_RWLOCK_XP_SET_WAITING (tmp_lock, P_RWLOCK_XP_WAITING_COUNT (tmp_lock) - 1); + } while (p_atomic_int_compare_and_exchange ((volatile pint *) &rwl_xp->lock, + (pint) tmp_lock, + (pint) counter) != TRUE); + } + } + + return TRUE; +} + +static pboolean +pp_rwlock_start_read_try_xp (PRWLock *lock) +{ + PRWLockXP *rwl_xp = ((PRWLockXP *) lock->lock); + puint32 tmp_lock; + puint32 counter; + + tmp_lock = (puint32) p_atomic_int_get ((const volatile pint *) &rwl_xp->lock); + + if (P_RWLOCK_XP_IS_WRITER (tmp_lock)) + return FALSE; + + counter = P_RWLOCK_XP_SET_READERS (tmp_lock, P_RWLOCK_XP_READER_COUNT (tmp_lock) + 1); + + return p_atomic_int_compare_and_exchange ((volatile pint *) &rwl_xp->lock, + (pint) tmp_lock, + (pint) counter); +} + +static pboolean +pp_rwlock_end_read_xp (PRWLock *lock) +{ + PRWLockXP *rwl_xp = ((PRWLockXP *) lock->lock); + puint32 tmp_lock; + puint32 counter; + + while (TRUE) { + tmp_lock = (puint32) p_atomic_int_get ((const volatile pint *) &rwl_xp->lock); + counter = P_RWLOCK_XP_READER_COUNT (tmp_lock); + + if (P_UNLIKELY (counter == 0)) + return TRUE; + + if (counter == 1 && P_RWLOCK_XP_WAITING_COUNT (tmp_lock) != 0) { + /* A duplicate wake up notification is possible */ + if (P_UNLIKELY (SetEvent (rwl_xp->event) == 0)) + P_WARNING ("PRWLock::pp_rwlock_end_read_xp: SetEvent() failed"); + } + + counter = P_RWLOCK_XP_SET_READERS (tmp_lock, counter - 1); + + if (p_atomic_int_compare_and_exchange ((volatile pint *) &rwl_xp->lock, + (pint) tmp_lock, + (pint) counter) == TRUE) + break; + } + + return TRUE; +} + +static pboolean +pp_rwlock_start_write_xp (PRWLock *lock) +{ + PRWLockXP *rwl_xp = ((PRWLockXP *) lock->lock); + int i; + puint32 tmp_lock; + puint32 counter; + + for (i = 0; ; ++i) { + tmp_lock = (puint32) p_atomic_int_get ((const volatile pint *) &rwl_xp->lock); + + if (P_RWLOCK_XP_IS_CLEAR (tmp_lock)) { + counter = P_RWLOCK_XP_SET_WRITER (tmp_lock); + + if (p_atomic_int_compare_and_exchange ((volatile pint *) &rwl_xp->lock, + (pint) tmp_lock, + (pint) counter) == TRUE) + return TRUE; + else + continue; + } else { + if (P_LIKELY (i < P_RWLOCK_XP_MAX_SPIN)) { + p_uthread_yield (); + continue; + } + + counter = P_RWLOCK_XP_SET_WAITING (tmp_lock, P_RWLOCK_XP_WAITING_COUNT (tmp_lock) + 1); + + if (p_atomic_int_compare_and_exchange ((volatile pint *) &rwl_xp->lock, + (pint) tmp_lock, + (pint) counter) != TRUE) + continue; + + i = 0; + + if (P_UNLIKELY (WaitForSingleObject (rwl_xp->event, INFINITE) != WAIT_OBJECT_0)) + P_WARNING ("PRWLock::pp_rwlock_start_write_xp: WaitForSingleObject() failed, go ahead"); + + do { + tmp_lock = p_atomic_int_get ((const volatile pint *) &rwl_xp->lock); + counter = P_RWLOCK_XP_SET_WAITING (tmp_lock, P_RWLOCK_XP_WAITING_COUNT (tmp_lock) - 1); + } while (p_atomic_int_compare_and_exchange ((volatile pint *) &rwl_xp->lock, + (pint) tmp_lock, + (pint) counter) != TRUE); + } + } + + return TRUE; +} + +static pboolean +pp_rwlock_start_write_try_xp (PRWLock *lock) +{ + PRWLockXP *rwl_xp = ((PRWLockXP *) lock->lock); + puint32 tmp_lock; + + tmp_lock = (puint32) p_atomic_int_get ((const volatile pint *) &rwl_xp->lock); + + if (P_RWLOCK_XP_IS_CLEAR (tmp_lock)) { + return p_atomic_int_compare_and_exchange ((volatile pint *) &rwl_xp->lock, + (pint) tmp_lock, + (pint) P_RWLOCK_XP_SET_WRITER (tmp_lock)); + } + + return FALSE; +} + +static pboolean +pp_rwlock_end_write_xp (PRWLock *lock) +{ + PRWLockXP *rwl_xp = ((PRWLockXP *) lock->lock); + puint32 tmp_lock; + + while (TRUE) { + while (TRUE) { + tmp_lock = (puint32) p_atomic_int_get ((const volatile pint *) &rwl_xp->lock); + + if (P_UNLIKELY (!P_RWLOCK_XP_IS_WRITER (tmp_lock))) + return TRUE; + + if (P_RWLOCK_XP_WAITING_COUNT (tmp_lock) == 0) + break; + + /* Only the one end-of-write call can be */ + if (P_UNLIKELY (SetEvent (rwl_xp->event) == 0)) + P_WARNING ("PRWLock::pp_rwlock_end_write_xp: SetEvent() failed"); + } + + if (p_atomic_int_compare_and_exchange ((volatile pint *) &rwl_xp->lock, + (pint) tmp_lock, + (pint) P_RWLOCK_XP_UNSET_WRITER (tmp_lock)) == TRUE) + break; + } + + return TRUE; +} + +P_LIB_API PRWLock * +p_rwlock_new (void) +{ + PRWLock *ret; + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PRWLock))) == NULL)) { + P_ERROR ("PRWLock::p_rwlock_new: failed to allocate memory"); + return NULL; + } + + if (P_UNLIKELY (pp_rwlock_init_func (ret) != TRUE)) { + P_ERROR ("PRWLock::p_rwlock_new: failed to initialize"); + p_free (ret); + return NULL; + } + + return ret; +} + +P_LIB_API pboolean +p_rwlock_reader_lock (PRWLock *lock) +{ + if (P_UNLIKELY (lock == NULL)) + return FALSE; + + return pp_rwlock_start_read_func (lock); +} + +P_LIB_API pboolean +p_rwlock_reader_trylock (PRWLock *lock) +{ + if (P_UNLIKELY (lock == NULL)) + return FALSE; + + return pp_rwlock_start_read_try_func (lock); +} + +P_LIB_API pboolean +p_rwlock_reader_unlock (PRWLock *lock) +{ + if (P_UNLIKELY (lock == NULL)) + return FALSE; + + return pp_rwlock_end_read_func (lock); +} + +P_LIB_API pboolean +p_rwlock_writer_lock (PRWLock *lock) +{ + if (P_UNLIKELY (lock == NULL)) + return FALSE; + + return pp_rwlock_start_write_func (lock); +} + +P_LIB_API pboolean +p_rwlock_writer_trylock (PRWLock *lock) +{ + if (P_UNLIKELY (lock == NULL)) + return FALSE; + + return pp_rwlock_start_write_try_func (lock); +} + +P_LIB_API pboolean +p_rwlock_writer_unlock (PRWLock *lock) +{ + if (P_UNLIKELY (lock == NULL)) + return FALSE; + + return pp_rwlock_end_write_func (lock); +} + +P_LIB_API void +p_rwlock_free (PRWLock *lock) +{ + if (P_UNLIKELY (lock == NULL)) + return; + + pp_rwlock_close_func (lock); + p_free (lock); +} + +void +p_rwlock_init (void) +{ + HMODULE hmodule; + + hmodule = GetModuleHandleA ("kernel32.dll"); + + if (P_UNLIKELY (hmodule == NULL)) { + P_ERROR ("PRWLock::p_rwlock_init: failed to load kernel32.dll module"); + return; + } + + pp_rwlock_vista_table.rwl_init = (InitializeSRWLockFunc) GetProcAddress (hmodule, + "InitializeSRWLock"); + + if (P_LIKELY (pp_rwlock_vista_table.rwl_init != NULL)) { + pp_rwlock_vista_table.rwl_excl_lock = (AcquireSRWLockExclusiveFunc) GetProcAddress (hmodule, + "AcquireSRWLockExclusive"); + pp_rwlock_vista_table.rwl_excl_lock_try = (TryAcquireSRWLockExclusiveFunc) GetProcAddress (hmodule, + "TryAcquireSRWLockExclusive"); + pp_rwlock_vista_table.rwl_excl_rel = (ReleaseSRWLockExclusiveFunc) GetProcAddress (hmodule, + "ReleaseSRWLockExclusive"); + pp_rwlock_vista_table.rwl_shr_lock = (AcquireSRWLockSharedFunc) GetProcAddress (hmodule, + "AcquireSRWLockShared"); + pp_rwlock_vista_table.rwl_shr_lock_try = (TryAcquireSRWLockSharedFunc) GetProcAddress (hmodule, + "TryAcquireSRWLockShared"); + pp_rwlock_vista_table.rwl_shr_rel = (ReleaseSRWLockSharedFunc) GetProcAddress (hmodule, + "ReleaseSRWLockShared"); + pp_rwlock_init_func = pp_rwlock_init_vista; + pp_rwlock_close_func = pp_rwlock_close_vista; + pp_rwlock_start_read_func = pp_rwlock_start_read_vista; + pp_rwlock_start_read_try_func = pp_rwlock_start_read_try_vista; + pp_rwlock_end_read_func = pp_rwlock_end_read_vista; + pp_rwlock_start_write_func = pp_rwlock_start_write_vista; + pp_rwlock_start_write_try_func = pp_rwlock_start_write_try_vista; + pp_rwlock_end_write_func = pp_rwlock_end_write_vista; + } else { + pp_rwlock_init_func = pp_rwlock_init_xp; + pp_rwlock_close_func = pp_rwlock_close_xp; + pp_rwlock_start_read_func = pp_rwlock_start_read_xp; + pp_rwlock_start_read_try_func = pp_rwlock_start_read_try_xp; + pp_rwlock_end_read_func = pp_rwlock_end_read_xp; + pp_rwlock_start_write_func = pp_rwlock_start_write_xp; + pp_rwlock_start_write_try_func = pp_rwlock_start_write_try_xp; + pp_rwlock_end_write_func = pp_rwlock_end_write_xp; + } +} + +void +p_rwlock_shutdown (void) +{ + memset (&pp_rwlock_vista_table, 0, sizeof (pp_rwlock_vista_table)); + + pp_rwlock_init_func = NULL; + pp_rwlock_close_func = NULL; + pp_rwlock_start_read_func = NULL; + pp_rwlock_start_read_try_func = NULL; + pp_rwlock_end_read_func = NULL; + pp_rwlock_start_write_func = NULL; + pp_rwlock_start_write_try_func = NULL; + pp_rwlock_end_write_func = NULL; +} diff --git a/3rdparty/plibsys/src/prwlock.h b/3rdparty/plibsys/src/prwlock.h new file mode 100644 index 0000000..9f61e4a --- /dev/null +++ b/3rdparty/plibsys/src/prwlock.h @@ -0,0 +1,185 @@ +/* + * The MIT License + * + * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * @file prwlock.h + * @brief Read-write lock + * @author Alexander Saprykin + * + * A read-write lock is a synchronization primitive which allows access to a + * critical section to not only the one thread, but instead it splits all + * threads into the two groups: + * - reader threads which perform only the reading operations without any shared + * data modifications; + * - writer threads which may perform the shared data modifitcations as well as + * its reading. + * + * When there are only the reader threads inside a critical section it is called + * a shared lock - actually you do not need any locking mechanism and all the + * threads share the lock. In this situation an arbitrary number of reader + * threads can perform shared data reading. + * + * The situation changes when a writer thread requests access to the same + * critical section. It will wait until all the current readers finish + * executing the critical section before acquiring the lock in exclusive manner: + * no one else can access the critical section until the writer finishes with + * it. Even another writer thread will have to wait for the lock to be released + * by the first writer before entering the critical section. + * + * To prevent writer startvation usually writers are in favor over readers, + * which is actually implementation dependent, though most operating systems try + * to follow this rule. + * + * A read-write lock is usually used when the writing operations are not + * performed too frequently, or when the number of reader threads is a way more + * than writer ones. + * + * A reader enters a critical section with p_rwlock_reader_lock() or + * p_rwlock_reader_trylock() and exits with p_rwlock_reader_unlock(). + * + * A writer enters the critical section with p_rwlock_writer_lock() or + * p_rwlock_writer_trylock() and exits with p_rwlock_writer_unlock(). + */ + +#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION) +# error "Header files shouldn't be included directly, consider using <plibsys.h> instead." +#endif + +#ifndef PLIBSYS_HEADER_PRWLOCK_H +#define PLIBSYS_HEADER_PRWLOCK_H + +#include <pmacros.h> +#include <ptypes.h> + +P_BEGIN_DECLS + +/** Read-write lock opaque data structure. */ +typedef struct PRWLock_ PRWLock; + +/** + * @brief Creates a new #PRWLock object. + * @return Pointer to a newly created #PRWLock object. + * @since 0.0.1 + */ +P_LIB_API PRWLock * p_rwlock_new (void); + +/** + * @brief Locks a read-write lock for reading. + * @param lock #PRWLock to lock. + * @return TRUE in case of success, FALSE otherwise. + * @since 0.0.1 + * @warning Do not lock a read-write lock recursively - it may lead to an + * application deadlock (implementation dependent). + * + * Forces the calling thread to sleep until the @a lock becomes available for + * locking. + */ +P_LIB_API pboolean p_rwlock_reader_lock (PRWLock *lock); + +/** + * @brief Tries to lock a read-write lock for reading immediately. + * @param lock #PRWLock to lock. + * @return TRUE in case of success, FALSE otherwise. + * @since 0.0.1 + * @warning Do not lock a read-write lock recursively - it may lead to an + * application deadlock (implementation dependent). + * + * Tries to lock the @a lock and returns immediately if it is not available for + * locking. + */ +P_LIB_API pboolean p_rwlock_reader_trylock (PRWLock *lock); + +/** + * @brief Releases a locked for reading read-write lock. + * @param lock #PRWLock to release. + * @return TRUE in case of success, FALSE otherwise. + * @since 0.0.1 + * @warning Do not use this function on non-locked read-write locks - behavior + * may be unpredictable. + * @warning Do not use this function to unlock a read-write lock which was + * locked for writing. + * + * If the @a lock was previously locked for reading then it becomes unlocked. + * + * It's implementation dependent whether only the same thread can lock and + * unlock the same read-write lock. + */ +P_LIB_API pboolean p_rwlock_reader_unlock (PRWLock *lock); + +/** + * @brief Locks a read-write lock for writing. + * @param lock #PRWLock to lock. + * @return TRUE in case of success, FALSE otherwise. + * @since 0.0.1 + * @warning Do not lock a read-write lock recursively - it may lead to an + * application deadlock (implementation dependent). + * + * Forces the calling thread to sleep until the @a lock becomes available for + * locking. + */ +P_LIB_API pboolean p_rwlock_writer_lock (PRWLock *lock); + +/** + * @brief Tries to lock a read-write lock immediately. + * @param lock #PRWLock to lock. + * @return TRUE in case of success, FALSE otherwise. + * @since 0.0.1 + * @warning Do not lock a read-write lock recursively - it may lead to an + * application deadlock (implementation dependent). + * + * Tries to lock the @a lock and returns immediately if it is not available for + * locking. + */ +P_LIB_API pboolean p_rwlock_writer_trylock (PRWLock *lock); + +/** + * @brief Releases a locked for writing read-write lock. + * @param lock #PRWLock to release. + * @return TRUE in case of success, FALSE otherwise. + * @since 0.0.1 + * @warning Do not use this function on non-locked read-write locks - behavior + * may be unpredictable. + * @warning Do not use this function to unlock a read-write lock which was + * locked for reading. + * + * If the @a lock was previously locked for writing then it becomes unlocked. + * + * It's implementation dependent whether only the same thread can lock and + * unlock the same read-write lock. + */ +P_LIB_API pboolean p_rwlock_writer_unlock (PRWLock *lock); + +/** + * @brief Frees a #PRWLock object. + * @param lock #PRWLock to free. + * @since 0.0.1 + * @warning It doesn't unlock the @a lock before freeing memory, so you should + * do it manually. + */ +P_LIB_API void p_rwlock_free (PRWLock *lock); + +P_END_DECLS + +#endif /* PLIBSYS_HEADER_PRWLOCK_H */ diff --git a/3rdparty/plibsys/src/psemaphore-amiga.c b/3rdparty/plibsys/src/psemaphore-amiga.c new file mode 100644 index 0000000..bff0acf --- /dev/null +++ b/3rdparty/plibsys/src/psemaphore-amiga.c @@ -0,0 +1,250 @@ +/* + * The MIT License + * + * Copyright (C) 2017-2018 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "perror.h" +#include "pmem.h" +#include "psemaphore.h" +#include "pipc-private.h" + +#include <stdlib.h> +#include <string.h> + +#include <exec/types.h> +#include <exec/semaphores.h> +#include <proto/exec.h> + +#define P_SEM_SUFFIX "_p_sem_object" +#define P_SEM_PRIV_SIZE (sizeof (psize)) + +struct PSemaphore_ { + struct SignalSemaphore *sem_shared; + pchar *platform_key; + pboolean is_owner; + PSemaphoreAccessMode mode; +}; + +static pboolean pp_semaphore_create_handle (PSemaphore *sem, PError **error); + +static pboolean +pp_semaphore_create_handle (PSemaphore *sem, + PError **error) +{ + struct SignalSemaphore *sem_sys; + psize name_len; + pchar *name; + + if (P_UNLIKELY (sem == NULL || sem->platform_key == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return FALSE; + } + + IExec->Forbid (); + + sem_sys = (struct SignalSemaphore *) IExec->FindSemaphore (sem->platform_key); + + if (sem_sys != NULL && sem->mode != P_SEM_ACCESS_CREATE) { + sem->sem_shared = sem_sys; + } else { + if (sem_sys != NULL && sem->mode == P_SEM_ACCESS_CREATE) { + IExec->RemSemaphore (sem_sys); + IExec->ObtainSemaphore (sem_sys); + IExec->ReleaseSemaphore (sem_sys); + + IExec->FreeVec (sem_sys->ss_Link.ln_Name); + IExec->FreeVec (((psize *) sem_sys) - 1); + } + + sem_sys = (struct SignalSemaphore *) IExec->AllocVecTags (sizeof (struct SignalSemaphore) + P_SEM_PRIV_SIZE, + AVT_Type, MEMF_SHARED, + AVT_ClearWithValue, 0, + TAG_END); + + if (P_UNLIKELY (sem_sys == NULL)) { + IExec->Permit (); + p_error_set_error_p (error, + (pint) P_ERROR_IPC_NO_RESOURCES, + 0, + "Failed to call AllocMem() to create semaphore"); + return FALSE; + } + + name_len = strlen (sem->platform_key); + name = (pchar *) IExec->AllocVecTags (name_len + 1, + AVT_ClearWithValue, 0, + TAG_END); + + if (P_UNLIKELY (name == NULL)) { + IExec->FreeVec (sem_sys); + IExec->Permit (); + p_error_set_error_p (error, + (pint) P_ERROR_IPC_NO_RESOURCES, + 0, + "Failed to call AllocMem() to create semaphore name"); + return FALSE; + } + + memcpy (name, sem->platform_key, name_len); + + /* Leave space in memory for counter */ + sem_sys = (struct SignalSemaphore *) (((psize *) sem_sys) + 1); + + sem_sys->ss_Link.ln_Name = name; + sem->sem_shared = sem_sys; + + /* Add to system list */ + IExec->AddSemaphore (sem_sys); + } + + *(((psize *) sem_sys) - 1) += 1; + + IExec->Permit (); + + return TRUE; +} + +P_LIB_API PSemaphore * +p_semaphore_new (const pchar *name, + pint init_val, + PSemaphoreAccessMode mode, + PError **error) +{ + PSemaphore *ret; + pchar *new_name; + + if (P_UNLIKELY (name == NULL || init_val < 0)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return NULL; + } + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PSemaphore))) == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_NO_RESOURCES, + 0, + "Failed to allocate memory for semaphore"); + return NULL; + } + + if (P_UNLIKELY ((new_name = p_malloc0 (strlen (name) + strlen (P_SEM_SUFFIX) + 1)) == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_NO_RESOURCES, + 0, + "Failed to allocate memory for semaphore"); + p_free (ret); + return NULL; + } + + strcpy (new_name, name); + strcat (new_name, P_SEM_SUFFIX); + + ret->platform_key = p_ipc_get_platform_key (new_name, FALSE); + ret->mode = mode; + + p_free (new_name); + + if (P_UNLIKELY (pp_semaphore_create_handle (ret, error) == FALSE)) { + p_semaphore_free (ret); + return NULL; + } + + return ret; +} + +P_LIB_API void +p_semaphore_take_ownership (PSemaphore *sem) +{ + if (P_UNLIKELY (sem == NULL)) + return; + + sem->is_owner = TRUE; +} + +P_LIB_API pboolean +p_semaphore_acquire (PSemaphore *sem, + PError **error) +{ + if (P_UNLIKELY (sem == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return FALSE; + } + + IExec->ObtainSemaphore (sem->sem_shared); + + return TRUE; +} + +P_LIB_API pboolean +p_semaphore_release (PSemaphore *sem, + PError **error) +{ + if (P_UNLIKELY (sem == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return FALSE; + } + + IExec->ReleaseSemaphore (sem->sem_shared); + + return TRUE; +} + +P_LIB_API void +p_semaphore_free (PSemaphore *sem) +{ + if (P_UNLIKELY (sem == NULL)) + return; + + if (P_UNLIKELY (sem->sem_shared != NULL)) { + IExec->Forbid (); + + *(((psize *) sem->sem_shared) - 1) -= 1; + + if (*(((psize *) sem->sem_shared) - 1) == 0 || sem->is_owner == TRUE) { + IExec->RemSemaphore (sem->sem_shared); + IExec->ObtainSemaphore (sem->sem_shared); + IExec->ReleaseSemaphore (sem->sem_shared); + + IExec->FreeVec (sem->sem_shared->ss_Link.ln_Name); + IExec->FreeVec (((psize *) sem->sem_shared) - 1); + } + + IExec->Permit (); + } + + if (P_LIKELY (sem->platform_key != NULL)) + p_free (sem->platform_key); + + p_free (sem); +} diff --git a/3rdparty/plibsys/src/psemaphore-none.c b/3rdparty/plibsys/src/psemaphore-none.c new file mode 100644 index 0000000..8744123 --- /dev/null +++ b/3rdparty/plibsys/src/psemaphore-none.c @@ -0,0 +1,91 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2018 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "psemaphore.h" + +#include <stdlib.h> + +struct PSemaphore_ { + pint hdl; +}; + +P_LIB_API PSemaphore * +p_semaphore_new (const pchar *name, + pint init_val, + PSemaphoreAccessMode mode, + PError **error) +{ + P_UNUSED (name); + P_UNUSED (init_val); + P_UNUSED (mode); + + p_error_set_error_p (error, + (pint) P_ERROR_IPC_NOT_IMPLEMENTED, + 0, + "No semaphore implementation"); + + return NULL; +} + +P_LIB_API void +p_semaphore_take_ownership (PSemaphore *sem) +{ + P_UNUSED (sem); +} + +P_LIB_API pboolean +p_semaphore_acquire (PSemaphore *sem, + PError **error) +{ + P_UNUSED (sem); + + p_error_set_error_p (error, + (pint) P_ERROR_IPC_NOT_IMPLEMENTED, + 0, + "No semaphore implementation"); + + return FALSE; +} + +P_LIB_API pboolean +p_semaphore_release (PSemaphore *sem, + PError **error) +{ + P_UNUSED (sem); + + p_error_set_error_p (error, + (pint) P_ERROR_IPC_NOT_IMPLEMENTED, + 0, + "No semaphore implementation"); + + return FALSE; +} + +P_LIB_API void +p_semaphore_free (PSemaphore *sem) +{ + P_UNUSED (sem); +} + diff --git a/3rdparty/plibsys/src/psemaphore-os2.c b/3rdparty/plibsys/src/psemaphore-os2.c new file mode 100644 index 0000000..72cc7d3 --- /dev/null +++ b/3rdparty/plibsys/src/psemaphore-os2.c @@ -0,0 +1,91 @@ +/* + * The MIT License + * + * Copyright (C) 2017-2018 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "psemaphore.h" + +#include <stdlib.h> + +struct PSemaphore_ { + pint hdl; +}; + +P_LIB_API PSemaphore * +p_semaphore_new (const pchar *name, + pint init_val, + PSemaphoreAccessMode mode, + PError **error) +{ + P_UNUSED (name); + P_UNUSED (init_val); + P_UNUSED (mode); + + p_error_set_error_p (error, + (pint) P_ERROR_IPC_NOT_IMPLEMENTED, + 0, + "No semaphore implementation"); + + return NULL; +} + +P_LIB_API void +p_semaphore_take_ownership (PSemaphore *sem) +{ + P_UNUSED (sem); +} + +P_LIB_API pboolean +p_semaphore_acquire (PSemaphore *sem, + PError **error) +{ + P_UNUSED (sem); + + p_error_set_error_p (error, + (pint) P_ERROR_IPC_NOT_IMPLEMENTED, + 0, + "No semaphore implementation"); + + return FALSE; +} + +P_LIB_API pboolean +p_semaphore_release (PSemaphore *sem, + PError **error) +{ + P_UNUSED (sem); + + p_error_set_error_p (error, + (pint) P_ERROR_IPC_NOT_IMPLEMENTED, + 0, + "No semaphore implementation"); + + return FALSE; +} + +P_LIB_API void +p_semaphore_free (PSemaphore *sem) +{ + P_UNUSED (sem); +} + diff --git a/3rdparty/plibsys/src/psemaphore-posix.c b/3rdparty/plibsys/src/psemaphore-posix.c new file mode 100644 index 0000000..f98a045 --- /dev/null +++ b/3rdparty/plibsys/src/psemaphore-posix.c @@ -0,0 +1,272 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2018 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "perror.h" +#include "pmem.h" +#include "psemaphore.h" +#include "perror-private.h" +#include "pipc-private.h" + +#include <stdlib.h> +#include <string.h> + +#include <unistd.h> +#include <fcntl.h> +#include <semaphore.h> +#include <errno.h> + +#define P_SEM_SUFFIX "_p_sem_object" + +typedef sem_t psem_hdl; + +/* On some HP-UX versions it may not be defined */ +#ifndef SEM_FAILED +# define SEM_FAILED ((sem_t *) -1) +#endif + +#ifdef P_OS_SOLARIS +# define P_SEM_INVALID_HDL (sem_t *) -1 +#else +# define P_SEM_INVALID_HDL SEM_FAILED +#endif + +struct PSemaphore_ { + pboolean sem_created; + pchar *platform_key; +#if defined (P_OS_VMS) && (PLIBSYS_SIZEOF_VOID_P == 4) +# pragma __pointer_size 64 +#endif + psem_hdl *sem_hdl; +#if defined (P_OS_VMS) && (PLIBSYS_SIZEOF_VOID_P == 4) +# pragma __pointer_size 32 +#endif + PSemaphoreAccessMode mode; + pint init_val; +}; + +static pboolean pp_semaphore_create_handle (PSemaphore *sem, PError **error); +static void pp_semaphore_clean_handle (PSemaphore *sem); + +static pboolean +pp_semaphore_create_handle (PSemaphore *sem, + PError **error) +{ + pint init_val; + + if (P_UNLIKELY (sem == NULL || sem->platform_key == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return FALSE; + } + + init_val = sem->init_val; + + /* Solaris may interrupt sem_open() call */ + while ((sem->sem_hdl = sem_open (sem->platform_key, + O_CREAT | O_EXCL, + 0660, + init_val)) == P_SEM_INVALID_HDL && + p_error_get_last_system () == EINTR) + ; + + if (sem->sem_hdl == P_SEM_INVALID_HDL) { + if (p_error_get_last_system () == EEXIST) { + if (sem->mode == P_SEM_ACCESS_CREATE) + sem_unlink (sem->platform_key); + else + init_val = 0; + + while ((sem->sem_hdl = sem_open (sem->platform_key, + 0, + 0, + init_val)) == P_SEM_INVALID_HDL && + p_error_get_last_system () == EINTR) + ; + } + } else + sem->sem_created = TRUE; + + if (P_UNLIKELY (sem->sem_hdl == P_SEM_INVALID_HDL)) { + p_error_set_error_p (error, + (pint) p_error_get_last_ipc (), + p_error_get_last_system (), + "Failed to call sem_open() to create semaphore"); + pp_semaphore_clean_handle (sem); + return FALSE; + } + + return TRUE; +} + +static void +pp_semaphore_clean_handle (PSemaphore *sem) +{ + if (P_UNLIKELY (sem->sem_hdl != P_SEM_INVALID_HDL && + sem_close (sem->sem_hdl) == -1)) + P_ERROR ("PSemaphore::pp_semaphore_clean_handle: sem_close() failed"); + + if (sem->sem_hdl != P_SEM_INVALID_HDL && + sem->sem_created == TRUE && + sem_unlink (sem->platform_key) == -1) + P_ERROR ("PSemaphore::pp_semaphore_clean_handle: sem_unlink() failed"); + + sem->sem_created = FALSE; + sem->sem_hdl = P_SEM_INVALID_HDL; +} + +P_LIB_API PSemaphore * +p_semaphore_new (const pchar *name, + pint init_val, + PSemaphoreAccessMode mode, + PError **error) +{ + PSemaphore *ret; + pchar *new_name; + + if (P_UNLIKELY (name == NULL || init_val < 0)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return NULL; + } + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PSemaphore))) == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_NO_RESOURCES, + 0, + "Failed to allocate memory for semaphore"); + return NULL; + } + + if (P_UNLIKELY ((new_name = p_malloc0 (strlen (name) + strlen (P_SEM_SUFFIX) + 1)) == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_NO_RESOURCES, + 0, + "Failed to allocate memory for semaphore"); + p_free (ret); + return NULL; + } + + strcpy (new_name, name); + strcat (new_name, P_SEM_SUFFIX); + +#if defined (P_OS_IRIX) || defined (P_OS_TRU64) + /* IRIX and Tru64 prefer filename styled IPC names */ + ret->platform_key = p_ipc_get_platform_key (new_name, FALSE); +#else + ret->platform_key = p_ipc_get_platform_key (new_name, TRUE); +#endif + ret->init_val = init_val; + ret->mode = mode; + + p_free (new_name); + + if (P_UNLIKELY (pp_semaphore_create_handle (ret, error) == FALSE)) { + p_semaphore_free (ret); + return NULL; + } + + return ret; +} + +P_LIB_API void +p_semaphore_take_ownership (PSemaphore *sem) +{ + if (P_UNLIKELY (sem == NULL)) + return; + + sem->sem_created = TRUE; +} + +P_LIB_API pboolean +p_semaphore_acquire (PSemaphore *sem, + PError **error) +{ + pboolean ret; + pint res; + + if (P_UNLIKELY (sem == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return FALSE; + } + + while ((res = sem_wait (sem->sem_hdl)) == -1 && p_error_get_last_system () == EINTR) + ; + + ret = (res == 0); + + if (P_UNLIKELY (ret == FALSE)) + p_error_set_error_p (error, + (pint) p_error_get_last_ipc (), + p_error_get_last_system (), + "Failed to call sem_wait() on semaphore"); + + return ret; +} + +P_LIB_API pboolean +p_semaphore_release (PSemaphore *sem, + PError **error) +{ + pboolean ret; + + if (P_UNLIKELY (sem == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return FALSE; + } + + ret = (sem_post (sem->sem_hdl) == 0); + + if (P_UNLIKELY (ret == FALSE)) + p_error_set_error_p (error, + (pint) p_error_get_last_ipc (), + p_error_get_last_system (), + "Failed to call sem_post() on semaphore"); + + return ret; +} + +P_LIB_API void +p_semaphore_free (PSemaphore *sem) +{ + if (P_UNLIKELY (sem == NULL)) + return; + + pp_semaphore_clean_handle (sem); + + if (P_LIKELY (sem->platform_key != NULL)) + p_free (sem->platform_key); + + p_free (sem); +} diff --git a/3rdparty/plibsys/src/psemaphore-sysv.c b/3rdparty/plibsys/src/psemaphore-sysv.c new file mode 100644 index 0000000..34b33fc --- /dev/null +++ b/3rdparty/plibsys/src/psemaphore-sysv.c @@ -0,0 +1,319 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2018 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "perror.h" +#include "pmem.h" +#include "psemaphore.h" +#include "perror-private.h" +#include "pipc-private.h" + +#include <stdlib.h> +#include <string.h> + +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/sem.h> +#include <sys/types.h> +#include <sys/ipc.h> + +#define P_SEM_SUFFIX "_p_sem_object" +#define P_SEM_INVALID_HDL -1 + +struct sembuf sem_lock = {0, -1, SEM_UNDO}; +struct sembuf sem_unlock = {0, 1, SEM_UNDO}; + +typedef union p_semun_ { + pint val; + struct semid_ds *buf; + pushort *array; +} p_semun; + +typedef int psem_hdl; + +struct PSemaphore_ { + pboolean file_created; + pboolean sem_created; + key_t unix_key; + pchar *platform_key; + psem_hdl sem_hdl; + PSemaphoreAccessMode mode; + pint init_val; +}; + +static pboolean pp_semaphore_create_handle (PSemaphore *sem, PError **error); +static void pp_semaphore_clean_handle (PSemaphore *sem); + +static pboolean +pp_semaphore_create_handle (PSemaphore *sem, PError **error) +{ + pint built; + p_semun semun_op; + + if (P_UNLIKELY (sem == NULL || sem->platform_key == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return FALSE; + } + + if (P_UNLIKELY ((built = p_ipc_unix_create_key_file (sem->platform_key)) == -1)) { + p_error_set_error_p (error, + (pint) p_error_get_last_ipc (), + p_error_get_last_system (), + "Failed to create key file"); + pp_semaphore_clean_handle (sem); + return FALSE; + } else if (built == 0) + sem->file_created = TRUE; + + if (P_UNLIKELY ((sem->unix_key = p_ipc_unix_get_ftok_key (sem->platform_key)) == -1)) { + p_error_set_error_p (error, + (pint) p_error_get_last_ipc (), + p_error_get_last_system (), + "Failed to get unique IPC key"); + pp_semaphore_clean_handle (sem); + return FALSE; + } + + if ((sem->sem_hdl = semget (sem->unix_key, + 1, + IPC_CREAT | IPC_EXCL | 0660)) == P_SEM_INVALID_HDL) { + if (p_error_get_last_system () == EEXIST) + sem->sem_hdl = semget (sem->unix_key, 1, 0660); + } else { + sem->sem_created = TRUE; + + /* Maybe key file left after the crash, so take it */ + sem->file_created = (built == 1); + } + + if (P_UNLIKELY (sem->sem_hdl == P_SEM_INVALID_HDL)) { + p_error_set_error_p (error, + (pint) p_error_get_last_ipc (), + p_error_get_last_system (), + "Failed to call semget() to create semaphore"); + pp_semaphore_clean_handle (sem); + return FALSE; + } + + if (sem->sem_created == TRUE || sem->mode == P_SEM_ACCESS_CREATE) { + semun_op.val = sem->init_val; + + if (P_UNLIKELY (semctl (sem->sem_hdl, 0, SETVAL, semun_op) == -1)) { + p_error_set_error_p (error, + (pint) p_error_get_last_ipc (), + p_error_get_last_system (), + "Failed to set semaphore initial value with semctl()"); + pp_semaphore_clean_handle (sem); + return FALSE; + } + } + + return TRUE; +} + +static void +pp_semaphore_clean_handle (PSemaphore *sem) +{ + if (sem->sem_hdl != P_SEM_INVALID_HDL && + sem->sem_created == TRUE && + semctl (sem->sem_hdl, 0, IPC_RMID) == -1) + P_ERROR ("PSemaphore::pp_semaphore_clean_handle: semctl() with IPC_RMID failed"); + + if (sem->file_created == TRUE && + sem->platform_key != NULL && + unlink (sem->platform_key) == -1) + P_ERROR ("PSemaphore::pp_semaphore_clean_handle: unlink() failed"); + + sem->file_created = FALSE; + sem->sem_created = FALSE; + sem->unix_key = -1; + sem->sem_hdl = P_SEM_INVALID_HDL; +} + +P_LIB_API PSemaphore * +p_semaphore_new (const pchar *name, + pint init_val, + PSemaphoreAccessMode mode, + PError **error) +{ + PSemaphore *ret; + pchar *new_name; + + if (P_UNLIKELY (name == NULL || init_val < 0)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return NULL; + } + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PSemaphore))) == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_NO_RESOURCES, + 0, + "Failed to allocate memory for semaphore"); + return NULL; + } + + if (P_UNLIKELY ((new_name = p_malloc0 (strlen (name) + strlen (P_SEM_SUFFIX) + 1)) == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_NO_RESOURCES, + 0, + "Failed to allocate memory for semaphore"); + p_free (ret); + return NULL; + } + + strcpy (new_name, name); + strcat (new_name, P_SEM_SUFFIX); + + ret->platform_key = p_ipc_get_platform_key (new_name, FALSE); + ret->init_val = init_val; + ret->mode = mode; + + p_free (new_name); + + if (P_UNLIKELY (pp_semaphore_create_handle (ret, error) == FALSE)) { + p_semaphore_free (ret); + return NULL; + } + + return ret; +} + +P_LIB_API void +p_semaphore_take_ownership (PSemaphore *sem) +{ + if (P_UNLIKELY (sem == NULL)) + return; + + sem->sem_created = TRUE; +} + +P_LIB_API pboolean +p_semaphore_acquire (PSemaphore *sem, + PError **error) +{ + pboolean ret; + pint res; + + if (P_UNLIKELY (sem == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return FALSE; + } + + while ((res = semop (sem->sem_hdl, &sem_lock, 1)) == -1 && + p_error_get_last_system () == EINTR) + ; + + ret = (res == 0); + + if (P_UNLIKELY (ret == FALSE && + (p_error_get_last_system () == EIDRM || + p_error_get_last_system () == EINVAL))) { + P_WARNING ("PSemaphore::p_semaphore_acquire: trying to recreate"); + pp_semaphore_clean_handle (sem); + + if (P_UNLIKELY (pp_semaphore_create_handle (sem, error) == FALSE)) + return FALSE; + + while ((res = semop (sem->sem_hdl, &sem_lock, 1)) == -1 && + p_error_get_last_system () == EINTR) + ; + + ret = (res == 0); + } + + if (P_UNLIKELY (ret == FALSE)) + p_error_set_error_p (error, + (pint) p_error_get_last_ipc (), + p_error_get_last_system (), + "Failed to call semop() on semaphore"); + + return ret; +} + +P_LIB_API pboolean +p_semaphore_release (PSemaphore *sem, + PError **error) +{ + pboolean ret; + pint res; + + if (P_UNLIKELY (sem == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return FALSE; + } + + while ((res = semop (sem->sem_hdl, &sem_unlock, 1)) == -1 && + p_error_get_last_system () == EINTR) + ; + + ret = (res == 0); + + if (P_UNLIKELY (ret == FALSE && + (p_error_get_last_system () == EIDRM || + p_error_get_last_system () == EINVAL))) { + P_WARNING ("PSemaphore::p_semaphore_release: trying to recreate"); + pp_semaphore_clean_handle (sem); + + if (P_UNLIKELY (pp_semaphore_create_handle (sem, error) == FALSE)) + return FALSE; + + return TRUE; + } + + if (P_UNLIKELY (ret == FALSE)) + p_error_set_error_p (error, + (pint) p_error_get_last_ipc (), + p_error_get_last_system (), + "Failed to call semop() on semaphore"); + + return ret; +} + +P_LIB_API void +p_semaphore_free (PSemaphore *sem) +{ + if (P_UNLIKELY (sem == NULL)) + return; + + pp_semaphore_clean_handle (sem); + + if (P_LIKELY (sem->platform_key != NULL)) + p_free (sem->platform_key); + + p_free (sem); +} diff --git a/3rdparty/plibsys/src/psemaphore-win.c b/3rdparty/plibsys/src/psemaphore-win.c new file mode 100644 index 0000000..228c054 --- /dev/null +++ b/3rdparty/plibsys/src/psemaphore-win.c @@ -0,0 +1,204 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2018 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "perror.h" +#include "pmem.h" +#include "psemaphore.h" +#include "perror-private.h" +#include "pipc-private.h" + +#include <stdlib.h> +#include <string.h> + +#define P_SEM_SUFFIX "_p_sem_object" +#define P_SEM_INVALID_HDL NULL + +typedef HANDLE psem_hdl; + +struct PSemaphore_ { + pchar *platform_key; + psem_hdl sem_hdl; + pint init_val; +}; + +static pboolean pp_semaphore_create_handle (PSemaphore *sem, PError **error); +static void pp_semaphore_clean_handle (PSemaphore *sem); + +static pboolean +pp_semaphore_create_handle (PSemaphore *sem, PError **error) +{ + if (P_UNLIKELY (sem == NULL || sem->platform_key == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return FALSE; + } + + /* Multibyte character set must be enabled */ + if (P_UNLIKELY ((sem->sem_hdl = CreateSemaphoreA (NULL, + sem->init_val, + MAXLONG, + sem->platform_key)) == P_SEM_INVALID_HDL)) { + p_error_set_error_p (error, + (pint) p_error_get_last_ipc (), + p_error_get_last_system (), + "Failed to call CreateSemaphore() to create semaphore"); + return FALSE; + } + + return TRUE; +} + +static void +pp_semaphore_clean_handle (PSemaphore *sem) +{ + if (P_UNLIKELY (sem->sem_hdl != P_SEM_INVALID_HDL && CloseHandle (sem->sem_hdl) == 0)) + P_ERROR ("PSemaphore::pp_semaphore_clean_handle: CloseHandle() failed"); + + sem->sem_hdl = P_SEM_INVALID_HDL; +} + +P_LIB_API PSemaphore * +p_semaphore_new (const pchar *name, + pint init_val, + PSemaphoreAccessMode mode, + PError **error) +{ + PSemaphore *ret; + pchar *new_name; + + P_UNUSED (mode); + + if (P_UNLIKELY (name == NULL || init_val < 0)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return NULL; + } + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PSemaphore))) == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_NO_RESOURCES, + 0, + "Failed to allocate memory for semaphore"); + return NULL; + } + + if (P_UNLIKELY ((new_name = p_malloc0 (strlen (name) + strlen (P_SEM_SUFFIX) + 1)) == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_NO_RESOURCES, + 0, + "Failed to allocate memory for semaphore"); + p_free (ret); + return NULL; + } + + strcpy (new_name, name); + strcpy (new_name, P_SEM_SUFFIX); + + ret->platform_key = p_ipc_get_platform_key (new_name, FALSE); + ret->init_val = init_val; + + p_free (new_name); + + if (P_UNLIKELY (pp_semaphore_create_handle (ret, error) == FALSE)) { + p_semaphore_free (ret); + return NULL; + } + + return ret; +} + +P_LIB_API void +p_semaphore_take_ownership (PSemaphore *sem) +{ + P_UNUSED (sem); +} + +P_LIB_API pboolean +p_semaphore_acquire (PSemaphore *sem, + PError **error) +{ + pboolean ret; + + if (P_UNLIKELY (sem == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return FALSE; + } + + ret = (WaitForSingleObject (sem->sem_hdl, INFINITE) == WAIT_OBJECT_0) ? TRUE : FALSE; + + if (P_UNLIKELY (ret == FALSE)) + p_error_set_error_p (error, + (pint) p_error_get_last_ipc (), + p_error_get_last_system (), + "Failed to call WaitForSingleObject() on semaphore"); + + return ret; +} + +P_LIB_API pboolean +p_semaphore_release (PSemaphore *sem, + PError **error) +{ + pboolean ret; + + if (P_UNLIKELY (sem == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return FALSE; + } + + ret = ReleaseSemaphore (sem->sem_hdl, 1, NULL) ? TRUE : FALSE; + + if (P_UNLIKELY (ret == FALSE)) + p_error_set_error_p (error, + (pint) p_error_get_last_ipc (), + p_error_get_last_system (), + "Failed to call ReleaseSemaphore() on semaphore"); + + return ret; +} + +P_LIB_API void +p_semaphore_free (PSemaphore *sem) +{ + if (P_UNLIKELY (sem == NULL)) + return; + + pp_semaphore_clean_handle (sem); + + if (P_LIKELY (sem->platform_key != NULL)) + p_free (sem->platform_key); + + p_free (sem); +} diff --git a/3rdparty/plibsys/src/psemaphore.h b/3rdparty/plibsys/src/psemaphore.h new file mode 100644 index 0000000..29fb48e --- /dev/null +++ b/3rdparty/plibsys/src/psemaphore.h @@ -0,0 +1,191 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2017 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * @file psemaphore.h + * @brief Semaphore routines + * @author Alexander Saprykin + * + * A semaphore is a synchronization primitive which controls access to shared + * data from the concurrently running threads. Unlike a mutex (which is a + * particular case of a binary semaphore) it allows concurrent access not to + * only the one thread. + * + * The semaphore has a counter which means the number of available resources + * (units). Before entering a critical section a thread must perform the so + * called P (acquire) operation: if the counter is positive it decrements the + * counter by 1 and continues execution; otherwise the thread is suspended until + * the counter becomes positive. Before leaving the critical section the thread + * must perform the so called V (release) operation: increments the counter by 1 + * and wakes up a waiting thread from the queue (if any). + * + * You can think about the semaphore as a resource controller: the P operation + * takes one unit, while the V operation gives one unit back. The thread could + * not continue execution without taking a resource unit. By setting the initial + * semaphore counter value you can control how much concurrent threads can work + * with a shared resource. + * + * This semaphore implementation is process-wide so you can synchronize not only + * the threads. But it makes this IPC primitive (actually like any other IPC + * primitive, as well) relatively heavy. Consider using a mutex or a spinlock + * instead if you do not need to cross a process boundary. + * + * A process-wide semaphore is identified by its name across the system, thus it + * is also called a named semaphore. Use p_semaphore_new() to open the named + * semaphore and p_semaphore_free() to close it. + * + * Please note the following platform specific differences: + * + * - Windows doesn't own IPC objects (processes own them), which means that a + * semaphore will be removed from the system after the last process or thread + * closes it (or after terminating all the processes and threads holding open + * semaphore). + * + * - UNIX systems own IPC objects. Because of that UNIX IPC objects can survive + * an application crash: an already used semaphore can be opened in a locked + * state and an application can fail into a deadlock or an inconsistent state. + * This could happen if you have not closed all the open semaphores explicitly + * before terminating the application. + * + * - IRIX allows to open several instances of a semaphore within the single + * process, but it will close the object after the first close call from any of + * the threads within the process. + * + * - AmigaOS has process-wide semaphores without actual tracking of counter, + * which means that semaphore behaves the same way as recursive mutex. + * + * - OpenVMS (as of 8.4 release) has declared prototypes for process-wide named + * semaphores but the actual implementation is broken. + * + * - OS/2 lacks support for process-wide named semaphores. + * + * - Syllable lacks support for process-wide named semaphores. + * + * - BeOS lacks support for process-wide named semaphores. + * + * Use the third argument as #P_SEM_ACCESS_CREATE in p_semaphore_new() to reset + * a semaphore value while opening it. This argument is ignored on Windows. You + * can also take ownership of the semaphore with p_semaphore_take_ownership() to + * explicitly remove it from the system after closing. + */ + +#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION) +# error "Header files shouldn't be included directly, consider using <plibsys.h> instead." +#endif + +#ifndef PLIBSYS_HEADER_PSEMAPHORE_H +#define PLIBSYS_HEADER_PSEMAPHORE_H + +#include <pmacros.h> +#include <ptypes.h> +#include <perror.h> + +P_BEGIN_DECLS + +/** Enum with semaphore creation modes. */ +typedef enum PSemaphoreAccessMode_ { + P_SEM_ACCESS_OPEN = 0, /**< Opens an existing semaphore or creates one with a given value. */ + P_SEM_ACCESS_CREATE = 1 /**< Creates semaphore, resets to a given value if exists. */ +} PSemaphoreAccessMode; + +/** Semaphore opaque data structure. */ +typedef struct PSemaphore_ PSemaphore; + +/** + * @brief Creates a new #PSemaphore object. + * @param name Semaphore name. + * @param init_val Initial semaphore value. + * @param mode Creation mode. + * @param[out] error Error report object, NULL to ignore. + * @return Pointer to a newly created #PSemaphore object in case of success, + * NULL otherwise. + * @since 0.0.1 + * + * The @a init_val is used only in one of following cases: a semaphore with the + * such name doesn't exist, or the semaphore with the such name exists but + * @a mode specified as #P_SEM_ACCESS_CREATE (non-Windows platforms only). In + * other cases @a init_val is ignored. The @a name is system-wide, so any other + * process can open that semaphore passing the same name. + */ +P_LIB_API PSemaphore * p_semaphore_new (const pchar *name, + pint init_val, + PSemaphoreAccessMode mode, + PError **error); + +/** + * @brief Takes ownership of a semaphore. + * @param sem Semaphore to take ownership. + * @since 0.0.1 + * + * If you take ownership of a semaphore object, p_semaphore_free() will try to + * completely unlink it and remove from the system. This is useful on UNIX + * systems where the semaphore can survive an application crash. On the Windows + * platform this call has no effect. + * + * The common usage of this call is upon application startup to ensure that the + * semaphore from the previous crash will be unlinked from the system. To do + * that, call p_semaphore_new(), take ownership of the semaphore object and + * remove it with the p_semaphore_free() call. After that, create it again. + * + * You can also do the same thing upon semaphore creation passing + * #P_SEM_ACCESS_CREATE to p_semaphore_new(). The only difference is that you + * should already know whether this semaphore object is from the previous crash + * or not. + */ +P_LIB_API void p_semaphore_take_ownership (PSemaphore *sem); + +/** + * @brief Acquires (P operation) a semaphore. + * @param sem #PSemaphore to acquire. + * @param[out] error Error report object, NULL to ignore. + * @return TRUE in case of success, FALSE otherwise. + * @since 0.0.1 + */ +P_LIB_API pboolean p_semaphore_acquire (PSemaphore *sem, + PError **error); + +/** + * @brief Releases (V operation) a semaphore. + * @param sem #PSemaphore to release. + * @param[out] error Error report object, NULL to ignore. + * @return TRUE in case of success, FALSE otherwise. + * @since 0.0.1 + */ +P_LIB_API pboolean p_semaphore_release (PSemaphore *sem, + PError **error); + +/** + * @brief Frees #PSemaphore object. + * @param sem #PSemaphore to free. + * @since 0.0.1 + * + * It doesn't release an acquired semaphore, be careful to not to make a + * deadlock while removing the acquired semaphore. + */ +P_LIB_API void p_semaphore_free (PSemaphore *sem); + +P_END_DECLS + +#endif /* PLIBSYS_HEADER_PSEMAPHORE_H */ diff --git a/3rdparty/plibsys/src/pshm-amiga.c b/3rdparty/plibsys/src/pshm-amiga.c new file mode 100644 index 0000000..4010ea2 --- /dev/null +++ b/3rdparty/plibsys/src/pshm-amiga.c @@ -0,0 +1,274 @@ +/* + * The MIT License + * + * Copyright (C) 2018 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "perror.h" +#include "pmem.h" +#include "psemaphore.h" +#include "pshm.h" +#include "pipc-private.h" + +#include <string.h> + +#include <exec/types.h> +#include <proto/exec.h> + +#define P_SHM_NAMESPACE "pshm" +#define P_SHM_SUFFIX "_p_shm_object" +#define P_SHM_PRIV_SIZE (2 * sizeof (psize)) + +struct PShm_ { + pboolean is_owner; + pchar *platform_key; + ppointer addr; + psize size; + PSemaphore *sem; + PShmAccessPerms perms; +}; + +static PErrorIPC pp_shm_get_ipc_error (puint32 err_code); +static pboolean pp_shm_create_handle (PShm *shm, PError **error); + +static PErrorIPC +pp_shm_get_ipc_error (puint32 err_code) +{ + if (err_code == ANMERROR_NOERROR) + return P_ERROR_IPC_NONE; + else if (err_code == ANMERROR_NOMEMORY) + return P_ERROR_IPC_NO_RESOURCES; + else if (err_code == ANMERROR_DUPLICATENAME) + return P_ERROR_IPC_EXISTS; + else if (err_code == ANMERROR_PARAMETER) + return P_ERROR_IPC_INVALID_ARGUMENT; + else + return P_ERROR_IPC_FAILED; +} + +static pboolean +pp_shm_create_handle (PShm *shm, + PError **error) +{ + ppointer mem_area; + puint32 err_code; + pboolean is_exists; + + if (P_UNLIKELY (shm == NULL || shm->platform_key == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return FALSE; + } + + IExec->Forbid (); + + mem_area = IExec->FindNamedMemory (P_SHM_NAMESPACE, shm->platform_key); + + if (mem_area == NULL) { + mem_area = IExec->AllocNamedMemoryTags (shm->size + P_SHM_PRIV_SIZE, + P_SHM_NAMESPACE, + shm->platform_key, + ANMT_Error, &err_code, + TAG_END); + + if (P_UNLIKELY (mem_area == NULL)) { + IExec->Permit (); + p_error_set_error_p (error, + (pint) pp_shm_get_ipc_error (err_code), + (pint) err_code, + "Failed to call AllocNamedMemoryTags() to create memory segment"); + return FALSE; + } + + memset (mem_area, 0, shm->size + P_SHM_PRIV_SIZE); + + /* Set size and counter */ + *((psize *) mem_area) = shm->size; + *((psize *) mem_area + 1) = 1; + + is_exists = FALSE; + } else { + *((psize *) mem_area + 1) += 1; + + shm->size = *((psize *) mem_area); + is_exists = TRUE; + } + + shm->addr = ((pchar *) mem_area) + P_SHM_PRIV_SIZE; + + if (P_UNLIKELY ((shm->sem = p_semaphore_new (shm->platform_key, 1, + is_exists ? P_SEM_ACCESS_OPEN : P_SEM_ACCESS_CREATE, + error)) == NULL)) { + IExec->FreeNamedMemory (P_SHM_NAMESPACE, shm->platform_key); + IExec->Permit (); + return FALSE; + } + + IExec->Permit (); + + return TRUE; +} + +P_LIB_API PShm * +p_shm_new (const pchar *name, + psize size, + PShmAccessPerms perms, + PError **error) +{ + PShm *ret; + pchar *new_name; + + if (P_UNLIKELY (name == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return NULL; + } + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PShm))) == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_NO_RESOURCES, + 0, + "Failed to allocate memory for shared segment"); + return NULL; + } + + if (P_UNLIKELY ((new_name = p_malloc0 (strlen (name) + strlen (P_SHM_SUFFIX) + 1)) == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_NO_RESOURCES, + 0, + "Failed to allocate memory for segment name"); + p_shm_free (ret); + return NULL; + } + + strcpy (new_name, name); + strcat (new_name, P_SHM_SUFFIX); + + ret->platform_key = p_ipc_get_platform_key (new_name, FALSE); + ret->perms = perms; + ret->size = size; + + p_free (new_name); + + if (P_UNLIKELY (pp_shm_create_handle (ret, error) == FALSE)) { + p_shm_free (ret); + return NULL; + } + + if (P_LIKELY (ret->size > size && size != 0)) + ret->size = size; + + return ret; +} + +P_LIB_API void +p_shm_take_ownership (PShm *shm) +{ + if (P_UNLIKELY (shm == NULL)) + return; + + shm->is_owner = TRUE; + p_semaphore_take_ownership (shm->sem); +} + +P_LIB_API void +p_shm_free (PShm *shm) +{ + if (P_UNLIKELY (shm == NULL)) + return; + + if (shm->addr != NULL) { + IExec->Forbid (); + + *((psize *) shm->addr - 1) -= 1; + + if (shm->is_owner || *((psize *) shm->addr - 1) == 0) { + p_semaphore_free (shm->sem); + shm->sem = NULL; + + IExec->FreeNamedMemory (P_SHM_NAMESPACE, shm->platform_key); + } + + IExec->Permit (); + } + + shm->is_owner = FALSE; + shm->addr = NULL; + shm->size = 0; + + if (P_LIKELY (shm->platform_key != NULL)) + p_free (shm->platform_key); + + p_free (shm); +} + +P_LIB_API pboolean +p_shm_lock (PShm *shm, + PError **error) +{ + if (P_UNLIKELY (shm == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return FALSE; + } + + return p_semaphore_acquire (shm->sem, error); +} + +P_LIB_API pboolean +p_shm_unlock (PShm *shm, + PError **error) +{ + if (P_UNLIKELY (shm == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return FALSE; + } + + return p_semaphore_release (shm->sem, error); +} + +P_LIB_API ppointer +p_shm_get_address (const PShm *shm) +{ + if (P_UNLIKELY (shm == NULL)) + return NULL; + + return shm->addr; +} + +P_LIB_API psize +p_shm_get_size (const PShm *shm) +{ + if (P_UNLIKELY (shm == NULL)) + return 0; + + return shm->size; +} diff --git a/3rdparty/plibsys/src/pshm-none.c b/3rdparty/plibsys/src/pshm-none.c new file mode 100644 index 0000000..17cf4a5 --- /dev/null +++ b/3rdparty/plibsys/src/pshm-none.c @@ -0,0 +1,94 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pshm.h" + +#include <stdlib.h> + +struct PShm_ { + pint hdl; +}; + +P_LIB_API PShm * +p_shm_new (const pchar *name, + psize size, + PShmAccessPerms perms, + PError **error) +{ + P_UNUSED (name); + P_UNUSED (size); + P_UNUSED (perms); + P_UNUSED (error); + + return NULL; +} + +P_LIB_API void +p_shm_take_ownership (PShm *shm) +{ + P_UNUSED (shm); +} + +P_LIB_API void +p_shm_free (PShm *shm) +{ + P_UNUSED (shm); +} + +P_LIB_API pboolean +p_shm_lock (PShm *shm, + PError **error) +{ + P_UNUSED (shm); + P_UNUSED (error); + + return FALSE; +} + +P_LIB_API pboolean +p_shm_unlock (PShm *shm, + PError **error) +{ + P_UNUSED (shm); + P_UNUSED (error); + + return FALSE; +} + +P_LIB_API ppointer +p_shm_get_address (const PShm *shm) +{ + P_UNUSED (shm); + + return NULL; +} + +P_LIB_API psize +p_shm_get_size (const PShm *shm) +{ + P_UNUSED (shm); + + return 0; +} diff --git a/3rdparty/plibsys/src/pshm-os2.c b/3rdparty/plibsys/src/pshm-os2.c new file mode 100644 index 0000000..92e0779 --- /dev/null +++ b/3rdparty/plibsys/src/pshm-os2.c @@ -0,0 +1,350 @@ +/* + * The MIT License + * + * Copyright (C) 2017 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "perror.h" +#include "pmem.h" +#include "pshm.h" +#include "perror-private.h" +#include "pipc-private.h" + +#include <stdlib.h> +#include <string.h> + +#define INCL_DOSMEMMGR +#define INCL_DOSSEMAPHORES +#define INCL_DOSERRORS +#include <os2.h> + +#define P_SHM_MEM_PREFIX "\\SHAREMEM\\" +#define P_SHM_SEM_PREFIX "\\SEM32\\" +#define P_SHM_SUFFIX "_p_shm_object" + +struct PShm_ { + pchar *platform_key; + ppointer addr; + psize size; + HMTX sem; + PShmAccessPerms perms; +}; + +static pboolean pp_shm_create_handle (PShm *shm, PError **error); +static void pp_shm_clean_handle (PShm *shm); + +static pboolean +pp_shm_create_handle (PShm *shm, + PError **error) +{ + pchar *mem_name; + pchar *sem_name; + APIRET ulrc; + ULONG flags; + + if (P_UNLIKELY (shm == NULL || shm->platform_key == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return FALSE; + } + + flags = PAG_COMMIT | PAG_READ; + + if (shm->perms != P_SHM_ACCESS_READONLY) + flags |= PAG_WRITE; + + if (P_UNLIKELY ((mem_name = p_malloc0 (strlen (shm->platform_key) + + strlen (P_SHM_MEM_PREFIX) + 1)) == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_NO_RESOURCES, + 0, + "Failed to allocate memory for shared memory name"); + return FALSE; + } + + strcpy (mem_name, P_SHM_MEM_PREFIX); + strcat (mem_name, shm->platform_key); + + while ((ulrc = DosAllocSharedMem ((PPVOID) &shm->addr, + (PSZ) mem_name, + shm->size, + flags)) == ERROR_INTERRUPT) + ; + + if (P_UNLIKELY (ulrc != NO_ERROR && ulrc != ERROR_ALREADY_EXISTS)) { + p_error_set_error_p (error, + (pint) p_error_get_ipc_from_system ((pint) ulrc), + (pint) ulrc, + "Failed to call DosAllocSharedMem() to allocate shared memory"); + p_free (mem_name); + pp_shm_clean_handle (shm); + return FALSE; + } + + if (ulrc == ERROR_ALREADY_EXISTS) { + ULONG real_size; + ULONG real_flags; + + flags = (shm->perms == P_SHM_ACCESS_READONLY) ? PAG_READ : (PAG_WRITE | PAG_READ); + + while ((ulrc = DosGetNamedSharedMem ((PPVOID) &shm->addr, + (PSZ) mem_name, + flags)) == ERROR_INTERRUPT) + ; + + p_free (mem_name); + + if (P_UNLIKELY (ulrc != NO_ERROR)) { + p_error_set_error_p (error, + (pint) p_error_get_ipc_from_system ((pint) ulrc), + (pint) ulrc, + "Failed to call DosGetNamedSharedMem() to get shared memory"); + pp_shm_clean_handle (shm); + return FALSE; + } + + real_size = (ULONG) shm->size; + + while ((ulrc = DosQueryMem ((PVOID) shm->addr, + &real_size, + &real_flags)) == ERROR_INTERRUPT) + ; + + if (P_UNLIKELY (ulrc != NO_ERROR)) { + p_error_set_error_p (error, + (pint) p_error_get_ipc_from_system ((pint) ulrc), + (pint) ulrc, + "Failed to call DosQueryMem() to get memory info"); + pp_shm_clean_handle (shm); + return FALSE; + } + + shm->size = (psize) real_size; + } else + p_free (mem_name); + + if (P_UNLIKELY ((sem_name = p_malloc0 (strlen (shm->platform_key) + + strlen (P_SHM_SEM_PREFIX) + 1)) == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_NO_RESOURCES, + 0, + "Failed to allocate memory for shared memory name"); + pp_shm_clean_handle (shm); + return FALSE; + } + + strcpy (sem_name, P_SHM_SEM_PREFIX); + strcat (sem_name, shm->platform_key); + + ulrc = DosCreateMutexSem ((PSZ) sem_name, &shm->sem, 0, FALSE); + + if (ulrc == ERROR_DUPLICATE_NAME) + ulrc = DosOpenMutexSem ((PSZ) sem_name, &shm->sem); + + p_free (sem_name); + + if (P_UNLIKELY (ulrc != NO_ERROR)) { + p_error_set_error_p (error, + (pint) p_error_get_ipc_from_system ((pint) ulrc), + (pint) ulrc, + "Failed to call DosCreateMutexSem() to create a lock"); + pp_shm_clean_handle (shm); + return FALSE; + } + + return TRUE; +} + +static void +pp_shm_clean_handle (PShm *shm) +{ + APIRET ulrc; + + if (P_UNLIKELY (shm->addr != NULL)) { + while ((ulrc = DosFreeMem ((PVOID) shm->addr)) == ERROR_INTERRUPT) + ; + + if (P_UNLIKELY (ulrc != NO_ERROR)) + P_ERROR ("PShm::pp_shm_clean_handle: DosFreeMem() failed"); + + shm->addr = NULL; + } + + if (P_LIKELY (shm->sem != NULLHANDLE)) { + if (P_UNLIKELY (DosCloseMutexSem (shm->sem) != NO_ERROR)) + P_ERROR ("PShm::pp_shm_clean_handle: DosCloseMutexSem() failed"); + + shm->sem = NULLHANDLE; + } + + shm->size = 0; +} + +P_LIB_API PShm * +p_shm_new (const pchar *name, + psize size, + PShmAccessPerms perms, + PError **error) +{ + PShm *ret; + pchar *new_name; + + if (P_UNLIKELY (name == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return NULL; + } + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PShm))) == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_NO_RESOURCES, + 0, + "Failed to allocate memory for shared segment"); + return NULL; + } + + if (P_UNLIKELY ((new_name = p_malloc0 (strlen (name) + strlen (P_SHM_SUFFIX) + 1)) == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_NO_RESOURCES, + 0, + "Failed to allocate memory for segment name"); + p_shm_free (ret); + return NULL; + } + + strcpy (new_name, name); + strcat (new_name, P_SHM_SUFFIX); + + ret->platform_key = p_ipc_get_platform_key (new_name, FALSE); + ret->perms = perms; + ret->size = size; + + p_free (new_name); + + if (P_UNLIKELY (pp_shm_create_handle (ret, error) == FALSE)) { + p_shm_free (ret); + return NULL; + } + + if (P_LIKELY (ret->size > size && size != 0)) + ret->size = size; + + return ret; +} + +P_LIB_API void +p_shm_take_ownership (PShm *shm) +{ + P_UNUSED (shm); +} + +P_LIB_API void +p_shm_free (PShm *shm) +{ + if (P_UNLIKELY (shm == NULL)) + return; + + pp_shm_clean_handle (shm); + + if (P_LIKELY (shm->platform_key != NULL)) + p_free (shm->platform_key); + + p_free (shm); +} + +P_LIB_API pboolean +p_shm_lock (PShm *shm, + PError **error) +{ + APIRET ulrc; + + if (P_UNLIKELY (shm == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return FALSE; + } + + while ((ulrc = DosRequestMutexSem (shm->sem, + (ULONG) SEM_INDEFINITE_WAIT)) == ERROR_INTERRUPT) + ; + + if (P_UNLIKELY (ulrc != NO_ERROR)) { + p_error_set_error_p (error, + (pint) p_error_get_ipc_from_system ((pint) ulrc), + (pint) ulrc, + "Failed to lock memory segment"); + return FALSE; + } + + return TRUE; +} + +P_LIB_API pboolean +p_shm_unlock (PShm *shm, + PError **error) +{ + APIRET ulrc; + + if (P_UNLIKELY (shm == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return FALSE; + } + + ulrc = DosReleaseMutexSem (shm->sem); + + if (P_UNLIKELY (ulrc != NO_ERROR)) { + p_error_set_error_p (error, + (pint) p_error_get_ipc_from_system ((pint) ulrc), + (pint) ulrc, + "Failed to unlock memory segment"); + return FALSE; + } + + return TRUE; +} + +P_LIB_API ppointer +p_shm_get_address (const PShm *shm) +{ + if (P_UNLIKELY (shm == NULL)) + return NULL; + + return shm->addr; +} + +P_LIB_API psize +p_shm_get_size (const PShm *shm) +{ + if (P_UNLIKELY (shm == NULL)) + return 0; + + return shm->size; +} diff --git a/3rdparty/plibsys/src/pshm-posix.c b/3rdparty/plibsys/src/pshm-posix.c new file mode 100644 index 0000000..3551b2c --- /dev/null +++ b/3rdparty/plibsys/src/pshm-posix.c @@ -0,0 +1,311 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2018 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "perror.h" +#include "pmem.h" +#include "psemaphore.h" +#include "pshm.h" +#include "perror-private.h" +#include "pipc-private.h" +#include "psysclose-private.h" + +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <errno.h> + +#define P_SHM_SUFFIX "_p_shm_object" +#define P_SHM_INVALID_HDL -1 + +struct PShm_ { + pboolean shm_created; + pchar *platform_key; + ppointer addr; + psize size; + PSemaphore *sem; + PShmAccessPerms perms; +}; + +static pboolean pp_shm_create_handle (PShm *shm, PError **error); +static void pp_shm_clean_handle (PShm *shm); + +static pboolean +pp_shm_create_handle (PShm *shm, + PError **error) +{ + pboolean is_exists; + pint fd, flags; + struct stat stat_buf; + + if (P_UNLIKELY (shm == NULL || shm->platform_key == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return FALSE; + } + + is_exists = FALSE; + + while ((fd = shm_open (shm->platform_key, + O_CREAT | O_EXCL | O_RDWR, + 0660)) == P_SHM_INVALID_HDL && + p_error_get_last_system () == EINTR) + ; + + if (fd == P_SHM_INVALID_HDL) { + if (p_error_get_last_system () == EEXIST) { + is_exists = TRUE; + + while ((fd = shm_open (shm->platform_key, + O_RDWR, + 0660)) == P_SHM_INVALID_HDL && + p_error_get_last_system () == EINTR) + ; + } + } else + shm->shm_created = TRUE; + + if (P_UNLIKELY (fd == P_SHM_INVALID_HDL)) { + p_error_set_error_p (error, + (pint) p_error_get_last_ipc (), + p_error_get_last_system (), + "Failed to call shm_open() to create memory segment"); + pp_shm_clean_handle (shm); + return FALSE; + } + + /* Try to get size of the existing file descriptor */ + if (is_exists) { + if (P_UNLIKELY (fstat (fd, &stat_buf) == -1)) { + p_error_set_error_p (error, + (pint) p_error_get_last_ipc (), + p_error_get_last_system (), + "Failed to call fstat() to get memory segment size"); + + if (P_UNLIKELY (p_sys_close (fd) != 0)) + P_WARNING ("PShm::pp_shm_create_handle: p_sys_close() failed(1)"); + + pp_shm_clean_handle (shm); + return FALSE; + } + + shm->size = (psize) stat_buf.st_size; + } else { + if (P_UNLIKELY ((ftruncate (fd, (off_t) shm->size)) == -1)) { + p_error_set_error_p (error, + (pint) p_error_get_last_ipc (), + p_error_get_last_system (), + "Failed to call ftruncate() to set memory segment size"); + + if (P_UNLIKELY (p_sys_close (fd) != 0)) + P_WARNING ("PShm::pp_shm_create_handle: p_sys_close() failed(2)"); + + pp_shm_clean_handle (shm); + return FALSE; + } + } + + flags = (shm->perms == P_SHM_ACCESS_READONLY) ? PROT_READ : PROT_READ | PROT_WRITE; + + if (P_UNLIKELY ((shm->addr = mmap (NULL, shm->size, flags, MAP_SHARED, fd, 0)) == (void *) -1)) { + p_error_set_error_p (error, + (pint) p_error_get_last_ipc (), + p_error_get_last_system (), + "Failed to call mmap() to map memory segment"); + shm->addr = NULL; + + if (P_UNLIKELY (p_sys_close (fd) != 0)) + P_WARNING ("PShm::pp_shm_create_handle: p_sys_close() failed(3)"); + + pp_shm_clean_handle (shm); + return FALSE; + } + + if (P_UNLIKELY (p_sys_close (fd) != 0)) + P_WARNING ("PShm::pp_shm_create_handle: p_sys_close() failed(4)"); + + if (P_UNLIKELY ((shm->sem = p_semaphore_new (shm->platform_key, 1, + is_exists ? P_SEM_ACCESS_OPEN : P_SEM_ACCESS_CREATE, + error)) == NULL)) { + pp_shm_clean_handle (shm); + return FALSE; + } + + return TRUE; +} + +static void +pp_shm_clean_handle (PShm *shm) +{ + if (P_UNLIKELY (shm->addr != NULL && munmap (shm->addr, shm->size) == -1)) + P_ERROR ("PShm::pp_shm_clean_handle: munmap () failed"); + + if (shm->shm_created == TRUE && shm_unlink (shm->platform_key) == -1) + P_ERROR ("PShm::pp_shm_clean_handle: shm_unlink() failed"); + + if (P_LIKELY (shm->sem != NULL)) { + p_semaphore_free (shm->sem); + shm->sem = NULL; + } + + shm->shm_created = FALSE; + shm->addr = NULL; + shm->size = 0; +} + +P_LIB_API PShm * +p_shm_new (const pchar *name, + psize size, + PShmAccessPerms perms, + PError **error) +{ + PShm *ret; + pchar *new_name; + + if (P_UNLIKELY (name == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return NULL; + } + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PShm))) == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_NO_RESOURCES, + 0, + "Failed to allocate memory for shared segment"); + return NULL; + } + + if (P_UNLIKELY ((new_name = p_malloc0 (strlen (name) + strlen (P_SHM_SUFFIX) + 1)) == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_NO_RESOURCES, + 0, + "Failed to allocate memory for segment name"); + p_shm_free (ret); + return NULL; + } + + strcpy (new_name, name); + strcat (new_name, P_SHM_SUFFIX); + +#if defined (P_OS_IRIX) || defined (P_OS_TRU64) + /* IRIX and Tru64 prefer filename styled IPC names */ + ret->platform_key = p_ipc_get_platform_key (new_name, FALSE); +#else + ret->platform_key = p_ipc_get_platform_key (new_name, TRUE); +#endif + ret->perms = perms; + ret->size = size; + + p_free (new_name); + + if (P_UNLIKELY (pp_shm_create_handle (ret, error) == FALSE)) { + p_shm_free (ret); + return NULL; + } + + if (P_LIKELY (ret->size > size && size != 0)) + ret->size = size; + + return ret; +} + +P_LIB_API void +p_shm_take_ownership (PShm *shm) +{ + if (P_UNLIKELY (shm == NULL)) + return; + + shm->shm_created = TRUE; + p_semaphore_take_ownership (shm->sem); +} + +P_LIB_API void +p_shm_free (PShm *shm) +{ + if (P_UNLIKELY (shm == NULL)) + return; + + pp_shm_clean_handle (shm); + + if (P_LIKELY (shm->platform_key != NULL)) + p_free (shm->platform_key); + + p_free (shm); +} + +P_LIB_API pboolean +p_shm_lock (PShm *shm, + PError **error) +{ + if (P_UNLIKELY (shm == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return FALSE; + } + + return p_semaphore_acquire (shm->sem, error); +} + +P_LIB_API pboolean +p_shm_unlock (PShm *shm, + PError **error) +{ + if (P_UNLIKELY (shm == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return FALSE; + } + + return p_semaphore_release (shm->sem, error); +} + +P_LIB_API ppointer +p_shm_get_address (const PShm *shm) +{ + if (P_UNLIKELY (shm == NULL)) + return NULL; + + return shm->addr; +} + +P_LIB_API psize +p_shm_get_size (const PShm *shm) +{ + if (P_UNLIKELY (shm == NULL)) + return 0; + + return shm->size; +} diff --git a/3rdparty/plibsys/src/pshm-sysv.c b/3rdparty/plibsys/src/pshm-sysv.c new file mode 100644 index 0000000..d7e6434 --- /dev/null +++ b/3rdparty/plibsys/src/pshm-sysv.c @@ -0,0 +1,307 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "perror.h" +#include "pmem.h" +#include "psemaphore.h" +#include "pshm.h" +#include "perror-private.h" +#include "pipc-private.h" + +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/shm.h> +#include <sys/types.h> +#include <sys/ipc.h> +#include <errno.h> + +#define P_SHM_SUFFIX "_p_shm_object" +#define P_SHM_INVALID_HDL -1 + +typedef pint pshm_hdl; + +struct PShm_ { + pboolean file_created; + key_t unix_key; + pchar *platform_key; + pshm_hdl shm_hdl; + ppointer addr; + psize size; + PSemaphore *sem; + PShmAccessPerms perms; +}; + +static pboolean pp_shm_create_handle (PShm *shm, PError **error); +static void pp_shm_clean_handle (PShm *shm); + +static pboolean +pp_shm_create_handle (PShm *shm, + PError **error) +{ + pboolean is_exists; + pint flags, built; + struct shmid_ds shm_stat; + + if (P_UNLIKELY (shm == NULL || shm->platform_key == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return FALSE; + } + + is_exists = FALSE; + + if (P_UNLIKELY ((built = p_ipc_unix_create_key_file (shm->platform_key)) == -1)) { + p_error_set_error_p (error, + (pint) p_error_get_last_ipc (), + p_error_get_last_system (), + "Failed to create key file"); + pp_shm_clean_handle (shm); + return FALSE; + } else if (built == 0) + shm->file_created = TRUE; + + if (P_UNLIKELY ((shm->unix_key = p_ipc_unix_get_ftok_key (shm->platform_key)) == -1)) { + p_error_set_error_p (error, + (pint) p_error_get_last_ipc (), + p_error_get_last_system (), + "Failed to get unique IPC key"); + pp_shm_clean_handle (shm); + return FALSE; + } + + flags = (shm->perms == P_SHM_ACCESS_READONLY) ? 0444 : 0660; + + if ((shm->shm_hdl = shmget (shm->unix_key, + shm->size, + IPC_CREAT | IPC_EXCL | flags)) == P_SHM_INVALID_HDL) { + if (p_error_get_last_system () == EEXIST) { + is_exists = TRUE; + + shm->shm_hdl = shmget (shm->unix_key, 0, flags); + } + } else + shm->file_created = (built == 1); + + if (P_UNLIKELY (shm->shm_hdl == P_SHM_INVALID_HDL)) { + p_error_set_error_p (error, + (pint) p_error_get_last_ipc (), + p_error_get_last_system (), + "Failed to call shmget() to create memory segment"); + pp_shm_clean_handle (shm); + return FALSE; + } + + if (P_UNLIKELY (shmctl (shm->shm_hdl, IPC_STAT, &shm_stat) == -1)) { + p_error_set_error_p (error, + (pint) p_error_get_last_ipc (), + p_error_get_last_system (), + "Failed to call shmctl() to get memory segment size"); + pp_shm_clean_handle (shm); + return FALSE; + } + + shm->size = shm_stat.shm_segsz; + + flags = (shm->perms == P_SHM_ACCESS_READONLY) ? SHM_RDONLY : 0; + + if (P_UNLIKELY ((shm->addr = shmat (shm->shm_hdl, 0, flags)) == (void *) -1)) { + p_error_set_error_p (error, + (pint) p_error_get_last_ipc (), + p_error_get_last_system (), + "Failed to call shmat() to attach to the memory segment"); + pp_shm_clean_handle (shm); + return FALSE; + } + + if (P_UNLIKELY ((shm->sem = p_semaphore_new (shm->platform_key, 1, + is_exists ? P_SEM_ACCESS_OPEN : P_SEM_ACCESS_CREATE, + error)) == NULL)) { + pp_shm_clean_handle (shm); + return FALSE; + } + + return TRUE; +} + +static void +pp_shm_clean_handle (PShm *shm) +{ + struct shmid_ds shm_stat; + + if (P_LIKELY (shm->addr != NULL)) { + if (P_UNLIKELY (shmdt (shm->addr) == -1)) + P_ERROR ("PShm::pp_shm_clean_handle: shmdt() failed"); + + if (P_UNLIKELY (shmctl (shm->shm_hdl, IPC_STAT, &shm_stat) == -1)) + P_ERROR ("PShm::pp_shm_clean_handle: shmctl() with IPC_STAT failed"); + + if (P_UNLIKELY (shm_stat.shm_nattch == 0 && shmctl (shm->shm_hdl, IPC_RMID, 0) == -1)) + P_ERROR ("PShm::pp_shm_clean_handle: shmctl() with IPC_RMID failed"); + } + + if (shm->file_created == TRUE && unlink (shm->platform_key) == -1) + P_ERROR ("PShm::pp_shm_clean_handle: unlink() failed"); + + if (P_LIKELY (shm->sem != NULL)) { + p_semaphore_free (shm->sem); + shm->sem = NULL; + } + + shm->file_created = FALSE; + shm->unix_key = -1; + shm->shm_hdl = P_SHM_INVALID_HDL; + shm->addr = NULL; + shm->size = 0; +} + +P_LIB_API PShm * +p_shm_new (const pchar *name, + psize size, + PShmAccessPerms perms, + PError **error) +{ + PShm *ret; + pchar *new_name; + + if (P_UNLIKELY (name == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return NULL; + } + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PShm))) == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_NO_RESOURCES, + 0, + "Failed to allocate memory for shared segment"); + return NULL; + } + + if (P_UNLIKELY ((new_name = p_malloc0 (strlen (name) + strlen (P_SHM_SUFFIX) + 1)) == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_NO_RESOURCES, + 0, + "Failed to allocate memory for segment name"); + p_shm_free (ret); + return NULL; + } + + strcpy (new_name, name); + strcat (new_name, P_SHM_SUFFIX); + + ret->platform_key = p_ipc_get_platform_key (new_name, FALSE); + ret->perms = perms; + ret->size = size; + + p_free (new_name); + + if (P_UNLIKELY (pp_shm_create_handle (ret, error) == FALSE)) { + p_shm_free (ret); + return NULL; + } + + if (P_LIKELY (ret->size > size && size != 0)) + ret->size = size; + + return ret; +} + +P_LIB_API void +p_shm_take_ownership (PShm *shm) +{ + if (P_UNLIKELY (shm == NULL)) + return; + + shm->file_created = TRUE; + p_semaphore_take_ownership (shm->sem); +} + +P_LIB_API void +p_shm_free (PShm *shm) +{ + if (P_UNLIKELY (shm == NULL)) + return; + + pp_shm_clean_handle (shm); + + if (P_LIKELY (shm->platform_key != NULL)) + p_free (shm->platform_key); + + p_free (shm); +} + +P_LIB_API pboolean +p_shm_lock (PShm *shm, + PError **error) +{ + if (P_UNLIKELY (shm == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return FALSE; + } + + return p_semaphore_acquire (shm->sem, error); +} + +P_LIB_API pboolean +p_shm_unlock (PShm *shm, + PError **error) +{ + if (P_UNLIKELY (shm == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return FALSE; + } + + return p_semaphore_release (shm->sem, error); +} + +P_LIB_API ppointer +p_shm_get_address (const PShm *shm) +{ + if (P_UNLIKELY (shm == NULL)) + return NULL; + + return shm->addr; +} + +P_LIB_API psize +p_shm_get_size (const PShm *shm) +{ + if (P_UNLIKELY (shm == NULL)) + return 0; + + return shm->size; +} diff --git a/3rdparty/plibsys/src/pshm-win.c b/3rdparty/plibsys/src/pshm-win.c new file mode 100644 index 0000000..3ae11f9 --- /dev/null +++ b/3rdparty/plibsys/src/pshm-win.c @@ -0,0 +1,262 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "perror.h" +#include "pmem.h" +#include "psemaphore.h" +#include "pshm.h" +#include "perror-private.h" +#include "pipc-private.h" + +#include <stdlib.h> +#include <string.h> + +#define P_SHM_INVALID_HDL NULL +#define P_SHM_SUFFIX "_p_shm_object" + +typedef HANDLE pshm_hdl; + +struct PShm_ { + pchar *platform_key; + pshm_hdl shm_hdl; + ppointer addr; + psize size; + PSemaphore *sem; + PShmAccessPerms perms; +}; + +static pboolean pp_shm_create_handle (PShm *shm, PError **error); +static void pp_shm_clean_handle (PShm *shm); + +static pboolean +pp_shm_create_handle (PShm *shm, + PError **error) +{ + pboolean is_exists; + MEMORY_BASIC_INFORMATION mem_stat; + DWORD protect; + + if (P_UNLIKELY (shm == NULL || shm->platform_key == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return FALSE; + } + + is_exists = FALSE; + + protect = (shm->perms == P_SHM_ACCESS_READONLY) ? PAGE_READONLY : PAGE_READWRITE; + + /* Multibyte character set must be enabled */ + if (P_UNLIKELY ((shm->shm_hdl = CreateFileMappingA (INVALID_HANDLE_VALUE, + NULL, + protect, + 0, + (DWORD) shm->size, + shm->platform_key)) == NULL)) { + p_error_set_error_p (error, + (pint) p_error_get_last_ipc (), + p_error_get_last_system (), + "Failed to call CreateFileMapping() to create file mapping"); + pp_shm_clean_handle (shm); + return FALSE; + } + + protect = (protect == PAGE_READONLY) ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS; + + if (P_UNLIKELY ((shm->addr = MapViewOfFile (shm->shm_hdl, protect, 0, 0, 0)) == NULL)) { + p_error_set_error_p (error, + (pint) p_error_get_last_ipc (), + p_error_get_last_system (), + "Failed to call MapViewOfFile() to map file to memory"); + pp_shm_clean_handle (shm); + return FALSE; + } + + if (p_error_get_last_system () == ERROR_ALREADY_EXISTS) + is_exists = TRUE; + + if (P_UNLIKELY (VirtualQuery (shm->addr, &mem_stat, sizeof (mem_stat)) == 0)) { + p_error_set_error_p (error, + (pint) p_error_get_last_ipc (), + p_error_get_last_system (), + "Failed to call VirtualQuery() to get memory map info"); + pp_shm_clean_handle (shm); + return FALSE; + } + + shm->size = mem_stat.RegionSize; + + if (P_UNLIKELY ((shm->sem = p_semaphore_new (shm->platform_key, 1, + is_exists ? P_SEM_ACCESS_OPEN : P_SEM_ACCESS_CREATE, + error)) == NULL)) { + pp_shm_clean_handle (shm); + return FALSE; + } + + return TRUE; +} + +static void +pp_shm_clean_handle (PShm *shm) +{ + if (P_UNLIKELY (shm->addr != NULL && UnmapViewOfFile ((char *) shm->addr) == 0)) + P_ERROR ("PShm::pp_shm_clean_handle: UnmapViewOfFile() failed"); + + if (P_UNLIKELY (shm->shm_hdl != P_SHM_INVALID_HDL && CloseHandle (shm->shm_hdl) == 0)) + P_ERROR ("PShm::pp_shm_clean_handle: CloseHandle() failed"); + + if (P_LIKELY (shm->sem != NULL)) { + p_semaphore_free (shm->sem); + shm->sem = NULL; + } + + shm->shm_hdl = P_SHM_INVALID_HDL; + shm->addr = NULL; + shm->size = 0; +} + +P_LIB_API PShm * +p_shm_new (const pchar *name, + psize size, + PShmAccessPerms perms, + PError **error) +{ + PShm *ret; + pchar *new_name; + + if (P_UNLIKELY (name == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return NULL; + } + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PShm))) == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_NO_RESOURCES, + 0, + "Failed to allocate memory for shared segment"); + return NULL; + } + + if (P_UNLIKELY ((new_name = p_malloc0 (strlen (name) + strlen (P_SHM_SUFFIX) + 1)) == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_NO_RESOURCES, + 0, + "Failed to allocate memory for segment name"); + p_shm_free (ret); + return NULL; + } + + strcpy (new_name, name); + strcat (new_name, P_SHM_SUFFIX); + + ret->platform_key = p_ipc_get_platform_key (new_name, FALSE); + ret->perms = perms; + ret->size = size; + + p_free (new_name); + + if (P_UNLIKELY (pp_shm_create_handle (ret, error) == FALSE)) { + p_shm_free (ret); + return NULL; + } + + if (P_LIKELY (ret->size > size && size != 0)) + ret->size = size; + + return ret; +} + +P_LIB_API void +p_shm_take_ownership (PShm *shm) +{ + P_UNUSED (shm); +} + +P_LIB_API void +p_shm_free (PShm *shm) +{ + if (P_UNLIKELY (shm == NULL)) + return; + + pp_shm_clean_handle (shm); + + if (P_LIKELY (shm->platform_key != NULL)) + p_free (shm->platform_key); + + p_free (shm); +} + +P_LIB_API pboolean +p_shm_lock (PShm *shm, + PError **error) +{ + if (P_UNLIKELY (shm == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return FALSE; + } + + return p_semaphore_acquire (shm->sem, error); +} + +P_LIB_API pboolean +p_shm_unlock (PShm *shm, + PError **error) +{ + if (P_UNLIKELY (shm == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return FALSE; + } + + return p_semaphore_release (shm->sem, error); +} + +P_LIB_API ppointer +p_shm_get_address (const PShm *shm) +{ + if (P_UNLIKELY (shm == NULL)) + return NULL; + + return shm->addr; +} + +P_LIB_API psize +p_shm_get_size (const PShm *shm) +{ + if (P_UNLIKELY (shm == NULL)) + return 0; + + return shm->size; +} diff --git a/3rdparty/plibsys/src/pshm.h b/3rdparty/plibsys/src/pshm.h new file mode 100644 index 0000000..096c009 --- /dev/null +++ b/3rdparty/plibsys/src/pshm.h @@ -0,0 +1,195 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2017 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * @file pshm.h + * @brief Shared memory + * @author Alexander Saprykin + * + * Shared memory is a memory segment which can be accessed from several threads + * or processes. It provides an efficient way to transfer large blocks of data + * between processes. It can be used as any other regular memory segment in an + * application. + * + * Shared memory acts like an inter-process communication method. This memory + * exchange implementation is process-wide so you can transfer data not only + * between the threads. But it makes this IPC method (actually like any other + * IPC method, as well) relatively heavy. Consider using other approaches + * instead if you do not need to cross the process boundary. + * + * A shared memory segment doesn't provide any synchronization primitives itself + * which means that several processes or threads can concurrently write and read + * from it. This can lead to data consistency problems. To avoid such situations + * a locking mechanism is provided: use p_shm_lock() before entering a critical + * section on the memory segment and p_shm_unlock() when leaving this section. + * The locking mechanism is working across the process boundary. + * + * A process-wide shared memory segment is identified by its name across the + * system, thus it is also called a named memory segment. Use p_shm_new() to + * open the named shared memory segment and p_shm_free() to close it. + * + * Please note the following platform specific differences: + * + * - Windows and OS/2 don't own IPC objects (processes own them), which means + * that a shared memory segment will be removed after the last process or thread + * detaches (or after terminating all the processes and threads attached to the + * segment) it. + * + * - UNIX systems own IPC objects. Because of that UNIX IPC objects can survive + * an application crash: the attached shared memory segment can contain data + * from the previous working session. This could happen if you have not detached + * from all the shared memory segments explicitly before terminating the + * application. + * + * - HP-UX has limitations due to its MPAS/MGAS features, so you couldn't attach + * to the same memory segment twice from the same process. + * + * - IRIX allows to open several instances of the same buffer within the single + * process, but it will close the object after the first close call from any of + * the threads within the process. + * + * - OpenVMS (as of 8.4 release) has broken implementation of process-wide named + * semaphores which leads to the broken shared memory also. + * + * - Syllable lacks support for process-wide named semaphores which leads to the + * absence of shared memory. + * + * - BeOS lacks support for process-wide named semaphores which leads to the + * absence of shared memory. + * + * You can take ownership of the shared memory segment with + * p_shm_take_ownership() to explicitly remove it from the system after closing. + */ + +#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION) +# error "Header files shouldn't be included directly, consider using <plibsys.h> instead." +#endif + +#ifndef PLIBSYS_HEADER_PSHM_H +#define PLIBSYS_HEADER_PSHM_H + +#include <pmacros.h> +#include <ptypes.h> +#include <perror.h> + +P_BEGIN_DECLS + +/** Enum with shared memory access permissions. */ +typedef enum PShmAccessPerms_ { + P_SHM_ACCESS_READONLY = 0, /**< Read-only access. */ + P_SHM_ACCESS_READWRITE = 1 /**< Read/write access. */ +} PShmAccessPerms; + +/** Shared memory opaque data structure. */ +typedef struct PShm_ PShm; + +/** + * @brief Creates a new #PShm object. + * @param name Shared memory name. + * @param size Size of the memory segment in bytes, can't be changed later. + * @param perms Memory segment permissions, see #PShmAccessPerms. + * @param[out] error Error report object, NULL to ignore. + * @return Pointer to a newly created #PShm object in case of success, NULL + * otherwise. + * @since 0.0.1 + */ +P_LIB_API PShm * p_shm_new (const pchar *name, + psize size, + PShmAccessPerms perms, + PError **error); + +/** + * @brief Takes ownership of a shared memory segment. + * @param shm Shared memory segment. + * @since 0.0.1 + * + * If you take ownership of the shared memory object, p_shm_free() will try to + * completely unlink it and remove from the system. This is useful on UNIX + * systems where shared memory can survive an application crash. On the Windows + * and OS/2 platforms this call has no effect. + * + * The common usage of this call is upon application startup to ensure that the + * memory segment from the previous crash will be unlinked from the system. To + * do that, call p_shm_new() and check if its condition is normal (the segment + * size, the data). If not, take ownership of the shared memory object and + * remove it with the p_shm_free() call. After that, create it again. + */ +P_LIB_API void p_shm_take_ownership (PShm *shm); + +/** + * @brief Frees #PShm object. + * @param shm #PShm to free. + * @since 0.0.1 + * + * It doesn't unlock a given shared memory segment, be careful to not to make a + * deadlock or a segfault while freeing the memory segment which is under usage. + */ +P_LIB_API void p_shm_free (PShm *shm); + +/** + * @brief Locks #PShm object for usage. + * @param shm #PShm to lock. + * @param[out] error Error report object, NULL to ignore. + * @return TRUE in case of success, FALSE otherwise. + * @since 0.0.1 + * + * If the object is already locked then the thread will be suspended until the + * object becomes unlocked. + */ +P_LIB_API pboolean p_shm_lock (PShm *shm, + PError **error); + +/** + * @brief Unlocks #PShm object. + * @param shm #PShm to unlock. + * @param[out] error Error report object, NULL to ignore. + * @return TRUE in case of success, FALSE otherwise. + * @since 0.0.1 + */ +P_LIB_API pboolean p_shm_unlock (PShm *shm, + PError **error); + +/** + * @brief Gets a starting address of a #PShm memory segment. + * @param shm #PShm to get the address for. + * @return Pointer to the starting address in case of success, NULL otherwise. + * @since 0.0.1 + */ +P_LIB_API ppointer p_shm_get_address (const PShm *shm); + +/** + * @brief Gets the size of a #PShm memory segment. + * @param shm #PShm to get the size for. + * @return Size of the given memory segment in case of success, 0 otherwise. + * @since 0.0.1 + * + * Note that the returned size would be a slightly larger than specified during + * the p_shm_new() call due to service information stored inside. + */ +P_LIB_API psize p_shm_get_size (const PShm *shm); + +P_END_DECLS + +#endif /* PLIBSYS_HEADER_PSHM_H */ diff --git a/3rdparty/plibsys/src/pshmbuffer.c b/3rdparty/plibsys/src/pshmbuffer.c new file mode 100644 index 0000000..7bdbbd3 --- /dev/null +++ b/3rdparty/plibsys/src/pshmbuffer.c @@ -0,0 +1,334 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2020 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pmem.h" +#include "pshm.h" +#include "pshmbuffer.h" + +#include <stdlib.h> +#include <string.h> + +#define P_SHM_BUFFER_READ_OFFSET 0 +#define P_SHM_BUFFER_WRITE_OFFSET sizeof (psize) +#define P_SHM_BUFFER_DATA_OFFSET sizeof (psize) * 2 + +struct PShmBuffer_ { + PShm *shm; + psize size; +}; + +static psize pp_shm_buffer_get_free_space (PShmBuffer *buf); +static psize pp_shm_buffer_get_used_space (PShmBuffer *buf); + +/* Warning: this function is not thread-safe, only for internal usage */ +static psize +pp_shm_buffer_get_free_space (PShmBuffer *buf) +{ + psize read_pos, write_pos; + ppointer addr; + + addr = p_shm_get_address (buf->shm); + + memcpy (&read_pos, (pchar *) addr + P_SHM_BUFFER_READ_OFFSET, sizeof (read_pos)); + memcpy (&write_pos, (pchar *) addr + P_SHM_BUFFER_WRITE_OFFSET, sizeof (write_pos)); + + if (write_pos < read_pos) + return read_pos - write_pos - 1; + else if (write_pos > read_pos) + return buf->size - (write_pos - read_pos) - 1; + else + return buf->size - 1; +} + +static psize +pp_shm_buffer_get_used_space (PShmBuffer *buf) +{ + psize read_pos, write_pos; + ppointer addr; + + addr = p_shm_get_address (buf->shm); + + memcpy (&read_pos, (pchar *) addr + P_SHM_BUFFER_READ_OFFSET, sizeof (read_pos)); + memcpy (&write_pos, (pchar *) addr + P_SHM_BUFFER_WRITE_OFFSET, sizeof (write_pos)); + + if (write_pos > read_pos) + return write_pos - read_pos; + else if (write_pos < read_pos) + return (buf->size - (read_pos - write_pos)); + else + return 0; +} + +P_LIB_API PShmBuffer * +p_shm_buffer_new (const pchar *name, + psize size, + PError **error) +{ + PShmBuffer *ret; + PShm *shm; + + if (P_UNLIKELY (name == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return NULL; + } + + if (P_UNLIKELY ((shm = p_shm_new (name, + (size != 0) ? size + P_SHM_BUFFER_DATA_OFFSET + 1 : 0, + P_SHM_ACCESS_READWRITE, + error)) == NULL)) + return NULL; + + if (P_UNLIKELY (p_shm_get_size (shm) <= P_SHM_BUFFER_DATA_OFFSET + 1)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_INVALID_ARGUMENT, + 0, + "Too small memory segment to hold required data"); + p_shm_free (shm); + return NULL; + } + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PShmBuffer))) == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_NO_RESOURCES, + 0, + "Failed to allocate memory for shared buffer"); + p_shm_free (shm); + return NULL; + } + + ret->shm = shm; + ret->size = p_shm_get_size (shm) - P_SHM_BUFFER_DATA_OFFSET; + + return ret; +} + +P_LIB_API void +p_shm_buffer_free (PShmBuffer *buf) +{ + if (P_UNLIKELY (buf == NULL)) + return; + + p_shm_free (buf->shm); + p_free (buf); +} + +P_LIB_API void +p_shm_buffer_take_ownership (PShmBuffer *buf) +{ + if (P_UNLIKELY (buf == NULL)) + return; + + p_shm_take_ownership (buf->shm); +} + +P_LIB_API pint +p_shm_buffer_read (PShmBuffer *buf, + ppointer storage, + psize len, + PError **error) +{ + psize read_pos, write_pos; + psize data_aval, to_copy; + puint i; + ppointer addr; + + if (P_UNLIKELY (buf == NULL || storage == NULL || len == 0)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return -1; + } + + if (P_UNLIKELY ((addr = p_shm_get_address (buf->shm)) == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_INVALID_ARGUMENT, + 0, + "Unable to get shared memory address"); + return -1; + } + + if (P_UNLIKELY (p_shm_lock (buf->shm, error) == FALSE)) + return -1; + + memcpy (&read_pos, (pchar *) addr + P_SHM_BUFFER_READ_OFFSET, sizeof (read_pos)); + memcpy (&write_pos, (pchar *) addr + P_SHM_BUFFER_WRITE_OFFSET, sizeof (write_pos)); + + if (read_pos == write_pos) { + if (P_UNLIKELY (p_shm_unlock (buf->shm, error) == FALSE)) + return -1; + + return 0; + } + + data_aval = pp_shm_buffer_get_used_space (buf); + to_copy = (data_aval <= len) ? data_aval : len; + + for (i = 0; i < to_copy; ++i) + memcpy ((pchar *) storage + i, + (pchar *) addr + P_SHM_BUFFER_DATA_OFFSET + ((read_pos + i) % buf->size), + 1); + + read_pos = (read_pos + to_copy) % buf->size; + memcpy ((pchar *) addr + P_SHM_BUFFER_READ_OFFSET, &read_pos, sizeof (read_pos)); + + if (P_UNLIKELY (p_shm_unlock (buf->shm, error) == FALSE)) + return -1; + + return (pint) to_copy; +} + +P_LIB_API pssize +p_shm_buffer_write (PShmBuffer *buf, + ppointer data, + psize len, + PError **error) +{ + psize read_pos, write_pos; + puint i; + ppointer addr; + + if (P_UNLIKELY (buf == NULL || data == NULL || len == 0)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return -1; + } + + if (P_UNLIKELY ((addr = p_shm_get_address (buf->shm)) == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_INVALID_ARGUMENT, + 0, + "Unable to get shared memory address"); + return -1; + } + + if (P_UNLIKELY (p_shm_lock (buf->shm, error) == FALSE)) + return -1; + + memcpy (&read_pos, (pchar *) addr + P_SHM_BUFFER_READ_OFFSET, sizeof (read_pos)); + memcpy (&write_pos, (pchar *) addr + P_SHM_BUFFER_WRITE_OFFSET, sizeof (write_pos)); + + if (pp_shm_buffer_get_free_space (buf) < len) { + if (P_UNLIKELY (p_shm_unlock (buf->shm, error) == FALSE)) + return -1; + + return 0; + } + + for (i = 0; i < len; ++i) + memcpy ((pchar *) addr + P_SHM_BUFFER_DATA_OFFSET + ((write_pos + i) % buf->size), + (pchar *) data + i, + 1); + + write_pos = (write_pos + len) % buf->size; + memcpy ((pchar *) addr + P_SHM_BUFFER_WRITE_OFFSET, &write_pos, sizeof (write_pos)); + + if (P_UNLIKELY (p_shm_unlock (buf->shm, error) == FALSE)) + return -1; + + return (pssize) len; +} + +P_LIB_API pssize +p_shm_buffer_get_free_space (PShmBuffer *buf, + PError **error) +{ + psize space; + + if (P_UNLIKELY (buf == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return -1; + } + + if (P_UNLIKELY (p_shm_lock (buf->shm, error) == FALSE)) + return -1; + + space = pp_shm_buffer_get_free_space (buf); + + if (P_UNLIKELY (p_shm_unlock (buf->shm, error) == FALSE)) + return -1; + + return (pssize) space; +} + +P_LIB_API pssize +p_shm_buffer_get_used_space (PShmBuffer *buf, + PError **error) +{ + psize space; + + if (P_UNLIKELY (buf == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IPC_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return -1; + } + + if (P_UNLIKELY (p_shm_lock (buf->shm, error) == FALSE)) + return -1; + + space = pp_shm_buffer_get_used_space (buf); + + if (P_UNLIKELY (p_shm_unlock (buf->shm, error) == FALSE)) + return -1; + + return (pssize) space; +} + +P_LIB_API void +p_shm_buffer_clear (PShmBuffer *buf) +{ + ppointer addr; + psize size; + + if (P_UNLIKELY (buf == NULL)) + return; + + if (P_UNLIKELY ((addr = p_shm_get_address (buf->shm)) == NULL)) { + P_ERROR ("PShmBuffer::p_shm_buffer_clear: p_shm_get_address() failed"); + return; + } + + size = p_shm_get_size (buf->shm); + + if (P_UNLIKELY (p_shm_lock (buf->shm, NULL) == FALSE)) { + P_ERROR ("PShmBuffer::p_shm_buffer_clear: p_shm_lock() failed"); + return; + } + + memset (addr, 0, size); + + if (P_UNLIKELY (p_shm_unlock (buf->shm, NULL) == FALSE)) + P_ERROR ("PShmBuffer::p_shm_buffer_clear: p_shm_unlock() failed"); +} diff --git a/3rdparty/plibsys/src/pshmbuffer.h b/3rdparty/plibsys/src/pshmbuffer.h new file mode 100644 index 0000000..5c8bcc1 --- /dev/null +++ b/3rdparty/plibsys/src/pshmbuffer.h @@ -0,0 +1,191 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2017 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * @file pshmbuffer.h + * @brief Shared memory buffer + * @author Alexander Saprykin + * + * A shared memory buffer works like any other buffer but it is built upon a + * shared memory region instead of the process-only address space. Thus it + * inherits all the advantages and disadvantages of shared memory behavior. You + * should read about #PShm before using this buffer implementation to understand + * underlying restrictions. + * + * The shared memory buffer is process-wide and identified by its name across + * the system, thus it can be opened by any process if it knows its name. Use + * p_shm_buffer_new() to open the shared memory buffer and p_shm_buffer_free() + * to close it. + * + * All read/write operations are completely thread- and process-safe, which + * means that no other synchronization primitive is required, even for inter- + * process access. A #PShm locking mechanism is used for access synchronization. + * + * The buffer is cyclic and non-overridable which means that you wouldn't get + * buffer overflow and wouldn't override previously written data until reading + * it. + * + * The read operation checks whether there is any data available and reads it in + * case of successful check. After reading the data used space in the buffer is + * marked as free and any subsequent write operation may overwrite it. Thus you + * couldn't read the same data twice. The read operation is performed with the + * p_shm_buffer_read() call. + * + * The write operation checks whether there is enough free space available and + * writes a given memory block only if the buffer has enough free space. + * Otherwise no data is written. The write operation is performed with the + * p_shm_buffer_write() call. + * + * Data can be read and written into the buffer only sequentially. There is no + * way to access an arbitrary address inside the buffer. + * + * You can take ownership of the shared memory buffer with + * p_shm_buffer_take_ownership() to explicitly remove it from the system after + * closing. Please refer to the #PShm description to understand the intention of + * this action. + */ + +#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION) +# error "Header files shouldn't be included directly, consider using <plibsys.h> instead." +#endif + +#ifndef PLIBSYS_HEADER_PSHMBUFFER_H +#define PLIBSYS_HEADER_PSHMBUFFER_H + +#include <ptypes.h> +#include <pmacros.h> +#include <perror.h> + +P_BEGIN_DECLS + +/** Shared memory buffer opaque data structure. */ +typedef struct PShmBuffer_ PShmBuffer; + +/** + * @brief Creates a new #PShmBuffer structure. + * @param name Unique buffer name. + * @param size Buffer size in bytes, can't be changed later. + * @param[out] error Error report object, NULL to ignore. + * @return Pointer to the #PShmBuffer structure in case of success, NULL + * otherwise. + * @since 0.0.1 + * + * If a buffer with the same name already exists then the @a size will be + * ignored and the existing buffer will be returned. + */ +P_LIB_API PShmBuffer * p_shm_buffer_new (const pchar *name, + psize size, + PError **error); + +/** + * @brief Frees #PShmBuffer structure. + * @param buf #PShmBuffer to free. + * @since 0.0.1 + * + * Note that a buffer will be completely removed from the system only after the + * last instance of the buffer with the same name is closed. + */ +P_LIB_API void p_shm_buffer_free (PShmBuffer *buf); + +/** + * @brief Takes ownership of a shared memory buffer. + * @param buf Shared memory buffer. + * @since 0.0.1 + * + * If you take ownership of the shared memory buffer, p_shm_buffer_free() will + * try to completely unlink it and remove from the system. This is useful on + * UNIX systems, where shared memory can survive an application crash. On the + * Windows and OS/2 platforms this call has no effect. + * + * The common usage of this call is upon application startup to ensure that the + * memory segment from the previous crash can be removed from the system. To do + * that, call p_shm_buffer_new() and check if its condition is normal (used + * space, free space). If not, take ownership of the shared memory buffer object + * and remove it with the p_shm_buffer_free() call. After that, create it again. + */ +P_LIB_API void p_shm_buffer_take_ownership (PShmBuffer *buf); + +/** + * @brief Tries to read data from a shared memory buffer. + * @param buf #PShmBuffer to read data from. + * @param[out] storage Output buffer to put data in. + * @param len Storage size in bytes. + * @param[out] error Error report object, NULL to ignore. + * @return Number of read bytes (can be 0 if buffer is empty), or -1 if error + * occured. + * @since 0.0.1 + */ +P_LIB_API pint p_shm_buffer_read (PShmBuffer *buf, + ppointer storage, + psize len, + PError **error); + +/** + * @brief Tries to write data into a shared memory buffer. + * @param buf #PShmBuffer to write data into. + * @param data Data to write. + * @param len Data size in bytes. + * @param[out] error Error report object, NULL to ignore. + * @return Number of written bytes (can be 0 if buffer is full), or -1 if error + * occured. + * @since 0.0.1 + * @note Write operation is performed only if the buffer has enough space for + * the given data size. + */ +P_LIB_API pssize p_shm_buffer_write (PShmBuffer *buf, + ppointer data, + psize len, + PError **error); + +/** + * @brief Gets free space in the shared memory buffer. + * @param buf #PShmBuffer to check space in. + * @param[out] error Error report object, NULL to ignore. + * @return Free space in bytes in case of success, -1 otherwise. + * @since 0.0.1 + */ +P_LIB_API pssize p_shm_buffer_get_free_space (PShmBuffer *buf, + PError **error); + +/** + * @brief Gets used space in the shared memory buffer. + * @param buf #PShmBuffer to check space in. + * @param[out] error Error report object, NULL to ignore. + * @return Used space in bytes in case of success, -1 otherwise. + * @since 0.0.1 + */ +P_LIB_API pssize p_shm_buffer_get_used_space (PShmBuffer *buf, + PError **error); + +/** + * @brief Clears all data in the buffer and fills it with zeros. + * @param buf #PShmBuffer to clear. + * @since 0.0.1 + */ +P_LIB_API void p_shm_buffer_clear (PShmBuffer *buf); + +P_END_DECLS + +#endif /* PLIBSYS_HEADER_PSHMBUFFER_H */ diff --git a/3rdparty/plibsys/src/psocket.c b/3rdparty/plibsys/src/psocket.c new file mode 100644 index 0000000..fb04379 --- /dev/null +++ b/3rdparty/plibsys/src/psocket.c @@ -0,0 +1,1644 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2017 Alexander Saprykin <saprykin.spb@gmail.com> + * Some workarounds have been used from Glib (comments are kept) + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pmem.h" +#include "psocket.h" +#ifdef P_OS_SCO +# include "ptimeprofiler.h" +#endif +#include "perror-private.h" +#include "plibsys-private.h" +#include "psysclose-private.h" + +#include <stdlib.h> +#include <string.h> + +#ifndef P_OS_WIN +# include <fcntl.h> +# include <errno.h> +# include <unistd.h> +# include <signal.h> +# ifdef P_OS_VMS +# include <stropts.h> +# endif +#endif + +#ifndef P_OS_WIN +# if defined (P_OS_BEOS) || defined (P_OS_MAC) || defined (P_OS_MAC9) || \ + defined (P_OS_OS2) || defined (P_OS_AMIGA) +# define P_SOCKET_USE_SELECT +# include <sys/select.h> +# include <sys/time.h> +# else +# define P_SOCKET_USE_POLL +# include <sys/poll.h> +# endif +#endif + +/* On old Solaris systems SOMAXCONN is set to 5 */ +#define P_SOCKET_DEFAULT_BACKLOG 5 + +struct PSocket_ { + PSocketFamily family; + PSocketProtocol protocol; + PSocketType type; + pint fd; + pint listen_backlog; + pint timeout; + puint blocking : 1; + puint keepalive : 1; + puint closed : 1; + puint connected : 1; + puint listening : 1; +#ifdef P_OS_WIN + WSAEVENT events; +#endif +#ifdef P_OS_SCO + PTimeProfiler *timer; +#endif +}; + +#ifndef SHUT_RD +# define SHUT_RD 0 +#endif + +#ifndef SHUT_WR +# define SHUT_WR 1 +#endif + +#ifndef SHUT_RDWR +# define SHUT_RDWR 2 +#endif + +#ifdef MSG_NOSIGNAL +# define P_SOCKET_DEFAULT_SEND_FLAGS MSG_NOSIGNAL +#else +# define P_SOCKET_DEFAULT_SEND_FLAGS 0 +#endif + +static pboolean pp_socket_set_fd_blocking (pint fd, pboolean blocking, PError **error); +static pboolean pp_socket_check (const PSocket *socket, PError **error); +static pboolean pp_socket_set_details_from_fd (PSocket *socket, PError **error); + +static pboolean +pp_socket_set_fd_blocking (pint fd, + pboolean blocking, + PError **error) +{ +#ifndef P_OS_WIN + pint32 arg; +#else + pulong arg; +#endif + +#ifndef P_OS_WIN +# ifdef P_OS_VMS + arg = !blocking; +# if (PLIBSYS_SIZEOF_VOID_P == 8) +# pragma __pointer_size 32 +# endif + /* Explicit (void *) cast is necessary */ + if (P_UNLIKELY (ioctl (fd, FIONBIO, (void *) &arg) < 0)) { +# if (PLIBSYS_SIZEOF_VOID_P == 8) +# pragma __pointer_size 64 +# endif +# else + if (P_UNLIKELY ((arg = fcntl (fd, F_GETFL, NULL)) < 0)) { + P_WARNING ("PSocket::pp_socket_set_fd_blocking: fcntl() failed"); + arg = 0; + } + + arg = (!blocking) ? (arg | O_NONBLOCK) : (arg & ~O_NONBLOCK); + + if (P_UNLIKELY (fcntl (fd, F_SETFL, arg) < 0)) { +# endif +#else + arg = !blocking; + + if (P_UNLIKELY (ioctlsocket (fd, FIONBIO, &arg) == SOCKET_ERROR)) { +#endif + p_error_set_error_p (error, + (pint) p_error_get_io_from_system (p_error_get_last_net ()), + (pint) p_error_get_last_net (), + "Failed to set socket blocking flags"); + return FALSE; + } + + return TRUE; +} + +static pboolean +pp_socket_check (const PSocket *socket, + PError **error) +{ + if (P_UNLIKELY (socket->closed)) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_NOT_AVAILABLE, + 0, + "Socket is already closed"); + return FALSE; + } + + return TRUE; +} + +static pboolean +pp_socket_set_details_from_fd (PSocket *socket, + PError **error) +{ +#ifdef SO_DOMAIN + PSocketFamily family; +#endif + struct sockaddr_storage address; + pint fd, value; + socklen_t addrlen, optlen; +#ifdef P_OS_WIN + /* See comment below */ + BOOL bool_val = FALSE; +#else + pint bool_val; +#endif + + fd = socket->fd; + optlen = sizeof (value); + + if (P_UNLIKELY (getsockopt (fd, SOL_SOCKET, SO_TYPE, (ppointer) &value, &optlen) != 0)) { + p_error_set_error_p (error, + (pint) p_error_get_io_from_system (p_error_get_last_net ()), + (pint) p_error_get_last_net (), + "Failed to call getsockopt() to get socket info for fd"); + return FALSE; + } + + if (P_UNLIKELY (optlen != sizeof (value))) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_INVALID_ARGUMENT, + 0, + "Failed to get socket info for fd, bad option length"); + return FALSE; + } + + switch (value) { + case SOCK_STREAM: + socket->type = P_SOCKET_TYPE_STREAM; + break; + + case SOCK_DGRAM: + socket->type = P_SOCKET_TYPE_DATAGRAM; + break; + +#ifdef SOCK_SEQPACKET + case SOCK_SEQPACKET: + socket->type = P_SOCKET_TYPE_SEQPACKET; + break; +#endif + + default: + socket->type = P_SOCKET_TYPE_UNKNOWN; + break; + } + + addrlen = sizeof (address); + + if (P_UNLIKELY (getsockname (fd, (struct sockaddr *) &address, &addrlen) != 0)) { + p_error_set_error_p (error, + (pint) p_error_get_io_from_system (p_error_get_last_net ()), + (pint) p_error_get_last_net (), + "Failed to call getsockname() to get socket address info"); + return FALSE; + } + +#ifdef SO_DOMAIN + if (!(addrlen > 0)) { + optlen = sizeof (family); + + if (P_UNLIKELY (getsockopt (socket->fd, + SOL_SOCKET, + SO_DOMAIN, + (ppointer) &family, + &optlen) != 0)) { + p_error_set_error_p (error, + (pint) p_error_get_io_from_system (p_error_get_last_net ()), + (pint) p_error_get_last_net (), + "Failed to call getsockopt() to get socket SO_DOMAIN option"); + return FALSE; + } + } +#endif + + switch (address.ss_family) { + case P_SOCKET_FAMILY_INET: + socket->family = P_SOCKET_FAMILY_INET; + break; +#ifdef AF_INET6 + case P_SOCKET_FAMILY_INET6: + socket->family = P_SOCKET_FAMILY_INET6; + break; +#endif + default: + socket->family = P_SOCKET_FAMILY_UNKNOWN; + break; + } + +#ifdef AF_INET6 + if (socket->family == P_SOCKET_FAMILY_INET6 || socket->family == P_SOCKET_FAMILY_INET) { +#else + if (socket->family == P_SOCKET_FAMILY_INET) { +#endif + switch (socket->type) { + case P_SOCKET_TYPE_STREAM: + socket->protocol = P_SOCKET_PROTOCOL_TCP; + break; + case P_SOCKET_TYPE_DATAGRAM: + socket->protocol = P_SOCKET_PROTOCOL_UDP; + break; + case P_SOCKET_TYPE_SEQPACKET: + socket->protocol = P_SOCKET_PROTOCOL_SCTP; + break; + case P_SOCKET_TYPE_UNKNOWN: + break; + } + } + + if (P_LIKELY (socket->family != P_SOCKET_FAMILY_UNKNOWN)) { + addrlen = sizeof (address); + + if (getpeername (fd, (struct sockaddr *) &address, &addrlen) >= 0) + socket->connected = TRUE; + } + + optlen = sizeof (bool_val); + + if (getsockopt (fd, SOL_SOCKET, SO_KEEPALIVE, (ppointer) &bool_val, &optlen) == 0) { +#ifndef P_OS_WIN + /* Experimentation indicates that the SO_KEEPALIVE value is + * actually a char on Windows, even if documentation claims it + * to be a BOOL which is a typedef for int. */ + if (optlen != sizeof (bool_val)) + P_WARNING ("PSocket::pp_socket_set_details_from_fd: getsockopt() with SO_KEEPALIVE failed"); +#endif + socket->keepalive = !!bool_val; + } else + /* Can't read, maybe not supported, assume FALSE */ + socket->keepalive = FALSE; + + return TRUE; +} + +pboolean +p_socket_init_once (void) +{ +#ifdef P_OS_WIN + WORD ver_req; + WSADATA wsa_data; + + ver_req = MAKEWORD (2, 2); + + if (P_UNLIKELY (WSAStartup (ver_req, &wsa_data) != 0)) + return FALSE; + + if (P_UNLIKELY (LOBYTE (wsa_data.wVersion) != 2 || HIBYTE (wsa_data.wVersion) != 2)) { + WSACleanup (); + return FALSE; + } +#else +# ifdef SIGPIPE + signal (SIGPIPE, SIG_IGN); +# endif +#endif + return TRUE; +} + +void +p_socket_close_once (void) +{ +#ifdef P_OS_WIN + WSACleanup (); +#endif +} + +P_LIB_API PSocket * +p_socket_new_from_fd (pint fd, + PError **error) +{ + PSocket *ret; +#if !defined (P_OS_WIN) && defined (SO_NOSIGPIPE) + pint flags; +#endif + + if (P_UNLIKELY (fd < 0)) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_INVALID_ARGUMENT, + 0, + "Unable to create socket from bad fd"); + return NULL; + } + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PSocket))) == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_NO_RESOURCES, + 0, + "Failed to allocate memory for socket"); + return NULL; + } + + ret->fd = fd; + + if (P_UNLIKELY (pp_socket_set_details_from_fd (ret, error) == FALSE)) { + p_free (ret); + return NULL; + } + + if (P_UNLIKELY (pp_socket_set_fd_blocking (ret->fd, FALSE, error) == FALSE)) { + p_free (ret); + return NULL; + } + +#if !defined (P_OS_WIN) && defined (SO_NOSIGPIPE) + flags = 1; + + if (setsockopt (ret->fd, SOL_SOCKET, SO_NOSIGPIPE, &flags, sizeof (flags)) < 0) + P_WARNING ("PSocket::p_socket_new_from_fd: setsockopt() with SO_NOSIGPIPE failed"); +#endif + + p_socket_set_listen_backlog (ret, P_SOCKET_DEFAULT_BACKLOG); + + ret->timeout = 0; + ret->blocking = TRUE; + +#ifdef P_OS_SCO + if (P_UNLIKELY ((ret->timer = p_time_profiler_new ()) == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_NO_RESOURCES, + 0, + "Failed to allocate memory for internal timer"); + p_free (ret); + return NULL; + } +#endif + +#ifdef P_OS_WIN + if (P_UNLIKELY ((ret->events = WSACreateEvent ()) == WSA_INVALID_EVENT)) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_FAILED, + (pint) p_error_get_last_net (), + "Failed to call WSACreateEvent() on socket"); + p_free (ret); + return NULL; + } +#endif + + return ret; +} + +P_LIB_API PSocket * +p_socket_new (PSocketFamily family, + PSocketType type, + PSocketProtocol protocol, + PError **error) +{ + PSocket *ret; + pint native_type, fd; +#ifndef P_OS_WIN + pint flags; +#endif + + if (P_UNLIKELY (family == P_SOCKET_FAMILY_UNKNOWN || + type == P_SOCKET_TYPE_UNKNOWN || + protocol == P_SOCKET_PROTOCOL_UNKNOWN)) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_INVALID_ARGUMENT, + 0, + "Invalid input socket family, type or protocol"); + return NULL; + } + + switch (type) { + case P_SOCKET_TYPE_STREAM: + native_type = SOCK_STREAM; + break; + + case P_SOCKET_TYPE_DATAGRAM: + native_type = SOCK_DGRAM; + break; + +#ifdef SOCK_SEQPACKET + case P_SOCKET_TYPE_SEQPACKET: + native_type = SOCK_SEQPACKET; + break; +#endif + + default: + p_error_set_error_p (error, + (pint) P_ERROR_IO_INVALID_ARGUMENT, + 0, + "Unable to create socket with unknown family"); + return NULL; + } + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PSocket))) == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_NO_RESOURCES, + 0, + "Failed to allocate memory for socket"); + return NULL; + } + +#ifdef P_OS_SCO + if (P_UNLIKELY ((ret->timer = p_time_profiler_new ()) == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_NO_RESOURCES, + 0, + "Failed to allocate memory for internal timer"); + p_free (ret); + return NULL; + } +#endif + +#ifdef SOCK_CLOEXEC + native_type |= SOCK_CLOEXEC; +#endif + if (P_UNLIKELY ((fd = (pint) socket (family, native_type, protocol)) < 0)) { + p_error_set_error_p (error, + (pint) p_error_get_io_from_system (p_error_get_last_net ()), + (pint) p_error_get_last_net (), + "Failed to call socket() to create socket"); +#ifdef P_OS_SCO + p_time_profiler_free (ret->timer); +#endif + p_free (ret); + return NULL; + } + +#ifndef P_OS_WIN + flags = fcntl (fd, F_GETFD, 0); + + if (P_LIKELY (flags != -1 && (flags & FD_CLOEXEC) == 0)) { + flags |= FD_CLOEXEC; + + if (P_UNLIKELY (fcntl (fd, F_SETFD, flags) < 0)) + P_WARNING ("PSocket::p_socket_new: fcntl() with FD_CLOEXEC failed"); + } +#endif + + ret->fd = fd; + +#ifdef P_OS_WIN + ret->events = WSA_INVALID_EVENT; +#endif + + if (P_UNLIKELY (pp_socket_set_fd_blocking (ret->fd, FALSE, error) == FALSE)) { + p_socket_free (ret); + return NULL; + } + +#if !defined (P_OS_WIN) && defined (SO_NOSIGPIPE) + flags = 1; + + if (setsockopt (ret->fd, SOL_SOCKET, SO_NOSIGPIPE, &flags, sizeof (flags)) < 0) + P_WARNING ("PSocket::p_socket_new: setsockopt() with SO_NOSIGPIPE failed"); +#endif + + ret->timeout = 0; + ret->blocking = TRUE; + ret->family = family; + ret->protocol = protocol; + ret->type = type; + + p_socket_set_listen_backlog (ret, P_SOCKET_DEFAULT_BACKLOG); + +#ifdef P_OS_WIN + if (P_UNLIKELY ((ret->events = WSACreateEvent ()) == WSA_INVALID_EVENT)) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_FAILED, + (pint) p_error_get_last_net (), + "Failed to call WSACreateEvent() on socket"); + p_socket_free (ret); + return NULL; + } +#endif + + return ret; +} + +P_LIB_API pint +p_socket_get_fd (const PSocket *socket) +{ + if (P_UNLIKELY (socket == NULL)) + return -1; + + return socket->fd; +} + +P_LIB_API PSocketFamily +p_socket_get_family (const PSocket *socket) +{ + if (P_UNLIKELY (socket == NULL)) + return P_SOCKET_FAMILY_UNKNOWN; + + return socket->family; +} + +P_LIB_API PSocketType +p_socket_get_type (const PSocket *socket) +{ + if (P_UNLIKELY (socket == NULL)) + return P_SOCKET_TYPE_UNKNOWN; + + return socket->type; +} + +P_LIB_API PSocketProtocol +p_socket_get_protocol (const PSocket *socket) +{ + if (P_UNLIKELY (socket == NULL)) + return P_SOCKET_PROTOCOL_UNKNOWN; + + return socket->protocol; +} + +P_LIB_API pboolean +p_socket_get_keepalive (const PSocket *socket) +{ + if (P_UNLIKELY (socket == NULL)) + return FALSE; + + return socket->keepalive; +} + +P_LIB_API pboolean +p_socket_get_blocking (PSocket *socket) +{ + if (P_UNLIKELY (socket == NULL)) + return FALSE; + + return socket->blocking; +} + +P_LIB_API int +p_socket_get_listen_backlog (const PSocket *socket) +{ + if (P_UNLIKELY (socket == NULL)) + return -1; + + return socket->listen_backlog; +} + +P_LIB_API pint +p_socket_get_timeout (const PSocket *socket) +{ + if (P_UNLIKELY (socket == NULL)) + return -1; + + return socket->timeout; +} + +P_LIB_API PSocketAddress * +p_socket_get_local_address (const PSocket *socket, + PError **error) +{ + struct sockaddr_storage buffer; + socklen_t len; + PSocketAddress *ret; + + if (P_UNLIKELY (socket == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return NULL; + } + + len = sizeof (buffer); + + if (P_UNLIKELY (getsockname (socket->fd, (struct sockaddr *) &buffer, &len) < 0)) { + p_error_set_error_p (error, + (pint) p_error_get_io_from_system (p_error_get_last_net ()), + (pint) p_error_get_last_net (), + "Failed to call getsockname() to get local socket address"); + return NULL; + } + + ret = p_socket_address_new_from_native (&buffer, (psize) len); + + if (P_UNLIKELY (ret == NULL)) + p_error_set_error_p (error, + (pint) P_ERROR_IO_FAILED, + 0, + "Failed to create socket address from native structure"); + + return ret; +} + +P_LIB_API PSocketAddress * +p_socket_get_remote_address (const PSocket *socket, + PError **error) +{ + struct sockaddr_storage buffer; + socklen_t len; + PSocketAddress *ret; + + if (P_UNLIKELY (socket == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return NULL; + } + + len = sizeof (buffer); + + if (P_UNLIKELY (getpeername (socket->fd, (struct sockaddr *) &buffer, &len) < 0)) { + p_error_set_error_p (error, + (pint) p_error_get_io_from_system (p_error_get_last_net ()), + (pint) p_error_get_last_net (), + "Failed to call getpeername() to get remote socket address"); + return NULL; + } + +#ifdef P_OS_SYLLABLE + /* Syllable has a bug with a wrong byte order for a TCP port, + * as it only supports IPv4 we can easily fix it here. */ + ((struct sockaddr_in *) &buffer)->sin_port = + p_htons (((struct sockaddr_in *) &buffer)->sin_port); +#endif + + ret = p_socket_address_new_from_native (&buffer, (psize) len); + + if (P_UNLIKELY (ret == NULL)) + p_error_set_error_p (error, + (pint) P_ERROR_IO_FAILED, + 0, + "Failed to create socket address from native structure"); + + return ret; +} + +P_LIB_API pboolean +p_socket_is_connected (const PSocket *socket) +{ + if (P_UNLIKELY (socket == NULL)) + return FALSE; + + return socket->connected; +} + +P_LIB_API pboolean +p_socket_is_closed (const PSocket *socket) +{ + if (P_UNLIKELY (socket == NULL)) + return TRUE; + + return socket->closed; +} + +P_LIB_API pboolean +p_socket_check_connect_result (PSocket *socket, + PError **error) +{ + socklen_t optlen; + pint val; + + if (P_UNLIKELY (socket == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return FALSE; + } + + optlen = sizeof (val); + + if (P_UNLIKELY (getsockopt (socket->fd, SOL_SOCKET, SO_ERROR, (ppointer) &val, &optlen) < 0)) { + p_error_set_error_p (error, + (pint) p_error_get_io_from_system (p_error_get_last_net ()), + (pint) p_error_get_last_net (), + "Failed to call getsockopt() to get connection status"); + return FALSE; + } + + if (P_UNLIKELY (val != 0)) + p_error_set_error_p (error, + (pint) p_error_get_io_from_system (val), + val, + "Error in socket layer"); + + socket->connected = (val == 0); + + return (val == 0); +} + +P_LIB_API void +p_socket_set_keepalive (PSocket *socket, + pboolean keepalive) +{ +#ifdef P_OS_WIN + pchar value; +#else + pint value; +#endif + + if (P_UNLIKELY (socket == NULL)) + return; + + if (socket->keepalive == (puint) !!keepalive) + return; + +#ifdef P_OS_WIN + value = !! (pchar) keepalive; +#else + value = !! (pint) keepalive; +#endif + if (setsockopt (socket->fd, SOL_SOCKET, SO_KEEPALIVE, &value, sizeof (value)) < 0) { + P_WARNING ("PSocket::p_socket_set_keepalive: setsockopt() with SO_KEEPALIVE failed"); + return; + } + + socket->keepalive = !! (pint) keepalive; +} + +P_LIB_API void +p_socket_set_blocking (PSocket *socket, + pboolean blocking) +{ + if (P_UNLIKELY (socket == NULL)) + return; + + socket->blocking = !! blocking; +} + +P_LIB_API void +p_socket_set_listen_backlog (PSocket *socket, + pint backlog) +{ + if (P_UNLIKELY (socket == NULL || socket->listening)) + return; + + socket->listen_backlog = backlog; +} + +P_LIB_API void +p_socket_set_timeout (PSocket *socket, + pint timeout) +{ + if (P_UNLIKELY (socket == NULL)) + return; + + if (timeout < 0) + timeout = 0; + + socket->timeout = timeout; +} + +P_LIB_API pboolean +p_socket_bind (const PSocket *socket, + PSocketAddress *address, + pboolean allow_reuse, + PError **error) +{ + struct sockaddr_storage addr; + +#ifdef SO_REUSEPORT + pboolean reuse_port; +#endif + +#ifdef P_OS_WIN + pchar value; +#else + pint value; +#endif + + if (P_UNLIKELY (socket == NULL || address == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return FALSE; + } + + if (P_UNLIKELY (pp_socket_check (socket, error) == FALSE)) + return FALSE; + + /* Windows allows to reuse the same address even for an active TCP + * connection, that's why on Windows we should use SO_REUSEADDR only + * for UDP sockets, UNIX doesn't have such behavior + * + * Ignore errors here, the only likely error is "not supported", and + * this is a "best effort" thing mainly */ + +#ifdef P_OS_WIN + value = !! (pchar) (allow_reuse && (socket->type == P_SOCKET_TYPE_DATAGRAM)); +#else + value = !! (pint) allow_reuse; +#endif + + if (setsockopt (socket->fd, SOL_SOCKET, SO_REUSEADDR, &value, sizeof (value)) < 0) + P_WARNING ("PSocket::p_socket_bind: setsockopt() with SO_REUSEADDR failed"); + +#ifdef SO_REUSEPORT + reuse_port = allow_reuse && (socket->type == P_SOCKET_TYPE_DATAGRAM); + +# ifdef P_OS_WIN + value = !! (pchar) reuse_port; +# else + value = !! (pint) reuse_port; +# endif + + if (setsockopt (socket->fd, SOL_SOCKET, SO_REUSEPORT, &value, sizeof (value)) < 0) + P_WARNING ("PSocket::p_socket_bind: setsockopt() with SO_REUSEPORT failed"); +#endif + + if (P_UNLIKELY (p_socket_address_to_native (address, &addr, sizeof (addr)) == FALSE)) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_FAILED, + 0, + "Failed to convert socket address to native structure"); + return FALSE; + } + + if (P_UNLIKELY (bind (socket->fd, + (struct sockaddr *) &addr, + (socklen_t) p_socket_address_get_native_size (address)) < 0)) { + p_error_set_error_p (error, + (pint) p_error_get_io_from_system (p_error_get_last_net ()), + (pint) p_error_get_last_net (), + "Failed to call bind() on socket"); + return FALSE; + } + + return TRUE; +} + +P_LIB_API pboolean +p_socket_connect (PSocket *socket, + PSocketAddress *address, + PError **error) +{ + struct sockaddr_storage buffer; + pint err_code; + pint conn_result; + PErrorIO sock_err; + + if (P_UNLIKELY (socket == NULL || address == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return FALSE; + } + + if (P_UNLIKELY (pp_socket_check (socket, error) == FALSE)) + return FALSE; + + if (P_UNLIKELY (p_socket_address_to_native (address, &buffer, sizeof (buffer)) == FALSE)) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_FAILED, + 0, + "Failed to convert socket address to native structure"); + return FALSE; + } + +#if !defined (P_OS_WIN) && defined (EINTR) + for (;;) { + conn_result = connect (socket->fd, (struct sockaddr *) &buffer, + (socklen_t) p_socket_address_get_native_size (address)); + + if (P_LIKELY (conn_result == 0)) + break; + + err_code = p_error_get_last_net (); + + if (err_code == EINTR) + continue; + else + break; + } +#else + conn_result = connect (socket->fd, (struct sockaddr *) &buffer, + (pint) p_socket_address_get_native_size (address)); + + if (conn_result != 0) + err_code = p_error_get_last_net (); +#endif + + if (conn_result == 0) { + socket->connected = TRUE; + return TRUE; + } + + sock_err = p_error_get_io_from_system (err_code); + + if (P_LIKELY (sock_err == P_ERROR_IO_WOULD_BLOCK || sock_err == P_ERROR_IO_IN_PROGRESS)) { + if (socket->blocking) { + if (p_socket_io_condition_wait (socket, + P_SOCKET_IO_CONDITION_POLLOUT, + error) == TRUE && + p_socket_check_connect_result (socket, error) == TRUE) { + socket->connected = TRUE; + return TRUE; + } + } else + p_error_set_error_p (error, + (pint) sock_err, + err_code, + "Couldn't block non-blocking socket"); + } else + p_error_set_error_p (error, + (pint) sock_err, + err_code, + "Failed to call connect() on socket"); + + return FALSE; +} + +P_LIB_API pboolean +p_socket_listen (PSocket *socket, + PError **error) +{ + if (P_UNLIKELY (socket == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return FALSE; + } + + if (P_UNLIKELY (pp_socket_check (socket, error) == FALSE)) + return FALSE; + + if (P_UNLIKELY (listen (socket->fd, socket->listen_backlog) < 0)) { + p_error_set_error_p (error, + (pint) p_error_get_io_from_system (p_error_get_last_net ()), + (pint) p_error_get_last_net (), + "Failed to call listen() on socket"); + return FALSE; + } + + socket->listening = TRUE; + return TRUE; +} + +P_LIB_API PSocket * +p_socket_accept (const PSocket *socket, + PError **error) +{ + PSocket *ret; + PErrorIO sock_err; + pint res; + pint err_code; +#ifndef P_OS_WIN + pint flags; +#endif + + if (P_UNLIKELY (socket == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return NULL; + } + + if (P_UNLIKELY (pp_socket_check (socket, error) == FALSE)) + return NULL; + + for (;;) { + if (socket->blocking && + p_socket_io_condition_wait (socket, + P_SOCKET_IO_CONDITION_POLLIN, + error) == FALSE) + return NULL; + + if ((res = (pint) accept (socket->fd, NULL, 0)) < 0) { + err_code = p_error_get_last_net (); +#if !defined (P_OS_WIN) && defined (EINTR) + if (p_error_get_last_net () == EINTR) + continue; +#endif + sock_err = p_error_get_io_from_system (err_code); + + if (socket->blocking && sock_err == P_ERROR_IO_WOULD_BLOCK) + continue; + + p_error_set_error_p (error, + (pint) sock_err, + err_code, + "Failed to call accept() on socket"); + + return NULL; + } + + break; + } + +#ifdef P_OS_WIN + /* The socket inherits the accepting sockets event mask and even object, + * we need to remove that */ + WSAEventSelect (res, NULL, 0); +#else + flags = fcntl (res, F_GETFD, 0); + + if (P_LIKELY (flags != -1 && (flags & FD_CLOEXEC) == 0)) { + flags |= FD_CLOEXEC; + + if (P_UNLIKELY (fcntl (res, F_SETFD, flags) < 0)) + P_WARNING ("PSocket::p_socket_accept: fcntl() with FD_CLOEXEC failed"); + } +#endif + + if (P_UNLIKELY ((ret = p_socket_new_from_fd (res, error)) == NULL)) { + if (P_UNLIKELY (p_sys_close (res) != 0)) + P_WARNING ("PSocket::p_socket_accept: p_sys_close() failed"); + } else + ret->protocol = socket->protocol; + + return ret; +} + +P_LIB_API pssize +p_socket_receive (const PSocket *socket, + pchar *buffer, + psize buflen, + PError **error) +{ + PErrorIO sock_err; + pssize ret; + pint err_code; + + if (P_UNLIKELY (socket == NULL || buffer == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return -1; + } + + if (P_UNLIKELY (pp_socket_check (socket, error) == FALSE)) + return -1; + + for (;;) { + if (socket->blocking && + p_socket_io_condition_wait (socket, + P_SOCKET_IO_CONDITION_POLLIN, + error) == FALSE) + return -1; + + if ((ret = recv (socket->fd, buffer, (socklen_t) buflen, 0)) < 0) { + err_code = p_error_get_last_net (); + +#if !defined (P_OS_WIN) && defined (EINTR) + if (err_code == EINTR) + continue; +#endif + sock_err = p_error_get_io_from_system (err_code); + + if (socket->blocking && sock_err == P_ERROR_IO_WOULD_BLOCK) + continue; + + p_error_set_error_p (error, + (pint) sock_err, + err_code, + "Failed to call recv() on socket"); + + return -1; + } + + break; + } + + return ret; +} + +P_LIB_API pssize +p_socket_receive_from (const PSocket *socket, + PSocketAddress **address, + pchar *buffer, + psize buflen, + PError **error) +{ + PErrorIO sock_err; + struct sockaddr_storage sa; + socklen_t optlen; + pssize ret; + pint err_code; + + if (P_UNLIKELY (socket == NULL || buffer == NULL || buflen == 0)) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return -1; + } + + if (P_UNLIKELY (pp_socket_check (socket, error) == FALSE)) + return -1; + + optlen = sizeof (sa); + + for (;;) { + if (socket->blocking && + p_socket_io_condition_wait (socket, + P_SOCKET_IO_CONDITION_POLLIN, + error) == FALSE) + return -1; + + if ((ret = recvfrom (socket->fd, + buffer, + (socklen_t) buflen, + 0, + (struct sockaddr *) &sa, + &optlen)) < 0) { + err_code = p_error_get_last_net (); + +#if !defined (P_OS_WIN) && defined (EINTR) + if (err_code == EINTR) + continue; +#endif + sock_err = p_error_get_io_from_system (err_code); + + if (socket->blocking && sock_err == P_ERROR_IO_WOULD_BLOCK) + continue; + + p_error_set_error_p (error, + (pint) sock_err, + err_code, + "Failed to call recvfrom() on socket"); + + return -1; + } + + break; + } + + if (address != NULL) + *address = p_socket_address_new_from_native (&sa, optlen); + + return ret; +} + +P_LIB_API pssize +p_socket_send (const PSocket *socket, + const pchar *buffer, + psize buflen, + PError **error) +{ + PErrorIO sock_err; + pssize ret; + pint err_code; + + if (P_UNLIKELY (socket == NULL || buffer == NULL || buflen == 0)) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return -1; + } + + if (P_UNLIKELY (pp_socket_check (socket, error) == FALSE)) + return -1; + + for (;;) { + if (socket->blocking && + p_socket_io_condition_wait (socket, + P_SOCKET_IO_CONDITION_POLLOUT, + error) == FALSE) + return -1; + + if ((ret = send (socket->fd, + buffer, + (socklen_t) buflen, + P_SOCKET_DEFAULT_SEND_FLAGS)) < 0) { + err_code = p_error_get_last_net (); + +#if !defined (P_OS_WIN) && defined (EINTR) + if (err_code == EINTR) + continue; +#endif + sock_err = p_error_get_io_from_system (err_code); + + if (socket->blocking && sock_err == P_ERROR_IO_WOULD_BLOCK) + continue; + + p_error_set_error_p (error, + (pint) sock_err, + err_code, + "Failed to call send() on socket"); + + return -1; + } + + break; + } + + return ret; +} + +P_LIB_API pssize +p_socket_send_to (const PSocket *socket, + PSocketAddress *address, + const pchar *buffer, + psize buflen, + PError **error) +{ + PErrorIO sock_err; + struct sockaddr_storage sa; + socklen_t optlen; + pssize ret; + pint err_code; + + if (!socket || !address || !buffer) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return -1; + } + + if (!pp_socket_check (socket, error)) + return -1; + + if (!p_socket_address_to_native (address, &sa, sizeof (sa))) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_FAILED, + 0, + "Failed to convert socket address to native structure"); + return -1; + } + + optlen = (socklen_t) p_socket_address_get_native_size (address); + + for (;;) { + if (socket->blocking && + p_socket_io_condition_wait (socket, P_SOCKET_IO_CONDITION_POLLOUT, error) == FALSE) + return -1; + + if ((ret = sendto (socket->fd, + buffer, + (socklen_t) buflen, + 0, + (struct sockaddr *) &sa, + optlen)) < 0) { + err_code = p_error_get_last_net (); + +#if !defined (P_OS_WIN) && defined (EINTR) + if (err_code == EINTR) + continue; +#endif + sock_err = p_error_get_io_from_system (err_code); + + if (socket->blocking && sock_err == P_ERROR_IO_WOULD_BLOCK) + continue; + + p_error_set_error_p (error, + (pint) sock_err, + err_code, + "Failed to call sendto() on socket"); + + return -1; + } + + break; + } + + return ret; +} + +P_LIB_API pboolean +p_socket_close (PSocket *socket, + PError **error) +{ + pint err_code; + + if (P_UNLIKELY (socket == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return FALSE; + } + + if (socket->closed) + return TRUE; + + if (P_LIKELY (p_sys_close (socket->fd) == 0)) { + socket->connected = FALSE; + socket->closed = TRUE; + socket->listening = FALSE; + socket->fd = -1; + + return TRUE; + } else { + err_code = p_error_get_last_net (); + + p_error_set_error_p (error, + (pint) p_error_get_io_from_system (err_code), + err_code, + "Failed to close socket"); + + return FALSE; + } +} + +P_LIB_API pboolean +p_socket_shutdown (PSocket *socket, + pboolean shutdown_read, + pboolean shutdown_write, + PError **error) +{ + pint how; + + if (P_UNLIKELY (socket == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return FALSE; + } + + if (P_UNLIKELY (pp_socket_check (socket, error) == FALSE)) + return FALSE; + + if (P_UNLIKELY (shutdown_read == FALSE && shutdown_write == FALSE)) + return TRUE; + +#ifndef P_OS_WIN + if (shutdown_read == TRUE && shutdown_write == TRUE) + how = SHUT_RDWR; + else if (shutdown_read == TRUE) + how = SHUT_RD; + else + how = SHUT_WR; +#else + if (shutdown_read == TRUE && shutdown_write == TRUE) + how = SD_BOTH; + else if (shutdown_read == TRUE) + how = SD_RECEIVE; + else + how = SD_SEND; +#endif + + if (P_UNLIKELY (shutdown (socket->fd, how) != 0)) { + p_error_set_error_p (error, + (pint) p_error_get_io_from_system (p_error_get_last_net ()), + (pint) p_error_get_last_net (), + "Failed to call shutdown() on socket"); + return FALSE; + } + + if (shutdown_read == TRUE && shutdown_write == TRUE) + socket->connected = FALSE; + + return TRUE; +} + +P_LIB_API void +p_socket_free (PSocket *socket) +{ + if (P_UNLIKELY (socket == NULL)) + return; + +#ifdef P_OS_WIN + if (P_LIKELY (socket->events != WSA_INVALID_EVENT)) + WSACloseEvent (socket->events); +#endif + + p_socket_close (socket, NULL); + +#ifdef P_OS_SCO + if (P_LIKELY (socket->timer != NULL)) + p_time_profiler_free (socket->timer); +#endif + + p_free (socket); +} + +P_LIB_API pboolean +p_socket_set_buffer_size (const PSocket *socket, + PSocketDirection dir, + psize size, + PError **error) +{ + pint optname; + pint optval; + + if (P_UNLIKELY (socket == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return FALSE; + } + + if (P_UNLIKELY (pp_socket_check (socket, error) == FALSE)) + return FALSE; + + optname = (dir == P_SOCKET_DIRECTION_RCV) ? SO_RCVBUF : SO_SNDBUF; + optval = (pint) size; + + if (P_UNLIKELY (setsockopt (socket->fd, + SOL_SOCKET, + optname, + (pconstpointer) &optval, + sizeof (optval)) != 0)) { + p_error_set_error_p (error, + (pint) p_error_get_io_from_system (p_error_get_last_net ()), + (pint) p_error_get_last_net (), + "Failed to call setsockopt() on socket to set buffer size"); + return FALSE; + } + + return TRUE; +} + +P_LIB_API pboolean +p_socket_io_condition_wait (const PSocket *socket, + PSocketIOCondition condition, + PError **error) +{ +#if defined (P_OS_WIN) + long network_events; + pint evret; + pint timeout; + + if (P_UNLIKELY (socket == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return FALSE; + } + + if (P_UNLIKELY (pp_socket_check (socket, error) == FALSE)) + return FALSE; + + timeout = socket->timeout > 0 ? socket->timeout : WSA_INFINITE; + + if (condition == P_SOCKET_IO_CONDITION_POLLIN) + network_events = FD_READ | FD_ACCEPT; + else + network_events = FD_WRITE | FD_CONNECT; + + WSAResetEvent (socket->events); + WSAEventSelect (socket->fd, socket->events, network_events); + + evret = WSAWaitForMultipleEvents (1, (const HANDLE *) &socket->events, TRUE, timeout, FALSE); + + if (evret == WSA_WAIT_EVENT_0) + return TRUE; + else if (evret == WSA_WAIT_TIMEOUT) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_TIMED_OUT, + (pint) p_error_get_last_net (), + "Timed out while waiting socket condition"); + return FALSE; + } else { + p_error_set_error_p (error, + (pint) p_error_get_io_from_system (p_error_get_last_net ()), + (pint) p_error_get_last_net (), + "Failed to call WSAWaitForMultipleEvents() on socket"); + return FALSE; + } +#elif defined (P_SOCKET_USE_POLL) + struct pollfd pfd; + pint evret; + pint timeout; + + if (P_UNLIKELY (socket == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return FALSE; + } + + if (P_UNLIKELY (pp_socket_check (socket, error) == FALSE)) + return FALSE; + + timeout = socket->timeout > 0 ? socket->timeout : -1; + + pfd.fd = socket->fd; + pfd.revents = 0; + + if (condition == P_SOCKET_IO_CONDITION_POLLIN) + pfd.events = POLLIN; + else + pfd.events = POLLOUT; + +# ifdef P_OS_SCO + p_time_profiler_reset (socket->timer); +# endif + + while (TRUE) { + evret = poll (&pfd, 1, timeout); + +# ifdef EINTR + if (evret == -1 && p_error_get_last_net () == EINTR) { +# ifdef P_OS_SCO + if (timeout < 0 || + (p_time_profiler_elapsed_usecs (socket->timer) / 1000) < (puint64) timeout) + continue; + else + evret = 0; +# else + continue; +# endif + } +# endif + + if (evret == 1) + return TRUE; + else if (evret == 0) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_TIMED_OUT, + (pint) p_error_get_last_net (), + "Timed out while waiting socket condition"); + return FALSE; + } else { + p_error_set_error_p (error, + (pint) p_error_get_io_from_system (p_error_get_last_net ()), + (pint) p_error_get_last_net (), + "Failed to call poll() on socket"); + return FALSE; + } + } +#else + fd_set fds; + struct timeval tv; + struct timeval * ptv; + pint evret; + + if (P_UNLIKELY (socket == NULL)) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_INVALID_ARGUMENT, + 0, + "Invalid input argument"); + return FALSE; + } + + if (P_UNLIKELY (pp_socket_check (socket, error) == FALSE)) + return FALSE; + + if (socket->timeout > 0) + ptv = &tv; + else + ptv = NULL; + + while (TRUE) { + FD_ZERO (&fds); + FD_SET (socket->fd, &fds); + + if (socket->timeout > 0) { + tv.tv_sec = socket->timeout / 1000; + tv.tv_usec = (socket->timeout % 1000) * 1000; + } + + if (condition == P_SOCKET_IO_CONDITION_POLLIN) + evret = select (socket->fd + 1, &fds, NULL, NULL, ptv); + else + evret = select (socket->fd + 1, NULL, &fds, NULL, ptv); + +#ifdef EINTR + if (evret == -1 && p_error_get_last_net () == EINTR) + continue; +#endif + + if (evret == 1) + return TRUE; + else if (evret == 0) { + p_error_set_error_p (error, + (pint) P_ERROR_IO_TIMED_OUT, + (pint) p_error_get_last_net (), + "Timed out while waiting socket condition"); + return FALSE; + } else { + p_error_set_error_p (error, + (pint) p_error_get_io_from_system (p_error_get_last_net ()), + (pint) p_error_get_last_net (), + "Failed to call select() on socket"); + return FALSE; + } + } +#endif +} diff --git a/3rdparty/plibsys/src/psocket.h b/3rdparty/plibsys/src/psocket.h new file mode 100644 index 0000000..989e707 --- /dev/null +++ b/3rdparty/plibsys/src/psocket.h @@ -0,0 +1,779 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * @file psocket.h + * @brief Socket implementation + * @author Alexander Saprykin + * + * A socket is a communication primitive usually working over a network. You can + * send data to someone's socket by its address and receive data as well through + * the same socket. This is one of the most popular and standardizated way for + * network communication supported by vast majority of all the modern operating + * systems. It also hides all the details of underlying networking protocols and + * other layers, providing a unified and transparent approach for communication. + * + * There are two kinds of socket: + * - connection oriented (or stream sockets, i.e. TCP); + * - connection-less (or datagram sockets, i.e. UDP). + * + * Connection oriented sockets work with data in a stream, connection-less + * sockets work with data using independent packets (datagrams). The former + * guarantees delivery, while the latter doesn't (actually some connection-less + * protocols provide delivery quarantee, i.e. SCTP). + * + * #PSocket supports INET and INET6 address families which specify network + * communication addresses used by created sockets: IPv4 and IPv6, + * correspondingly. INET6 family is not supported on all platforms, refer to + * documentation for a particular target platform. + * + * #PSocket supports different underlying data transfer protocols: TCP, UDP and + * others. Note that not all protocols can be used with any socket type, i.e. + * you can use the TCP protocol with a stream socket, but you can't use the UDP + * protocol with the stream socket. You can specify #P_SOCKET_PROTOCOL_DEFAULT + * protocol when creating a socket and appropriate the best matching socket type + * will be selected. + * + * In a common socket communication case server and client sides are involved. + * Depending on whether sockets are connection oriented, there are slightly + * different action sequences for data exchanging. + * + * For connection oriented sockets the server side acts as following: + * - creates a socket using p_socket_new(); + * - binds the socket to a particular local address using p_socket_bind(); + * - starts to listen incoming connections using p_socket_listen(); + * - takes an incoming connection from the internal queue using + * p_socket_accept(). + * + * The client side acts as following: + * - creates a socket using p_socket_new(); + * - binds the socket to a particular local address using p_socket_bind(); + * - connects to the server using p_socket_connect(). + * + * After the connection was successfully established, both the sides can send + * and receive data from each other using p_socket_send() and + * p_socket_receive(). Binding of the client socket is actually optional. + * + * When using connection-less sockets, all is a bit simpler. There is no server + * side or client side - anyone can send and receive data without establishing a + * connection. Just create a socket, bind it to a local address and send/receive + * data using p_socket_send_to() and p_socket_receive(). You can also call + * p_socket_connect() on a connection-less socket to prevent passing the target + * address each time when sending data and then use p_socket_send() instead of + * p_socket_send_to(). This time binding is required. + * + * #PSocket can operate in blocking and non-blocking (async) modes. By default + * it is in the blocking mode. When using #PSocket in the blocking mode each + * non-immediate call on it will block a caller thread until an I/O operation + * will be completed. For example, the p_socket_accept() call can wait for an + * incoming connection for some time, and calling it on a blocking socket will + * prevent the caller thread from further execution until it receives a new + * incoming connection. In the non-blocking mode any call will return + * immediately and you must check its result. You can set the socket mode using + * p_socket_set_blocking(). + * + * #PSocket always puts a socket descriptor (or SOCKET handle on Windows) into + * the non-blocking mode and emulates the blocking mode if required. If you need + * to perform some hacks and need blocking behavior from the descriptor for some + * reason, use p_socket_get_fd() to get an internal socket descriptor (SOCKET + * handle on Windows). + * + * The close-on-exec flag is always set on the socket desciptor. Use + * p_socket_get_fd() to overwrite this behavior. + * + * #PSocket ignores the SIGPIPE signal on UNIX systems if possible. Take it into + * account if you want to handle this signal. + * + * Note that before using the #PSocket API you must call p_libsys_init() in + * order to initialize system resources (on UNIX this will do nothing, but on + * Windows this routine is required). Usually this routine should be called on a + * program's start. + * + * Here is an example of #PSocket usage: + * @code + * PSocketAddress *addr; + * PSocket *sock; + * + * p_libsys_init (); + * ... + * if ((addr = p_socket_address_new ("127.0.0.1", 5432)) == NULL) { + * ... + * } + * + * if ((sock = p_socket_new (P_SOCKET_FAMILY_INET, + * P_SOCKET_TYPE_DATAGRAM, + * P_SOCKET_PROTOCOL_UDP)) == NULL) { + * p_socket_address_free (addr); + * ... + * } + * + * if (!p_socket_bind (sock, addr, FALSE)) { + * p_socket_address_free(addr); + * p_socket_free(sock); + * ... + * } + * + * ... + * p_socket_address_free (addr); + * p_socket_close (sock); + * p_socket_free (sock); + * p_libsys_shutdown (); + * @endcode + * Here a UDP socket was created, bound to the localhost address and the port + * @a 5432. Do not forget to close the socket and free memory after its usage. + */ + +#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION) +# error "Header files shouldn't be included directly, consider using <plibsys.h> instead." +#endif + +#ifndef PLIBSYS_HEADER_PSOCKET_H +#define PLIBSYS_HEADER_PSOCKET_H + +#include <pmacros.h> +#include <psocketaddress.h> +#include <perror.h> + +P_BEGIN_DECLS + +/** Socket protocols specified by the IANA. */ +typedef enum PSocketProtocol_ { + P_SOCKET_PROTOCOL_UNKNOWN = -1, /**< Unknown protocol. */ + P_SOCKET_PROTOCOL_DEFAULT = 0, /**< Default protocol. */ + P_SOCKET_PROTOCOL_TCP = 6, /**< TCP protocol. */ + P_SOCKET_PROTOCOL_UDP = 17, /**< UDP protocol. */ + P_SOCKET_PROTOCOL_SCTP = 132 /**< SCTP protocol. */ +} PSocketProtocol; + +/** Socket types. */ +typedef enum PSocketType_ { + P_SOCKET_TYPE_UNKNOWN = 0, /**< Unknown type. */ + P_SOCKET_TYPE_STREAM = 1, /**< Connection oritented, reliable, stream of bytes (i.e. TCP). */ + P_SOCKET_TYPE_DATAGRAM = 2, /**< Connection-less, unreliable, datagram passing (i.e. UDP). */ + P_SOCKET_TYPE_SEQPACKET = 3 /**< Connection-less, reliable, datagram passing (i.e. SCTP). */ +} PSocketType; + +/** Socket direction for data operations. */ +typedef enum PSocketDirection_ { + P_SOCKET_DIRECTION_SND = 0, /**< Send direction. */ + P_SOCKET_DIRECTION_RCV = 1 /**< Receive direction. */ +} PSocketDirection; + +/** Socket IO waiting (polling) conditions. */ +typedef enum PSocketIOCondition_ { + P_SOCKET_IO_CONDITION_POLLIN = 1, /**< Ready to read. */ + P_SOCKET_IO_CONDITION_POLLOUT = 2 /**< Ready to write. */ +} PSocketIOCondition; + +/** Socket opaque structure. */ +typedef struct PSocket_ PSocket; + +/** + * @brief Creates a new #PSocket object from a file descriptor. + * @param fd File descriptor to create the socket from. + * @param[out] error Error report object, NULL to ignore. + * @return Pointer to #PSocket in case of success, NULL otherwise. + * @since 0.0.1 + * @sa p_socket_new(), p_socket_get_fd() + * + * The given file descriptor @a fd will be put in a non-blocking mode. #PSocket + * will emulate a blocking mode if required. + * + * If the socket was not bound yet then on some systems (i.e. Windows) call may + * fail to get a socket family from the descriptor thus failing to construct the + * #PSocket object. + */ +P_LIB_API PSocket * p_socket_new_from_fd (pint fd, + PError **error); + +/** + * @brief Creates a new #PSocket object. + * @param family Socket family. + * @param type Socket type. + * @param protocol Socket data transfer protocol. + * @param[out] error Error report object, NULL to ignore. + * @return Pointer to #PSocket in case of success, NULL otherwise. + * @since 0.0.1 + * @note If all the given parameters are not compatible with each other, then + * the function will fail. Use #P_SOCKET_PROTOCOL_DEFAULT to automatically + * match the best protocol for a particular @a type. + * @sa #PSocketFamily, #PSocketType, #PSocketProtocol, p_socket_new_from_fd() + * + * The @a protocol is passed directly to the operating system socket() call, + * #PSocketProtocol has the same values as the system definitions. You can pass + * any existing protocol value to this call if you know it exactly. + */ +P_LIB_API PSocket * p_socket_new (PSocketFamily family, + PSocketType type, + PSocketProtocol protocol, + PError **error); + +/** + * @brief Gets an underlying file descriptor of a @a socket. + * @param socket #PSocket to get the file descriptor for. + * @return File descriptor in case of success, -1 otherwise. + * @since 0.0.1 + * @sa p_socket_new_from_fd() + */ +P_LIB_API pint p_socket_get_fd (const PSocket *socket); + +/** + * @brief Gets a @a socket address family. + * @param socket #PSocket to get the address family for. + * @return #PSocketFamily in case of success, #P_SOCKET_FAMILY_UNKNOWN + * otherwise. + * @since 0.0.1 + * @sa #PSocketFamily, p_socket_new() + * + * The socket address family specifies address space which will be used to + * communicate with other sockets. For now, the INET and INET6 families are + * supported. The INET6 family is available only if the operating system + * supports it. + */ +P_LIB_API PSocketFamily p_socket_get_family (const PSocket *socket); + +/** + * @brief Gets a @a socket type. + * @param socket #PSocket to get the type for. + * @return #PSocketType in case of success, #P_SOCKET_TYPE_UNKNOWN otherwise. + * @since 0.0.1 + * @sa #PSocketType, p_socket_new() + */ +P_LIB_API PSocketType p_socket_get_type (const PSocket *socket); + +/** + * @brief Gets a @a socket data transfer protocol. + * @param socket #PSocket to get the data transfer protocol for. + * @return #PSocketProtocol in case of success, #P_SOCKET_PROTOCOL_UNKNOWN + * otherwise. + * @since 0.0.1 + * @sa #PSocketProtocol, p_socket_new() + */ +P_LIB_API PSocketProtocol p_socket_get_protocol (const PSocket *socket); + +/** + * @brief Checks whether the SO_KEEPALIVE flag is enabled. + * @param socket #PSocket to check the SO_KEEPALIVE flag for. + * @return TRUE if the SO_KEEPALIVE flag is enabled, FALSE otherwise. + * @since 0.0.1 + * @sa p_socket_set_keepalive() + * + * This option only has effect for connection oriented sockets. After a + * connection has been established between two sockets, they periodically send + * ping packets to each other to make sure that the connection is alive. A + * time interval between alive packets is system dependent and varies from + * several minutes to several hours. + * + * The main usage of this option is to detect dead clients on a server side and + * close such the broken sockets to free resources for the actual clients which + * may want to connect to the server. Some servers may let clients to be idle + * for a long time, so such an option helps to detect died clients faster + * without sending them real data. It's some kind of garbage collecting. + */ +P_LIB_API pboolean p_socket_get_keepalive (const PSocket *socket); + +/** + * @brief Checks whether @a socket is used in a blocking mode. + * @param socket #PSocket to check the blocking mode for. + * @return TRUE if @a socket is in the blocking mode, FALSE otherwise. + * @note A blocking socket will wait for an I/O operation to be completed before + * returning to the caller function. + * @since 0.0.1 + * @sa p_socket_set_blocking() + * + * The underlying socket descriptor is always set to the non-blocking mode by + * default and #PSocket emulates the blocking mode if required. + */ +P_LIB_API pboolean p_socket_get_blocking (PSocket *socket); + +/** + * @brief Gets a @a socket listen backlog parameter. + * @param socket #PSocket to get the listen backlog parameter for. + * @return Listen backlog parameter in case of success, -1 otherwise. + * @since 0.0.1 + * @sa p_socket_set_listen_backlog(), p_socket_listen() + * + * This parameter only has meaning for the connection oriented sockets. The + * backlog parameter specifies how much pending connections from other clients + * can be stored in the internal (system) queue. If the socket has already the + * number of pending connections equal to the backlog parameter, and another + * client attempts to connect on that time, it (client) will either be refused + * or retransmitted. This behavior is system and protocol dependent. + * + * Some systems may not allow to set it to high values. By default #PSocket + * attempts to set it to 5. + */ +P_LIB_API pint p_socket_get_listen_backlog (const PSocket *socket); + +/** + * @brief Gets a @a socket timeout for blocking I/O operations. + * @param socket #PSocket to get the timeout for. + * @return Timeout for blocking I/O operations in milliseconds, -1 in case of + * fail. + * @since 0.0.1 + * @sa p_socket_set_timeout(), p_socket_io_condition_wait() + * + * For a blocking socket a timeout value means maximum amount of time for which + * a blocking call will wait until a newtwork I/O operation completes. If the + * operation is not finished after the timeout, the blocking call returns with + * the error set to #P_ERROR_IO_TIMED_OUT. + * + * For a non-blocking socket the timeout affects only on the + * p_socket_io_condition_wait() maximum waiting time. + * + * Zero timeout means that the operation which requires a time to complete + * network I/O will be blocked until the operation finishes or error occurres. + */ +P_LIB_API pint p_socket_get_timeout (const PSocket *socket); + +/** + * @brief Gets a @a socket local (bound) address. + * @param socket #PSocket to get the local address for. + * @param[out] error Error report object, NULL to ignore. + * @return #PSocketAddress with the socket local address in case of success, + * NULL otherwise. + * @since 0.0.1 + * @sa p_socket_bind() + * + * If the @a socket was not bound explicitly with p_socket_bind() or implicitly + * with p_socket_connect(), the call will fail. + */ +P_LIB_API PSocketAddress * p_socket_get_local_address (const PSocket *socket, + PError **error); + +/** + * @brief Gets a @a socket remote endpoint address. + * @param socket #PSocket to get the remote endpoint address for. + * @param[out] error Error report object, NULL to ignore. + * @return #PSocketAddress with the socket endpoint remote address in case of + * success, NULL otherwise. + * @since 0.0.1 + * @sa p_socket_connect() + * + * If the @a socket was not connected to the endpoint address with + * p_socket_connect(), the call will fail. + * + * @warning On Syllable this call will always return NULL for connection-less + * sockets (though connecting is possible). + */ +P_LIB_API PSocketAddress * p_socket_get_remote_address (const PSocket *socket, + PError **error); + +/** + * @brief Checks whether a @a socket is connected. + * @param socket #PSocket to check a connection for. + * @return TRUE if the @a socket is connected, FALSE otherwise. + * @since 0.0.1 + * @sa p_socket_connect(), p_socket_check_connect_result() + * + * This function doesn't check if the socket is still connected, it only checks + * whether the p_socket_connect() call was successfully performed on the + * @a socket. + */ +P_LIB_API pboolean p_socket_is_connected (const PSocket *socket); + +/** + * @brief Checks whether a @a socket is closed. + * @param socket #PSocket to check a closed state. + * @return TRUE if the @a socket is closed, FALSE otherwise. + * @since 0.0.1 + * @sa p_socket_close(), p_socket_shutdown() + * + * If the socket is in a non-blocking mode this call will not return TRUE until + * p_socket_check_connect_result() is called. The socket will be closed if + * p_socket_shutdown() is called for both the directions. + */ +P_LIB_API pboolean p_socket_is_closed (const PSocket *socket); + +/** + * @brief Checks a connection state after calling p_socket_connect(). + * @param socket #PSocket to check the connection state for. + * @param[out] error Error report object, NULL to ignore. + * @return TRUE if was no error, FALSE otherwise. + * @since 0.0.1 + * @sa p_socket_io_condition_wait() + * @warning Not supported on Syllable for connection-less sockets. + * + * Usually this call is used after calling p_socket_connect() on a socket in a + * non-blocking mode to check the connection state. If call returns the FALSE + * result then the connection checking call has failed or there was an error + * during the connection and you should check the last error using an @a error + * object. + * + * If the socket is still pending for the connection you will get the + * #P_ERROR_IO_IN_PROGRESS error code. + * + * After calling p_socket_connect() on a non-blocking socket, you can wait for + * a connection operation to be finished using p_socket_io_condition_wait() + * with the #P_SOCKET_IO_CONDITION_POLLOUT option. + */ +P_LIB_API pboolean p_socket_check_connect_result (PSocket *socket, + PError **error); + +/** + * @brief Sets the @a socket SO_KEEPALIVE flag. + * @param socket #PSocket to set the SO_KEEPALIVE flag for. + * @param keepalive Value for the SO_KEEPALIVE flag. + * @since 0.0.1 + * @sa p_socket_get_keepalive() + * + * See p_socket_get_keepalive() documentation for a description of this option. + */ +P_LIB_API void p_socket_set_keepalive (PSocket *socket, + pboolean keepalive); + +/** + * @brief Sets a @a socket blocking mode. + * @param socket #PSocket to set the blocking mode for. + * @param blocking Whether to set the @a socket into the blocking mode. + * @note A blocking socket will wait for an I/O operation to be completed + * before returning to the caller function. + * @note On some operating systems a blocking timeout may be less than threads + * scheduling granularity, so the actual timeout can be greater than specified + * one. + * @since 0.0.1 + * @sa p_socket_get_blocking() + */ +P_LIB_API void p_socket_set_blocking (PSocket *socket, + pboolean blocking); + +/** + * @brief Sets a @a socket listen backlog parameter. + * @param socket #PSocket to set the listen backlog parameter for. + * @param backlog Value for the listen backlog parameter. + * @note This parameter can take effect only if it was set before calling + * p_socket_listen(). Otherwise it will be ignored by underlying socket + * system calls. + * @since 0.0.1 + * @sa p_socket_get_listen_backlog() + * + * See p_socket_get_listen_backlog() documentation for a description of this + * option. + */ +P_LIB_API void p_socket_set_listen_backlog (PSocket *socket, + pint backlog); + +/** + * @brief Sets a @a socket timeout value for blocking I/O operations. + * @param socket #PSocket to set the @a timeout for. + * @param timeout Timeout value in milliseconds. + * @since 0.0.1 + * @sa p_socket_get_timeoout(), p_socket_io_condition_wait() + * + * See p_socket_get_timeout() documentation for a description of this option. + */ +P_LIB_API void p_socket_set_timeout (PSocket *socket, + pint timeout); + +/** + * @brief Binds a @a socket to a given local address. + * @param socket #PSocket to bind. + * @param address #PSocketAddress to bind the given @a socket to. + * @param allow_reuse Whether to allow socket's address reusing. + * @param[out] error Error report object, NULL to ignore. + * @return TRUE in case of success, FALSE otherwise. + * @since 0.0.1 + * @sa p_socket_get_local_address() + * + * The @a allow_reuse option allows to resolve address conflicts for several + * bound sockets. It controls the SO_REUSEADDR socket flag. + * + * In a common case two or more sockets can't be bound to the same address + * (a network address and a port) for the same data transfer protocol (i.e. TCP + * or UDP) because they will be in a conflicted state for data receiving. But + * the socket can be also bound for the any network interface (i.e. 0.0.0.0 + * network address) and a particular port. If you will try to bind another + * socket without the @a allow_reuse option to a particular network address + * (i.e. 127.0.0.1) and the same port, the p_socket_bind() call will fail. + * + * With the @a allow_reuse option the system will resolve this conflict: the + * socket will be bound to the particular address and port (and will receive + * data targeted to this particular address) while the first socket will be + * receiving all other data matching the bound address. + * + * This option is system dependent, some systems do not support it. Also some + * systems have option to reuse the address port (SO_REUSEPORT) in the same way, + * too. + * + * Connection oriented sockets have another problem - the so called linger time. + * It is a time required by the system to properly close a socket connection + * (and this process can be quite complicated). This time can be measured from + * several minutes to several hours (!). The socket in such a state is + * half-dead, but it holds the bound address and attempt to bind another socket + * on this address will fail. The @a allow_reuse option allows to bind another + * socket on such a half-dead address, but behavior can be unexpected, it's + * better to refer to the system documentation for that. + * + * In general case, a server socket should be bound with the @a allow_reuse set + * to TRUE, while a client socket shouldn't set this option to TRUE. If you + * restart the client quickly with the same address it can fail to bind. + */ +P_LIB_API pboolean p_socket_bind (const PSocket *socket, + PSocketAddress *address, + pboolean allow_reuse, + PError **error); + +/** + * @brief Connects a @a socket to a given remote address. + * @param socket #PSocket to connect. + * @param address #PSocketAddress to connect the @a socket to. + * @param[out] error Error report object, NULL to ignore. + * @return TRUE in case of success, FALSE otherwise. + * @since 0.0.1 + * @sa p_socket_is_connected(), p_socket_check_connect_result(), + * p_socket_get_remote_address(), p_socket_io_condition_wait() + * @warning On Syllable this method changes a local port of the connection + * oriented socket in case of success. + * + * Calling this method on the connection-less socket will bind it to the remote + * address and the p_socket_send() method can be used instead of + * p_socket_send_to(), so you do not need to specify the remote (target) address + * each time you need to send data. The socket will also discard incoming data + * from other addresses. The same call again will re-bind it to another remote + * address. + * + * For the connection oriented socket it tries to establish a connection with + * a listening remote socket. The same call again will have no effect and will + * fail. + * + * If the @a socket is in a non-blocking mode, then you can wait for the + * connection using p_socket_io_condition_wait() with the + * #P_SOCKET_IO_CONDITION_POLLOUT parameter. You should check the connection + * result after that using p_socket_check_connect_result(). + */ +P_LIB_API pboolean p_socket_connect (PSocket *socket, + PSocketAddress *address, + PError **error); + +/** + * @brief Puts a @a socket into a listening state. + * @param socket #PSocket to start listening. + * @param[out] error Error report object, NULL to ignore. + * @return TRUE in case of success, FALSE otherwise. + * @since 0.0.1 + * @sa p_socket_get_listen_backlog(), p_socket_set_listen_backlog() + * + * This call has meaning only for connection oriented sockets. Before starting + * to accept incoming connections, the socket must be put into the passive mode + * using p_socket_listen(). After that p_socket_accept() can be used to + * accept incoming connections. + * + * Maximum number of pending connections is defined by the backlog parameter, + * see p_socket_get_listen_backlog() documentation for more information. The + * backlog parameter must be set before calling p_socket_listen() to take + * effect. + */ +P_LIB_API pboolean p_socket_listen (PSocket *socket, + PError **error); + +/** + * @brief Accepts a @a socket incoming connection. + * @param socket #PSocket to accept the incoming connection from. + * @param[out] error Error report object, NULL to ignore. + * @return New #PSocket with the accepted connection in case of success, NULL + * otherwise. + * @since 0.0.1 + * + * This call has meaning only for connection oriented sockets. The socket can + * accept new incoming connections only after calling p_socket_bind() and + * p_socket_listen(). + */ +P_LIB_API PSocket * p_socket_accept (const PSocket *socket, + PError **error); + +/** + * @brief Receives data from a given @a socket. + * @param socket #PSocket to receive data from. + * @param buffer Buffer to write received data in. + * @param buflen Length of @a buffer. + * @param[out] error Error report object, NULL to ignore. + * @return Size in bytes of written data in case of success, -1 otherwise. + * @note If the @a socket is in a blocking mode, then the caller will be blocked + * until data arrives. + * @since 0.0.1 + * @sa p_socket_receive_from(), p_socket_connect() + * + * If the @a buflen is less than the received data size, only @a buflen bytes of + * data will be written to the @a buffer, and excess bytes may be discarded + * depending on a socket message type. + * + * This call is normally used only with the a connected socket, see + * p_socket_connect(). + */ +P_LIB_API pssize p_socket_receive (const PSocket *socket, + pchar *buffer, + psize buflen, + PError **error); + +/** + * @brief Receives data from a given @a socket and saves a remote address. + * @param socket #PSocket to receive data from. + * @param[out] address Pointer to store the remote address in case of success, + * may be NULL. The caller is responsible to free it after usage. + * @param buffer Buffer to write received data in. + * @param buflen Length of @a buffer. + * @param[out] error Error report object, NULL to ignore. + * @return Size in bytes of written data in case of success, -1 otherwise. + * @note If the @a socket is in a blocking mode, then the caller will be blocked + * until data arrives. + * @since 0.0.1 + * @sa p_socket_receive() + * + * If the @a buflen is less than the received data size, only @a buflen bytes of + * data will be written to the @a buffer, and excess bytes may be discarded + * depending on a socket message type. + * + * This call is normally used only with a connection-less socket. + */ +P_LIB_API pssize p_socket_receive_from (const PSocket *socket, + PSocketAddress **address, + pchar *buffer, + psize buflen, + PError **error); + +/** + * @brief Sends data through a given @a socket. + * @param socket #PSocket to send data through. + * @param buffer Buffer with data to send. + * @param buflen Length of @a buffer. + * @param[out] error Error report object, NULL to ignore. + * @return Size in bytes of sent data in case of success, -1 otherwise. + * @note If the @a socket is in a blocking mode, then the caller will be blocked + * until data sent. + * @since 0.0.1 + * @sa p_socket_send_to() + * + * Do not use this call for connection-less sockets which are not connected to a + * remote address using p_socket_connect() because it will always fail, use + * p_socket_send_to() instead. + */ +P_LIB_API pssize p_socket_send (const PSocket *socket, + const pchar *buffer, + psize buflen, + PError **error); + +/** + * @brief Sends data through a given @a socket to a given address. + * @param socket #PSocket to send data through. + * @param address #PSocketAddress to send data to. + * @param buffer Buffer with data to send. + * @param buflen Length of @a buffer. + * @param[out] error Error report object, NULL to ignore. + * @return Size in bytes of sent data in case of success, -1 otherwise. + * @note If the @a socket is in a blocking mode, then the caller will be blocked + * until data sent. + * @since 0.0.1 + * @sa p_socket_send() + * + * This call is used when dealing with connection-less sockets. You can bind + * such a socket to a remote address using p_socket_connect() and use + * p_socket_send() instead. If you are working with connection oriented sockets + * then use p_socket_send() after establishing a connection. + */ +P_LIB_API pssize p_socket_send_to (const PSocket *socket, + PSocketAddress *address, + const pchar *buffer, + psize buflen, + PError **error); + +/** + * @brief Closes a @a socket. + * @param socket #PSocket to close. + * @param[out] error Error report object, NULL to ignore. + * @return TRUE in case of success, FALSE otherwise. + * @since 0.0.1 + * @sa p_socket_free(), p_socket_is_closed() + * + * For connection oriented sockets some time is required to completely close + * a socket connection. See documentation for p_socket_bind() for more + * information. + */ +P_LIB_API pboolean p_socket_close (PSocket *socket, + PError **error); + +/** + * @brief Shutdowns a full-duplex @a socket data transfer link. + * @param socket #PSocket to shutdown link. + * @param shutdown_read Whether to shutdown the incoming data transfer link. + * @param shutdown_write Whether to shutdown the outcoming data transfer link. + * @param[out] error Error report object, NULL to ignore. + * @return TRUE in case of success, FALSE otherwise. + * @note Shutdown of any link is possible only on the socket in a connected + * state, otherwise the call will fail. + * @since 0.0.1 + * + * After shutdowning the data transfer link couldn't be restored in any + * direction. It is often used to gracefully close a connection for a connection + * oriented socket. + */ +P_LIB_API pboolean p_socket_shutdown (PSocket *socket, + pboolean shutdown_read, + pboolean shutdown_write, + PError **error); + +/** + * @brief Closes a @a socket (if not closed yet) and frees its resources. + * @param socket #PSocket to free resources from. + * @since 0.0.1 + * @sa p_socket_close() + */ +P_LIB_API void p_socket_free (PSocket *socket); + +/** + * @brief Sets the @a socket buffer size for a given data transfer direction. + * @param socket #PSocket to set the buffer size for. + * @param dir Direction to set the buffer size on. + * @param size Size of the buffer to set, in bytes. + * @param[out] error Error report object, NULL to ignore. + * @return TRUE in case of success, FALSE otherwise. + * @since 0.0.1 + * @warning Not supported on Syllable. + */ +P_LIB_API pboolean p_socket_set_buffer_size (const PSocket *socket, + PSocketDirection dir, + psize size, + PError **error); + +/** + * @brief Waits for a specified I/O @a condition on @a socket. + * @param socket #PSocket to wait for @a condition on. + * @param condition An I/O condition to wait for on @a socket. + * @param[out] error Error report object, NULL to ignore. + * @return TRUE if @a condition has been met, FALSE otherwise. + * @since 0.0.1 + * @sa p_socket_get_timeout(), p_socket_set_timeout() + * + * Waits until @a condition will be met on @a socket or an error occurred. If + * timeout was set using p_socket_set_timeout() and a network I/O operation + * doesn't finish until timeout expired, call will fail with + * #P_ERROR_IO_TIMED_OUT error code. + */ +P_LIB_API pboolean p_socket_io_condition_wait (const PSocket *socket, + PSocketIOCondition condition, + PError **error); + +P_END_DECLS + +#endif /* PLIBSYS_HEADER_PSOCKET_H */ diff --git a/3rdparty/plibsys/src/psocketaddress.c b/3rdparty/plibsys/src/psocketaddress.c new file mode 100644 index 0000000..2338c83 --- /dev/null +++ b/3rdparty/plibsys/src/psocketaddress.c @@ -0,0 +1,619 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pmem.h" +#include "pstring.h" +#include "psocketaddress.h" +#include "plibsys-private.h" + +#include <stdlib.h> +#include <string.h> + +#ifndef P_OS_WIN +# include <arpa/inet.h> +#endif + +#if defined (PLIBSYS_HAS_GETADDRINFO) && !defined (PLIBSYS_SOCKADDR_IN6_HAS_SCOPEID) +# undef PLIBSYS_HAS_GETADDRINFO +#endif + +#ifdef PLIBSYS_HAS_GETADDRINFO +# include <netdb.h> +#endif + +/* According to Open Group specifications */ +#ifndef INET_ADDRSTRLEN +# ifdef P_OS_WIN + /* On Windows it includes port number */ +# define INET_ADDRSTRLEN 22 +# else +# define INET_ADDRSTRLEN 16 +# endif +#endif + +#ifdef AF_INET6 +# ifndef INET6_ADDRSTRLEN +# ifdef P_OS_WIN + /* On Windows it includes port number */ +# define INET6_ADDRSTRLEN 65 +# else +# define INET6_ADDRSTRLEN 46 +# endif +# endif +#endif + +#ifdef P_OS_VMS +# if PLIBSYS_SIZEOF_VOID_P == 8 +# define addrinfo __addrinfo64 +# endif +#endif + +#if defined (P_OS_BEOS) || defined (P_OS_OS2) +# ifdef AF_INET6 +# undef AF_INET6 +# endif +#endif + +struct PSocketAddress_ { + PSocketFamily family; + union addr_ { + struct in_addr sin_addr; +#ifdef AF_INET6 + struct in6_addr sin6_addr; +#endif + } addr; + puint16 port; + puint32 flowinfo; + puint32 scope_id; +}; + +P_LIB_API PSocketAddress * +p_socket_address_new_from_native (pconstpointer native, + psize len) +{ + PSocketAddress *ret; + puint16 family; + + if (P_UNLIKELY (native == NULL || len == 0)) + return NULL; + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PSocketAddress))) == NULL)) + return NULL; + + family = ((struct sockaddr *) native)->sa_family; + + if (family == AF_INET) { + if (len < sizeof (struct sockaddr_in)) { + P_WARNING ("PSocketAddress::p_socket_address_new_from_native: invalid IPv4 native size"); + p_free (ret); + return NULL; + } + + memcpy (&ret->addr.sin_addr, &((struct sockaddr_in *) native)->sin_addr, sizeof (struct in_addr)); + ret->family = P_SOCKET_FAMILY_INET; + ret->port = p_ntohs (((struct sockaddr_in *) native)->sin_port); + return ret; + } +#ifdef AF_INET6 + else if (family == AF_INET6) { + if (len < sizeof (struct sockaddr_in6)) { + P_WARNING ("PSocketAddress::p_socket_address_new_from_native: invalid IPv6 native size"); + p_free (ret); + return NULL; + } + + memcpy (&ret->addr.sin6_addr, + &((struct sockaddr_in6 *) native)->sin6_addr, + sizeof (struct in6_addr)); + + ret->family = P_SOCKET_FAMILY_INET6; + ret->port = p_ntohs (((struct sockaddr_in *) native)->sin_port); +#ifdef PLIBSYS_SOCKADDR_IN6_HAS_FLOWINFO + ret->flowinfo = ((struct sockaddr_in6 *) native)->sin6_flowinfo; +#endif +#ifdef PLIBSYS_SOCKADDR_IN6_HAS_SCOPEID + ret->scope_id = ((struct sockaddr_in6 *) native)->sin6_scope_id; +#endif + return ret; + } +#endif + else { + p_free (ret); + return NULL; + } +} + +P_LIB_API PSocketAddress * +p_socket_address_new (const pchar *address, + puint16 port) +{ + PSocketAddress *ret; +#if defined (P_OS_WIN) || defined (PLIBSYS_HAS_GETADDRINFO) + struct addrinfo hints; + struct addrinfo *res; +#endif + +#ifdef P_OS_WIN + struct sockaddr_storage sa; + struct sockaddr_in *sin = (struct sockaddr_in *) &sa; +# ifdef AF_INET6 + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) &sa; +# endif /* AF_INET6 */ + pint len; +#endif /* P_OS_WIN */ + + if (P_UNLIKELY (address == NULL)) + return NULL; + +#if (defined (P_OS_WIN) || defined (PLIBSYS_HAS_GETADDRINFO)) && defined (AF_INET6) + if (strchr (address, ':') != NULL) { + memset (&hints, 0, sizeof (hints)); + + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; +# ifndef P_OS_UNIXWARE + hints.ai_flags = AI_NUMERICHOST; +# endif + + if (P_UNLIKELY (getaddrinfo (address, NULL, &hints, &res) != 0)) + return NULL; + + if (P_LIKELY (res->ai_family == AF_INET6 && + res->ai_addrlen == sizeof (struct sockaddr_in6))) { + ((struct sockaddr_in6 *) res->ai_addr)->sin6_port = p_htons (port); + ret = p_socket_address_new_from_native (res->ai_addr, res->ai_addrlen); + } else + ret = NULL; + + freeaddrinfo (res); + + return ret; + } +#endif + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PSocketAddress))) == NULL)) { + P_ERROR ("PSocketAddress::p_socket_address_new: failed to allocate memory"); + return NULL; + } + + ret->port = port; + +#ifdef P_OS_WIN + memset (&sa, 0, sizeof (sa)); + len = sizeof (sa); + sin->sin_family = AF_INET; + + if (WSAStringToAddressA ((LPSTR) address, AF_INET, NULL, (LPSOCKADDR) &sa, &len) == 0) { + memcpy (&ret->addr.sin_addr, &sin->sin_addr, sizeof (struct in_addr)); + ret->family = P_SOCKET_FAMILY_INET; + return ret; + } +# ifdef AF_INET6 + else { + sin6->sin6_family = AF_INET6; + + if (WSAStringToAddressA ((LPSTR) address, AF_INET6, NULL, (LPSOCKADDR) &sa, &len) == 0) { + memcpy (&ret->addr.sin6_addr, &sin6->sin6_addr, sizeof (struct in6_addr)); + ret->family = P_SOCKET_FAMILY_INET6; + return ret; + } + } +# endif /* AF_INET6 */ +#else /* P_OS_WIN */ + if (inet_pton (AF_INET, address, &ret->addr.sin_addr) > 0) { + ret->family = P_SOCKET_FAMILY_INET; + return ret; + } +# ifdef AF_INET6 + else if (inet_pton (AF_INET6, address, &ret->addr.sin6_addr) > 0) { + ret->family = P_SOCKET_FAMILY_INET6; + return ret; + } +# endif /* AF_INET6 */ +#endif /* P_OS_WIN */ + + p_free (ret); + return NULL; +} + +P_LIB_API PSocketAddress * +p_socket_address_new_any (PSocketFamily family, + puint16 port) +{ + PSocketAddress *ret; + puchar any_addr[] = {0, 0, 0, 0}; +#ifdef AF_INET6 + struct in6_addr any6_addr = IN6ADDR_ANY_INIT; +#endif + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PSocketAddress))) == NULL)) { + P_ERROR ("PSocketAddress::p_socket_address_new_any: failed to allocate memory"); + return NULL; + } + + if (family == P_SOCKET_FAMILY_INET) + memcpy (&ret->addr.sin_addr, any_addr, sizeof (any_addr)); +#ifdef AF_INET6 + else if (family == P_SOCKET_FAMILY_INET6) + memcpy (&ret->addr.sin6_addr, &any6_addr.s6_addr, sizeof (any6_addr.s6_addr)); +#endif + else { + p_free (ret); + return NULL; + } + + ret->family = family; + ret->port = port; + + return ret; +} + +P_LIB_API PSocketAddress * +p_socket_address_new_loopback (PSocketFamily family, + puint16 port) +{ + PSocketAddress *ret; + puchar loop_addr[] = {127, 0, 0, 0}; +#ifdef AF_INET6 + struct in6_addr loop6_addr = IN6ADDR_LOOPBACK_INIT; +#endif + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PSocketAddress))) == NULL)) { + P_ERROR ("PSocketAddress::p_socket_address_new_loopback: failed to allocate memory"); + return NULL; + } + + if (family == P_SOCKET_FAMILY_INET) + memcpy (&ret->addr.sin_addr, loop_addr, sizeof (loop_addr)); +#ifdef AF_INET6 + else if (family == P_SOCKET_FAMILY_INET6) + memcpy (&ret->addr.sin6_addr, &loop6_addr.s6_addr, sizeof (loop6_addr.s6_addr)); +#endif + else { + p_free (ret); + return NULL; + } + + ret->family = family; + ret->port = port; + + return ret; +} + +P_LIB_API pboolean +p_socket_address_to_native (const PSocketAddress *addr, + ppointer dest, + psize destlen) +{ + struct sockaddr_in *sin; +#ifdef AF_INET6 + struct sockaddr_in6 *sin6; +#endif + + if (P_UNLIKELY (addr == NULL || dest == NULL || destlen == 0)) + return FALSE; + + sin = (struct sockaddr_in *) dest; +#ifdef AF_INET6 + sin6 = (struct sockaddr_in6 *) dest; +#endif + + if (addr->family == P_SOCKET_FAMILY_INET) { + if (P_UNLIKELY (destlen < sizeof (struct sockaddr_in))) { + P_WARNING ("PSocketAddress::p_socket_address_to_native: invalid buffer size for IPv4"); + return FALSE; + } + + memcpy (&sin->sin_addr, &addr->addr.sin_addr, sizeof (struct in_addr)); + sin->sin_family = AF_INET; + sin->sin_port = p_htons (addr->port); + memset (sin->sin_zero, 0, sizeof (sin->sin_zero)); + return TRUE; + } +#ifdef AF_INET6 + else if (addr->family == P_SOCKET_FAMILY_INET6) { + if (P_UNLIKELY (destlen < sizeof (struct sockaddr_in6))) { + P_ERROR ("PSocketAddress::p_socket_address_to_native: invalid buffer size for IPv6"); + return FALSE; + } + + memcpy (&sin6->sin6_addr, &addr->addr.sin6_addr, sizeof (struct in6_addr)); + sin6->sin6_family = AF_INET6; + sin6->sin6_port = p_htons (addr->port); +#ifdef PLIBSYS_SOCKADDR_IN6_HAS_FLOWINFO + sin6->sin6_flowinfo = addr->flowinfo; +#endif +#ifdef PLIBSYS_SOCKADDR_IN6_HAS_SCOPEID + sin6->sin6_scope_id = addr->scope_id; +#endif + return TRUE; + } +#endif + else { + P_WARNING ("PSocketAddress::p_socket_address_to_native: unsupported socket address"); + return FALSE; + } +} + +P_LIB_API psize +p_socket_address_get_native_size (const PSocketAddress *addr) +{ + if (P_UNLIKELY (addr == NULL)) + return 0; + + if (addr->family == P_SOCKET_FAMILY_INET) + return sizeof (struct sockaddr_in); +#ifdef AF_INET6 + else if (addr->family == P_SOCKET_FAMILY_INET6) + return sizeof (struct sockaddr_in6); +#endif + else { + P_WARNING ("PSocketAddress::p_socket_address_get_native_size: unsupported socket family"); + return 0; + } +} + +P_LIB_API PSocketFamily +p_socket_address_get_family (const PSocketAddress *addr) +{ + if (P_UNLIKELY (addr == NULL)) + return P_SOCKET_FAMILY_UNKNOWN; + + return addr->family; +} + +P_LIB_API pchar * +p_socket_address_get_address (const PSocketAddress *addr) +{ +#ifdef AF_INET6 + pchar buffer[INET6_ADDRSTRLEN]; +#else + pchar buffer[INET_ADDRSTRLEN]; +#endif + +#ifdef P_OS_WIN + DWORD buflen = sizeof (buffer); + DWORD addrlen; + struct sockaddr_storage sa; + struct sockaddr_in *sin; +# ifdef AF_INET6 + struct sockaddr_in6 *sin6; +# endif /* AF_INET6 */ +#endif /* P_OS_WIN */ + + if (P_UNLIKELY (addr == NULL || addr->family == P_SOCKET_FAMILY_UNKNOWN)) + return NULL; +#ifdef P_OS_WIN + sin = (struct sockaddr_in *) &sa; +# ifdef AF_INET6 + sin6 = (struct sockaddr_in6 *) &sa; +# endif /* AF_INET6 */ +#endif /* P_OS_WIN */ + +#ifdef P_OS_WIN + memset (&sa, 0, sizeof (sa)); +#endif + +#ifdef P_OS_WIN + sa.ss_family = addr->family; + + if (addr->family == P_SOCKET_FAMILY_INET) { + addrlen = sizeof (struct sockaddr_in); + memcpy (&sin->sin_addr, &addr->addr.sin_addr, sizeof (struct in_addr)); + } +# ifdef AF_INET6 + else { + addrlen = sizeof (struct sockaddr_in6); + memcpy (&sin6->sin6_addr, &addr->addr.sin6_addr, sizeof (struct in6_addr)); + } +# endif /* AF_INET6 */ + + if (P_UNLIKELY (WSAAddressToStringA ((LPSOCKADDR) &sa, + addrlen, + NULL, + (LPSTR) buffer, + &buflen) != 0)) + return NULL; +#else /* !P_OS_WIN */ + if (addr->family == P_SOCKET_FAMILY_INET) + inet_ntop (AF_INET, &addr->addr.sin_addr, buffer, sizeof (buffer)); +# ifdef AF_INET6 + else + inet_ntop (AF_INET6, &addr->addr.sin6_addr, buffer, sizeof (buffer)); +# endif /* AF_INET6 */ +#endif /* P_OS_WIN */ + + return p_strdup (buffer); +} + +P_LIB_API puint16 +p_socket_address_get_port (const PSocketAddress *addr) +{ + if (P_UNLIKELY (addr == NULL)) + return 0; + + return addr->port; +} + +P_LIB_API puint32 +p_socket_address_get_flow_info (const PSocketAddress *addr) +{ + if (P_UNLIKELY (addr == NULL)) + return 0; + +#if !defined (AF_INET6) || !defined (PLIBSYS_SOCKADDR_IN6_HAS_FLOWINFO) + return 0; +#else + if (P_UNLIKELY (addr->family != P_SOCKET_FAMILY_INET6)) + return 0; + + return addr->flowinfo; +#endif +} + +P_LIB_API puint32 +p_socket_address_get_scope_id (const PSocketAddress *addr) +{ + if (P_UNLIKELY (addr == NULL)) + return 0; + +#if !defined (AF_INET6) || !defined (PLIBSYS_SOCKADDR_IN6_HAS_SCOPEID) + return 0; +#else + if (P_UNLIKELY (addr->family != P_SOCKET_FAMILY_INET6)) + return 0; + + return addr->scope_id; +#endif +} + +P_LIB_API void +p_socket_address_set_flow_info (PSocketAddress *addr, + puint32 flowinfo) +{ + if (P_UNLIKELY (addr == NULL)) + return; + +#if !defined (AF_INET6) || !defined (PLIBSYS_SOCKADDR_IN6_HAS_FLOWINFO) + P_UNUSED (flowinfo); + return; +#else + if (P_UNLIKELY (addr->family != P_SOCKET_FAMILY_INET6)) + return; + + addr->flowinfo = flowinfo; +#endif +} + +P_LIB_API void +p_socket_address_set_scope_id (PSocketAddress *addr, + puint32 scope_id) +{ + if (P_UNLIKELY (addr == NULL)) + return; + +#if !defined (AF_INET6) || !defined (PLIBSYS_SOCKADDR_IN6_HAS_SCOPEID) + P_UNUSED (scope_id); + return; +#else + if (P_UNLIKELY (addr->family != P_SOCKET_FAMILY_INET6)) + return; + + addr->scope_id = scope_id; +#endif +} + +P_LIB_API pboolean +p_socket_address_is_flow_info_supported (void) +{ +#ifdef AF_INET6 +# ifdef PLIBSYS_SOCKADDR_IN6_HAS_FLOWINFO + return TRUE; +# else + return FALSE; +# endif +#else + return FALSE; +#endif +} + +P_LIB_API pboolean +p_socket_address_is_scope_id_supported (void) +{ +#ifdef AF_INET6 +# ifdef PLIBSYS_SOCKADDR_IN6_HAS_SCOPEID + return TRUE; +# else + return FALSE; +# endif +#else + return FALSE; +#endif +} + +P_LIB_API pboolean +p_socket_address_is_ipv6_supported (void) +{ +#ifdef AF_INET6 + return TRUE; +#else + return FALSE; +#endif +} + +P_LIB_API pboolean +p_socket_address_is_any (const PSocketAddress *addr) +{ + puint32 addr4; + + if (P_UNLIKELY (addr == NULL || addr->family == P_SOCKET_FAMILY_UNKNOWN)) + return FALSE; + + if (addr->family == P_SOCKET_FAMILY_INET) { + addr4 = p_ntohl (* ((puint32 *) &addr->addr.sin_addr)); + + return (addr4 == INADDR_ANY); + } +#ifdef AF_INET6 + else + return IN6_IS_ADDR_UNSPECIFIED (&addr->addr.sin6_addr); +#else + else + return FALSE; +#endif +} + +P_LIB_API pboolean +p_socket_address_is_loopback (const PSocketAddress *addr) +{ + puint32 addr4; + + if (P_UNLIKELY (addr == NULL || addr->family == P_SOCKET_FAMILY_UNKNOWN)) + return FALSE; + + if (addr->family == P_SOCKET_FAMILY_INET) { + addr4 = p_ntohl (* ((puint32 *) &addr->addr.sin_addr)); + + /* 127.0.0.0/8 */ + return ((addr4 & 0xff000000) == 0x7f000000); + } +#ifdef AF_INET6 + else + return IN6_IS_ADDR_LOOPBACK (&addr->addr.sin6_addr); +#else + else + return FALSE; +#endif +} + +P_LIB_API void +p_socket_address_free (PSocketAddress *addr) +{ + if (P_UNLIKELY (addr == NULL)) + return; + + p_free (addr); +} diff --git a/3rdparty/plibsys/src/psocketaddress.h b/3rdparty/plibsys/src/psocketaddress.h new file mode 100644 index 0000000..d77ca1c --- /dev/null +++ b/3rdparty/plibsys/src/psocketaddress.h @@ -0,0 +1,284 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2017 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * @file psocketaddress.h + * @brief Socket address wrapper + * @author Alexander Saprykin + * + * A socket address is usually represented by a network address (IPv4 or IPv6) + * and a port number (though some other naming schemes and parameters are + * possible). + * + * Socket address parameters are stored inside a special system (native) + * structure in the binary (raw) form. The native structure varies with an + * operating system and a network protocol. #PSocketAddress acts like a thin + * wrapper around that native address structure and unifies manipulation of + * socket address data. + * + * #PSocketAddress supports IPv4 and IPv6 addresses which consist of an IP + * address and a port number. IPv6 support is system dependent and not provided + * for all the platforms. Sometimes you may also need to enable IPv6 support in + * the system to make it working. + * + * Convenient methods to create special addresses are provided: for the loopback + * interface use p_socket_address_new_loopback(), for the any-address interface + * use p_socket_address_new_any(). + * + * If you want to get the underlying native address structure for further usage + * in system calls use p_socket_address_to_native(), and + * p_socket_address_new_from_native() for a vice versa conversion. + */ + +#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION) +# error "Header files shouldn't be included directly, consider using <plibsys.h> instead." +#endif + +#ifndef PLIBSYS_HEADER_PSOCKETADDRESS_H +#define PLIBSYS_HEADER_PSOCKETADDRESS_H + +#include <pmacros.h> +#include <ptypes.h> + +#ifndef P_OS_WIN +# include <sys/types.h> +# include <sys/socket.h> +# include <netinet/in.h> +#endif + +P_BEGIN_DECLS + +/** Socket address family. */ +typedef enum PSocketFamily_ { + P_SOCKET_FAMILY_UNKNOWN = 0, /**< Unknown family. */ + P_SOCKET_FAMILY_INET = AF_INET, /**< IPv4 family. */ +#ifdef AF_INET6 + P_SOCKET_FAMILY_INET6 = AF_INET6 /**< IPv6 family. */ +#else + P_SOCKET_FAMILY_INET6 = -1 /**< No IPv6 family. */ +#endif +} PSocketFamily; + +/** Socket address opaque structure. */ +typedef struct PSocketAddress_ PSocketAddress; + +/** + * @brief Creates new #PSocketAddress from the native socket address raw data. + * @param native Pointer to the native socket address raw data. + * @param len Raw data length, in bytes. + * @return Pointer to #PSocketAddress in case of success, NULL otherwise. + * @since 0.0.1 + */ +P_LIB_API PSocketAddress * p_socket_address_new_from_native (pconstpointer native, + psize len); + +/** + * @brief Creates new #PSocketAddress. + * @param address String representation of an address (i.e. "172.146.45.5"). + * @param port Port number. + * @return Pointer to #PSocketAddress in case of success, NULL otherwise. + * @since 0.0.1 + * @note It tries to automatically detect a socket family. + * + * If the @a address is an IPv6 address, it can also contain a scope index + * separated from the address by the '%' literal). Most target platforms should + * correctly parse such an address though some old operating systems may fail in + * case of lack of the getaddrinfo() call. + */ +P_LIB_API PSocketAddress * p_socket_address_new (const pchar *address, + puint16 port); + +/** + * @brief Creates new #PSocketAddress for the any-address representation. + * @param family Socket family. + * @param port Port number. + * @return Pointer to #PSocketAddress in case of success, NULL otherwise. + * @since 0.0.1 + * @note This call creates a network address for the set of all possible + * addresses, so you can't use it for receiving or sending data on a particular + * network address. If you need to bind a socket to the specific address + * (i.e. 127.0.0.1) use p_socket_address_new() instead. + */ +P_LIB_API PSocketAddress * p_socket_address_new_any (PSocketFamily family, + puint16 port); + +/** + * @brief Creates new #PSocketAddress for the loopback interface. + * @param family Socket family. + * @param port Port number. + * @return Pointer to #PSocketAddress in case of success, NULL otherwise. + * @since 0.0.1 + * @note This call creates a network address for the entire loopback network + * interface, so you can't use it for receiving or sending data on a particular + * network address. If you need to bind a socket to the specific address + * (i.e. 127.0.0.1) use p_socket_address_new() instead. + */ +P_LIB_API PSocketAddress * p_socket_address_new_loopback (PSocketFamily family, + puint16 port); + +/** + * @brief Converts #PSocketAddress to the native socket address raw data. + * @param addr #PSocketAddress to convert. + * @param[out] dest Output buffer for raw data. + * @param destlen Length in bytes of the @a dest buffer. + * @return TRUE in case of success, FALSE otherwise. + * @since 0.0.1 + */ +P_LIB_API pboolean p_socket_address_to_native (const PSocketAddress *addr, + ppointer dest, + psize destlen); + +/** + * @brief Gets the size of the native socket address raw data, in bytes. + * @param addr #PSocketAddress to get the size of native address raw data for. + * @return Size of the native socket address raw data in case of success, 0 + * otherwise. + * @since 0.0.1 + */ +P_LIB_API psize p_socket_address_get_native_size (const PSocketAddress *addr); + +/** + * @brief Gets a family of a socket address. + * @param addr #PSocketAddress to get the family for. + * @return #PSocketFamily of the socket address. + * @since 0.0.1 + */ +P_LIB_API PSocketFamily p_socket_address_get_family (const PSocketAddress *addr); + +/** + * @brief Gets a socket address in a string representation, i.e. "172.146.45.5". + * @param addr #PSocketAddress to get address string for. + * @return Pointer to the string representation of the socket address in case of + * success, NULL otherwise. The caller takes ownership of the returned pointer. + * @since 0.0.1 + */ +P_LIB_API pchar * p_socket_address_get_address (const PSocketAddress *addr); + +/** + * @brief Gets a port number of a socket address. + * @param addr #PSocketAddress to get the port number for. + * @return Port number in case of success, 0 otherwise. + * @since 0.0.1 + */ +P_LIB_API puint16 p_socket_address_get_port (const PSocketAddress *addr); + +/** + * @brief Gets IPv6 traffic class and flow information. + * @param addr #PSocketAddress to get flow information for. + * @return IPv6 traffic class and flow information. + * @since 0.0.1 + * @note This call is valid only for an IPv6 address, otherwise 0 is returned. + * @note Some operating systems may not support this property. + * @sa p_socket_address_is_flow_info_supported() + */ +P_LIB_API puint32 p_socket_address_get_flow_info (const PSocketAddress *addr); + +/** + * @brief Gets an IPv6 set of interfaces for a scope. + * @param addr #PSocketAddress to get the set of interfaces for. + * @return Index that identifies the set of interfaces for a scope. + * @since 0.0.1 + * @note This call is valid only for an IPv6 address, otherwise 0 is returned. + * @note Some operating systems may not support this property. + * @sa p_socket_address_is_scope_id_supported() + */ +P_LIB_API puint32 p_socket_address_get_scope_id (const PSocketAddress *addr); + +/** + * @brief Sets IPv6 traffic class and flow information. + * @param addr #PSocketAddress to set flow information for. + * @param flowinfo Flow information to set. + * @since 0.0.1 + * @note This call is valid only for an IPv6 address. + * @note Some operating systems may not support this property. + * @sa p_socket_address_is_flow_info_supported() + */ +P_LIB_API void p_socket_address_set_flow_info (PSocketAddress *addr, + puint32 flowinfo); + +/** + * @brief Sets an IPv6 set of interfaces for a scope. + * @param addr #PSocketAddress to set the set of interfaces for. + * @param scope_id Index that identifies the set of interfaces for a scope. + * @since 0.0.1 + * @note This call is valid only for an IPv6 address. + * @note Some operating systems may not support this property. + * @sa p_socket_address_is_scope_id_supported() + */ +P_LIB_API void p_socket_address_set_scope_id (PSocketAddress *addr, + puint32 scope_id); + +/** + * @brief Checks whether flow information is supported in IPv6. + * @return TRUE in case of success, FALSE otherwise. + * @since 0.0.1 + */ +P_LIB_API pboolean p_socket_address_is_flow_info_supported (void); + +/** + * @brief Checks whether a set of interfaces for a scope is supported in IPv6. + * @return TRUE in case of success, FALSE otherwise. + * @since 0.0.1 + */ +P_LIB_API pboolean p_socket_address_is_scope_id_supported (void); + +/** + * @brief Checks whether IPv6 protocol is supported. + * @return TRUE in case of success, FALSE otherwise. + * @since 0.0.3 + */ +P_LIB_API pboolean p_socket_address_is_ipv6_supported (void); + +/** + * @brief Checks whether a given socket address is an any-address + * representation. Such an address is a 0.0.0.0. + * @param addr #PSocketAddress to check. + * @return TRUE if the @a addr is the any-address representation, FALSE + * otherwise. + * @since 0.0.1 + * @sa p_socket_address_new_any() + */ +P_LIB_API pboolean p_socket_address_is_any (const PSocketAddress *addr); + +/** + * @brief Checks whether a given socket address is for the loopback interface. + * Such an address is a 127.x.x.x. + * @param addr #PSocketAddress to check. + * @return TRUE if the @a addr is for the loopback interface, FALSE otherwise. + * @since 0.0.1 + * @sa p_socket_address_new_loopback() + */ +P_LIB_API pboolean p_socket_address_is_loopback (const PSocketAddress *addr); + +/** + * @brief Frees a socket address structure and its resources. + * @param addr #PSocketAddress to free. + * @since 0.0.1 + */ +P_LIB_API void p_socket_address_free (PSocketAddress *addr); + +P_END_DECLS + +#endif /* PLIBSYS_HEADER_PSOCKETADDRESS_H */ diff --git a/3rdparty/plibsys/src/pspinlock-c11.c b/3rdparty/plibsys/src/pspinlock-c11.c new file mode 100644 index 0000000..1e77954 --- /dev/null +++ b/3rdparty/plibsys/src/pspinlock-c11.c @@ -0,0 +1,103 @@ +/* + * The MIT License + * + * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pmem.h" +#include "pspinlock.h" + +#ifdef P_CC_SUN +# define PSPINLOCK_INT_CAST(x) (pint *) (x) +#else +# define PSPINLOCK_INT_CAST(x) x +#endif + +struct PSpinLock_ { + volatile pint spin; +}; + +P_LIB_API PSpinLock * +p_spinlock_new (void) +{ + PSpinLock *ret; + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PSpinLock))) == NULL)) { + P_ERROR ("PSpinLock::p_spinlock_new: failed to allocate memory"); + return NULL; + } + + return ret; +} + +P_LIB_API pboolean +p_spinlock_lock (PSpinLock *spinlock) +{ + pint tmp_int; + + if (P_UNLIKELY (spinlock == NULL)) + return FALSE; + + do { + tmp_int = 0; + } while ((pboolean) __atomic_compare_exchange_n (PSPINLOCK_INT_CAST (&(spinlock->spin)), + &tmp_int, + 1, + 0, + __ATOMIC_ACQUIRE, + __ATOMIC_RELAXED) == FALSE); + + return TRUE; +} + +P_LIB_API pboolean +p_spinlock_trylock (PSpinLock *spinlock) +{ + pint tmp_int = 0; + + if (P_UNLIKELY (spinlock == NULL)) + return FALSE; + + return (pboolean) __atomic_compare_exchange_n (PSPINLOCK_INT_CAST (&(spinlock->spin)), + &tmp_int, + 1, + 0, + __ATOMIC_ACQUIRE, + __ATOMIC_RELAXED); +} + +P_LIB_API pboolean +p_spinlock_unlock (PSpinLock *spinlock) +{ + if (P_UNLIKELY (spinlock == NULL)) + return FALSE; + + __atomic_store_4 (PSPINLOCK_INT_CAST (&(spinlock->spin)), 0, __ATOMIC_RELEASE); + + return TRUE; +} + +P_LIB_API void +p_spinlock_free (PSpinLock *spinlock) +{ + p_free (spinlock); +} diff --git a/3rdparty/plibsys/src/pspinlock-decc.c b/3rdparty/plibsys/src/pspinlock-decc.c new file mode 100644 index 0000000..0a5a044 --- /dev/null +++ b/3rdparty/plibsys/src/pspinlock-decc.c @@ -0,0 +1,87 @@ +/* + * The MIT License + * + * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pmem.h" +#include "pspinlock.h" + +#ifdef P_OS_VMS +# include <builtins.h> +#else +# include <machine/builtins.h> +#endif + +struct PSpinLock_ { + volatile pint spin; +}; + +P_LIB_API PSpinLock * +p_spinlock_new (void) +{ + PSpinLock *ret; + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PSpinLock))) == NULL)) { + P_ERROR ("PSpinLock::p_spinlock_new: failed to allocate memory"); + return NULL; + } + + return ret; +} + +P_LIB_API pboolean +p_spinlock_lock (PSpinLock *spinlock) +{ + if (P_UNLIKELY (spinlock == NULL)) + return FALSE; + + (void) __LOCK_LONG ((volatile void *) &(spinlock->spin)); + + return TRUE; +} + +P_LIB_API pboolean +p_spinlock_trylock (PSpinLock *spinlock) +{ + if (P_UNLIKELY (spinlock == NULL)) + return FALSE; + + return __LOCK_LONG_RETRY ((volatile void *) &(spinlock->spin), 1) == 1 ? TRUE : FALSE; +} + +P_LIB_API pboolean +p_spinlock_unlock (PSpinLock *spinlock) +{ + if (P_UNLIKELY (spinlock == NULL)) + return FALSE; + + (void) __UNLOCK_LONG ((volatile void *) &(spinlock->spin)); + + return TRUE; +} + +P_LIB_API void +p_spinlock_free (PSpinLock *spinlock) +{ + p_free (spinlock); +} diff --git a/3rdparty/plibsys/src/pspinlock-sim.c b/3rdparty/plibsys/src/pspinlock-sim.c new file mode 100644 index 0000000..bf9479f --- /dev/null +++ b/3rdparty/plibsys/src/pspinlock-sim.c @@ -0,0 +1,88 @@ +/* + * The MIT License + * + * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pmem.h" +#include "pmutex.h" +#include "pspinlock.h" + +struct PSpinLock_ { + PMutex *mutex; +}; + +P_LIB_API PSpinLock * +p_spinlock_new (void) +{ + PSpinLock *ret; + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PSpinLock))) == NULL)) { + P_ERROR ("PSpinLock::p_spinlock_new: failed to allocate memory"); + return NULL; + } + + if (P_UNLIKELY ((ret->mutex = p_mutex_new ()) == NULL)) { + P_ERROR ("PSpinLock::p_spinlock_new: p_mutex_new() failed"); + p_free (ret); + return NULL; + } + + return ret; +} + +P_LIB_API pboolean +p_spinlock_lock (PSpinLock *spinlock) +{ + if (P_UNLIKELY (spinlock == NULL)) + return FALSE; + + return p_mutex_lock (spinlock->mutex); +} + +P_LIB_API pboolean +p_spinlock_trylock (PSpinLock *spinlock) +{ + if (spinlock == NULL) + return FALSE; + + return p_mutex_trylock (spinlock->mutex); +} + +P_LIB_API pboolean +p_spinlock_unlock (PSpinLock *spinlock) +{ + if (P_UNLIKELY (spinlock == NULL)) + return FALSE; + + return p_mutex_unlock (spinlock->mutex); +} + +P_LIB_API void +p_spinlock_free (PSpinLock *spinlock) +{ + if (P_UNLIKELY (spinlock == NULL)) + return; + + p_mutex_free (spinlock->mutex); + p_free (spinlock); +} diff --git a/3rdparty/plibsys/src/pspinlock-sync.c b/3rdparty/plibsys/src/pspinlock-sync.c new file mode 100644 index 0000000..e15e2a8 --- /dev/null +++ b/3rdparty/plibsys/src/pspinlock-sync.c @@ -0,0 +1,83 @@ +/* + * The MIT License + * + * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pmem.h" +#include "pspinlock.h" + +struct PSpinLock_ { + volatile pint spin; +}; + +P_LIB_API PSpinLock * +p_spinlock_new (void) +{ + PSpinLock *ret; + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PSpinLock))) == NULL)) { + P_ERROR ("PSpinLock::p_spinlock_new: failed to allocate memory"); + return NULL; + } + + return ret; +} + +P_LIB_API pboolean +p_spinlock_lock (PSpinLock *spinlock) +{ + if (P_UNLIKELY (spinlock == NULL)) + return FALSE; + + while ((pboolean) __sync_bool_compare_and_swap (&(spinlock->spin), 0, 1) == FALSE) + ; + + return TRUE; +} + +P_LIB_API pboolean +p_spinlock_trylock (PSpinLock *spinlock) +{ + if (P_UNLIKELY (spinlock == NULL)) + return FALSE; + + return (pboolean) __sync_bool_compare_and_swap (&(spinlock->spin), 0, 1); +} + +P_LIB_API pboolean +p_spinlock_unlock (PSpinLock *spinlock) +{ + if (P_UNLIKELY (spinlock == NULL)) + return FALSE; + + spinlock->spin = 0; + __sync_synchronize (); + + return TRUE; +} + +P_LIB_API void +p_spinlock_free (PSpinLock *spinlock) +{ + p_free (spinlock); +} diff --git a/3rdparty/plibsys/src/pspinlock-win.c b/3rdparty/plibsys/src/pspinlock-win.c new file mode 100644 index 0000000..73ccbdc --- /dev/null +++ b/3rdparty/plibsys/src/pspinlock-win.c @@ -0,0 +1,83 @@ +/* + * The MIT License + * + * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pmem.h" +#include "patomic.h" +#include "pspinlock.h" + +struct PSpinLock_ { + volatile pint spin; +}; + +P_LIB_API PSpinLock * +p_spinlock_new (void) +{ + PSpinLock *ret; + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PSpinLock))) == NULL)) { + P_ERROR ("PSpinLock::p_spinlock_new: failed to allocate memory"); + return NULL; + } + + return ret; +} + +P_LIB_API pboolean +p_spinlock_lock (PSpinLock *spinlock) +{ + if (P_UNLIKELY (spinlock == NULL)) + return FALSE; + + while (p_atomic_int_compare_and_exchange (&(spinlock->spin), 0, 1) == FALSE) + ; + + return TRUE; +} + +P_LIB_API pboolean +p_spinlock_trylock (PSpinLock *spinlock) +{ + if (P_UNLIKELY (spinlock == NULL)) + return FALSE; + + return p_atomic_int_compare_and_exchange (&(spinlock->spin), 0, 1); +} + +P_LIB_API pboolean +p_spinlock_unlock (PSpinLock *spinlock) +{ + if (P_UNLIKELY (spinlock == NULL)) + return FALSE; + + p_atomic_int_set (&(spinlock->spin), 0); + + return TRUE; +} + +P_LIB_API void +p_spinlock_free (PSpinLock *spinlock) +{ + p_free (spinlock); +} diff --git a/3rdparty/plibsys/src/pspinlock.h b/3rdparty/plibsys/src/pspinlock.h new file mode 100644 index 0000000..aac3b3b --- /dev/null +++ b/3rdparty/plibsys/src/pspinlock.h @@ -0,0 +1,142 @@ +/* + * The MIT License + * + * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * @file pspinlock.h + * @brief Light-weight atomic spinlock + * @author Alexander Saprykin + * + * A spinlock is an inter-thread synchronization primitive based on atomic + * operations. It allows to guard a critical section from concurrent access of + * multiple threads at once. It is very similar to a mutex in semantics, but + * inside it provides a more light-weight and fast locking mechanism without + * thread sleeping and undesirable context switching. Thus spinlocks should be + * used only for small code sections, otherwise long-time spinning can cause + * extensive CPU time waste by waiting threads. + * + * As the spinlock is based on atomic operations it would have the real meaning + * only if an underlying atomic model is lock-free (not simulated using the + * mutex). You can check if the atomic model is lock-free with + * p_atomic_is_lock_free(). Otherwise usage of spinlocks will be the same as the + * ordinary mutex. + * + * To create a new spinlock primitive the p_spinlock_new() routine should be + * called, to delete the unused spinlock primitive use p_spinlock_free(). + * + * Use p_spinlock_lock() or p_spinlock_trylock() to synchronize access at the + * beginning of the critical section. Only the one thread is allowed to pass + * this call, others will wait for the p_spinlock_unlock() call which marks the + * end of the critical section. This way the critical section code is guarded + * against concurrent access of multiple threads at once. + */ + +#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION) +# error "Header files shouldn't be included directly, consider using <plibsys.h> instead." +#endif + +#ifndef PLIBSYS_HEADER_PSPINLOCK_H +#define PLIBSYS_HEADER_PSPINLOCK_H + +#include <pmacros.h> +#include <ptypes.h> + +P_BEGIN_DECLS + +/** Spinlock opaque data structure. */ +typedef struct PSpinLock_ PSpinLock; + +/** + * @brief Creates a new #PSpinLock object. + * @return Pointer to a newly created #PSpinLock object. + * @since 0.0.1 + */ +P_LIB_API PSpinLock * p_spinlock_new (void); + +/** + * @brief Locks a spinlock. + * @param spinlock #PSpinLock to lock. + * @return TRUE in case of success, FALSE otherwise. + * @since 0.0.1 + * + * A thread will not sleep in this call if another thread is holding the lock, + * instead it will try to lock @a spinlock in an infinite loop. + * + * If the atomic model is not lock-free this call will have the same effect + * as p_mutex_lock(). + * + * Do not lock a spinlock recursively - this may lead to an application + * deadlock. + */ +P_LIB_API pboolean p_spinlock_lock (PSpinLock *spinlock); + +/** + * @brief Tries to lock a spinlock immediately. + * @param spinlock #PSpinLock to lock. + * @return TRUE in case of success, FALSE otherwise. + * @since 0.0.1 + * + * Tries to lock @a spinlock and returns immediately if it is not available for + * locking. + * + * If the atomic model is not lock-free this call will have the same effect + * as p_mutex_trylock(). + * + * Do not lock a spinlock recursively - this may lead to an application + * deadlock. + */ +P_LIB_API pboolean p_spinlock_trylock (PSpinLock *spinlock); + +/** + * @brief Releases a locked spinlock. + * @param spinlock #PSpinLock to release. + * @return TRUE in case of success, FALSE otherwise. + * @since 0.0.1 + * + * If @a spinlock was previously locked then it becomes unlocked. Any thread + * can unlock any spinlock. It is also safe to call this routine on an unlocked + * spinlock. + * + * If the atomic model is not lock-free this call will have the same effect + * as p_mutex_unlock(), thus it is not safe to call this routine on an unlocked + * spinlock. + */ +P_LIB_API pboolean p_spinlock_unlock (PSpinLock *spinlock); + +/** + * @brief Frees #PSpinLock object. + * @param spinlock #PSpinLock to free. + * @since 0.0.1 + * + * It doesn't unlock @a spinlock before freeing memory, so you should do it + * manually. + * + * If the atomic model is not lock-free this call will have the same effect + * as p_mutex_free(). + */ +P_LIB_API void p_spinlock_free (PSpinLock *spinlock); + +P_END_DECLS + +#endif /* PLIBSYS_HEADER_PSPINLOCK_H */ diff --git a/3rdparty/plibsys/src/pstdarg.h b/3rdparty/plibsys/src/pstdarg.h new file mode 100644 index 0000000..bd12f8a --- /dev/null +++ b/3rdparty/plibsys/src/pstdarg.h @@ -0,0 +1,318 @@ +/* + * The MIT License + * + * Copyright (C) 2017 Jean-Damien Durand <jeandamiendurand@free.fr> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * @file pstdarg.h + * @brief Variable arguments + * @author Jean-Damien Durand + * + * Functions declared with a variable number of arguments use macros to step + * through them. + * + * The #p_va_list type must be declared in such function, #p_va_start, + * #p_va_arg and #p_va_end are used to initialize, to step through and to end + * the navigation, respectively. + * + * A variable number of arguments can be propagated to another function which + * accepts a #p_va_list parameter, using #p_va_copy. + * + * Any use of #p_va_start or #p_va_copy must have a corresponding #p_va_end. + * + * Using a variable number of parameters requires a known-in-advance contract + * between the caller and the callee on the number of parameters and their types. + * Note, that this mechanism is a weakly typed: the compiler will always apply + * default type promotion, regardless if you explicitely typecast an argument in + * the stack, i.e.: + * - Integer arguments of types lower than #pint are always promoted to #pint, + * or #puint if #pint is not enough. + * - Arguments of type @a float are always promoted to @a double. + * - Arrays are always promoted to pointers. + * + * You need to be very careful when using variable arguments. Improper usage may + * lead to program crash. In order to avoid type casting mistakes, consider + * using macros for variable arguments with explicit type casting provided below. + * Though you still can use #p_va_arg if you know what are you doing. + * + * Please note, that stdarg.h implementation is not compatible with varargs.h, + * and only one of them should be used in a compilation unit. You must be sure + * that you don't use varargs.h along with the current implmenetation, otherwise + * runtime failures are inevitable. + */ + +#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION) +# error "Header files shouldn't be included directly, consider using <plibsys.h> instead." +#endif + +#ifndef PLIBSYS_HEADER_PSTDARG_H +#define PLIBSYS_HEADER_PSTDARG_H + +#include <pmacros.h> +#include <ptypes.h> + +#include <stdio.h> +#include <stdarg.h> +#ifndef PLIBSYS_VA_COPY +# include <string.h> +#endif + +/** + * @brief Opaque type holding variable number of arguments navigation. + * @since 0.0.4 + */ +#define p_va_list va_list + +/** + * @brief Initializes navigation through a variable number of arguments. + * @param ap Declared object of type #p_va_list. + * @param last Name of the last argument before @a ap. + * @since 0.0.4 + * @note There must be a corresponding call to #p_va_end(ap) afterwards. + */ +#define p_va_start(ap, last) va_start(ap, last) + +/** + * @brief Gets the next argument in the list. + * @param ap Declared object of type va_list, previously initalized with + * #p_va_start. + * @param type Type of the next argument. + * @return Value of the next argument. + * @since 0.0.4 + */ + +#define p_va_arg(ap, type) (type) va_arg(ap, type) + +/** + * @brief Ends the navigation. + * @param ap Declared object of type #p_va_list. + * @since 0.0.4 + * @note There must be a corresponding call to #p_va_start(ap) before. + */ +#define p_va_end(ap) va_end(ap) + +/** + * @brief Copies a navigation object. + * @param dst Destination object of type #p_va_list. + * @param src Source object of type #p_va_list. + * @since 0.0.4 + * @note The state of @a src is copied as well, @a dst is initialized as if + * #p_va_start(dst) would have been called. There must be a corresponding call + * to #p_va_end(dst) afterwards. + */ +#ifdef PLIBSYS_VA_COPY +# define p_va_copy(dst, src) PLIBSYS_VA_COPY(dst, src) +#else +# define p_va_copy(dst, src) ((void) memcpy (&(dst), &(src), sizeof (va_list))) +#endif + +/** + * @def pint8_va_arg + * @brief Unstacks a #pint8 variable. + * @param ap #p_va_list stack. + * @return #pint8 value. + * @since 0.0.4 + */ +#define pint8_va_arg(ap) ((pint8) p_va_arg(ap, pint)) + +/** + * @def puint8_va_arg + * @brief Unstacks a #puint8 variable. + * @param ap #p_va_list stack. + * @return #puint8 value. + * @since 0.0.4 + */ +#define puint8_va_arg(ap) ((puint8) p_va_arg(ap, puint)) + +/** + * @def pint16_va_arg + * @brief Unstacks a #pint16 variable. + * @param ap #p_va_list stack. + * @return #pint16 value. + * @since 0.0.4 + */ +#define pint16_va_arg(ap) ((pint16) p_va_arg(ap, pint)) + +/** + * @def puint16_va_arg + * @brief Unstacks a #puint16 variable. + * @param ap #p_va_list stack. + * @return #puint16 value. + * @since 0.0.4 + */ +#define puint16_va_arg(ap) ((puint16) p_va_arg(ap, puint)) + +/** + * @def pint32_va_arg + * @brief Unstacks a #pint32 variable. + * @param ap #p_va_list stack. + * @return #pint32 value. + * @since 0.0.4 + */ +#define pint32_va_arg(ap) ((pint32) p_va_arg(ap, pint)) + +/** + * @def puint32_va_arg + * @brief Unstacks a #puint32 variable. + * @param ap #p_va_list stack. + * @return #puint32 value. + * @since 0.0.4 + */ +#define puint32_va_arg(ap) ((puint32) p_va_arg(ap, puint)) + +/** + * @def pint64_va_arg + * @brief Unstacks a #pint64 variable. + * @param ap #p_va_list stack. + * @return #pint64 value. + * @since 0.0.4 + */ +#define pint64_va_arg(ap) (p_va_arg(ap, pint64)) + +/** + * @def puint64_va_arg + * @brief Unstacks a #puint64 variable. + * @param ap #p_va_list stack. + * @return #puint64 value. + * @since 0.0.4 + */ +#define puint64_va_arg(ap) (p_va_arg(ap, puint64)) + +/** + * @def ppointer_va_arg + * @brief Unstacks a #ppointer variable. + * @param ap #p_va_list stack. + * @return #ppointer value. + * @since 0.0.4 + */ +#define ppointer_va_arg(ap) (p_va_arg(ap, ppointer)) + +/** + * @def pconstpointer_va_arg + * @brief Unstacks a #pconstpointer variable. + * @param ap #p_va_list stack. + * @return #pconstpointer value. + * @since 0.0.4 + */ +#define pconstpointer_va_arg(ap) (p_va_arg(ap, pconstpointer)) + +/** + * @def pboolean_va_arg + * @brief Unstacks a #pboolean variable. + * @param ap #p_va_list stack. + * @return #pboolean value. + * @since 0.0.4 + */ +#define pboolean_va_arg(ap) ((pboolean) p_va_arg(ap, pint)) + +/** + * @def pchar_va_arg + * @brief Unstacks a #pchar variable. + * @param ap #p_va_list stack. + * @return #pchar value. + * @since 0.0.4 + */ +#define pchar_va_arg(ap) ((pchar) p_va_arg(ap, pint)) + +/** + * @def pshort_va_arg + * @brief Unstacks a #pshort variable. + * @param ap #p_va_list stack. + * @return #pshort value. + * @since 0.0.4 + */ +#define pshort_va_arg(ap) ((pshort) p_va_arg(ap, pint)) + +/** + * @def pint_va_arg + * @brief Unstacks a #pint variable. + * @param ap #p_va_list stack. + * @return #pint value. + * @since 0.0.4 + */ +#define pint_va_arg(ap) (p_va_arg(ap, pint)) + +/** + * @def plong_va_arg + * @brief Unstacks a #plong variable. + * @param ap #p_va_list stack. + * @return #plong value. + * @since 0.0.4 + */ +#define plong_va_arg(ap) (p_va_arg(ap, plong)) + +/** + * @def puchar_va_arg + * @brief Unstacks a #puchar variable. + * @param ap #p_va_list stack. + * @return #puchar value. + * @since 0.0.4 + */ +#define puchar_va_arg(ap) ((puchar) p_va_arg(ap, puint)) + +/** + * @def pushort_va_arg + * @brief Unstacks a #pushort variable. + * @param ap #p_va_list stack. + * @return #pushort value. + * @since 0.0.4 + */ +#define pushort_va_arg(ap) ((pushort) p_va_arg(ap, puint)) + +/** + * @def puint_va_arg + * @brief Unstacks a #puint variable. + * @param ap #p_va_list stack. + * @return #puint value. + * @since 0.0.4 + */ +#define puint_va_arg(ap) (p_va_arg(ap, puint)) + +/** + * @def pulong_va_arg + * @brief Unstacks a #pulong variable. + * @param ap #p_va_list stack. + * @return #pulong value. + * @since 0.0.4 + */ +#define pulong_va_arg(ap) (p_va_arg(ap, pulong)) + +/** + * @def pfloat_va_arg + * @brief Unstacks a #pfloat variable. + * @param ap #p_va_list stack. + * @return #pfloat value. + * @since 0.0.4 + */ +#define pfloat_va_arg(ap) ((pfloat) p_va_arg(ap, pdouble)) + +/** + * @def pdouble_va_arg + * @brief Unstacks a #pdouble variable. + * @param ap #p_va_list stack. + * @return #pdouble value. + * @since 0.0.4 + */ +#define pdouble_va_arg(ap) (p_va_arg(ap, pdouble)) + +#endif /* PLIBSYS_HEADER_PSTDARG_H */ diff --git a/3rdparty/plibsys/src/pstring.c b/3rdparty/plibsys/src/pstring.c new file mode 100644 index 0000000..ed46ec5 --- /dev/null +++ b/3rdparty/plibsys/src/pstring.c @@ -0,0 +1,206 @@ +/* + * The MIT License + * + * Copyright (C) 2011-2016 Alexander Saprykin <saprykin.spb@gmail.com> + * Copyright (C) 2009 Tom Van Baak (tvb) www.LeapSecond.com + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pmem.h" +#include "pstring.h" + +#include <string.h> +#include <ctype.h> + +#define P_STR_MAX_EXPON 308 + +P_LIB_API pchar * +p_strdup (const pchar *str) +{ + pchar *ret; + psize len; + + if (P_UNLIKELY (str == NULL)) + return NULL; + + len = strlen (str) + 1; + + if (P_UNLIKELY ((ret = p_malloc (len)) == NULL)) + return NULL; + + memcpy (ret, str, len); + + return ret; +} + +P_LIB_API pchar * +p_strchomp (const pchar *str) +{ + pssize pos_start, pos_end; + psize str_len; + pchar *ret; + const pchar *ptr; + + if (P_UNLIKELY (str == NULL)) + return NULL; + + ptr = str; + pos_start = 0; + pos_end = ((pssize) strlen (str)) - 1; + + while (pos_start < pos_end && isspace (* ((const puchar *) ptr++))) + ++pos_start; + + ptr = str + pos_end; + + while (pos_end > 0 && isspace (* ((const puchar *) ptr--))) + --pos_end; + + if (pos_end < pos_start) + return p_strdup ("\0"); + + if (pos_end == pos_start && isspace (* ((const puchar *) (str + pos_end)))) + return p_strdup ("\0"); + + str_len = (psize) (pos_end - pos_start + 2); + + if (P_UNLIKELY ((ret = p_malloc0 (str_len)) == NULL)) + return NULL; + + memcpy (ret, str + pos_start, str_len - 1); + *(ret + str_len - 1) = '\0'; + + return ret; +} + +P_LIB_API pchar * +p_strtok (pchar *str, const pchar *delim, pchar **buf) +{ + if (P_UNLIKELY (delim == NULL)) + return str; + +#ifdef P_OS_WIN +# ifdef P_CC_MSVC + if (P_UNLIKELY (buf == NULL)) + return str; +# if _MSC_VER < 1400 + P_UNUSED (buf); + return strtok (str, delim); +# else + return strtok_s (str, delim, buf); +# endif +# else + P_UNUSED (buf); + return strtok (str, delim); +# endif +#else + if (P_UNLIKELY (buf == NULL)) + return str; + + return strtok_r (str, delim, buf); +#endif +} + +P_LIB_API double +p_strtod (const pchar *str) +{ + double sign; + double value; + double scale; + double pow10; + puint expon; + pint frac; + pchar *orig_str, *strp; + + orig_str = p_strchomp (str); + + if (P_UNLIKELY (orig_str == NULL)) + return 0.0; + + strp = orig_str; + sign = 1.0; + + if (*strp == '-') { + sign = -1.0; + strp += 1; + } else if (*strp == '+') + strp += 1; + + /* Get digits before decimal point or exponent, if any */ + for (value = 0.0; isdigit ((pint) *strp); strp += 1) + value = value * 10.0 + (*strp - '0'); + + /* Get digits after decimal point, if any */ + if (*strp == '.') { + pow10 = 10.0; + strp += 1; + + while (isdigit ((pint) *strp)) { + value += (*strp - '0') / pow10; + pow10 *= 10.0; + strp += 1; + } + } + + /* Handle exponent, if any */ + frac = 0; + scale = 1.0; + + if ((*strp == 'e') || (*strp == 'E')) { + /* Get sign of exponent, if any */ + strp += 1; + + if (*strp == '-') { + frac = 1; + strp += 1; + + } else if (*strp == '+') + strp += 1; + + /* Get digits of exponent, if any */ + for (expon = 0; isdigit ((pint) *strp); strp += 1) + expon = expon * 10 + (puint) ((*strp - '0')); + + if (P_UNLIKELY (expon > P_STR_MAX_EXPON)) + expon = P_STR_MAX_EXPON; + + /* Calculate scaling factor */ + while (expon >= 50) { + scale *= 1e50; + expon -= 50; + } + + while (expon >= 8) { + scale *= 1e8; + expon -= 8; + } + + while (expon > 0) { + scale *= 10.0; + expon -= 1; + } + } + + p_free (orig_str); + + /* Return signed and scaled floating point result */ + return sign * (frac ? (value / scale) : (value * scale)); +} diff --git a/3rdparty/plibsys/src/pstring.h b/3rdparty/plibsys/src/pstring.h new file mode 100644 index 0000000..76ebf9d --- /dev/null +++ b/3rdparty/plibsys/src/pstring.h @@ -0,0 +1,117 @@ +/* + * The MIT License + * + * Copyright (C) 2011-2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * @file pstring.h + * @brief String manipulation routines + * @author Alexander Saprykin + * + * Strings are represented as a sequence of single-byte characters (from the + * ASCII table) with the trailing zero character (\0). + * + * Some useful string manipulation routines are represented here. + */ + +#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION) +# error "Header files shouldn't be included directly, consider using <plibsys.h> instead." +#endif + +#ifndef PLIBSYS_HEADER_PSTRING_H +#define PLIBSYS_HEADER_PSTRING_H + +#include <pmacros.h> +#include <ptypes.h> + +P_BEGIN_DECLS + +/** + * @brief Copies a string. + * @param str String with the trailing zero to copy. + * @return Copy of the @a str in case of success, NULL otherwise. The caller + * takes ownership of the returned string. + * @since 0.0.1 + */ +P_LIB_API pchar * p_strdup (const pchar *str); + +/** + * @brief Removes trailing and leading whitespaces. + * @param str String with the trailing zero to process. + * @return Newlly allocated string in case of success, NULL otherwise. The + * caller takes ownership of the returned string. + * @since 0.0.1 + */ +P_LIB_API pchar * p_strchomp (const pchar *str); + +/** + * @brief Tokenizes a string by given delimiters. + * @param[in,out] str String to tokenize. + * @param delim List of delimiters to split the string. + * @param buf Context to store tokenize info. + * @return Pointer to a splitted zero-terminated string in case of success, NULL + * otherwise. + * @since 0.0.1 + * @note The @a str is modified by this call, so take care for that. The + * returned pointer points on a @a str substring, so you do not need to call + * p_free() on it. + * + * The common usage of this call is following: + * @code + * pchar *token, *buf; + * pchar str[] = "This is a test string" + * pchar delim[] = " \t" + * ... + * token = p_strtok (str, delim, &buf); + * + * while (token != NULL) { + * printf ("Splitted string: %s\n", token); + * token = p_strtok (NULL, delim, &buf); + * } + * @endcode + * Take attention that you need to pass the original string only once, after + * that you should pass NULL instead. You can also pass different delimiters + * each time. + * + * Some platforms do not support the third parameter and it can be remained + * unused. In that case this call wouldn't be thread-safe. + */ +P_LIB_API pchar * p_strtok (pchar *str, + const pchar *delim, + pchar **buf); + +/** + * @brief Converts a string to @a double without a locale dependency. + * @param str String to convert. + * @return Floating point value in case of success, 0 otherwise. + * @since 0.0.1 + * + * Since the atof() system call is locale dependent, you can use this call to + * convert string variables to @a double values. The decimal point is '.' as in + * the 'C' locale. + */ +P_LIB_API double p_strtod (const pchar *str); + +P_END_DECLS + +#endif /* PLIBSYS_HEADER_PSTRING_H */ diff --git a/3rdparty/plibsys/src/psysclose-darwin.c b/3rdparty/plibsys/src/psysclose-darwin.c new file mode 100644 index 0000000..7c44024 --- /dev/null +++ b/3rdparty/plibsys/src/psysclose-darwin.c @@ -0,0 +1,93 @@ +/* + * The MIT License + * + * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * Copyright 2013 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * http://crbug.com/269623 + * http://openradar.appspot.com/14999594 + * + * When the default version of close used on macOS fails with EINTR, the + * file descriptor is not in a deterministic state. It may have been closed, + * or it may not have been. This makes it impossible to gracefully recover + * from the error. If the close is retried after the FD has been closed, the + * subsequent close can report EBADF, or worse, it can close an unrelated FD + * opened by another thread. If the close is not retried after the FD has been + * left open, the FD is leaked. Neither of these are good options. + * + * macOS provides an alternate version of close, close$NOCANCEL. This + * version will never fail with EINTR before the FD is actually closed. With + * this version, it is thus safe to call close without checking for EINTR (as + * the HANDLE_EINTR macro does) and not risk leaking the FD. In fact, mixing + * this verison of close with HANDLE_EINTR is hazardous. + * + * The $NOCANCEL variants of various system calls are activated by compiling + * with __DARWIN_NON_CANCELABLE, which prevents them from being pthread + * cancellation points. Rather than taking such a heavy-handed approach, this + * file implements an alternative: to use the $NOCANCEL variant of close (thus + * preventing it from being a pthread cancellation point) without affecting + * any other system calls. + * + * This file operates by providing a close function with the non-$NOCANCEL + * symbol name expected for the compilation environment as set by <unistd.h> + * and <sys/cdefs.h> (the DARWIN_ALIAS_C macro). That function calls the + * $NOCANCEL variant, which is resolved from libsyscall. By linking with this + * version of close prior to the libsyscall version, close's implementation is + * overridden. + */ + +#include <sys/cdefs.h> + +/* If the non-cancelable variants of all system calls have already been chosen, + * do nothing. */ +#if !__DARWIN_NON_CANCELABLE +# if __DARWIN_UNIX03 && !__DARWIN_ONLY_UNIX_CONFORMANCE +/* When there's a choice between UNIX2003 and pre-UNIX2003 and UNIX2003 has + * been chosen. */ +extern int close$NOCANCEL$UNIX2003 (int fd); +# define PLIBSYS_CLOSE_INTERFACE close$NOCANCEL$UNIX2003 +# elif !__DARWIN_UNIX03 && !__DARWIN_ONLY_UNIX_CONFORMANCE +/* When there's a choice between UNIX2003 and pre-UNIX2003 and pre-UNIX2003 + * has been chosen. There's no close$NOCANCEL symbol in this case, so use + * close$NOCANCEL$UNIX2003 as the implementation. It does the same thing that + * close$NOCANCEL would do. */ +extern int close$NOCANCEL$UNIX2003 (int fd); +# define PLIBSYS_CLOSE_INTERFACE close$NOCANCEL$UNIX2003 +# else +/* When only UNIX2003 is supported. */ +extern int close$NOCANCEL (int fd); +# define PLIBSYS_CLOSE_INTERFACE close$NOCANCEL +# endif +#endif + +#include "psysclose-private.h" + +pint +p_sys_close (pint fd) +{ + return PLIBSYS_CLOSE_INTERFACE (fd); +} diff --git a/3rdparty/plibsys/src/psysclose-private.h b/3rdparty/plibsys/src/psysclose-private.h new file mode 100644 index 0000000..629e2cb --- /dev/null +++ b/3rdparty/plibsys/src/psysclose-private.h @@ -0,0 +1,47 @@ +/* + * The MIT License + * + * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION) +# error "Header files shouldn't be included directly, consider using <plibsys.h> instead." +#endif + +#ifndef PLIBSYS_HEADER_PSYSCLOSE_PRIVATE_H +#define PLIBSYS_HEADER_PSYSCLOSE_PRIVATE_H + +#include "pmacros.h" +#include "ptypes.h" + +P_BEGIN_DECLS + +/** + * @brief Safely closes a file descriptor. + * @param fd File descriptor to close. + * @return -1 in case of success, 0 otherwise. + */ +pint p_sys_close (pint fd); + +P_END_DECLS + +#endif /* PLIBSYS_HEADER_PSYSCLOSE_PRIVATE_H */ diff --git a/3rdparty/plibsys/src/psysclose-unix.c b/3rdparty/plibsys/src/psysclose-unix.c new file mode 100644 index 0000000..1a43dd6 --- /dev/null +++ b/3rdparty/plibsys/src/psysclose-unix.c @@ -0,0 +1,54 @@ +/* + * The MIT License + * + * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "perror.h" +#include "psysclose-private.h" + +#include <unistd.h> +#include <errno.h> + +pint +p_sys_close (pint fd) +{ +#if defined (EINTR) && defined (P_OS_HPUX) + pint res, err_code; + + for (;;) { + res = close (fd); + + if (P_LIKELY (res == 0)) + return 0; + + err_code = p_error_get_last_system (); + + if (err_code == EINTR) + continue; + else + return -1; + } +#else + return close (fd); +#endif +} diff --git a/3rdparty/plibsys/src/psysclose-win.c b/3rdparty/plibsys/src/psysclose-win.c new file mode 100644 index 0000000..57f83b5 --- /dev/null +++ b/3rdparty/plibsys/src/psysclose-win.c @@ -0,0 +1,33 @@ +/* + * The MIT License + * + * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "psysclose-private.h" + +pint +p_sys_close (pint fd) +{ + /* On Windows we can only close a socket descriptor */ + return closesocket (fd) == 0 ? 0 : -1; +} diff --git a/3rdparty/plibsys/src/ptimeprofiler-amiga.c b/3rdparty/plibsys/src/ptimeprofiler-amiga.c new file mode 100644 index 0000000..c90f1d7 --- /dev/null +++ b/3rdparty/plibsys/src/ptimeprofiler-amiga.c @@ -0,0 +1,70 @@ +/* + * The MIT License + * + * Copyright (C) 2017 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "ptimeprofiler.h" +#include "ptimeprofiler-private.h" + +#include <proto/timer.h> + +static puint64 pp_time_profiler_freq = 1; + +puint64 +p_time_profiler_get_ticks_internal () +{ + struct EClockVal eclock; + + ITimer->ReadEClock (&eclock); + + return (((puint64) eclock.ev_hi) * pp_time_profiler_freq + (puint64) eclock.ev_lo); +} + +puint64 +p_time_profiler_elapsed_usecs_internal (const PTimeProfiler *profiler) +{ + puint64 value; + + value = p_time_profiler_get_ticks_internal (); + + /* Check for register overflow */ + + if (P_UNLIKELY (value < profiler->counter)) + value += (((puint64) 1) << 32) * pp_time_profiler_freq; + + return (value - profiler->counter) * 1000000ULL / pp_time_profiler_freq; +} + +void +p_time_profiler_init (void) +{ + struct EClockVal eclock; + + pp_time_profiler_freq = (puint64) ITimer->ReadEClock (&eclock); +} + +void +p_time_profiler_shutdown (void) +{ + pp_time_profiler_freq = 1; +} diff --git a/3rdparty/plibsys/src/ptimeprofiler-beos.c b/3rdparty/plibsys/src/ptimeprofiler-beos.c new file mode 100644 index 0000000..8d651d7 --- /dev/null +++ b/3rdparty/plibsys/src/ptimeprofiler-beos.c @@ -0,0 +1,51 @@ +/* + * The MIT License + * + * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "ptimeprofiler.h" +#include "ptimeprofiler-private.h" + +#include <kernel/OS.h> + +puint64 +p_time_profiler_get_ticks_internal () +{ + return (puint64) system_time (); +} + +puint64 +p_time_profiler_elapsed_usecs_internal (const PTimeProfiler *profiler) +{ + return ((puint64) system_time ()) - profiler->counter; +} + +void +p_time_profiler_init (void) +{ +} + +void +p_time_profiler_shutdown (void) +{ +} diff --git a/3rdparty/plibsys/src/ptimeprofiler-generic.c b/3rdparty/plibsys/src/ptimeprofiler-generic.c new file mode 100644 index 0000000..d984d47 --- /dev/null +++ b/3rdparty/plibsys/src/ptimeprofiler-generic.c @@ -0,0 +1,58 @@ +/* + * The MIT License + * + * Copyright (C) 2013-2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "ptimeprofiler.h" +#include "ptimeprofiler-private.h" + +#include <time.h> + +puint64 +p_time_profiler_get_ticks_internal () +{ + pint64 val; + + if (P_UNLIKELY ((val = (pint64) time (NULL)) == -1)) { + P_ERROR ("PTimeProfiler::p_time_profiler_get_ticks_internal: time() failed"); + return 0; + } + + return (puint64) (val * 1000000); +} + +puint64 +p_time_profiler_elapsed_usecs_internal (const PTimeProfiler *profiler) +{ + return p_time_profiler_get_ticks_internal () - profiler->counter; +} + +void +p_time_profiler_init (void) +{ +} + +void +p_time_profiler_shutdown (void) +{ +} diff --git a/3rdparty/plibsys/src/ptimeprofiler-mach.c b/3rdparty/plibsys/src/ptimeprofiler-mach.c new file mode 100644 index 0000000..6bef3c7 --- /dev/null +++ b/3rdparty/plibsys/src/ptimeprofiler-mach.c @@ -0,0 +1,73 @@ +/* + * The MIT License + * + * Copyright (C) 2013-2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "ptimeprofiler.h" +#include "ptimeprofiler-private.h" + +#include <mach/mach_time.h> + +static puint64 pp_time_profiler_freq_num = 0; +static puint64 pp_time_profiler_freq_denom = 0; + +puint64 +p_time_profiler_get_ticks_internal () +{ + puint64 val = mach_absolute_time (); + + /* To prevent overflow */ + val /= 1000; + + val *= pp_time_profiler_freq_num; + val /= pp_time_profiler_freq_denom; + + return val; +} + +puint64 +p_time_profiler_elapsed_usecs_internal (const PTimeProfiler *profiler) +{ + return p_time_profiler_get_ticks_internal () - profiler->counter; +} + +void +p_time_profiler_init (void) +{ + mach_timebase_info_data_t tb; + + if (P_UNLIKELY (mach_timebase_info (&tb) != KERN_SUCCESS || tb.denom == 0)) { + P_ERROR ("PTimeProfiler::p_time_profiler_init: mach_timebase_info() failed"); + return; + } + + pp_time_profiler_freq_num = (puint64) tb.numer; + pp_time_profiler_freq_denom = (puint64) tb.denom; +} + +void +p_time_profiler_shutdown (void) +{ + pp_time_profiler_freq_num = 0; + pp_time_profiler_freq_denom = 0; +} diff --git a/3rdparty/plibsys/src/ptimeprofiler-os2.c b/3rdparty/plibsys/src/ptimeprofiler-os2.c new file mode 100644 index 0000000..e511bd1 --- /dev/null +++ b/3rdparty/plibsys/src/ptimeprofiler-os2.c @@ -0,0 +1,107 @@ +/* + * The MIT License + * + * Copyright (C) 2017 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "ptimeprofiler.h" +#include "ptimeprofiler-private.h" + +#define INCL_DOSPROFILE +#define INCL_DOSERRORS +#include <os2.h> + +#if PLIBSYS_HAS_LLDIV +# ifdef P_CC_GNU +# define __USE_ISOC99 +# endif +# include <stdlib.h> +#endif + +static puint64 pp_time_profiler_freq = 1; + +puint64 +p_time_profiler_get_ticks_internal () +{ + union { + puint64 ticks; + QWORD tcounter; + } tick_time; + + if (P_UNLIKELY (DosTmrQueryTime (&tick_time.tcounter) != NO_ERROR)) { + P_ERROR ("PTimeProfiler::p_time_profiler_get_ticks_internal: DosTmrQueryTime() failed"); + return 0; + } + + return tick_time.ticks; +} + +puint64 +p_time_profiler_elapsed_usecs_internal (const PTimeProfiler *profiler) +{ + puint64 ticks; +#if PLIBSYS_HAS_LLDIV + lldiv_t ldres; +#endif + puint64 quot; + puint64 rem; + + ticks = p_time_profiler_get_ticks_internal (); + + if (ticks < profiler->counter) { + P_WARNING ("PTimeProfiler::p_time_profiler_elapsed_usecs_internal: negative jitter"); + return 1; + } + + ticks -= profiler->counter; + +#if PLIBSYS_HAS_LLDIV + ldres = lldiv ((long long) ticks, (long long) pp_time_profiler_freq); + + quot = ldres.quot; + rem = ldres.rem; +#else + quot = ticks / pp_time_profiler_freq; + rem = ticks % pp_time_profiler_freq; +#endif + + return (puint64) (quot * 1000000LL + (rem * 1000000LL) / pp_time_profiler_freq); +} + +void +p_time_profiler_init (void) +{ + ULONG freq; + + if (P_UNLIKELY (DosTmrQueryFreq (&freq) != NO_ERROR)) { + P_ERROR ("PTimeProfiler::p_time_profiler_init: DosTmrQueryFreq() failed"); + return; + } + + pp_time_profiler_freq = (puint64) freq; +} + +void +p_time_profiler_shutdown (void) +{ + pp_time_profiler_freq = 1; +} diff --git a/3rdparty/plibsys/src/ptimeprofiler-posix.c b/3rdparty/plibsys/src/ptimeprofiler-posix.c new file mode 100644 index 0000000..b913bbb --- /dev/null +++ b/3rdparty/plibsys/src/ptimeprofiler-posix.c @@ -0,0 +1,109 @@ +/* + * The MIT License + * + * Copyright (C) 2013-2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "ptimeprofiler.h" +#include "ptimeprofiler-private.h" + +#include <unistd.h> +#include <time.h> +#include <sys/time.h> + +#ifndef _POSIX_MONOTONIC_CLOCK +# define _POSIX_MONOTONIC_CLOCK (-1) +#endif + +typedef puint64 (* PPOSIXTicksFunc) (void); + +static PPOSIXTicksFunc pp_time_profiler_ticks_func = NULL; + +#if (_POSIX_MONOTONIC_CLOCK >= 0) || defined (P_OS_IRIX) +static puint64 pp_time_profiler_get_ticks_clock (); +#endif + +static puint64 pp_time_profiler_get_ticks_gtod (); + +#if (_POSIX_MONOTONIC_CLOCK >= 0) || defined (P_OS_IRIX) +static puint64 +pp_time_profiler_get_ticks_clock () +{ + struct timespec ts; + +#ifdef P_OS_IRIX + if (P_UNLIKELY (clock_gettime (CLOCK_SGI_CYCLE, &ts) != 0)) { +#else + if (P_UNLIKELY (clock_gettime (CLOCK_MONOTONIC, &ts) != 0)) { +#endif + P_ERROR ("PTimeProfiler::pp_time_profiler_get_ticks_clock: clock_gettime() failed"); + return pp_time_profiler_get_ticks_gtod (); + } else + return (puint64) (ts.tv_sec * 1000000 + ts.tv_nsec / 1000); +} +#endif + +static puint64 +pp_time_profiler_get_ticks_gtod () +{ + struct timeval tv; + + if (P_UNLIKELY (gettimeofday (&tv, NULL) != 0)) { + P_ERROR ("PTimeProfiler::pp_time_profiler_get_ticks_gtod: gettimeofday() failed"); + return 0; + } + + return (puint64) (tv.tv_sec * 1000000 + tv.tv_usec); +} + +puint64 +p_time_profiler_get_ticks_internal () +{ + return pp_time_profiler_ticks_func (); +} + +puint64 +p_time_profiler_elapsed_usecs_internal (const PTimeProfiler *profiler) +{ + return pp_time_profiler_ticks_func () - profiler->counter; +} + +void +p_time_profiler_init (void) +{ +#if defined (P_OS_IRIX) || (_POSIX_MONOTONIC_CLOCK > 0) + pp_time_profiler_ticks_func = (PPOSIXTicksFunc) pp_time_profiler_get_ticks_clock; +#elif (_POSIX_MONOTONIC_CLOCK == 0) && defined (_SC_MONOTONIC_CLOCK) + if (P_LIKELY (sysconf (_SC_MONOTONIC_CLOCK) > 0)) + pp_time_profiler_ticks_func = (PPOSIXTicksFunc) pp_time_profiler_get_ticks_clock; + else + pp_time_profiler_ticks_func = (PPOSIXTicksFunc) pp_time_profiler_get_ticks_gtod; +#else + pp_time_profiler_ticks_func = (PPOSIXTicksFunc) pp_time_profiler_get_ticks_gtod; +#endif +} + +void +p_time_profiler_shutdown (void) +{ + pp_time_profiler_ticks_func = NULL; +} diff --git a/3rdparty/plibsys/src/ptimeprofiler-private.h b/3rdparty/plibsys/src/ptimeprofiler-private.h new file mode 100644 index 0000000..c7c250d --- /dev/null +++ b/3rdparty/plibsys/src/ptimeprofiler-private.h @@ -0,0 +1,45 @@ +/* + * The MIT License + * + * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION) +# error "Header files shouldn't be included directly, consider using <plibsys.h> instead." +#endif + +#ifndef PLIBSYS_HEADER_PTIMEPROFILER_PRIVATE_H +#define PLIBSYS_HEADER_PTIMEPROFILER_PRIVATE_H + +#include "pmacros.h" +#include "ptypes.h" + +P_BEGIN_DECLS + +/** Time profiler opaque data structure. */ +struct PTimeProfiler_ { + puint64 counter; /**< Ticks counter. */ +}; + +P_END_DECLS + +#endif /* PLIBSYS_HEADER_PTIMEPROFILER_PRIVATE_H */ diff --git a/3rdparty/plibsys/src/ptimeprofiler-solaris.c b/3rdparty/plibsys/src/ptimeprofiler-solaris.c new file mode 100644 index 0000000..5832614 --- /dev/null +++ b/3rdparty/plibsys/src/ptimeprofiler-solaris.c @@ -0,0 +1,53 @@ +/* + * The MIT License + * + * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "ptimeprofiler.h" +#include "ptimeprofiler-private.h" + +#include <unistd.h> +#include <time.h> +#include <sys/time.h> + +puint64 +p_time_profiler_get_ticks_internal () +{ + return (puint64) gethrtime (); +} + +puint64 +p_time_profiler_elapsed_usecs_internal (const PTimeProfiler *profiler) +{ + return (((puint64) gethrtime ()) - profiler->counter) / 1000; +} + +void +p_time_profiler_init (void) +{ +} + +void +p_time_profiler_shutdown (void) +{ +} diff --git a/3rdparty/plibsys/src/ptimeprofiler-win.c b/3rdparty/plibsys/src/ptimeprofiler-win.c new file mode 100644 index 0000000..22ee72f --- /dev/null +++ b/3rdparty/plibsys/src/ptimeprofiler-win.c @@ -0,0 +1,169 @@ +/* + * The MIT License + * + * Copyright (C) 2015-2017 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* https://msdn.microsoft.com/ru-ru/library/windows/desktop/dn553408(v=vs.85).aspx */ + +#include "ptimeprofiler.h" +#include "ptimeprofiler-private.h" + +#include <time.h> + +#if PLIBSYS_HAS_LLDIV +# include <stdlib.h> +#endif + +typedef puint64 (WINAPI * PWin32TicksFunc) (void); +typedef puint64 (* PWin32ElapsedFunc) (puint64 last_counter); + +static PWin32TicksFunc pp_time_profiler_ticks_func = NULL; +static PWin32ElapsedFunc pp_time_profiler_elapsed_func = NULL; +static puint64 pp_time_profiler_freq = 1; + +static puint64 WINAPI pp_time_profiler_get_hr_ticks (void); +static puint64 pp_time_profiler_elapsed_hr (puint64 last_counter); +static puint64 pp_time_profiler_elapsed_tick64 (puint64 last_counter); +static puint64 pp_time_profiler_elapsed_tick (puint64 last_counter); + +static puint64 WINAPI +pp_time_profiler_get_hr_ticks (void) +{ + LARGE_INTEGER tcounter; + + if (P_UNLIKELY (QueryPerformanceCounter (&tcounter) == FALSE)) { + P_ERROR ("PTimeProfiler::pp_time_profiler_get_hr_ticks: QueryPerformanceCounter() failed"); + tcounter.QuadPart = 0; + } + + return (puint64) tcounter.QuadPart; +} + +static puint64 +pp_time_profiler_elapsed_hr (puint64 last_counter) +{ + puint64 ticks; +#ifdef PLIBSYS_HAS_LLDIV + lldiv_t ldres; +#endif + puint64 quot; + puint64 rem; + + ticks = pp_time_profiler_ticks_func () - last_counter; + +#ifdef PLIBSYS_HAS_LLDIV + ldres = lldiv ((long long) ticks, (long long) pp_time_profiler_freq); + + quot = ldres.quot; + rem = ldres.rem; +#else + quot = ticks / pp_time_profiler_freq; + rem = ticks % pp_time_profiler_freq; +#endif + + return (puint64) (quot * 1000000 + (rem * 1000000) / pp_time_profiler_freq); +} + +static puint64 +pp_time_profiler_elapsed_tick64 (puint64 last_counter) +{ + return (pp_time_profiler_ticks_func () - last_counter) * 1000; +} + +static puint64 +pp_time_profiler_elapsed_tick (puint64 last_counter) +{ + puint64 val; + puint64 high_bit; + + high_bit = 0; + val = pp_time_profiler_ticks_func (); + + if (P_UNLIKELY (val < last_counter)) + high_bit = 1; + + return ((val | (high_bit << 32)) - last_counter) * 1000; +} + +puint64 +p_time_profiler_get_ticks_internal () +{ + return pp_time_profiler_ticks_func (); +} + +puint64 +p_time_profiler_elapsed_usecs_internal (const PTimeProfiler *profiler) +{ + return pp_time_profiler_elapsed_func (profiler->counter); +} + +void +p_time_profiler_init (void) +{ + LARGE_INTEGER tcounter; + HMODULE hmodule; + pboolean has_qpc; + + has_qpc = (QueryPerformanceCounter (&tcounter) != 0 && tcounter.QuadPart != 0) ? TRUE : FALSE; + + if (has_qpc == TRUE) { + if (P_UNLIKELY (QueryPerformanceFrequency (&tcounter) == 0)) { + P_ERROR ("PTimeProfiler::p_time_profiler_init: QueryPerformanceFrequency() failed"); + has_qpc = FALSE; + } else { + pp_time_profiler_freq = (puint64) (tcounter.QuadPart); + pp_time_profiler_ticks_func = (PWin32TicksFunc) pp_time_profiler_get_hr_ticks; + pp_time_profiler_elapsed_func = (PWin32ElapsedFunc) pp_time_profiler_elapsed_hr; + } + } + + if (P_UNLIKELY (has_qpc == FALSE)) { + hmodule = GetModuleHandleA ("kernel32.dll"); + + if (P_UNLIKELY (hmodule == NULL)) { + P_ERROR ("PTimeProfiler::p_time_profiler_init: failed to load kernel32.dll module"); + return; + } + + pp_time_profiler_ticks_func = (PWin32TicksFunc) GetProcAddress (hmodule, "GetTickCount64"); + pp_time_profiler_elapsed_func = (PWin32ElapsedFunc) pp_time_profiler_elapsed_tick64; + + if (P_UNLIKELY (pp_time_profiler_ticks_func == NULL)) { + pp_time_profiler_ticks_func = (PWin32TicksFunc) GetProcAddress (hmodule, "GetTickCount"); + pp_time_profiler_elapsed_func = (PWin32ElapsedFunc) pp_time_profiler_elapsed_tick; + } + + if (P_UNLIKELY (pp_time_profiler_ticks_func == NULL)) { + P_ERROR ("PTimeProfiler::p_time_profiler_init: no reliable tick counter"); + pp_time_profiler_elapsed_func = NULL; + } + } +} + +void +p_time_profiler_shutdown (void) +{ + pp_time_profiler_freq = 1; + pp_time_profiler_ticks_func = NULL; + pp_time_profiler_elapsed_func = NULL; +} diff --git a/3rdparty/plibsys/src/ptimeprofiler.c b/3rdparty/plibsys/src/ptimeprofiler.c new file mode 100644 index 0000000..a23ea54 --- /dev/null +++ b/3rdparty/plibsys/src/ptimeprofiler.c @@ -0,0 +1,70 @@ +/* + * The MIT License + * + * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pmem.h" +#include "ptimeprofiler.h" +#include "ptimeprofiler-private.h" + +extern puint64 p_time_profiler_get_ticks_internal (void); +extern puint64 p_time_profiler_elapsed_usecs_internal (const PTimeProfiler *profiler); + +P_LIB_API PTimeProfiler * +p_time_profiler_new () +{ + PTimeProfiler *ret; + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PTimeProfiler))) == NULL)) { + P_ERROR ("PTimeProfiler: failed to allocate memory"); + return NULL; + } + + ret->counter = p_time_profiler_get_ticks_internal (); + + return ret; +} + +P_LIB_API void +p_time_profiler_reset (PTimeProfiler *profiler) +{ + if (P_UNLIKELY (profiler == NULL)) + return; + + profiler->counter = p_time_profiler_get_ticks_internal (); +} + +P_LIB_API puint64 +p_time_profiler_elapsed_usecs (const PTimeProfiler *profiler) +{ + if (P_UNLIKELY (profiler == NULL)) + return 0; + + return p_time_profiler_elapsed_usecs_internal (profiler); +} + +P_LIB_API void +p_time_profiler_free (PTimeProfiler *profiler) +{ + p_free (profiler); +} diff --git a/3rdparty/plibsys/src/ptimeprofiler.h b/3rdparty/plibsys/src/ptimeprofiler.h new file mode 100644 index 0000000..6dbf4ab --- /dev/null +++ b/3rdparty/plibsys/src/ptimeprofiler.h @@ -0,0 +1,93 @@ +/* + * The MIT License + * + * Copyright (C) 2013-2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * @file ptimeprofiler.h + * @brief Time profiler + * @author Alexander Saprykin + * + * #PTimeProfiler acts like a time chronometer: in any moment of time you can + * make a time slice to see how much time elapsed since the last slice or timer + * start. + * + * This profiler is useful to gather information about execution time for calls + * or parts of the code. It can help to leverage bottle-necks in your code. + * + * To start using a profiler create a new one with p_time_profiler_new() call + * and p_time_profiler_elapsed_usecs() to get elapsed time since the creation. + * If you need to reset a profiler use p_time_profiler_reset(). Remove a + * profiler with p_time_profiler_free(). + */ + +#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION) +# error "Header files shouldn't be included directly, consider using <plibsys.h> instead." +#endif + +#ifndef PLIBSYS_HEADER_PTIMEPROFILER_H +#define PLIBSYS_HEADER_PTIMEPROFILER_H + +#include <pmacros.h> +#include <ptypes.h> + +P_BEGIN_DECLS + +/** Time profiler opaque data structure. */ +typedef struct PTimeProfiler_ PTimeProfiler; + +/** + * @brief Creates a new #PTimeProfiler object. + * @return Pointer to a newly created #PTimeProfiler object. + * @since 0.0.1 + */ +P_LIB_API PTimeProfiler * p_time_profiler_new (void); + +/** + * @brief Resets the #PTimeProfiler's internal counter to zero. + * @param profiler Time profiler to reset. + * @since 0.0.1 + * + * After a reset the time profiler begins to count elapsed time from that moment + * of time. + */ +P_LIB_API void p_time_profiler_reset (PTimeProfiler * profiler); + +/** + * @brief Calculates elapsed time since the last reset or creation. + * @param profiler Time profiler to calculate elapsed time for. + * @return Microseconds elapsed since the last reset or creation. + * @since 0.0.1 + */ +P_LIB_API puint64 p_time_profiler_elapsed_usecs (const PTimeProfiler * profiler); + +/** + * @brief Frees #PTimeProfiler object. + * @param profiler #PTimeProfiler to free. + * @since 0.0.1 + */ +P_LIB_API void p_time_profiler_free (PTimeProfiler * profiler); + +P_END_DECLS + +#endif /* PLIBSYS_HEADER_PTIMEPROFILER_H */ diff --git a/3rdparty/plibsys/src/ptree-avl.c b/3rdparty/plibsys/src/ptree-avl.c new file mode 100644 index 0000000..c100e5c --- /dev/null +++ b/3rdparty/plibsys/src/ptree-avl.c @@ -0,0 +1,481 @@ +/* + * The MIT License + * + * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pmem.h" +#include "ptree-avl.h" + +typedef struct PTreeAVLNode_ { + struct PTreeBaseNode_ base; + struct PTreeAVLNode_ *parent; + pint balance_factor; +} PTreeAVLNode; + +static void pp_tree_avl_rotate_left (PTreeAVLNode *node, PTreeBaseNode **root); +static void pp_tree_avl_rotate_right (PTreeAVLNode *node, PTreeBaseNode **root); +static void pp_tree_avl_rotate_left_right (PTreeAVLNode *node, PTreeBaseNode **root); +static void pp_tree_avl_rotate_right_left (PTreeAVLNode *node, PTreeBaseNode **root); +static void pp_tree_avl_balance_insert (PTreeAVLNode *node, PTreeBaseNode **root); +static void pp_tree_avl_balance_remove (PTreeAVLNode *node, PTreeBaseNode **root); + +static void +pp_tree_avl_rotate_left (PTreeAVLNode *node, PTreeBaseNode **root) +{ + node->parent->base.right = node->base.left; + + if (node->base.left != NULL) + ((PTreeAVLNode *) node->base.left)->parent = (PTreeAVLNode *) node->parent; + + node->base.left = (PTreeBaseNode *) node->parent; + node->parent = ((PTreeAVLNode *) node->base.left)->parent; + ((PTreeAVLNode *) node->base.left)->parent = node; + + if (P_LIKELY (node->parent != NULL)) { + if (node->parent->base.left == node->base.left) + node->parent->base.left = (PTreeBaseNode *) node; + else + node->parent->base.right = (PTreeBaseNode *) node; + } else + *root = (PTreeBaseNode *) node; + + /* Restore balance factor */ + ((PTreeAVLNode *) node)->balance_factor +=1; + ((PTreeAVLNode *) node->base.left)->balance_factor = -((PTreeAVLNode *) node)->balance_factor; +} + +static void +pp_tree_avl_rotate_right (PTreeAVLNode *node, PTreeBaseNode **root) +{ + node->parent->base.left = node->base.right; + + if (node->base.right != NULL) + ((PTreeAVLNode *) node->base.right)->parent = (PTreeAVLNode *) node->parent; + + node->base.right = (PTreeBaseNode *) node->parent; + node->parent = ((PTreeAVLNode *) node->base.right)->parent; + ((PTreeAVLNode *) node->base.right)->parent = node; + + if (P_LIKELY (node->parent != NULL)) { + if (node->parent->base.left == node->base.right) + node->parent->base.left = (PTreeBaseNode *) node; + else + node->parent->base.right = (PTreeBaseNode *) node; + } else + *root = (PTreeBaseNode *) node; + + /* Restore balance factor */ + ((PTreeAVLNode *) node)->balance_factor -= 1; + ((PTreeAVLNode *) node->base.right)->balance_factor = -((PTreeAVLNode *) node)->balance_factor; +} + +static void +pp_tree_avl_rotate_left_right (PTreeAVLNode *node, PTreeBaseNode **root) +{ + PTreeAVLNode *tmp_node; + + tmp_node = (PTreeAVLNode *) node->base.right; + node->base.right = tmp_node->base.left; + + if (node->base.right != NULL) + ((PTreeAVLNode *) node->base.right)->parent = node; + + tmp_node->parent = node->parent->parent; + + if (P_LIKELY (tmp_node->parent != NULL)) { + if (tmp_node->parent->base.left == (PTreeBaseNode *) node->parent) + tmp_node->parent->base.left = (PTreeBaseNode *) tmp_node; + else + tmp_node->parent->base.right = (PTreeBaseNode *) tmp_node; + } else + *root = (PTreeBaseNode *) tmp_node; + + node->parent->base.left = tmp_node->base.right; + + if (node->parent->base.left != NULL) + ((PTreeAVLNode *) node->parent->base.left)->parent = node->parent; + + tmp_node->base.right = (PTreeBaseNode *) node->parent; + ((PTreeAVLNode *) tmp_node->base.right)->parent = tmp_node; + + tmp_node->base.left = (PTreeBaseNode *) node; + node->parent = tmp_node; + + /* Restore balance factor */ + if (tmp_node->balance_factor == 1) { + ((PTreeAVLNode *) tmp_node->base.left)->balance_factor = 0; + ((PTreeAVLNode *) tmp_node->base.right)->balance_factor = -1; + } else if (tmp_node->balance_factor == -1) { + ((PTreeAVLNode *) tmp_node->base.left)->balance_factor = 1; + ((PTreeAVLNode *) tmp_node->base.right)->balance_factor = 0; + } else { + ((PTreeAVLNode *) tmp_node->base.left)->balance_factor = 0; + ((PTreeAVLNode *) tmp_node->base.right)->balance_factor = 0; + } + + tmp_node->balance_factor = 0; +} + +static void +pp_tree_avl_rotate_right_left (PTreeAVLNode *node, PTreeBaseNode **root) +{ + PTreeAVLNode *tmp_node; + + tmp_node = (PTreeAVLNode *) node->base.left; + node->base.left = tmp_node->base.right; + + if (node->base.left != NULL) + ((PTreeAVLNode *) node->base.left)->parent = node; + + tmp_node->parent = node->parent->parent; + + if (P_LIKELY (tmp_node->parent != NULL)) { + if (tmp_node->parent->base.left == (PTreeBaseNode *) node->parent) + tmp_node->parent->base.left = (PTreeBaseNode *) tmp_node; + else + tmp_node->parent->base.right = (PTreeBaseNode *) tmp_node; + } else + *root = (PTreeBaseNode *) tmp_node; + + node->parent->base.right = tmp_node->base.left; + + if (node->parent->base.right != NULL) + ((PTreeAVLNode *) node->parent->base.right)->parent = node->parent; + + tmp_node->base.left = (PTreeBaseNode *) node->parent; + ((PTreeAVLNode *) tmp_node->base.left)->parent = tmp_node; + + tmp_node->base.right = (PTreeBaseNode *) node; + node->parent = tmp_node; + + /* Restore balance factor */ + if (tmp_node->balance_factor == 1) { + ((PTreeAVLNode *) tmp_node->base.left)->balance_factor = 0; + ((PTreeAVLNode *) tmp_node->base.right)->balance_factor = -1; + } else if (tmp_node->balance_factor == -1) { + ((PTreeAVLNode *) tmp_node->base.left)->balance_factor = 1; + ((PTreeAVLNode *) tmp_node->base.right)->balance_factor = 0; + } else { + ((PTreeAVLNode *) tmp_node->base.left)->balance_factor = 0; + ((PTreeAVLNode *) tmp_node->base.right)->balance_factor = 0; + } + + tmp_node->balance_factor = 0; +} + +static void +pp_tree_avl_balance_insert (PTreeAVLNode *node, PTreeBaseNode **root) +{ + PTreeAVLNode *parent; + + while (TRUE) { + parent = node->parent; + + if (P_UNLIKELY (parent == NULL)) + break; + + if (parent->base.left == (PTreeBaseNode *) node) { + if (parent->balance_factor == 1) { + if (node->balance_factor == -1) + /* Case 1: Left-right rotate + * + * (5) (4) + * / \ / \ + * (3) A --> (3) (5) + * / \ / \ / \ + * B (4) B C D A + * / \ + * C D + */ + pp_tree_avl_rotate_left_right (node, root); + else + /* Case 2: Right rotate + * + * (5) (4) + * / \ / \ + * (4) A --> (3) (5) + * / \ / \ / \ + * (3) B C D B A + * / \ + * C D + */ + pp_tree_avl_rotate_right (node, root); + + break; + } else if (parent->balance_factor == -1) { + /* Case 3: Increase parent balance factor */ + parent->balance_factor = 0; + break; + } else + /* Case 4: Increase parent balance factor */ + parent->balance_factor = 1; + } else { + if (parent->balance_factor == -1) { + if (node->balance_factor == 1) + /* Case 1: Right-left rotate + * + * (3) (4) + * / \ / \ + * A (5) --> (3) (5) + * / \ / \ / \ + * (4) B A C D B + * / \ + * C D + */ + pp_tree_avl_rotate_right_left (node, root); + else + /* Case 2: Left rotate + * + * (3) (4) + * / \ / \ + * A (4) --> (3) (5) + * / \ / \ / \ + * B (5) A B C D + * / \ + * C D + */ + pp_tree_avl_rotate_left (node, root); + + break; + } else if (parent->balance_factor == 1) { + /* Case 3: Decrease parent balance factor */ + parent->balance_factor = 0; + break; + } else + /* Case 4: Decrease parent balance factor */ + parent->balance_factor = -1; + } + + node = node->parent; + } +} + +static void +pp_tree_avl_balance_remove (PTreeAVLNode *node, PTreeBaseNode **root) +{ + PTreeAVLNode *parent; + PTreeAVLNode *sibling; + pint sibling_balance; + + while (TRUE) { + parent = node->parent; + + if (P_UNLIKELY (parent == NULL)) + break; + + if (parent->base.left == (PTreeBaseNode *) node) { + if (parent->balance_factor == -1) { + sibling = (PTreeAVLNode *) parent->base.right; + sibling_balance = sibling->balance_factor; + + if (sibling->balance_factor == 1) + /* Case 1 */ + pp_tree_avl_rotate_right_left (sibling, root); + else + /* Case 2 */ + pp_tree_avl_rotate_left (sibling, root); + + node = parent; + + if (sibling_balance == 0) + break; + } else if (parent->balance_factor == 0) { + /* Case 3 */ + parent->balance_factor = -1; + break; + } else + /* Case 4 */ + parent->balance_factor = 0; + } else { + if (parent->balance_factor == 1) { + sibling = (PTreeAVLNode *) parent->base.left; + sibling_balance = sibling->balance_factor; + + if (sibling->balance_factor == -1) + /* Case 1 */ + pp_tree_avl_rotate_left_right (sibling, root); + else + /* Case 2 */ + pp_tree_avl_rotate_right (sibling, root); + + node = parent; + + if (sibling_balance == 0) + break; + } else if (parent->balance_factor == 0) { + /* Case 3 */ + parent->balance_factor = 1; + break; + } else + /* Case 4 */ + parent->balance_factor = 0; + } + + node = node->parent; + } +} + +pboolean +p_tree_avl_insert (PTreeBaseNode **root_node, + PCompareDataFunc compare_func, + ppointer data, + PDestroyFunc key_destroy_func, + PDestroyFunc value_destroy_func, + ppointer key, + ppointer value) +{ + PTreeBaseNode **cur_node; + PTreeBaseNode *parent_node; + pint cmp_result; + + cur_node = root_node; + parent_node = *root_node; + + /* Find where to insert the node */ + while (*cur_node != NULL) { + cmp_result = compare_func (key, (*cur_node)->key, data); + + if (cmp_result < 0) { + parent_node = *cur_node; + cur_node = &(*cur_node)->left; + } else if (cmp_result > 0) { + parent_node = *cur_node; + cur_node = &(*cur_node)->right; + } else + break; + } + + /* If we have existing one - replace a key-value pair */ + if (*cur_node != NULL) { + if (key_destroy_func != NULL) + key_destroy_func ((*cur_node)->key); + + if (value_destroy_func != NULL) + value_destroy_func ((*cur_node)->value); + + (*cur_node)->key = key; + (*cur_node)->value = value; + + return FALSE; + } + + if (P_UNLIKELY ((*cur_node = p_malloc0 (sizeof (PTreeAVLNode))) == NULL)) + return FALSE; + + (*cur_node)->key = key; + (*cur_node)->value = value; + + ((PTreeAVLNode *) *cur_node)->balance_factor = 0; + ((PTreeAVLNode *) *cur_node)->parent = (PTreeAVLNode *) parent_node; + + /* Balance the tree */ + pp_tree_avl_balance_insert (((PTreeAVLNode *) *cur_node), root_node); + + return TRUE; +} + +pboolean +p_tree_avl_remove (PTreeBaseNode **root_node, + PCompareDataFunc compare_func, + ppointer data, + PDestroyFunc key_destroy_func, + PDestroyFunc value_destroy_func, + pconstpointer key) +{ + PTreeBaseNode *cur_node; + PTreeBaseNode *prev_node; + PTreeBaseNode *child_node; + PTreeAVLNode *child_parent; + pint cmp_result; + + cur_node = *root_node; + + while (cur_node != NULL) { + cmp_result = compare_func (key, cur_node->key, data); + + if (cmp_result < 0) + cur_node = cur_node->left; + else if (cmp_result > 0) + cur_node = cur_node->right; + else + break; + } + + if (P_UNLIKELY (cur_node == NULL)) + return FALSE; + + if (cur_node->left != NULL && cur_node->right != NULL) { + prev_node = cur_node->left; + + while (prev_node->right != NULL) + prev_node = prev_node->right; + + cur_node->key = prev_node->key; + cur_node->value = prev_node->value; + + /* Mark node for removal */ + cur_node = prev_node; + } + + child_node = cur_node->left == NULL ? cur_node->right : cur_node->left; + + if (child_node == NULL) + pp_tree_avl_balance_remove ((PTreeAVLNode *) cur_node, root_node); + + /* Replace node with its child */ + if (P_UNLIKELY (cur_node == *root_node)) { + *root_node = child_node; + child_parent = NULL; + } else { + child_parent = ((PTreeAVLNode *) cur_node)->parent; + + if (child_parent->base.left == cur_node) + child_parent->base.left = child_node; + else + child_parent->base.right = child_node; + } + + if (child_node != NULL) { + ((PTreeAVLNode *) child_node)->parent = child_parent; + + /* Balance the tree */ + pp_tree_avl_balance_remove ((PTreeAVLNode *) child_node, root_node); + } + + /* Free unused node */ + if (key_destroy_func != NULL) + key_destroy_func (cur_node->key); + + if (value_destroy_func != NULL) + value_destroy_func (cur_node->value); + + p_free (cur_node); + + return TRUE; +} + +void +p_tree_avl_node_free (PTreeBaseNode *node) +{ + p_free (node); +} diff --git a/3rdparty/plibsys/src/ptree-avl.h b/3rdparty/plibsys/src/ptree-avl.h new file mode 100644 index 0000000..dccba2a --- /dev/null +++ b/3rdparty/plibsys/src/ptree-avl.h @@ -0,0 +1,58 @@ +/* + * The MIT License + * + * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION) +# error "Header files shouldn't be included directly, consider using <plibsys.h> instead." +#endif + +#ifndef PLIBSYS_HEADER_PTREEAVL_H +#define PLIBSYS_HEADER_PTREEAVL_H + +#include "pmacros.h" +#include "ptypes.h" +#include "ptree-private.h" + +P_BEGIN_DECLS + +pboolean p_tree_avl_insert (PTreeBaseNode **root_node, + PCompareDataFunc compare_func, + ppointer data, + PDestroyFunc key_destroy_func, + PDestroyFunc value_destroy_func, + ppointer key, + ppointer value); + +pboolean p_tree_avl_remove (PTreeBaseNode **root_node, + PCompareDataFunc compare_func, + ppointer data, + PDestroyFunc key_destroy_func, + PDestroyFunc value_destroy_func, + pconstpointer key); + +void p_tree_avl_node_free (PTreeBaseNode *node); + +P_END_DECLS + +#endif /* PLIBSYS_HEADER_PTREEAVL_H */ diff --git a/3rdparty/plibsys/src/ptree-bst.c b/3rdparty/plibsys/src/ptree-bst.c new file mode 100644 index 0000000..02fae28 --- /dev/null +++ b/3rdparty/plibsys/src/ptree-bst.c @@ -0,0 +1,140 @@ +/* + * The MIT License + * + * Copyright (C) 2015-2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pmem.h" +#include "ptree-bst.h" + +pboolean +p_tree_bst_insert (PTreeBaseNode **root_node, + PCompareDataFunc compare_func, + ppointer data, + PDestroyFunc key_destroy_func, + PDestroyFunc value_destroy_func, + ppointer key, + ppointer value) +{ + PTreeBaseNode **cur_node; + pint cmp_result; + + cur_node = root_node; + + while (*cur_node != NULL) { + cmp_result = compare_func (key, (*cur_node)->key, data); + + if (cmp_result < 0) + cur_node = &(*cur_node)->left; + else if (cmp_result > 0) + cur_node = &(*cur_node)->right; + else + break; + } + + if ((*cur_node) == NULL) { + if (P_UNLIKELY ((*cur_node = p_malloc0 (sizeof (PTreeBaseNode))) == NULL)) + return FALSE; + + (*cur_node)->key = key; + (*cur_node)->value = value; + + return TRUE; + } else { + if (key_destroy_func != NULL) + key_destroy_func ((*cur_node)->key); + + if (value_destroy_func != NULL) + value_destroy_func ((*cur_node)->value); + + (*cur_node)->key = key; + (*cur_node)->value = value; + + return FALSE; + } +} + +pboolean +p_tree_bst_remove (PTreeBaseNode **root_node, + PCompareDataFunc compare_func, + ppointer data, + PDestroyFunc key_destroy_func, + PDestroyFunc value_destroy_func, + pconstpointer key) +{ + PTreeBaseNode *cur_node; + PTreeBaseNode *prev_node; + PTreeBaseNode **node_pointer; + pint cmp_result; + + cur_node = *root_node; + node_pointer = root_node; + + while (cur_node != NULL) { + cmp_result = compare_func (key, cur_node->key, data); + + if (cmp_result < 0) { + node_pointer = &cur_node->left; + cur_node = cur_node->left; + } else if (cmp_result > 0) { + node_pointer = &cur_node->right; + cur_node = cur_node->right; + } else + break; + } + + if (P_UNLIKELY (cur_node == NULL)) + return FALSE; + + if (cur_node->left != NULL && cur_node->right != NULL) { + node_pointer = &cur_node->left; + prev_node = cur_node->left; + + while (prev_node->right != NULL) { + node_pointer = &prev_node->right; + prev_node = prev_node->right; + } + + cur_node->key = prev_node->key; + cur_node->value = prev_node->value; + + cur_node = prev_node; + } + + *node_pointer = cur_node->left == NULL ? cur_node->right : cur_node->left; + + if (key_destroy_func != NULL) + key_destroy_func (cur_node->key); + + if (value_destroy_func != NULL) + value_destroy_func (cur_node->value); + + p_free (cur_node); + + return TRUE; +} + +void +p_tree_bst_node_free (PTreeBaseNode *node) +{ + p_free (node); +} diff --git a/3rdparty/plibsys/src/ptree-bst.h b/3rdparty/plibsys/src/ptree-bst.h new file mode 100644 index 0000000..a0ac18d --- /dev/null +++ b/3rdparty/plibsys/src/ptree-bst.h @@ -0,0 +1,58 @@ +/* + * The MIT License + * + * Copyright (C) 2015-2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION) +# error "Header files shouldn't be included directly, consider using <plibsys.h> instead." +#endif + +#ifndef PLIBSYS_HEADER_PTREEBST_H +#define PLIBSYS_HEADER_PTREEBST_H + +#include "pmacros.h" +#include "ptypes.h" +#include "ptree-private.h" + +P_BEGIN_DECLS + +pboolean p_tree_bst_insert (PTreeBaseNode **root_node, + PCompareDataFunc compare_func, + ppointer data, + PDestroyFunc key_destroy_func, + PDestroyFunc value_destroy_func, + ppointer key, + ppointer value); + +pboolean p_tree_bst_remove (PTreeBaseNode **root_node, + PCompareDataFunc compare_func, + ppointer data, + PDestroyFunc key_destroy_func, + PDestroyFunc value_destroy_func, + pconstpointer key); + +void p_tree_bst_node_free (PTreeBaseNode *node); + +P_END_DECLS + +#endif /* PLIBSYS_HEADER_PTREEBST_H */ diff --git a/3rdparty/plibsys/src/ptree-private.h b/3rdparty/plibsys/src/ptree-private.h new file mode 100644 index 0000000..5af1a67 --- /dev/null +++ b/3rdparty/plibsys/src/ptree-private.h @@ -0,0 +1,48 @@ +/* + * The MIT License + * + * Copyright (C) 2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION) +# error "Header files shouldn't be included directly, consider using <plibsys.h> instead." +#endif + +#ifndef PLIBSYS_HEADER_PTREE_PRIVATE_H +#define PLIBSYS_HEADER_PTREE_PRIVATE_H + +#include "pmacros.h" +#include "ptypes.h" + +P_BEGIN_DECLS + +/** Base tree leaf structure. */ +typedef struct PTreeBaseNode_ { + struct PTreeBaseNode_ *left; /**< Left child. */ + struct PTreeBaseNode_ *right; /**< Right child. */ + ppointer key; /**< Node key. */ + ppointer value; /**< Node value. */ +} PTreeBaseNode; + +P_END_DECLS + +#endif /* PLIBSYS_HEADER_PTREE_PRIVATE_H */ diff --git a/3rdparty/plibsys/src/ptree-rb.c b/3rdparty/plibsys/src/ptree-rb.c new file mode 100644 index 0000000..53c7b09 --- /dev/null +++ b/3rdparty/plibsys/src/ptree-rb.c @@ -0,0 +1,484 @@ +/* + * The MIT License + * + * Copyright (C) 2015-2016 Alexander Saprykin <saprykin.spb@gmail.com> + * Illustrations have been taken from the Linux kernel rbtree.c + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pmem.h" +#include "ptree-rb.h" + +typedef enum PTreeRBColor_ { + P_TREE_RB_COLOR_RED = 0x01, + P_TREE_RB_COLOR_BLACK = 0x02 +} PTreeRBColor; + +typedef struct PTreeRBNode_ { + struct PTreeBaseNode_ base; + struct PTreeRBNode_ *parent; + PTreeRBColor color; +} PTreeRBNode; + +static pboolean pp_tree_rb_is_black (PTreeRBNode *node); +static pboolean pp_tree_rb_is_red (PTreeRBNode *node); +static PTreeRBNode * pp_tree_rb_get_gparent (PTreeRBNode *node); +static PTreeRBNode * pp_tree_rb_get_uncle (PTreeRBNode *node); +static PTreeRBNode * pp_tree_rb_get_sibling (PTreeRBNode *node); +static void pp_tree_rb_rotate_left (PTreeRBNode *node, PTreeBaseNode **root); +static void pp_tree_rb_rotate_right (PTreeRBNode *node, PTreeBaseNode **root); +static void pp_tree_rb_balance_insert (PTreeRBNode *node, PTreeBaseNode **root); +static void pp_tree_rb_balance_remove (PTreeRBNode *node, PTreeBaseNode **root); + +static pboolean +pp_tree_rb_is_black (PTreeRBNode *node) +{ + if (node == NULL) + return TRUE; + + return ((node->color) & P_TREE_RB_COLOR_BLACK) > 0 ? TRUE : FALSE; +} + +static pboolean +pp_tree_rb_is_red (PTreeRBNode *node) +{ + return ((node->color) & P_TREE_RB_COLOR_RED) > 0 ? TRUE : FALSE; +} + +static PTreeRBNode * +pp_tree_rb_get_gparent (PTreeRBNode *node) +{ + return node->parent->parent; +} + +static PTreeRBNode * +pp_tree_rb_get_uncle (PTreeRBNode *node) +{ + PTreeRBNode *gparent = pp_tree_rb_get_gparent (node); + + if ((PTreeRBNode *) gparent->base.left == node->parent) + return (PTreeRBNode *) gparent->base.right; + else + return (PTreeRBNode *) gparent->base.left; +} + +static PTreeRBNode * +pp_tree_rb_get_sibling (PTreeRBNode *node) +{ + if (node->parent->base.left == (PTreeBaseNode *) node) + return (PTreeRBNode *) node->parent->base.right; + else + return (PTreeRBNode *) node->parent->base.left; +} + +static void +pp_tree_rb_rotate_left (PTreeRBNode *node, PTreeBaseNode **root) +{ + PTreeBaseNode *tmp_node; + + tmp_node = node->base.right; + + if (P_LIKELY (node->parent != NULL)) { + if (node->parent->base.left == (PTreeBaseNode *) node) + node->parent->base.left = tmp_node; + else + node->parent->base.right = tmp_node; + } + + node->base.right = tmp_node->left; + + if (tmp_node->left != NULL) + ((PTreeRBNode *) tmp_node->left)->parent = node; + + tmp_node->left = (PTreeBaseNode *) node; + ((PTreeRBNode *) tmp_node)->parent = node->parent; + node->parent = (PTreeRBNode *) tmp_node; + + if (P_UNLIKELY (((PTreeRBNode *) tmp_node)->parent == NULL)) + *root = tmp_node; +} + +static void +pp_tree_rb_rotate_right (PTreeRBNode *node, PTreeBaseNode **root) +{ + PTreeBaseNode *tmp_node; + + tmp_node = node->base.left; + + if (P_LIKELY (node->parent != NULL)) { + if (node->parent->base.left == (PTreeBaseNode *) node) + node->parent->base.left = tmp_node; + else + node->parent->base.right = tmp_node; + } + + node->base.left = tmp_node->right; + + if (tmp_node->right != NULL) + ((PTreeRBNode *) tmp_node->right)->parent = node; + + tmp_node->right = (PTreeBaseNode *) node; + ((PTreeRBNode *) tmp_node)->parent = node->parent; + node->parent = (PTreeRBNode *) tmp_node; + + if (P_UNLIKELY (((PTreeRBNode *) tmp_node)->parent == NULL)) + *root = tmp_node; +} + +static void +pp_tree_rb_balance_insert (PTreeRBNode *node, PTreeBaseNode **root) +{ + PTreeRBNode *uncle; + PTreeRBNode *gparent; + + while (TRUE) { + /* Case 1: We are at the root */ + if (P_UNLIKELY (node->parent == NULL)) { + node->color = P_TREE_RB_COLOR_BLACK; + break; + } + + /* Case 2: We have a black parent */ + if (pp_tree_rb_is_black (node->parent) == TRUE) + break; + + uncle = pp_tree_rb_get_uncle (node); + gparent = pp_tree_rb_get_gparent (node); + + /* Case 3: Both parent and uncle are red, flip colors + * + * G g + * / \ / \ + * p u --> P U + * / / + * n n + */ + if (uncle != NULL && pp_tree_rb_is_red (uncle) == TRUE) { + node->parent->color = P_TREE_RB_COLOR_BLACK; + uncle->color = P_TREE_RB_COLOR_BLACK; + gparent->color = P_TREE_RB_COLOR_RED; + + /* Continue iteratively from gparent */ + node = gparent; + continue; + } + + if (node->parent == (PTreeRBNode *) gparent->base.left) { + if (node == (PTreeRBNode *) node->parent->base.right) { + /* Case 4a: Left rotate at parent + * + * G G + * / \ / \ + * p U --> n U + * \ / + * n p + */ + pp_tree_rb_rotate_left (node->parent, root); + + node = (PTreeRBNode *) node->base.left; + } + + gparent->color = P_TREE_RB_COLOR_RED; + node->parent->color = P_TREE_RB_COLOR_BLACK; + + /* Case 5a: Right rotate at gparent + * + * G P + * / \ / \ + * p U --> n g + * / \ + * n U + */ + pp_tree_rb_rotate_right (gparent, root); + + break; + } else { + if (node == (PTreeRBNode *) node->parent->base.left) { + /* Case 4b: Right rotate at parent */ + pp_tree_rb_rotate_right (node->parent, root); + + node = (PTreeRBNode *) node->base.right; + } + + gparent->color = P_TREE_RB_COLOR_RED; + node->parent->color = P_TREE_RB_COLOR_BLACK; + + /* Case 5b: Left rotate at gparent*/ + pp_tree_rb_rotate_left (gparent, root); + + break; + } + } +} + +static void +pp_tree_rb_balance_remove (PTreeRBNode *node, PTreeBaseNode **root) +{ + PTreeRBNode *sibling; + + while (TRUE) { + /* Case 1: We are at the root */ + if (P_UNLIKELY (node->parent == NULL)) + break; + + sibling = pp_tree_rb_get_sibling (node); + + if (pp_tree_rb_is_red (sibling) == TRUE) { + /* + * Case 2: Left (right) rotate at parent + * + * P S + * / \ / \ + * N s --> p Sr + * / \ / \ + * Sl Sr N Sl + */ + node->parent->color = P_TREE_RB_COLOR_RED; + sibling->color = P_TREE_RB_COLOR_BLACK; + + if ((PTreeBaseNode *) node == node->parent->base.left) + pp_tree_rb_rotate_left (node->parent, root); + else + pp_tree_rb_rotate_right (node->parent, root); + + sibling = pp_tree_rb_get_sibling (node); + } + + /* + * Case 3: Sibling (parent) color flip + * + * (p) (p) + * / \ / \ + * N S --> N s + * / \ / \ + * Sl Sr Sl Sr + */ + if (pp_tree_rb_is_black ((PTreeRBNode *) sibling->base.left) == TRUE && + pp_tree_rb_is_black ((PTreeRBNode *) sibling->base.right) == TRUE) { + sibling->color = P_TREE_RB_COLOR_RED; + + if (pp_tree_rb_is_black (node->parent) == TRUE) { + node = node->parent; + continue; + } else { + node->parent->color = P_TREE_RB_COLOR_BLACK; + break; + } + } + + /* + * Case 4: Right (left) rotate at sibling + * + * (p) (p) + * / \ / \ + * N S --> N Sl + * / \ \ + * sl Sr s + * \ + * Sr + */ + if ((PTreeBaseNode *) node == node->parent->base.left && + pp_tree_rb_is_black ((PTreeRBNode *) sibling->base.right) == TRUE) { + sibling->color = P_TREE_RB_COLOR_RED; + ((PTreeRBNode *) sibling->base.left)->color = P_TREE_RB_COLOR_BLACK; + + pp_tree_rb_rotate_right (sibling, root); + + sibling = pp_tree_rb_get_sibling (node); + } else if ((PTreeBaseNode *) node == node->parent->base.right && + pp_tree_rb_is_black ((PTreeRBNode *) sibling->base.left) == TRUE) { + sibling->color = P_TREE_RB_COLOR_RED; + ((PTreeRBNode *) sibling->base.right)->color = P_TREE_RB_COLOR_BLACK; + + pp_tree_rb_rotate_left (sibling, root); + + sibling = pp_tree_rb_get_sibling (node); + } + + /* + * Case 5: Left (right) rotate at parent and color flips + * + * (p) (s) + * / \ / \ + * N S --> P Sr + * / \ / \ + * (sl) sr N (sl) + */ + sibling->color = node->parent->color; + node->parent->color = P_TREE_RB_COLOR_BLACK; + + if ((PTreeBaseNode *) node == node->parent->base.left) { + ((PTreeRBNode *) sibling->base.right)->color = P_TREE_RB_COLOR_BLACK; + pp_tree_rb_rotate_left (node->parent, root); + } else { + ((PTreeRBNode *) sibling->base.left)->color = P_TREE_RB_COLOR_BLACK; + pp_tree_rb_rotate_right (node->parent, root); + } + + break; + } +} + +pboolean +p_tree_rb_insert (PTreeBaseNode **root_node, + PCompareDataFunc compare_func, + ppointer data, + PDestroyFunc key_destroy_func, + PDestroyFunc value_destroy_func, + ppointer key, + ppointer value) +{ + PTreeBaseNode **cur_node; + PTreeBaseNode *parent_node; + pint cmp_result; + + cur_node = root_node; + parent_node = *root_node; + + /* Find where to insert the node */ + while (*cur_node != NULL) { + cmp_result = compare_func (key, (*cur_node)->key, data); + + if (cmp_result < 0) { + parent_node = *cur_node; + cur_node = &(*cur_node)->left; + } else if (cmp_result > 0) { + parent_node = *cur_node; + cur_node = &(*cur_node)->right; + } else + break; + } + + /* If we have existing one - replace a key-value pair */ + if (*cur_node != NULL) { + if (key_destroy_func != NULL) + key_destroy_func ((*cur_node)->key); + + if (value_destroy_func != NULL) + value_destroy_func ((*cur_node)->value); + + (*cur_node)->key = key; + (*cur_node)->value = value; + + return FALSE; + } + + if (P_UNLIKELY ((*cur_node = p_malloc0 (sizeof (PTreeRBNode))) == NULL)) + return FALSE; + + (*cur_node)->key = key; + (*cur_node)->value = value; + + ((PTreeRBNode *) *cur_node)->color = P_TREE_RB_COLOR_RED; + ((PTreeRBNode *) *cur_node)->parent = (PTreeRBNode *) parent_node; + + /* Balance the tree */ + pp_tree_rb_balance_insert ((PTreeRBNode *) *cur_node, root_node); + + return TRUE; +} + +pboolean +p_tree_rb_remove (PTreeBaseNode **root_node, + PCompareDataFunc compare_func, + ppointer data, + PDestroyFunc key_destroy_func, + PDestroyFunc value_destroy_func, + pconstpointer key) +{ + PTreeBaseNode *cur_node; + PTreeBaseNode *prev_node; + PTreeBaseNode *child_node; + PTreeRBNode *child_parent; + pint cmp_result; + + cur_node = *root_node; + + while (cur_node != NULL) { + cmp_result = compare_func (key, cur_node->key, data); + + if (cmp_result < 0) + cur_node = cur_node->left; + else if (cmp_result > 0) + cur_node = cur_node->right; + else + break; + } + + if (P_UNLIKELY (cur_node == NULL)) + return FALSE; + + if (cur_node->left != NULL && cur_node->right != NULL) { + prev_node = cur_node->left; + + while (prev_node->right != NULL) + prev_node = prev_node->right; + + cur_node->key = prev_node->key; + cur_node->value = prev_node->value; + + /* Mark node for removal */ + cur_node = prev_node; + } + + child_node = cur_node->left == NULL ? cur_node->right : cur_node->left; + + if (child_node == NULL && pp_tree_rb_is_black ((PTreeRBNode *) cur_node) == TRUE) + pp_tree_rb_balance_remove ((PTreeRBNode *) cur_node, root_node); + + /* Replace node with its child */ + if (cur_node == *root_node) { + *root_node = child_node; + child_parent = NULL; + } else { + child_parent = ((PTreeRBNode *) cur_node)->parent; + + if (child_parent->base.left == cur_node) + child_parent->base.left = child_node; + else + child_parent->base.right = child_node; + } + + if (child_node != NULL) { + ((PTreeRBNode *) child_node)->parent = child_parent; + + /* Check if we need to repaint the node */ + if (pp_tree_rb_is_black ((PTreeRBNode *) cur_node) == TRUE) + ((PTreeRBNode *) child_node)->color = P_TREE_RB_COLOR_BLACK; + } + + /* Free unused node */ + if (key_destroy_func != NULL) + key_destroy_func (cur_node->key); + + if (value_destroy_func != NULL) + value_destroy_func (cur_node->value); + + p_free (cur_node); + + return TRUE; +} + +void +p_tree_rb_node_free (PTreeBaseNode *node) +{ + p_free (node); +} diff --git a/3rdparty/plibsys/src/ptree-rb.h b/3rdparty/plibsys/src/ptree-rb.h new file mode 100644 index 0000000..7cf20be --- /dev/null +++ b/3rdparty/plibsys/src/ptree-rb.h @@ -0,0 +1,58 @@ +/* + * The MIT License + * + * Copyright (C) 2015-2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION) +# error "Header files shouldn't be included directly, consider using <plibsys.h> instead." +#endif + +#ifndef PLIBSYS_HEADER_PTREERB_H +#define PLIBSYS_HEADER_PTREERB_H + +#include "pmacros.h" +#include "ptypes.h" +#include "ptree-private.h" + +P_BEGIN_DECLS + +pboolean p_tree_rb_insert (PTreeBaseNode **root_node, + PCompareDataFunc compare_func, + ppointer data, + PDestroyFunc key_destroy_func, + PDestroyFunc value_destroy_func, + ppointer key, + ppointer value); + +pboolean p_tree_rb_remove (PTreeBaseNode **root_node, + PCompareDataFunc compare_func, + ppointer data, + PDestroyFunc key_destroy_func, + PDestroyFunc value_destroy_func, + pconstpointer key); + +void p_tree_rb_node_free (PTreeBaseNode *node); + +P_END_DECLS + +#endif /* PLIBSYS_HEADER_PTREERB_H */ diff --git a/3rdparty/plibsys/src/ptree.c b/3rdparty/plibsys/src/ptree.c new file mode 100644 index 0000000..d61aee7 --- /dev/null +++ b/3rdparty/plibsys/src/ptree.c @@ -0,0 +1,315 @@ +/* + * The MIT License + * + * Copyright (C) 2015-2017 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pmem.h" +#include "ptree.h" +#include "ptree-avl.h" +#include "ptree-bst.h" +#include "ptree-rb.h" + +typedef pboolean (*PTreeInsertNode) (PTreeBaseNode **root_node, + PCompareDataFunc compare_func, + ppointer data, + PDestroyFunc key_destroy_func, + PDestroyFunc value_destroy_func, + ppointer key, + ppointer value); + +typedef pboolean (*PTreeRemoveNode) (PTreeBaseNode **root_node, + PCompareDataFunc compare_func, + ppointer data, + PDestroyFunc key_destroy_func, + PDestroyFunc value_destroy_func, + pconstpointer key); + +typedef void (*PTreeFreeNode) (PTreeBaseNode *node); + +struct PTree_ { + PTreeBaseNode *root; + PTreeInsertNode insert_node_func; + PTreeRemoveNode remove_node_func; + PTreeFreeNode free_node_func; + PDestroyFunc key_destroy_func; + PDestroyFunc value_destroy_func; + PCompareDataFunc compare_func; + ppointer data; + PTreeType type; + pint nnodes; +}; + +P_LIB_API PTree * +p_tree_new (PTreeType type, + PCompareFunc func) +{ + return p_tree_new_full (type, (PCompareDataFunc) func, NULL, NULL, NULL); +} + +P_LIB_API PTree * +p_tree_new_with_data (PTreeType type, + PCompareDataFunc func, + ppointer data) +{ + return p_tree_new_full (type, func, data, NULL, NULL); +} + +P_LIB_API PTree * +p_tree_new_full (PTreeType type, + PCompareDataFunc func, + ppointer data, + PDestroyFunc key_destroy, + PDestroyFunc value_destroy) +{ + PTree *ret; + + if (P_UNLIKELY (!(type >= P_TREE_TYPE_BINARY && type <= P_TREE_TYPE_AVL))) + return NULL; + + if (P_UNLIKELY (func == NULL)) + return NULL; + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PTree))) == NULL)) { + P_ERROR ("PTree::p_tree_new_full: failed to allocate memory"); + return NULL; + } + + ret->type = type; + ret->compare_func = func; + ret->data = data; + ret->key_destroy_func = key_destroy; + ret->value_destroy_func = value_destroy; + + switch (type) { + case P_TREE_TYPE_BINARY: + ret->insert_node_func = p_tree_bst_insert; + ret->remove_node_func = p_tree_bst_remove; + ret->free_node_func = p_tree_bst_node_free; + break; + case P_TREE_TYPE_RB: + ret->insert_node_func = p_tree_rb_insert; + ret->remove_node_func = p_tree_rb_remove; + ret->free_node_func = p_tree_rb_node_free; + break; + case P_TREE_TYPE_AVL: + ret->insert_node_func = p_tree_avl_insert; + ret->remove_node_func = p_tree_avl_remove; + ret->free_node_func = p_tree_avl_node_free; + break; + } + + return ret; +} + +P_LIB_API void +p_tree_insert (PTree *tree, + ppointer key, + ppointer value) +{ + pboolean result; + + if (P_UNLIKELY (tree == NULL)) + return; + + result = tree->insert_node_func (&tree->root, + tree->compare_func, + tree->data, + tree->key_destroy_func, + tree->value_destroy_func, + key, + value); + + if (result == TRUE) + ++tree->nnodes; +} + +P_LIB_API pboolean +p_tree_remove (PTree *tree, + pconstpointer key) +{ + pboolean result; + + if (P_UNLIKELY (tree == NULL || tree->root == NULL)) + return FALSE; + + result = tree->remove_node_func (&tree->root, + tree->compare_func, + tree->data, + tree->key_destroy_func, + tree->value_destroy_func, + key); + if (result == TRUE) + --tree->nnodes; + + return result; +} + +P_LIB_API ppointer +p_tree_lookup (PTree *tree, + pconstpointer key) +{ + PTreeBaseNode *cur_node; + pint cmp_result; + + if (P_UNLIKELY (tree == NULL)) + return NULL; + + cur_node = tree->root; + + while (cur_node != NULL) { + cmp_result = tree->compare_func (key, cur_node->key, tree->data); + + if (cmp_result < 0) + cur_node = cur_node->left; + else if (cmp_result > 0) + cur_node = cur_node->right; + else + return cur_node->value; + } + + return NULL; +} + +P_LIB_API void +p_tree_foreach (PTree *tree, + PTraverseFunc traverse_func, + ppointer user_data) +{ + PTreeBaseNode *cur_node; + PTreeBaseNode *prev_node; + pint mod_counter; + pboolean need_stop; + + if (P_UNLIKELY (tree == NULL || traverse_func == NULL)) + return; + + if (P_UNLIKELY (tree->root == NULL)) + return; + + cur_node = tree->root; + mod_counter = 0; + need_stop = FALSE; + + while (cur_node != NULL) { + if (cur_node->left == NULL) { + if (need_stop == FALSE) + need_stop = traverse_func (cur_node->key, + cur_node->value, + user_data); + + cur_node = cur_node->right; + } else { + prev_node = cur_node->left; + + while (prev_node->right != NULL && prev_node->right != cur_node) + prev_node = prev_node->right; + + if (prev_node->right == NULL) { + prev_node->right = cur_node; + cur_node = cur_node->left; + + ++mod_counter; + } else { + if (need_stop == FALSE) + need_stop = traverse_func (cur_node->key, + cur_node->value, + user_data); + + cur_node = cur_node->right; + prev_node->right = NULL; + + --mod_counter; + + if (need_stop == TRUE && mod_counter == 0) + return; + } + } + } +} + +P_LIB_API void +p_tree_clear (PTree *tree) +{ + PTreeBaseNode *cur_node; + PTreeBaseNode *prev_node; + PTreeBaseNode *next_node; + + if (P_UNLIKELY (tree == NULL || tree->root == NULL)) + return; + + cur_node = tree->root; + + while (cur_node != NULL) { + if (cur_node->left == NULL) { + next_node = cur_node->right; + + if (tree->key_destroy_func != NULL) + tree->key_destroy_func (cur_node->key); + + if (tree->value_destroy_func != NULL) + tree->value_destroy_func (cur_node->value); + + tree->free_node_func (cur_node); + --tree->nnodes; + + cur_node = next_node; + } else { + prev_node = cur_node->left; + + while (prev_node->right != NULL) + prev_node = prev_node->right; + + prev_node->right = cur_node; + next_node = cur_node->left; + cur_node->left = NULL; + cur_node = next_node; + } + } + + tree->root = NULL; +} + +P_LIB_API PTreeType +p_tree_get_type (const PTree *tree) +{ + if (P_UNLIKELY (tree == NULL)) + return (PTreeType) -1; + + return tree->type; +} + +P_LIB_API pint +p_tree_get_nnodes (const PTree *tree) +{ + if (P_UNLIKELY (tree == NULL)) + return 0; + + return tree->nnodes; +} + +P_LIB_API void +p_tree_free (PTree *tree) +{ + p_tree_clear (tree); + p_free (tree); +} diff --git a/3rdparty/plibsys/src/ptree.h b/3rdparty/plibsys/src/ptree.h new file mode 100644 index 0000000..d43e7ae --- /dev/null +++ b/3rdparty/plibsys/src/ptree.h @@ -0,0 +1,230 @@ +/* + * The MIT License + * + * Copyright (C) 2015-2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * @file ptree.h + * @brief Binary tree data structure + * @author Alexander Saprykin + * + * #PTree represents a binary search tree structure for faster lookup than + * a plain array or a list. It has average O(logN) time complexity to search + * a key-value pair, and O(N) in the worst case (when a tree is degenerated into + * the list). + * + * Currently #PTree supports the following tree types: + * - unbalanced binary search tree; + * - red-black self-balancing tree; + * - AVL self-balancing tree. + * + * Use p_tree_new(), or its detailed variations like p_tree_new_with_data() and + * p_tree_new_full() to create a tree structure. Take attention that a caller + * owns the key and the value data passed when inserting new nodes, so you + * should manually free the memory after the tree usage. Or you can provide + * destroy notification functions for the keys and the values separately. + * + * New key-value pairs can be inserted with p_tree_insert() and removed with + * p_tree_remove(). + * + * Use p_tree_lookup() to find the value by a given key. You can also traverse + * the tree in-order with p_tree_foreach(). + * + * Release memory with p_tree_free() or clear a tree with p_tree_clear(). Keys + * and values would be destroyed only if the corresponding notification + * functions were provided. + * + * Note: all operations with the tree are non-recursive, only iterative calls + * are used. + */ + +#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION) +# error "Header files shouldn't be included directly, consider using <plibsys.h> instead." +#endif + +#ifndef PLIBSYS_HEADER_PTREE_H +#define PLIBSYS_HEADER_PTREE_H + +#include <pmacros.h> +#include <ptypes.h> + +P_BEGIN_DECLS + +/** Tree opaque data structure. */ +typedef struct PTree_ PTree; + +/** Internal data organization algorithm for #PTree. */ +typedef enum PTreeType_ { + P_TREE_TYPE_BINARY = 0, /**< Unbalanced binary tree. */ + P_TREE_TYPE_RB = 1, /**< Red-black self-balancing tree. */ + P_TREE_TYPE_AVL = 2 /**< AVL self-balancing tree. */ +} PTreeType; + +/** + * @brief Initializes new #PTree. + * @param type Tree algorithm type to use, can't be changed later. + * @param func Key compare function. + * @return Newly initialized #PTree object in case of success, NULL otherwise. + * @since 0.0.1 + * + * The caller takes ownership of all the keys and the values passed to the tree. + */ +P_LIB_API PTree * p_tree_new (PTreeType type, + PCompareFunc func); + +/** + * @brief Initializes new #PTree with additional data. + * @param type Tree algorithm type to use, can't be changed later. + * @param func Key compare function. + * @param data Data to be passed to @a func along with the keys. + * @return Newly initialized #PTree object in case of success, NULL otherwise. + * @since 0.0.1 + * + * The caller takes ownership of all the keys and the values passed to the tree. + */ +P_LIB_API PTree * p_tree_new_with_data (PTreeType type, + PCompareDataFunc func, + ppointer data); + +/** + * @brief Initializes new #PTree with additional data and memory management. + * @param type Tree algorithm type to use, can't be changed later. + * @param func Key compare function. + * @param data Data to be passed to @a func along with the keys. + * @param key_destroy Function to call on every key before the node destruction, + * maybe NULL. + * @param value_destroy Function to call on every value before the node + * destruction, maybe NULL. + * @return Newly initialized #PTree object in case of success, NULL otherwise. + * @since 0.0.1 + * + * Upon every node destruction the corresponding key and value functions would + * be called. + */ +P_LIB_API PTree * p_tree_new_full (PTreeType type, + PCompareDataFunc func, + ppointer data, + PDestroyFunc key_destroy, + PDestroyFunc value_destroy); + +/** + * @brief Inserts a new key-value pair into a tree. + * @param tree #PTree to insert a node in. + * @param key Key to insert. + * @param value Value corresponding to the given @a key. + * @since 0.0.1 + * + * If the @a key already exists in the tree then it will be replaced with the + * new one. If a key destroy function was provided it would be called on the old + * key. If a value destroy function was provided it would be called on the old + * value. + */ +P_LIB_API void p_tree_insert (PTree *tree, + ppointer key, + ppointer value); + +/** + * @brief Removes a key from a tree. + * @param tree #PTree to remove a key from. + * @param key A key to lookup. + * @return TRUE if the key was removed, FALSE if the key was not found. + * @since 0.0.1 + * + * If a key destroy function was provided it would be called on the key. If a + * value destroy function was provided it would be called on the old value. + */ +P_LIB_API pboolean p_tree_remove (PTree *tree, + pconstpointer key); + +/** + * @brief Lookups a value by a given key. + * @param tree #PTree to lookup in. + * @param key Key to lookup. + * @return Value for the given @a key in case of success, NULL otherwise. + * @since 0.0.1 + */ +P_LIB_API ppointer p_tree_lookup (PTree *tree, + pconstpointer key); + +/** + * @brief Iterates in-order through the tree nodes. + * @param tree A tree to traverse. + * @param traverse_func Function for traversing. + * @param user_data Additional (maybe NULL) user-provided data for the + * @a traverse_func. + * @since 0.0.1 + * @note Morris (non-recursive, non-stack) traversing algorithm is being used. + * + * The tree should not be modified while traversing. The internal tree structure + * can be modified along the traversing process, so keep it in mind for + * concurrent access. + */ +P_LIB_API void p_tree_foreach (PTree *tree, + PTraverseFunc traverse_func, + ppointer user_data); + +/** + * @brief Clears a tree. + * @param tree #PTree to clear. + * @since 0.0.1 + * @note Modified Morris (non-recursive, non-stack) traversing algorithm is + * being used. + * + * All the keys will be deleted. Key and value destroy functions would be called + * on every node if any of them was provided. + */ +P_LIB_API void p_tree_clear (PTree *tree); + +/** + * @brief Gets a tree algorithm type. + * @param tree #PTree object to get the type for. + * @return Tree internal organization algorithm used for a given object. + * @since 0.0.1 + */ +P_LIB_API PTreeType p_tree_get_type (const PTree *tree); + +/** + * @brief Gets node count. + * @param tree #PTree to get node count for. + * @return Node count. + * @since 0.0.1 + * + * If the tree is empty or an invalid pointer is given it returns 0. + */ +P_LIB_API pint p_tree_get_nnodes (const PTree *tree); + +/** + * @brief Frees a previously initialized tree object. + * @param tree #PTree object to free. + * @since 0.0.1 + * @note Modified Morris (non-recursive, non-stack) traversing algorithm is + * being used. + * + * All the keys will be deleted. Key and value destroy functions would be called + * on every node if any of them was provided. + */ +P_LIB_API void p_tree_free (PTree *tree); + +P_END_DECLS + +#endif /* PLIBSYS_HEADER_PTREE_H */ diff --git a/3rdparty/plibsys/src/ptypes.h b/3rdparty/plibsys/src/ptypes.h new file mode 100644 index 0000000..46f2621 --- /dev/null +++ b/3rdparty/plibsys/src/ptypes.h @@ -0,0 +1,1122 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2016 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * @file ptypes.h + * @brief Types definitions + * @author Alexander Saprykin + * + * Every operating system in pair with a compiler has its own set of data types. + * Here you can find unified platform independent data types which guarantee the + * same bit-size on every supported platform: #pint8, #pint16, #pint32, #pint64 + * and their unsigned variants. Also other types are defined for convinience: + * #ppointer, #pboolean, #pint, #plong, #pdouble and more. + * + * Along with the types, length and format modifiers are defined. They can be + * used to print and scan data from/to a variable. + * + * Sometimes it is useful to use an integer variable as a pointer, i.e. to + * prevent memory allocation when using hash tables or trees. Use special macros + * for that case: #PINT_TO_POINTER, #PPOINTER_TO_INT and their variants. Note + * that it will not work with 64-bit data types. + * + * To check data type limits use P_MIN* and P_MAX* macros. + * + * If you need to check system endianness compare the P_BYTE_ORDER definition + * with the #P_LITTLE_ENDIAN or #P_BIG_ENDIAN macro. + * + * To convert between the little and big endian byte orders use the Px_TO_LE, + * Px_TO_BE, Px_FROM_LE and Px_FROM_BE macros. Macros for the network<->host + * byte order conversion are also provided: #p_ntohl, #p_ntohs, #p_ntohs and + * #p_ntohl. All the described above macros depend on the target system + * endianness. Use PUINTx_SWAP_BYTES to manually swap data types independently + * from the endianness. + * + * You can also find some of the function definitions used within the library. + */ + +#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION) +# error "Header files shouldn't be included directly, consider using <plibsys.h> instead." +#endif + +#ifndef PLIBSYS_HEADER_PTYPES_H +#define PLIBSYS_HEADER_PTYPES_H + +#include <plibsysconfig.h> +#include <pmacros.h> + +P_BEGIN_DECLS + +/** Type for signed 8 bit. */ +typedef signed char pint8; +/** Type for unsigned 8 bit. */ +typedef unsigned char puint8; +/** Type for signed 16 bit. */ +typedef signed short pint16; +/** Type for unsigned 16 bit. */ +typedef unsigned short puint16; +/** Type for signed 32 bit. */ +typedef signed int pint32; +/** Type for unsigned 32 bit. */ +typedef unsigned int puint32; + +/** + * @var pint64 + * @brief Type for signed 64 bit. + */ + +/** + * @var puint64 + * @brief Type for unsigned 64 bit. + */ + +#if defined (P_OS_WIN) && (defined (P_CC_MSVC) || defined (P_CC_BORLAND)) + typedef signed __int64 pint64; + typedef unsigned __int64 puint64; +#else +# if PLIBSYS_SIZEOF_LONG == 8 + typedef signed long pint64; + typedef unsigned long puint64; +# else + typedef signed long long pint64; + typedef unsigned long long puint64; +# endif +#endif + +/** Type for a pointer. */ +typedef void * ppointer; +/** Type for a const pointer. */ +typedef const void * pconstpointer; + +/** Type for a bool. */ +typedef signed int pboolean; +/** Type for a char. */ +typedef char pchar; +/** Type for a short. */ +typedef short pshort; +/** Type for an int. */ +typedef int pint; +/** Type for a long. */ +typedef long plong; + +/** Type for an unsigned char. */ +typedef unsigned char puchar; +/** Type for an unsigned short. */ +typedef unsigned short pushort; +/** Type for an unsigned int. */ +typedef unsigned int puint; +/** Type for an unsigned long. */ +typedef unsigned long pulong; + +/** Type for a float. */ +typedef float pfloat; +/** Type for a double precision float. */ +typedef double pdouble; + +/** + * @var pssize + * @brief Type for a platform independent signed size_t. + */ + +/** + * @var psize + * @brief Type for a platform independent size_t. + */ + +/** + * @def PSIZE_MODIFIER + * @brief Platform dependent length modifier for conversion specifiers of + * #psize or #pssize type for printing and scanning values. It is a string + * literal, but doesn't include the percent sign so you can add precision and + * length modifiers and append a conversion specifier. + * @code + * psize size_val = 256; + * printf ("%#" PSIZE_MODIFIER "x", size_val); + * @endcode + */ + +/** + * @def PSSIZE_FORMAT + * @brief Platform dependent conversion specifier of #pssize type for printing + * and scanning values. + * @code + * pssize size_val = 100; + * printf ("%" PSSIZE_FORMAT, size_val); + * @endcode + */ + +/** + * @def PSIZE_FORMAT + * @brief Platform dependent conversion specifier of #psize type for printing + * and scanning values. + */ + +/** + * @def P_MAXSIZE + * @brief Maximum value of a #psize type. + */ + +/** + * @def P_MINSSIZE + * @brief Minimum value of a #pssize type. + */ + +/** + * @def P_MAXSSIZE + * @brief Maximum value of a #pssize type. + */ + +#if PLIBSYS_SIZEOF_SIZE_T == 8 +# if defined (P_OS_WIN) && (defined (P_CC_MSVC) || defined (P_CC_BORLAND)) + typedef signed __int64 pssize; + typedef unsigned __int64 psize; + #define PSIZE_MODIFIER "I64" + #define PSSIZE_FORMAT "I64d" + #define PSIZE_FORMAT "I64u" + #define P_MAXSIZE P_MAXUINT64 + #define P_MINSSIZE P_MININT64 + #define P_MAXSSIZE P_MAXINT64 +# else +# if PLIBSYS_SIZEOF_LONG == 8 + typedef long pssize; + typedef unsigned long psize; + #define PSIZE_MODIFIER "l" + #define PSSIZE_FORMAT "li" + #define PSIZE_FORMAT "lu" + #define P_MAXSIZE P_MAXULONG + #define P_MINSSIZE P_MINLONG + #define P_MAXSSIZE P_MAXLONG +# else + typedef long long pssize; + typedef unsigned long long psize; + #define PSIZE_MODIFIER "ll" + #define PSSIZE_FORMAT "lli" + #define PSIZE_FORMAT "llu" + #define P_MAXSIZE P_MAXUINT64 + #define P_MINSSIZE P_MININT64 + #define P_MAXSSIZE P_MAXINT64 +# endif +# endif +#else + typedef signed int pssize; + typedef unsigned int psize; + #define PSIZE_MODIFIER "" + #define PSSIZE_FORMAT "i" + #define PSIZE_FORMAT "u" + #define P_MAXSIZE P_MAXUINT + #define P_MINSSIZE P_MININT + #define P_MAXSSIZE P_MAXINT +#endif + +/** + * @var pintptr + * @brief Type for a platform independent signed pointer represented by an + * integer. + */ + +/** + * @var puintptr + * @brief Type for a platform independent unsigned pointer represented by an + * integer. + */ + +/** + * @def PINTPTR_MODIFIER + * @brief Platform dependent length modifier for conversion specifiers of + * #pintptr or #puintptr type for printing and scanning values. It is a string + * literal, but doesn't include the percent sign so you can add precision and + * length modifiers and append a conversion specifier. + */ + +/** + * @def PINTPTR_FORMAT + * @brief Platform dependent conversion specifier of #pintptr type for printing + * and scanning values. + */ + +/** + * @def PUINTPTR_FORMAT + * @brief Platform dependent conversion specifier of #puintptr type for + * printing and scanning values. + */ + +#if PLIBSYS_SIZEOF_VOID_P == 8 +# if defined (P_OS_WIN) && (defined (P_CC_MSVC) || defined (P_CC_BORLAND)) + typedef signed __int64 pintptr; + typedef unsigned __int64 puintptr; + #define PINTPTR_MODIFIER "I64" + #define PINTPTR_FORMAT "I64i" + #define PUINTPTR_FORMAT "I64u" +# else +# if PLIBSYS_SIZEOF_LONG == 8 + typedef long pintptr; + typedef unsigned long puintptr; + #define PINTPTR_MODIFIER "l" + #define PINTPTR_FORMAT "li" + #define PUINTPTR_FORMAT "lu" +# else + typedef long long pintptr; + typedef unsigned long long puintptr; + #define PINTPTR_MODIFIER "ll" + #define PINTPTR_FORMAT "lli" + #define PUINTPTR_FORMAT "llu" +# endif +# endif +#else + typedef signed int pintptr; + typedef unsigned int puintptr; + #define PINTPTR_MODIFIER "" + #define PINTPTR_FORMAT "i" + #define PUINTPTR_FORMAT "u" +#endif + +/** Platform independent offset_t definition. */ +typedef pint64 poffset; + +#if PLIBSYS_SIZEOF_VOID_P == 8 +# define P_INT_TO_POINTER(i) ((void *) (long long) (i)) +# define P_POINTER_TO_INT(p) ((int) (long long) (p)) +# define PPOINTER_TO_INT(p) ((pint) ((pint64) (p))) +# define PPOINTER_TO_UINT(p) ((puint) ((puint64) (p))) +# define PINT_TO_POINTER(i) ((ppointer) (pint64) (i)) +# define PUINT_TO_POINTER(u) ((ppointer) (puint64) (u)) +#else +# define P_INT_TO_POINTER(i) ((void *) (long) (i)) +# define P_POINTER_TO_INT(p) ((int) (long) (p)) +# define PPOINTER_TO_INT(p) ((pint) ((plong) (p))) +# define PPOINTER_TO_UINT(p) ((puint) ((pulong) (p))) +# define PINT_TO_POINTER(i) ((ppointer) (plong) (i)) +# define PUINT_TO_POINTER(u) ((ppointer) (pulong) (u)) +#endif + +/** + * @def P_INT_TO_POINTER + * @brief Casts an int to a pointer. + * @param i Variable to cast. + * @return Casted variable. + * @since 0.0.1 + */ + +/** + * @def P_POINTER_TO_INT + * @brief Casts a pointer to an int. + * @param p Pointer to cast. + * @return Casted pointer. + * @since 0.0.1 + */ + +/** + * @def PPOINTER_TO_INT + * @brief Casts a #ppointer to a #pint value. + * @param p #ppointer to cast. + * @return Casted #ppointer. + * @since 0.0.1 + */ + + /** + * @def PPOINTER_TO_UINT + * @brief Casts a #ppointer to a #pint value. + * @param p #ppointer to cast. + * @return Casted #ppointer. + * @since 0.0.1 + */ + + /** + * @def PINT_TO_POINTER + * @brief Casts a #pint value to a #ppointer. + * @param i #pint to cast. + * @return Casted #pint. + * @since 0.0.1 + */ + + /** + * @def PUINT_TO_POINTER + * @brief Casts a #puint value to a #ppointer. + * @param u #puint to cast. + * @return Casted #puint. + * @since 0.0.1 + */ + +/** Casts a #psize value to a #ppointer. */ +#define PSIZE_TO_POINTER(i) ((ppointer) ((psize) (i))) +/** Casts a #ppointer to a #psize value. */ +#define PPOINTER_TO_PSIZE(p) ((psize) (p)) + +/** Min value for a 8-bit int. */ +#define P_MININT8 ((pint8) 0x80) +/** Max value for a 8-bit int. */ +#define P_MAXINT8 ((pint8) 0x7F) +/** Max value for a 8-bit unsigned int. */ +#define P_MAXUINT8 ((puint8) 0xFF) + +/** Min value for a 16-bit int. */ +#define P_MININT16 ((pint16) 0x8000) +/** Max value for a 16-bit int. */ +#define P_MAXINT16 ((pint16) 0x7FFF) +/** Max value for a 16-bit unsigned int. */ +#define P_MAXUINT16 ((puint16) 0xFFFF) + +/** Min value for a 32-bit int. */ +#define P_MININT32 ((pint32) 0x80000000) +/** Max value for a 32-bit int. */ +#define P_MAXINT32 ((pint32) 0x7FFFFFFF) +/** Max value for a 32-bit unsigned int. */ +#define P_MAXUINT32 ((puint32) 0xFFFFFFFF) + +/** Min value for a 64-bit int. */ +#define P_MININT64 ((pint64) 0x8000000000000000LL) +/** Max value for a 64-bit int. */ +#define P_MAXINT64 ((pint64) 0x7FFFFFFFFFFFFFFFLL) +/** Max value for a 64-bit unsigned int. */ +#define P_MAXUINT64 ((puint64) 0xFFFFFFFFFFFFFFFFULL) + +/** + * @def PINT16_MODIFIER + * @brief Platform dependent length modifier for conversion specifiers of + * #pint16 or #puint16 type for printing and scanning values. It is a string + * literal, but doesn't include the percent sign so you can add precision and + * length modifiers and append a conversion specifier. + */ + +/** + * @def PINT16_FORMAT + * @brief Platform dependent conversion specifier of #pint16 type for printing + * and scanning values. + */ + +/** + * @def PUINT16_FORMAT + * @brief Platform dependent conversion specifier of #puint16 type for printing + * and scanning values. + */ + +/** + * @def PINT32_MODIFIER + * @brief Platform dependent length modifier for conversion specifiers of + * #pint32 or #puint32 type for printing and scanning values. It is a string + * literal, but doesn't include the percent sign so you can add precision and + * length modifiers and append a conversion specifier. + */ + +/** + * @def PINT32_FORMAT + * @brief Platform dependent conversion specifier of #pint32 type for printing + * and scanning values. + */ + +/** + * @def PUINT32_FORMAT + * @brief Platform dependent conversion specifier of #puint32 type for printing + * and scanning values. + */ + +/** + * @def PINT64_MODIFIER + * @brief Platform dependent length modifier for conversion specifiers of + * #pint64 or #puint64 type for printing and scanning values. It is a string + * literal, but doesn't include the percent sign so you can add precision and + * length modifiers and append a conversion specifier. + */ + +/** + * @def PINT64_FORMAT + * @brief Platform dependent conversion specifier of #pint64 type for printing + * and scanning values. + */ + +/** + * @def PUINT64_FORMAT + * @brief Platform dependent conversion specifier of #puint64 type for printing + * and scanning values. + */ + +/** + * @def POFFSET_MODIFIER + * @brief Platform dependent length modifier for conversion specifiers of + * #poffset type for printing and scanning values. It is a string literal, but + * doesn't include the percent sign so you can add precision and length + * modifiers and append a conversion specifier. + */ + +/** + * @def POFFSET_FORMAT + * @brief Platform dependent conversion specifier of #poffset type for printing + * and scanning values. + */ + +#if defined (P_OS_WIN) && (defined (P_CC_MSVC) || defined (P_CC_BORLAND)) + #define PINT16_MODIFIER "h" +#else + #define PINT16_MODIFIER "" +#endif + +#define PINT16_FORMAT "hi" +#define PUINT16_FORMAT "hu" + +#define PINT32_MODIFIER "" +#define PINT32_FORMAT "i" +#define PUINT32_FORMAT "u" + +#if defined (P_OS_WIN) && (defined (P_CC_MSVC) || defined (P_CC_BORLAND)) + #define PINT64_MODIFIER "I64" + #define PINT64_FORMAT "I64i" + #define PUINT64_FORMAT "I64u" +#else +# if PLIBSYS_SIZEOF_LONG == 8 + #define PINT64_MODIFIER "l" + #define PINT64_FORMAT "li" + #define PUINT64_FORMAT "lu" +# else + #define PINT64_MODIFIER "ll" + #define PINT64_FORMAT "lli" + #define PUINT64_FORMAT "llu" +# endif +#endif + +#define POFFSET_MODIFIER PINT64_MODIFIER +#define POFFSET_FORMAT PINT64_FORMAT + +/* Endian checks, see P_BYTE_ORDER in plibsysconfig.h */ + +/** Little endian mark. */ +#define P_LITTLE_ENDIAN 1234 +/** Big endian mark. */ +#define P_BIG_ENDIAN 4321 + +/** + * @def PINT16_TO_LE + * @brief Swaps a #pint16 variable from the host to the little endian order. + * @param val Value to swap. + * @return Swapped value. + * @since 0.0.1 + */ + +/** + * @def PUINT16_TO_LE + * @brief Swaps a #puint16 variable from the host to the little the endian order. + * @param val Value to swap. + * @return Swapped value. + * @since 0.0.1 + */ + +/** + * @def PINT16_TO_BE + * @brief Swaps a #pint16 variable from the host to the big endian order. + * @param val Value to swap. + * @return Swapped value. + * @since 0.0.1 + */ + +/** + * @def PUINT16_TO_BE + * @brief Swaps a #puint16 variable from the host to the big endian order. + * @param val Value to swap. + * @return Swapped value. + * @since 0.0.1 + */ + +/** + * @def PINT32_TO_LE + * @brief Swaps a #pint32 variable from the host to the little endian order. + * @param val Value to swap. + * @return Swapped value. + * @since 0.0.1 + */ + +/** + * @def PUINT32_TO_LE + * @brief Swaps a #puint32 variable from the host to the little endian order. + * @param val Value to swap. + * @return Swapped value. + * @since 0.0.1 + */ + +/** + * @def PINT32_TO_BE + * @brief Swaps a #pint32 variable from the host to the big endian order. + * @param val Value to swap. + * @return Swapped value. + * @since 0.0.1 + */ + +/** + * @def PUINT32_TO_BE + * @brief Swaps a #puint32 variable from the host to the big endian order. + * @param val Value to swap. + * @return Swapped value. + * @since 0.0.1 + */ + +/** + * @def PINT64_TO_LE + * @brief Swaps a #pint64 variable from the host to the little endian order. + * @param val Value to swap. + * @return Swapped value. + * @since 0.0.1 + */ + +/** + * @def PUINT64_TO_LE + * @brief Swaps a #puint64 variable from the host to the little endian order. + * @param val Value to swap. + * @return Swapped value. + * @since 0.0.1 + */ + +/** + * @def PINT64_TO_BE + * @brief Swaps a #pint64 variable from the host to the big endian order. + * @param val Value to swap. + * @return Swapped value. + * @since 0.0.1 + */ + +/** + * @def PUINT64_TO_BE + * @brief Swaps a #puint64 variable from the host to the big endian order. + * @param val Value to swap. + * @return Swapped value. + * @since 0.0.1 + */ + +/** + * @def PLONG_TO_LE + * @brief Swaps a #plong variable from the host to the little endian order. + * @param val Value to swap. + * @return Swapped value. + * @since 0.0.1 + */ + +/** + * @def PULONG_TO_LE + * @brief Swaps a #pulong variable from the host to the little endian order. + * @param val Value to swap. + * @return Swapped value. + * @since 0.0.1 + */ + +/** + * @def PLONG_TO_BE + * @brief Swaps a #plong variable from the host to the big endian order. + * @param val Value to swap. + * @return Swapped value. + * @since 0.0.1 + */ + +/** + * @def PULONG_TO_BE + * @brief Swaps a #pulong variable from the host to the big endian order. + * @param val Value to swap. + * @return Swapped value. + * @since 0.0.1 + */ + +/** + * @def PSSIZE_TO_LE + * @brief Swaps a #pssize variable from the host to the little endian order. + * @param val Value to swap. + * @return Swapped value. + * @since 0.0.1 + */ + +/** + * @def PSIZE_TO_LE + * @brief Swaps a #psize variable from the host to the little endian order. + * @param val Value to swap. + * @return Swapped value. + * @since 0.0.1 + */ + +/** + * @def PSSIZE_TO_BE + * @brief Swaps a #pssize variable from the host to the big endian order. + * @param val Value to swap. + * @return Swapped value. + * @since 0.0.1 + */ + +/** + * @def PSIZE_TO_BE + * @brief Swaps a #psize variable from the host to the big endian order. + * @param val Value to swap. + * @return Swapped value. + * @since 0.0.1 + */ + +/** + * @def PINT_TO_LE + * @brief Swaps a #pint variable from the host to the little endian order. + * @param val Value to swap. + * @return Swapped value. + * @since 0.0.1 + */ + +/** + * @def PUINT_TO_LE + * @brief Swaps a #puint variable from the host to the little endian order. + * @param val Value to swap. + * @return Swapped value. + * @since 0.0.1 + */ + +/** + * @def PINT_TO_BE + * @brief Swaps a #pint variable from the host to the big endian order. + * @param val Value to swap. + * @return Swapped value. + * @since 0.0.1 + */ + +/** + * @def PUINT_TO_BE + * @brief Swaps a #puint variable from the host to the big endian order. + * @param val Value to swap. + * @return Swapped value. + * @since 0.0.1 + */ + +#if P_BYTE_ORDER == P_LITTLE_ENDIAN + #define PINT16_TO_LE(val) ((pint16) (val)) + #define PUINT16_TO_LE(val) ((puint16) (val)) + #define PINT16_TO_BE(val) ((pint16) PUINT16_SWAP_BYTES (val)) + #define PUINT16_TO_BE(val) (PUINT16_SWAP_BYTES (val)) + #define PINT32_TO_LE(val) ((pint32) (val)) + #define PUINT32_TO_LE(val) ((puint32) (val)) + #define PINT32_TO_BE(val) ((pint32) PUINT32_SWAP_BYTES (val)) + #define PUINT32_TO_BE(val) (PUINT32_SWAP_BYTES (val)) + #define PINT64_TO_LE(val) ((pint64) (val)) + #define PUINT64_TO_LE(val) ((puint64) (val)) + #define PINT64_TO_BE(val) ((pint64) PUINT64_SWAP_BYTES (val)) + #define PUINT64_TO_BE(val) (PUINT64_SWAP_BYTES (val)) +# if PLIBSYS_SIZEOF_LONG == 8 + #define PLONG_TO_LE(val) ((plong) PINT64_TO_LE (val)) + #define PULONG_TO_LE(val) ((pulong) PUINT64_TO_LE (val)) + #define PLONG_TO_BE(val) ((plong) PINT64_TO_BE (val)) + #define PULONG_TO_BE(val) ((pulong) PUINT64_TO_BE (val)) +# else + #define PLONG_TO_LE(val) ((plong) PINT32_TO_LE (val)) + #define PULONG_TO_LE(val) ((pulong) PUINT32_TO_LE (val)) + #define PLONG_TO_BE(val) ((plong) PINT32_TO_BE (val)) + #define PULONG_TO_BE(val) ((pulong) PUINT32_TO_BE (val)) +# endif +# if PLIBSYS_SIZEOF_SIZE_T == 8 + #define PSIZE_TO_LE(val) ((psize) PUINT64_TO_LE (val)) + #define PSSIZE_TO_LE(val) ((pssize) PINT64_TO_LE (val)) + #define PSIZE_TO_BE(val) ((psize) PUINT64_TO_BE (val)) + #define PSSIZE_TO_BE(val) ((pssize) PINT64_TO_BE (val)) +# else + #define PSIZE_TO_LE(val) ((psize) PUINT32_TO_LE (val)) + #define PSSIZE_TO_LE(val) ((pssize) PINT32_TO_LE (val)) + #define PSIZE_TO_BE(val) ((psize) PUINT32_TO_BE (val)) + #define PSSIZE_TO_BE(val) ((pssize) PINT32_TO_BE (val)) +# endif + #define PINT_TO_LE(val) ((pint) PINT32_TO_LE (val)) + #define PUINT_TO_LE(val) ((puint) PUINT32_TO_LE (val)) + #define PINT_TO_BE(val) ((pint) PINT32_TO_BE (val)) + #define PUINT_TO_BE(val) ((puint) PUINT32_TO_BE (val)) + +#else + #define PINT16_TO_LE(val) ((pint16) PUINT16_SWAP_BYTES (val)) + #define PUINT16_TO_LE(val) (PUINT16_SWAP_BYTES (val)) + #define PINT16_TO_BE(val) ((pint16) (val)) + #define PUINT16_TO_BE(val) ((puint16) (val)) + #define PINT32_TO_LE(val) ((pint32) PUINT32_SWAP_BYTES (val)) + #define PUINT32_TO_LE(val) (PUINT32_SWAP_BYTES (val)) + #define PINT32_TO_BE(val) ((pint32) (val)) + #define PUINT32_TO_BE(val) ((puint32) (val)) + #define PINT64_TO_LE(val) ((pint64) PUINT64_SWAP_BYTES (val)) + #define PUINT64_TO_LE(val) (PUINT64_SWAP_BYTES (val)) + #define PINT64_TO_BE(val) ((pint64) (val)) + #define PUINT64_TO_BE(val) ((puint64) (val)) +# if PLIBSYS_SIZEOF_LONG == 8 + #define PLONG_TO_LE(val) ((plong) PINT64_TO_LE (val)) + #define PULONG_TO_LE(val) ((pulong) PUINT64_TO_LE (val)) + #define PLONG_TO_BE(val) ((plong) PINT64_TO_BE (val)) + #define PULONG_TO_BE(val) ((pulong) PUINT64_TO_BE (val)) +# else + #define PLONG_TO_LE(val) ((plong) PINT32_TO_LE (val)) + #define PULONG_TO_LE(val) ((pulong) PUINT32_TO_LE (val)) + #define PLONG_TO_BE(val) ((plong) PINT32_TO_BE (val)) + #define PULONG_TO_BE(val) ((pulong) PUINT32_TO_BE (val)) +# endif +# if PLIBSYS_SIZEOF_SIZE_T == 8 + #define PSIZE_TO_LE(val) ((psize) PUINT64_TO_LE (val)) + #define PSSIZE_TO_LE(val) ((pssize) PINT64_TO_LE (val)) + #define PSIZE_TO_BE(val) ((psize) PUINT64_TO_BE (val)) + #define PSSIZE_TO_BE(val) ((pssize) PINT64_TO_BE (val)) +# else + #define PSIZE_TO_LE(val) ((psize) PUINT32_TO_LE (val)) + #define PSSIZE_TO_LE(val) ((pssize) PINT32_TO_LE (val)) + #define PSIZE_TO_BE(val) ((psize) PUINT32_TO_BE (val)) + #define PSSIZE_TO_BE(val) ((pssize) PINT32_TO_BE (val)) +# endif + #define PINT_TO_LE(val) ((pint) PINT32_TO_LE (val)) + #define PUINT_TO_LE(val) ((puint) PUINT32_TO_LE (val)) + #define PINT_TO_BE(val) ((pint) PINT32_TO_BE (val)) + #define PUINT_TO_BE(val) ((puint) PUINT32_TO_BE (val)) +#endif + +/* Functions for bit swapping */ + +/** + * @brief Swaps a 16-bit unsigned int. + * @param val Value to swap. + * @return Swapped 16-bit unsigned int. + * @since 0.0.1 + */ +#define PUINT16_SWAP_BYTES(val) \ + ((puint16) (((puint16) (val)) >> 8 | ((puint16) (val)) << 8)) + +/** + * @brief Swaps a 32-bit unsigned int. + * @param val Value to swap. + * @return Swapped 32-bit unsigned int. + * @since 0.0.1 + */ +#define PUINT32_SWAP_BYTES(val) ((puint32) ( \ + (((puint32) (val)) >> 24) | \ + ((((puint32) (val)) << 8) & ((puint32) 0x00FF0000U)) | \ + ((((puint32) (val)) >> 8) & ((puint32) 0x0000FF00U)) | \ + (((puint32) (val)) << 24))) + +/** + * @brief Swaps a 64-bit unsigned int. + * @param val Value to swap. + * @return Swapped 64-bit unsigned int. + * @since 0.0.1 + */ +#define PUINT64_SWAP_BYTES(val) ((puint64) ( \ + (((puint64) (val)) >> 56) | \ + ((((puint64) (val)) << 40) & ((puint64) 0x00FF000000000000ULL)) | \ + ((((puint64) (val)) << 24) & ((puint64) 0x0000FF0000000000ULL)) | \ + ((((puint64) (val)) << 8) & ((puint64) 0x000000FF00000000ULL)) | \ + ((((puint64) (val)) >> 8) & ((puint64) 0x00000000FF000000ULL)) | \ + ((((puint64) (val)) >> 24) & ((puint64) 0x0000000000FF0000ULL)) | \ + ((((puint64) (val)) >> 40) & ((puint64) 0x000000000000FF00ULL)) | \ + (((puint64) (val)) << 56))) + +/* Functions, similar to ?_TO_? functions */ + +/** + * @brief Swaps a 16-bit int from the little endian byte order to the host one. + * @param val Value to swap. + * @return Swapped 16-bit int. + * @since 0.0.1 + */ +#define PINT16_FROM_LE(val) (PINT16_TO_LE (val)) + +/** + * @brief Swaps a 16-bit unsigned int from the little endian byte order to the + * host one. + * @param val Value to swap. + * @return Swapped 16-bit unsigned int. + * @since 0.0.1 + */ +#define PUINT16_FROM_LE(val) (PUINT16_TO_LE (val)) + +/** + * @brief Swaps a 16-bit int from the big endian byte order to the host one. + * @param val Value to swap. + * @return Swapped 16-bit int. + * @since 0.0.1 + */ +#define PINT16_FROM_BE(val) (PINT16_TO_BE (val)) + +/** + * @brief Swaps a 16-bit unsigned int from the big endian byte order to the host + * one. + * @param val Value to swap. + * @return Swapped 16-bit unsigned int. + * @since 0.0.1 + */ +#define PUINT16_FROM_BE(val) (PUINT16_TO_BE (val)) + + +/** + * @brief Swaps a 32-bit int from the little endian byte order to the host one. + * @param val Value to swap. + * @return Swapped 32-bit int. + * @since 0.0.1 + */ +#define PINT32_FROM_LE(val) (PINT32_TO_LE (val)) + +/** + * @brief Swaps a 32-bit unsigned int from the little endian byte order to the + * host one. + * @param val Value to swap. + * @return Swapped 32-bit unsigned int. + * @since 0.0.1 + */ +#define PUINT32_FROM_LE(val) (PUINT32_TO_LE (val)) + +/** + * @brief Swaps a 32-bit int from the big endian byte order to the host one. + * @param val Value to swap. + * @return Swapped 32-bit int. + * @since 0.0.1 + */ +#define PINT32_FROM_BE(val) (PINT32_TO_BE (val)) + +/** + * @brief Swaps a 32-bit unsigned int from the big endian byte order to the host + * one. + * @param val Value to swap. + * @return Swapped 32-bit unsigned int. + * @since 0.0.1 + */ +#define PUINT32_FROM_BE(val) (PUINT32_TO_BE (val)) + +/** + * @brief Swaps a 64-bit int from the little endian byte order to the host one. + * @param val Value to swap. + * @return Swapped 64-bit int. + * @since 0.0.1 + */ +#define PINT64_FROM_LE(val) (PINT64_TO_LE (val)) + +/** + * @brief Swaps a 64-bit unsigned int from the little endian byte order to the + * host one. + * @param val Value to swap. + * @return Swapped 64-bit unsigned int. + * @since 0.0.1 + */ +#define PUINT64_FROM_LE(val) (PUINT64_TO_LE (val)) + +/** + * @brief Swaps a 64-bit int from the big endian byte order to the host one. + * @param val Value to swap. + * @return Swapped 64-bit int. + * @since 0.0.1 + */ +#define PINT64_FROM_BE(val) (PINT64_TO_BE (val)) + +/** + * @brief Swaps a 64-bit unsigned int from the big endian byte order to the host + * one. + * @param val Value to swap. + * @return Swapped 64-bit unsigned int. + * @since 0.0.1 + */ +#define PUINT64_FROM_BE(val) (PUINT64_TO_BE (val)) + +/** + * @brief Swaps a long int from the little endian byte order to the host one. + * @param val Value to swap. + * @return Swapped long int. + * @since 0.0.1 + */ +#define PLONG_FROM_LE(val) (PLONG_TO_LE (val)) + +/** + * @brief Swaps an unsigned long int from the little endian byte order to the + * host one. + * @param val Value to swap. + * @return Swapped unsigned long int. + * @since 0.0.1 + */ +#define PULONG_FROM_LE(val) (PULONG_TO_LE (val)) + +/** + * @brief Swaps a long int from the big endian byte order to the host one. + * @param val Value to swap. + * @return Swapped long int. + * @since 0.0.1 + */ +#define PLONG_FROM_BE(val) (PLONG_TO_BE (val)) + +/** + * @brief Swaps an unsigned long int from the big endian byte order to the host + * one. + * @param val Value to swap. + * @return Swapped unsigned long int. + * @since 0.0.1 + */ +#define PULONG_FROM_BE(val) (PULONG_TO_BE (val)) + +/** + * @brief Swaps a #pint from the little endian byte order to the host one. + * @param val Value to swap. + * @return Swapped #pint. + * @since 0.0.1 + */ +#define PINT_FROM_LE(val) (PINT_TO_LE (val)) + +/** + * @brief Swaps a #puint from the little endian byte order to the host one. + * @param val Value to swap. + * @return Swapped #puint. + * @since 0.0.1 + */ +#define PUINT_FROM_LE(val) (PUINT_TO_LE (val)) + +/** + * @brief Swaps a #pint from the big endian byte order to the host one. + * @param val Value to swap. + * @return Swapped #pint. + * @since 0.0.1 + */ +#define PINT_FROM_BE(val) (PINT_TO_BE (val)) + +/** + * @brief Swaps a #puint from the big endian byte order to the host one. + * @param val Value to swap. + * @return Swapped #puint. + * @since 0.0.1 + */ +#define PUINT_FROM_BE(val) (PUINT_TO_BE (val)) + +/** + * @brief Swaps a #psize from the little endian byte order to the host one. + * @param val Value to swap. + * @return Swapped #psize. + * @since 0.0.1 + */ +#define PSIZE_FROM_LE(val) (PSIZE_TO_LE (val)) + +/** + * @brief Swaps a #pssize from the little endian byte order to the host one. + * @param val Value to swap. + * @return Swapped #pssize. + * @since 0.0.1 + */ +#define PSSIZE_FROM_LE(val) (PSSIZE_TO_LE (val)) + +/** + * @brief Swaps a #psize from the big endian byte order to the host one. + * @param val Value to swap. + * @return Swapped #psize. + * @since 0.0.1 + */ +#define PSIZE_FROM_BE(val) (PSIZE_TO_BE (val)) + +/** + * @brief Swaps a #pssize from the big endian byte order to the host one. + * @param val Value to swap. + * @return Swapped #pssize. + * @since 0.0.1 + */ +#define PSSIZE_FROM_BE(val) (PSSIZE_TO_BE (val)) + +/* Host-network order functions */ + +/** + * @brief Swaps a long int from the network byte order to the host one. + * @param val Value to swap. + * @return Swapped long int. + * @since 0.0.1 + */ +#define p_ntohl(val) (PUINT32_FROM_BE (val)) + +/** + * @brief Swaps a short int from the network byte order to the host one. + * @param val Value to swap. + * @return Swapped short int. + * @since 0.0.1 + */ +#define p_ntohs(val) (PUINT16_FROM_BE (val)) + +/** + * @brief Swaps a long int from the host byte order to the network one. + * @param val Value to swap. + * @return Swapped long int. + * @since 0.0.1 + */ +#define p_htonl(val) (PUINT32_TO_BE (val)) + +/** + * @brief Swaps a short int from the host byte order to the network one. + * @param val Value to swap. + * @return Swapped short int. + * @since 0.0.1 + */ +#define p_htons(val) (PUINT16_TO_BE (val)) + +#ifndef FALSE +/** Type definition for a false boolean value. */ +# define FALSE (0) +#endif + +#ifndef TRUE +/** Type definition for a true boolean value. */ +# define TRUE (!FALSE) +#endif + +/** + * @brief Platform independent system handle. + */ +typedef void * P_HANDLE; + +/** + * @brief Function to traverse through a key-value container. + * @param key The key of an item. + * @param value The value of the item. + * @param user_data Data provided by a user, maybe NULL. + * @return FALSE to continue traversing, TRUE to stop it. + * @since 0.0.1 + */ +typedef pboolean (*PTraverseFunc) (ppointer key, + ppointer value, + ppointer user_data); + +/** + * @brief General purpose function. + * @param data Main argument related to a context value. + * @param user_data Additional (maybe NULL) user-provided data. + * @since 0.0.1 + */ +typedef void (*PFunc) (ppointer data, ppointer user_data); + +/** + * @brief Object destroy notification function. + * @param data Pointer to an object to be destroyed. + * @since 0.0.1 + */ +typedef void (*PDestroyFunc) (ppointer data); + +/** + * @brief Compares two values. + * @param a First value to compare. + * @param b Second value to compare. + * @return -1 if the first value is less than the second, 1 if the first value + * is greater than the second, 0 otherwise. + * @since 0.0.1 + */ +typedef pint (*PCompareFunc) (pconstpointer a, pconstpointer b); + +/** + * @brief Compares two values with additional data. + * @param a First value to compare. + * @param b Second value to compare. + * @param data Addition data, may be NULL. + * @return -1 if the first value is less than the second, 1 if the first value + * is greater than the second, 0 otherwise. + * @since 0.0.1 + */ +typedef pint (*PCompareDataFunc) (pconstpointer a, pconstpointer b, ppointer data); + +P_END_DECLS + +#endif /* PLIBSYS_HEADER_PTYPES_H */ diff --git a/3rdparty/plibsys/src/puthread-amiga.c b/3rdparty/plibsys/src/puthread-amiga.c new file mode 100644 index 0000000..ac23496 --- /dev/null +++ b/3rdparty/plibsys/src/puthread-amiga.c @@ -0,0 +1,673 @@ +/* + * The MIT License + * + * Copyright (C) 2017-2019 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pmem.h" +#include "patomic.h" +#include "pcondvariable.h" +#include "plist.h" +#include "pmutex.h" +#include "puthread.h" +#include "puthread-private.h" + +#include <stdlib.h> +#include <string.h> +#include <setjmp.h> + +#include <proto/exec.h> +#include <proto/dos.h> + +#define PUTHREAD_AMIGA_MAX_TLS_KEYS 128 +#define PUTHREAD_AMIGA_MIN_STACK 524288 +#define PUTHREAD_AMIGA_MAX_CLEANS 4 + +typedef struct { + pboolean in_use; + PDestroyFunc free_func; +} PUThreadTLSKey; + +typedef struct { + pint id; + struct Task *task; + jmp_buf jmpbuf; + ppointer tls_values[PUTHREAD_AMIGA_MAX_TLS_KEYS]; +} PUThreadInfo; + +struct PUThread_ { + PUThreadBase base; + PUThreadFunc proxy; + PCondVariable *join_cond; + struct Task *task; +}; + +typedef pint puthread_key_t; + +struct PUThreadKey_ { + puthread_key_t key; + PDestroyFunc free_func; +}; + +static PMutex *pp_uthread_glob_mutex = NULL; +static PList *pp_uthread_list = NULL; +static pint pp_uthread_last_id = 0; + +static PUThreadTLSKey pp_uthread_tls_keys[PUTHREAD_AMIGA_MAX_TLS_KEYS]; + +static pint pp_uthread_get_amiga_priority (PUThreadPriority prio); +static puthread_key_t pp_uthread_get_tls_key (PUThreadKey *key); +static pint pp_uthread_find_next_id (void); +static PUThreadInfo * pp_uthread_find_thread_info (struct Task *task); +static PUThreadInfo * pp_uthread_find_or_create_thread_info (struct Task *task); +static pint pp_uthread_amiga_proxy (void); + +static pint +pp_uthread_get_amiga_priority (PUThreadPriority prio) +{ + /* Priority limit is [-128, 127] */ + + switch (prio) { + case P_UTHREAD_PRIORITY_INHERIT: + return 0; + case P_UTHREAD_PRIORITY_IDLE: + return -128; + case P_UTHREAD_PRIORITY_LOWEST: + return -50; + case P_UTHREAD_PRIORITY_LOW: + return -25; + case P_UTHREAD_PRIORITY_NORMAL: + return 0; + case P_UTHREAD_PRIORITY_HIGH: + return 25; + case P_UTHREAD_PRIORITY_HIGHEST: + return 50; + case P_UTHREAD_PRIORITY_TIMECRITICAL: + return 127; + default: + return 0; + } +} + +static puthread_key_t +pp_uthread_get_tls_key (PUThreadKey *key) +{ + puthread_key_t thread_key; + pint key_idx; + + thread_key = (puthread_key_t) p_atomic_int_get (&key->key); + + if (P_LIKELY (thread_key >= 0)) + return thread_key; + + p_mutex_lock (pp_uthread_glob_mutex); + + if (key->key >= 0) { + p_mutex_unlock (pp_uthread_glob_mutex); + return key->key; + } + + /* Find free TLS key index */ + + for (key_idx = 0; key_idx < PUTHREAD_AMIGA_MAX_TLS_KEYS; ++key_idx) { + if (P_LIKELY (pp_uthread_tls_keys[key_idx].in_use == FALSE)) { + pp_uthread_tls_keys[key_idx].in_use = TRUE; + pp_uthread_tls_keys[key_idx].free_func = key->free_func; + + break; + } + } + + if (key_idx == PUTHREAD_AMIGA_MAX_TLS_KEYS) { + p_mutex_unlock (pp_uthread_glob_mutex); + P_ERROR ("PUThread::pp_uthread_get_tls_key: all slots for TLS keys are used"); + return -1; + } + + key->key = key_idx; + + p_mutex_unlock (pp_uthread_glob_mutex); + + return key_idx; +} + +/* Must be used only inside a protected critical region */ + +static pint +pp_uthread_find_next_id (void) +{ + PList *cur_list; + PUThreadInfo *thread_info; + pboolean have_dup; + pboolean was_found = FALSE; + pint cur_id = pp_uthread_last_id; + pint of_counter = 0; + + while (was_found == FALSE && of_counter < 2) { + have_dup = FALSE; + cur_id = (cur_id == P_MAXINT32) ? 0 : cur_id + 1; + + if (cur_id == 0) + ++of_counter; + + for (cur_list = pp_uthread_list; cur_list != NULL; cur_list = cur_list->next) { + thread_info = (PUThreadInfo *) cur_list->data; + + if (thread_info->id == cur_id) { + have_dup = TRUE; + break; + } + } + + if (have_dup == FALSE) + was_found = TRUE; + } + + if (P_UNLIKELY (of_counter == 2)) + return -1; + + pp_uthread_last_id = cur_id; + + return cur_id; +} + +/* Must be used only inside a protected critical region */ + +static PUThreadInfo * +pp_uthread_find_thread_info (struct Task *task) +{ + PList *cur_list; + PUThreadInfo *thread_info; + + for (cur_list = pp_uthread_list; cur_list != NULL; cur_list = cur_list->next) { + thread_info = (PUThreadInfo *) cur_list->data; + + if (thread_info->task == task) + return thread_info; + } + + return NULL; +} + +/* Must be used only inside a protected critical region */ + +static PUThreadInfo * +pp_uthread_find_or_create_thread_info (struct Task *task) +{ + PUThreadInfo *thread_info; + pint task_id; + + thread_info = pp_uthread_find_thread_info (task); + + if (thread_info == NULL) { + /* Call is from a forein thread */ + + task_id = pp_uthread_find_next_id (); + + if (P_UNLIKELY (task_id == -1)) { + /* Beyond the limit of the number of threads */ + P_ERROR ("PUThread::pp_uthread_find_or_create_thread_info: no free thread slots left"); + return NULL; + } + + if (P_UNLIKELY ((thread_info = p_malloc0 (sizeof (PUThreadInfo))) == NULL)) { + P_ERROR ("PUThread::pp_uthread_find_or_create_thread_info: failed to allocate memory"); + return NULL; + } + + thread_info->id = task_id; + thread_info->task = task; + + pp_uthread_list = p_list_append (pp_uthread_list, thread_info); + } + + return thread_info; +} + +static pint +pp_uthread_amiga_proxy (void) +{ + PUThread *thread; + PUThreadInfo *thread_info; + struct Task *task; + PDestroyFunc dest_func; + ppointer dest_data; + pboolean need_pass; + pint i; + pint clean_counter; + + /* Wait for outer routine to finish data initialization */ + + p_mutex_lock (pp_uthread_glob_mutex); + + task = IExec->FindTask (NULL); + thread = (PUThread *) (task->tc_UserData); + thread_info = pp_uthread_find_thread_info (task); + + p_mutex_unlock (pp_uthread_glob_mutex); + + IExec->SetTaskPri (task, pp_uthread_get_amiga_priority (thread->base.prio)); + + if (!setjmp (thread_info->jmpbuf)) + thread->proxy (thread); + + /* Clean up TLS values */ + + p_mutex_lock (pp_uthread_glob_mutex); + + need_pass = TRUE; + clean_counter = 0; + + while (need_pass && clean_counter < PUTHREAD_AMIGA_MAX_CLEANS) { + need_pass = FALSE; + + for (i = 0; i < PUTHREAD_AMIGA_MAX_TLS_KEYS; ++i) { + if (pp_uthread_tls_keys[i].in_use == TRUE) { + dest_func = pp_uthread_tls_keys[i].free_func; + dest_data = thread_info->tls_values[i]; + + if (dest_func != NULL && dest_data != NULL) { + /* Destructor may do some trick with TLS as well */ + thread_info->tls_values[i] = NULL; + + p_mutex_unlock (pp_uthread_glob_mutex); + (dest_func) (dest_data); + p_mutex_lock (pp_uthread_glob_mutex); + + need_pass = TRUE; + } + } + } + + ++clean_counter; + } + + pp_uthread_list = p_list_remove (pp_uthread_list, thread_info); + + p_free (thread_info); + + p_mutex_unlock (pp_uthread_glob_mutex); + + /* Signal to possible waiter */ + + p_cond_variable_broadcast (thread->join_cond); +} + +void +p_uthread_init_internal (void) +{ + if (P_LIKELY (pp_uthread_glob_mutex == NULL)) { + pp_uthread_glob_mutex = p_mutex_new (); + pp_uthread_list = NULL; + pp_uthread_last_id = 0; + + memset (pp_uthread_tls_keys, 0, sizeof (PUThreadTLSKey) * PUTHREAD_AMIGA_MAX_TLS_KEYS); + } +} + +void +p_uthread_shutdown_internal (void) +{ + PList *cur_list; + PUThreadInfo *thread_info; + PDestroyFunc dest_func; + ppointer dest_data; + pboolean need_pass; + pint i; + pint clean_counter; + + /* Perform destructors */ + + p_mutex_lock (pp_uthread_glob_mutex); + + need_pass = TRUE; + clean_counter = 0; + + while (need_pass && clean_counter < PUTHREAD_AMIGA_MAX_CLEANS) { + need_pass = FALSE; + + for (i = 0; i < PUTHREAD_AMIGA_MAX_TLS_KEYS; ++i) { + if (pp_uthread_tls_keys[i].in_use == FALSE) + continue; + + dest_func = pp_uthread_tls_keys[i].free_func; + + if (dest_func == NULL) + continue; + + for (cur_list = pp_uthread_list; cur_list != NULL; cur_list = cur_list->next) { + thread_info = (PUThreadInfo *) cur_list->data; + dest_data = thread_info->tls_values[i]; + + if (dest_data != NULL) { + /* Destructor may do some trick with TLS as well */ + + thread_info->tls_values[i] = NULL; + + p_mutex_unlock (pp_uthread_glob_mutex); + (dest_func) (dest_data); + p_mutex_lock (pp_uthread_glob_mutex); + + need_pass = TRUE; + } + } + } + } + + /* Clean the list */ + + p_list_foreach (pp_uthread_list, (PFunc) p_free, NULL); + p_list_free (pp_uthread_list); + + pp_uthread_list = NULL; + + p_mutex_unlock (pp_uthread_glob_mutex); + + if (P_LIKELY (pp_uthread_glob_mutex != NULL)) { + p_mutex_free (pp_uthread_glob_mutex); + pp_uthread_glob_mutex = NULL; + } +} + +void +p_uthread_win32_thread_detach (void) +{ +} + +void +p_uthread_free_internal (PUThread *thread) +{ + if (thread->join_cond != NULL) + p_cond_variable_free (thread->join_cond); + + p_free (thread); +} + +PUThread * +p_uthread_create_internal (PUThreadFunc func, + pboolean joinable, + PUThreadPriority prio, + psize stack_size) +{ + PUThread *ret; + PUThreadInfo *thread_info; + struct Task *task; + pint task_id; + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PUThread))) == NULL)) { + P_ERROR ("PUThread::p_uthread_create_internal: failed to allocate memory"); + return NULL; + } + + if (P_UNLIKELY ((ret->join_cond = p_cond_variable_new ()) == NULL)) { + P_ERROR ("PUThread::p_uthread_create_internal: failed to allocate condvar"); + p_uthread_free_internal (ret); + return NULL; + } + + if (P_UNLIKELY ((thread_info = p_malloc0 (sizeof (PUThreadInfo))) == NULL)) { + P_ERROR ("PUThread::p_uthread_create_internal: failed to allocate memory (2)"); + p_uthread_free_internal (ret); + return NULL; + } + + p_mutex_lock (pp_uthread_glob_mutex); + + task_id = pp_uthread_find_next_id (); + + if (P_UNLIKELY (task_id == -1)) { + p_mutex_unlock (pp_uthread_glob_mutex); + P_ERROR ("PUThread::p_uthread_create_internal: no free thread slots left"); + p_uthread_free_internal (ret); + p_free (thread_info); + return NULL; + } + + ret->proxy = func; + ret->base.prio = prio; + ret->base.joinable = joinable; + + if (stack_size < PUTHREAD_AMIGA_MIN_STACK) + stack_size = PUTHREAD_AMIGA_MIN_STACK; + + task = (struct Task *) IDOS->CreateNewProcTags (NP_Entry, pp_uthread_amiga_proxy, + NP_StackSize, stack_size, + NP_UserData, ret, + NP_Child, TRUE, + TAG_END); + + if (P_UNLIKELY (task == NULL)) { + p_mutex_unlock (pp_uthread_glob_mutex); + P_ERROR ("PUThread::p_uthread_create_internal: CreateTaskTags() failed"); + p_uthread_free_internal (ret); + p_free (thread_info); + return NULL; + } + + thread_info->task = task; + thread_info->id = task_id; + + pp_uthread_list = p_list_append (pp_uthread_list, thread_info); + + ret->task = task; + + p_mutex_unlock (pp_uthread_glob_mutex); + + return ret; +} + +void +p_uthread_exit_internal (void) +{ + PUThreadInfo *thread_info; + + p_mutex_lock (pp_uthread_glob_mutex); + + thread_info = pp_uthread_find_thread_info (IExec->FindTask (NULL)); + + p_mutex_unlock (pp_uthread_glob_mutex); + + if (P_UNLIKELY (thread_info == NULL)) { + P_WARNING ("PUThread::p_uthread_exit_internal: trying to exit from foreign thread"); + return; + } + + longjmp (thread_info->jmpbuf, 1); +} + +void +p_uthread_wait_internal (PUThread *thread) +{ + PUThreadInfo *thread_info; + + p_mutex_lock (pp_uthread_glob_mutex); + + thread_info = pp_uthread_find_thread_info (thread->task); + + if (thread_info == NULL) { + p_mutex_unlock (pp_uthread_glob_mutex); + return; + } + + p_cond_variable_wait (thread->join_cond, pp_uthread_glob_mutex); + p_mutex_unlock (pp_uthread_glob_mutex); +} + +void +p_uthread_set_name_internal (PUThread *thread) +{ + struct Task *task = thread->task; + + task->tc_Node.ln_Name = thread->base.name; +} + +P_LIB_API void +p_uthread_yield (void) +{ + BYTE old_prio; + struct Task *task; + + task = IExec->FindTask (NULL); + + old_prio = IExec->SetTaskPri (task, -10); + IExec->SetTaskPri (task, old_prio); +} + +P_LIB_API pboolean +p_uthread_set_priority (PUThread *thread, + PUThreadPriority prio) +{ + if (P_UNLIKELY (thread == NULL)) + return FALSE; + + IExec->SetTaskPri (thread->task, pp_uthread_get_amiga_priority (prio)); + + thread->base.prio = prio; + return TRUE; +} + +P_LIB_API P_HANDLE +p_uthread_current_id (void) +{ + PUThreadInfo *thread_info; + + p_mutex_lock (pp_uthread_glob_mutex); + + thread_info = pp_uthread_find_or_create_thread_info (IExec->FindTask (NULL)); + + p_mutex_unlock (pp_uthread_glob_mutex); + + if (P_UNLIKELY (thread_info == NULL)) + P_WARNING ("PUThread::p_uthread_current_id: failed to integrate foreign thread"); + + return (thread_info == NULL) ? NULL : (P_HANDLE) ((psize) thread_info->id); +} + +P_LIB_API PUThreadKey * +p_uthread_local_new (PDestroyFunc free_func) +{ + PUThreadKey *ret; + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PUThreadKey))) == NULL)) { + P_ERROR ("PUThread::p_uthread_local_new: failed to allocate memory"); + return NULL; + } + + ret->key = -1; + ret->free_func = free_func; + + return ret; +} + +P_LIB_API void +p_uthread_local_free (PUThreadKey *key) +{ + if (P_UNLIKELY (key == NULL)) + return; + + p_free (key); +} + +P_LIB_API ppointer +p_uthread_get_local (PUThreadKey *key) +{ + PUThreadInfo *thread_info; + puthread_key_t tls_key; + ppointer value = NULL; + + if (P_UNLIKELY (key == NULL)) + return NULL; + + if (P_UNLIKELY ((tls_key = pp_uthread_get_tls_key (key)) == -1)) + return NULL; + + p_mutex_lock (pp_uthread_glob_mutex); + + thread_info = pp_uthread_find_thread_info (IExec->FindTask (NULL)); + + if (P_LIKELY (thread_info != NULL)) + value = thread_info->tls_values[tls_key]; + + p_mutex_unlock (pp_uthread_glob_mutex); + + return value; +} + +P_LIB_API void +p_uthread_set_local (PUThreadKey *key, + ppointer value) +{ + PUThreadInfo *thread_info; + puthread_key_t tls_key; + + if (P_UNLIKELY (key == NULL)) + return; + + tls_key = pp_uthread_get_tls_key (key); + + if (P_LIKELY (tls_key != -1)) { + p_mutex_lock (pp_uthread_glob_mutex); + + thread_info = pp_uthread_find_or_create_thread_info (IExec->FindTask (NULL)); + + if (P_LIKELY (thread_info != NULL)) { + if (P_LIKELY (pp_uthread_tls_keys[tls_key].in_use == TRUE)) + thread_info->tls_values[tls_key] = value; + } + + p_mutex_unlock (pp_uthread_glob_mutex); + } +} + +P_LIB_API void +p_uthread_replace_local (PUThreadKey *key, + ppointer value) +{ + PUThreadInfo *thread_info; + puthread_key_t tls_key; + ppointer old_value; + + if (P_UNLIKELY (key == NULL)) + return; + + tls_key = pp_uthread_get_tls_key (key); + + if (P_UNLIKELY (tls_key == -1)) + return; + + p_mutex_lock (pp_uthread_glob_mutex); + + if (P_LIKELY (pp_uthread_tls_keys[tls_key].in_use == TRUE)) { + thread_info = pp_uthread_find_or_create_thread_info (IExec->FindTask (NULL)); + + if (P_LIKELY (thread_info != NULL)) { + old_value = thread_info->tls_values[tls_key]; + + if (old_value != NULL && key->free_func != NULL) + key->free_func (old_value); + + thread_info->tls_values[tls_key] = value; + } + } + + p_mutex_unlock (pp_uthread_glob_mutex); +} diff --git a/3rdparty/plibsys/src/puthread-atheos.c b/3rdparty/plibsys/src/puthread-atheos.c new file mode 100644 index 0000000..8d33ec5 --- /dev/null +++ b/3rdparty/plibsys/src/puthread-atheos.c @@ -0,0 +1,317 @@ +/* + * The MIT License + * + * Copyright (C) 2016-2019 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pmem.h" +#include "patomic.h" +#include "puthread.h" +#include "puthread-private.h" + +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include <atheos/threads.h> +#include <atheos/tld.h> + +typedef thread_id puthread_hdl; + +struct PUThread_ { + PUThreadBase base; + puthread_hdl hdl; +}; + +struct PUThreadKey_ { + pint key; + PDestroyFunc free_func; +}; + +static pint pp_uthread_get_atheos_priority (PUThreadPriority prio); +static pint pp_uthread_get_tls_key (PUThreadKey *key); + +static pint +pp_uthread_get_atheos_priority (PUThreadPriority prio) +{ + switch (prio) { + case P_UTHREAD_PRIORITY_INHERIT: + { + thread_info thr_info; + + memset (&thr_info, 0, sizeof (thr_info)); + + if (P_UNLIKELY (get_thread_info (get_thread_id (NULL), &thr_info) != 0)) { + P_WARNING ("PUThread::pp_uthread_get_atheos_priority: failed to get thread info"); + return NORMAL_PRIORITY; + } else + return thr_info.ti_priority; + } + + case P_UTHREAD_PRIORITY_IDLE: + return IDLE_PRIORITY; + case P_UTHREAD_PRIORITY_LOWEST: + return LOW_PRIORITY / 2; + case P_UTHREAD_PRIORITY_LOW: + return LOW_PRIORITY; + case P_UTHREAD_PRIORITY_NORMAL: + return NORMAL_PRIORITY; + case P_UTHREAD_PRIORITY_HIGH: + return DISPLAY_PRIORITY; + case P_UTHREAD_PRIORITY_HIGHEST: + return URGENT_DISPLAY_PRIORITY; + case P_UTHREAD_PRIORITY_TIMECRITICAL: + return REALTIME_PRIORITY; + } +} + +static pint +pp_uthread_get_tls_key (PUThreadKey *key) +{ + pint thread_key; + + thread_key = p_atomic_int_get ((const volatile pint *) &key->key); + + if (P_LIKELY (thread_key >= 0)) + return thread_key; + + if (P_UNLIKELY ((thread_key = alloc_tld (key->free_func)) < 0)) { + P_ERROR ("PUThread::pp_uthread_get_tls_key: alloc_tld() failed"); + return -1; + } + + if (P_UNLIKELY (p_atomic_int_compare_and_exchange ((volatile pint *) &key->key, + -1, + thread_key) == FALSE)) { + if (P_UNLIKELY (free_tld (thread_key) != 0)) { + P_ERROR ("PUThread::pp_uthread_get_tls_key: free_tld() failed"); + return -1; + } + + thread_key = key->key; + } + + return thread_key; +} + +void +p_uthread_init_internal (void) +{ +} + +void +p_uthread_shutdown_internal (void) +{ +} + +void +p_uthread_win32_thread_detach (void) +{ +} + +PUThread * +p_uthread_create_internal (PUThreadFunc func, + pboolean joinable, + PUThreadPriority prio, + psize stack_size) +{ + PUThread *ret; + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PUThread))) == NULL)) { + P_ERROR ("PUThread::p_uthread_create_internal: failed to allocate memory"); + return NULL; + } + + if (P_UNLIKELY ((ret->hdl = spawn_thread ("", + func, + pp_uthread_get_atheos_priority (prio), + stack_size, + ret)) < 0)) { + P_ERROR ("PUThread::p_uthread_create_internal: spawn_thread() failed"); + p_free (ret); + return NULL; + } + + if (P_UNLIKELY (resume_thread (ret->hdl) != 0)) { + P_ERROR ("PUThread::p_uthread_create_internal: resume_thread() failed"); + p_free (ret); + return NULL; + } + + ret->base.joinable = joinable; + ret->base.prio = prio; + + return ret; +} + +void +p_uthread_exit_internal (void) +{ + exit_thread (0); +} + +void +p_uthread_wait_internal (PUThread *thread) +{ + wait_for_thread (thread->hdl); +} + +void +p_uthread_set_name_internal (PUThread *thread) +{ + pchar *thr_name = NULL; + psize namelen = 0; + pint res = 0; + pboolean is_alloc = FALSE; + + thr_name = thread->base.name; + namelen = strlen (thr_name); + + if (namelen > OS_NAME_LENGTH - 1) { + if (P_UNLIKELY ((thr_name = p_malloc0 (namelen + 1)) == NULL)) { + P_ERROR ("PUThread::p_uthread_set_name_internal: failed to allocate memory"); + return; + } + + memcpy (thr_name, thread->base.name, OS_NAME_LENGTH - 1); + + is_alloc = TRUE; + } + + if (rename_thread (thread->hdl, thr_name) != 0) + P_WARNING ("PUThread::p_uthread_set_name_internal: failed to set thread system name"); + + if (is_alloc == TRUE) + p_free (thr_name); +} + +void +p_uthread_free_internal (PUThread *thread) +{ + p_free (thread); +} + +P_LIB_API void +p_uthread_yield (void) +{ + sched_yield (); +} + +P_LIB_API pboolean +p_uthread_set_priority (PUThread *thread, + PUThreadPriority prio) +{ + if (P_UNLIKELY (thread == NULL)) + return FALSE; + + set_thread_priority (thread->hdl, pp_uthread_get_atheos_priority (prio)); + + thread->base.prio = prio; + + return TRUE; +} + +P_LIB_API P_HANDLE +p_uthread_current_id (void) +{ + return (P_HANDLE) ((psize) get_thread_id (NULL)); +} + +P_LIB_API PUThreadKey * +p_uthread_local_new (PDestroyFunc free_func) +{ + PUThreadKey *ret; + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PUThreadKey))) == NULL)) { + P_ERROR ("PUThread::p_uthread_local_new: failed to allocate memory"); + return NULL; + } + + ret->key = -1; + ret->free_func = free_func; + + return ret; +} + +P_LIB_API void +p_uthread_local_free (PUThreadKey *key) +{ + if (P_UNLIKELY (key == NULL)) + return; + + p_free (key); +} + +P_LIB_API ppointer +p_uthread_get_local (PUThreadKey *key) +{ + pint tls_key; + + if (P_UNLIKELY (key == NULL)) + return NULL; + + tls_key = pp_uthread_get_tls_key (key); + + if (P_LIKELY (tls_key >= 0)) + return get_tld (tls_key); + + return NULL; +} + +P_LIB_API void +p_uthread_set_local (PUThreadKey *key, + ppointer value) +{ + pint tls_key; + + if (P_UNLIKELY (key == NULL)) + return; + + tls_key = pp_uthread_get_tls_key (key); + + if (tls_key >= 0) + set_tld (tls_key, value); +} + +P_LIB_API void +p_uthread_replace_local (PUThreadKey *key, + ppointer value) +{ + pint tls_key; + ppointer old_value; + + if (P_UNLIKELY (key == NULL)) + return; + + tls_key = pp_uthread_get_tls_key (key); + + if (P_UNLIKELY (tls_key < 0)) + return; + + old_value = get_tld (tls_key); + + if (old_value != NULL && key->free_func != NULL) + key->free_func (old_value); + + set_tld (tls_key, value); +} diff --git a/3rdparty/plibsys/src/puthread-beos.c b/3rdparty/plibsys/src/puthread-beos.c new file mode 100644 index 0000000..1f49747 --- /dev/null +++ b/3rdparty/plibsys/src/puthread-beos.c @@ -0,0 +1,431 @@ +/* + * The MIT License + * + * Copyright (C) 2016-2019 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pmem.h" +#include "patomic.h" +#include "pmutex.h" +#include "puthread.h" +#include "puthread-private.h" + +#include <unistd.h> +#include <stdlib.h> +#include <string.h> + +#include <kernel/OS.h> +#include <kernel/scheduler.h> +#include <support/TLS.h> + +typedef thread_id puthread_hdl; + +struct PUThread_ { + PUThreadBase base; + puthread_hdl hdl; + PUThreadFunc proxy; +}; + +struct PUThreadKey_ { + pint key; + PDestroyFunc free_func; +}; + +typedef struct PUThreadDestructor_ PUThreadDestructor; + +struct PUThreadDestructor_ { + pint key_idx; + PDestroyFunc free_func; + PUThreadDestructor *next; +}; + +static PUThreadDestructor * volatile pp_uthread_tls_destructors = NULL; + +static PMutex *pp_uthread_tls_mutex = NULL; + +static pint pp_uthread_get_beos_priority (PUThreadPriority prio); +static pint pp_uthread_get_tls_key (PUThreadKey *key); +static void pp_uthread_clean_destructors (void); +static pint pp_uthread_beos_proxy (ppointer data); + +static pint +pp_uthread_get_beos_priority (PUThreadPriority prio) +{ + switch (prio) { + case P_UTHREAD_PRIORITY_INHERIT: + { + thread_info thr_info; + + memset (&thr_info, 0, sizeof (thr_info)); + + if (P_UNLIKELY (get_thread_info (find_thread (NULL), &thr_info) != B_OK)) { + P_WARNING ("PUThread::pp_uthread_get_beos_priority: failed to get thread info"); + return B_NORMAL_PRIORITY; + } else + return thr_info.priority; + } + + case P_UTHREAD_PRIORITY_IDLE: + return B_LOW_PRIORITY; + case P_UTHREAD_PRIORITY_LOWEST: + return B_NORMAL_PRIORITY / 4; + case P_UTHREAD_PRIORITY_LOW: + return B_NORMAL_PRIORITY / 2; + case P_UTHREAD_PRIORITY_NORMAL: + return B_NORMAL_PRIORITY; + case P_UTHREAD_PRIORITY_HIGH: + return B_DISPLAY_PRIORITY; + case P_UTHREAD_PRIORITY_HIGHEST: + return B_URGENT_DISPLAY_PRIORITY; + case P_UTHREAD_PRIORITY_TIMECRITICAL: + return B_REAL_TIME_PRIORITY; + } +} + +static pint +pp_uthread_get_tls_key (PUThreadKey *key) +{ + pint thread_key; + + thread_key = p_atomic_int_get ((const volatile pint *) &key->key); + + if (P_LIKELY (thread_key >= 0)) + return thread_key; + + p_mutex_lock (pp_uthread_tls_mutex); + + thread_key = key->key; + + if (P_LIKELY (thread_key == -1)) { + PUThreadDestructor *destr = NULL; + + if (key->free_func != NULL) { + if (P_UNLIKELY ((destr = p_malloc0 (sizeof (PUThreadDestructor))) == NULL)) { + P_ERROR ("PUThread::pp_uthread_get_tls_key: failed to allocate memory"); + p_mutex_unlock (pp_uthread_tls_mutex); + return -1; + } + } + + if (P_UNLIKELY ((thread_key = tls_allocate ()) < 0)) { + P_ERROR ("PUThread::pp_uthread_get_tls_key: tls_allocate() failed"); + p_free (destr); + p_mutex_unlock (pp_uthread_tls_mutex); + return -1; + } + + if (destr != NULL) { + destr->key_idx = thread_key; + destr->free_func = key->free_func; + destr->next = pp_uthread_tls_destructors; + + /* At the same time thread exit could be performed at there is no + * lock for the global destructor list */ + if (P_UNLIKELY (p_atomic_pointer_compare_and_exchange ((void * volatile *) &pp_uthread_tls_destructors, + (void *) destr->next, + (void *) destr) == FALSE)) { + P_ERROR ("PUThread::pp_uthread_get_tls_key: p_atomic_pointer_compare_and_exchange() failed"); + p_free (destr); + p_mutex_unlock (pp_uthread_tls_mutex); + return -1; + } + } + + key->key = thread_key; + } + + p_mutex_unlock (pp_uthread_tls_mutex); + + return thread_key; +} + +static void +pp_uthread_clean_destructors (void) +{ + pboolean was_called; + + do { + PUThreadDestructor *destr; + + was_called = FALSE; + + destr = (PUThreadDestructor *) p_atomic_pointer_get ((const void * volatile *) &pp_uthread_tls_destructors); + + while (destr != NULL) { + ppointer value; + + value = tls_get (destr->key_idx); + + if (value != NULL && destr->free_func != NULL) { + tls_set (destr->key_idx, NULL); + destr->free_func (value); + was_called = TRUE; + } + + destr = destr->next; + } + } while (was_called); +} + +static pint +pp_uthread_beos_proxy (ppointer data) +{ + PUThread *thread = data; + + thread->proxy (thread); + + pp_uthread_clean_destructors (); + + return 0; +} + +void +p_uthread_init_internal (void) +{ + if (P_LIKELY (pp_uthread_tls_mutex == NULL)) + pp_uthread_tls_mutex = p_mutex_new (); +} + +void +p_uthread_shutdown_internal (void) +{ + PUThreadDestructor *destr; + + pp_uthread_clean_destructors (); + + destr = pp_uthread_tls_destructors; + + while (destr != NULL) { + PUThreadDestructor *next_destr = destr->next; + + p_free (destr); + destr = next_destr; + } + + pp_uthread_tls_destructors = NULL; + + if (P_LIKELY (pp_uthread_tls_mutex != NULL)) { + p_mutex_free (pp_uthread_tls_mutex); + pp_uthread_tls_mutex = NULL; + } +} + +void +p_uthread_win32_thread_detach (void) +{ +} + +PUThread * +p_uthread_create_internal (PUThreadFunc func, + pboolean joinable, + PUThreadPriority prio, + psize stack_size) +{ + PUThread *ret; + + P_UNUSED (stack_size); + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PUThread))) == NULL)) { + P_ERROR ("PUThread::p_uthread_create_internal: failed to allocate memory"); + return NULL; + } + + ret->proxy = func; + + if (P_UNLIKELY ((ret->hdl = spawn_thread ((thread_func) pp_uthread_beos_proxy, + "", + pp_uthread_get_beos_priority (prio), + ret)) < B_OK)) { + P_ERROR ("PUThread::p_uthread_create_internal: spawn_thread() failed"); + p_free (ret); + return NULL; + } + + if (P_UNLIKELY (resume_thread (ret->hdl) != B_OK)) { + P_ERROR ("PUThread::p_uthread_create_internal: resume_thread() failed"); + p_free (ret); + return NULL; + } + + ret->base.joinable = joinable; + ret->base.prio = prio; + + return ret; +} + +void +p_uthread_exit_internal (void) +{ + pp_uthread_clean_destructors (); + exit_thread (0); +} + +void +p_uthread_wait_internal (PUThread *thread) +{ + status_t exit_value; + + wait_for_thread (thread->hdl, &exit_value); +} + +void +p_uthread_set_name_internal (PUThread *thread) +{ + pchar *thr_name = NULL; + psize namelen = 0; + pint res = 0; + pboolean is_alloc = FALSE; + + thr_name = thread->base.name; + namelen = strlen (thr_name); + + if (namelen > B_OS_NAME_LENGTH - 1) { + if (P_UNLIKELY ((thr_name = p_malloc0 (namelen + 1)) == NULL)) { + P_ERROR ("PUThread::p_uthread_set_name_internal: failed to allocate memory"); + return; + } + + memcpy (thr_name, thread->base.name, B_OS_NAME_LENGTH - 1); + + is_alloc = TRUE; + } + + if (rename_thread (thread->hdl, thr_name) != 0) + P_WARNING ("PUThread::p_uthread_set_name_internal: failed to set thread system name"); + + if (is_alloc == TRUE) + p_free (thr_name); +} + +void +p_uthread_free_internal (PUThread *thread) +{ + p_free (thread); +} + +P_LIB_API void +p_uthread_yield (void) +{ + snooze ((bigtime_t) 0); +} + +P_LIB_API pboolean +p_uthread_set_priority (PUThread *thread, + PUThreadPriority prio) +{ + if (P_UNLIKELY (thread == NULL)) + return FALSE; + + if (set_thread_priority (thread->hdl, pp_uthread_get_beos_priority (prio)) < B_OK) { + P_ERROR ("PUThread::p_uthread_create_internal: set_thread_priority() failed"); + return FALSE; + } + + thread->base.prio = prio; + + return TRUE; +} + +P_LIB_API P_HANDLE +p_uthread_current_id (void) +{ + return (P_HANDLE) ((psize) find_thread (NULL)); +} + +P_LIB_API PUThreadKey * +p_uthread_local_new (PDestroyFunc free_func) +{ + PUThreadKey *ret; + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PUThreadKey))) == NULL)) { + P_ERROR ("PUThread::p_uthread_local_new: failed to allocate memory"); + return NULL; + } + + ret->key = -1; + ret->free_func = free_func; + + return ret; +} + +P_LIB_API void +p_uthread_local_free (PUThreadKey *key) +{ + if (P_UNLIKELY (key == NULL)) + return; + + p_free (key); +} + +P_LIB_API ppointer +p_uthread_get_local (PUThreadKey *key) +{ + pint tls_key; + + if (P_UNLIKELY (key == NULL)) + return NULL; + + tls_key = pp_uthread_get_tls_key (key); + + if (P_LIKELY (tls_key >= 0)) + return tls_get (tls_key); + + return NULL; +} + +P_LIB_API void +p_uthread_set_local (PUThreadKey *key, + ppointer value) +{ + pint tls_key; + + if (P_UNLIKELY (key == NULL)) + return; + + tls_key = pp_uthread_get_tls_key (key); + + if (tls_key >= 0) + tls_set (tls_key, value); +} + +P_LIB_API void +p_uthread_replace_local (PUThreadKey *key, + ppointer value) +{ + pint tls_key; + ppointer old_value; + + if (P_UNLIKELY (key == NULL)) + return; + + tls_key = pp_uthread_get_tls_key (key); + + if (P_UNLIKELY (tls_key < 0)) + return; + + old_value = tls_get (tls_key); + + if (old_value != NULL && key->free_func != NULL) + key->free_func (old_value); + + tls_set (tls_key, value); +} diff --git a/3rdparty/plibsys/src/puthread-none.c b/3rdparty/plibsys/src/puthread-none.c new file mode 100644 index 0000000..c2ecc75 --- /dev/null +++ b/3rdparty/plibsys/src/puthread-none.c @@ -0,0 +1,142 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2019 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pmem.h" +#include "puthread.h" +#include "puthread-private.h" + +#include <stdlib.h> + +struct PUThread_ { + pint hdl; +}; + +void +p_uthread_init_internal (void) +{ +} + +void +p_uthread_shutdown_internal (void) +{ +} + +void +p_uthread_win32_thread_detach (void) +{ +} + +PUThread * +p_uthread_create_internal (PUThreadFunc func, + pboolean joinable, + PUThreadPriority prio, + psize stack_size) +{ + P_UNUSED (func); + P_UNUSED (joinable); + P_UNUSED (prio); + P_UNUSED (stack_size); + + return NULL; +} + +void +p_uthread_exit_internal (void) +{ +} + +void +p_uthread_wait_internal (PUThread *thread) +{ + P_UNUSED (thread); +} + +void +p_uthread_set_name_internal (PUThread *thread) +{ + P_UNUSED (thread); +} + +void +p_uthread_free_internal (PUThread *thread) +{ + P_UNUSED (thread); +} + +P_LIB_API void +p_uthread_yield (void) +{ +} + +P_LIB_API pboolean +p_uthread_set_priority (PUThread *thread, + PUThreadPriority prio) +{ + P_UNUSED (thread); + P_UNUSED (prio); + + return FALSE; +} + +P_LIB_API P_HANDLE +p_uthread_current_id (void) +{ + return (P_HANDLE) 0; +} + +P_LIB_API PUThreadKey * +p_uthread_local_new (PDestroyFunc free_func) +{ + P_UNUSED (free_func); + return NULL; +} + +P_LIB_API void +p_uthread_local_free (PUThreadKey *key) +{ + P_UNUSED (key); +} + +P_LIB_API ppointer +p_uthread_get_local (PUThreadKey *key) +{ + P_UNUSED (key); +} + +P_LIB_API void +p_uthread_set_local (PUThreadKey *key, + ppointer value) +{ + P_UNUSED (key); + P_UNUSED (value); +} + +P_LIB_API void +p_uthread_replace_local (PUThreadKey *key, + ppointer value) +{ + P_UNUSED (key); + P_UNUSED (value); +} diff --git a/3rdparty/plibsys/src/puthread-os2.c b/3rdparty/plibsys/src/puthread-os2.c new file mode 100644 index 0000000..8bb3731 --- /dev/null +++ b/3rdparty/plibsys/src/puthread-os2.c @@ -0,0 +1,458 @@ +/* + * The MIT License + * + * Copyright (C) 2017-2019 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#define INCL_DOSPROCESS +#define INCL_DOSERRORS +#include <os2.h> +#include <process.h> + +#ifdef P_DEBUG +# undef P_DEBUG +#endif + +#include "pmem.h" +#include "patomic.h" +#include "pmutex.h" +#include "puthread.h" +#include "puthread-private.h" + +#include <stdlib.h> + +typedef TID puthread_hdl; + +struct PUThread_ { + PUThreadBase base; + puthread_hdl hdl; + PUThreadFunc proxy; +}; + +struct PUThreadKey_ { + PULONG key; + PDestroyFunc free_func; +}; + +typedef struct PUThreadDestructor_ PUThreadDestructor; + +struct PUThreadDestructor_ { + PULONG key; + PDestroyFunc free_func; + PUThreadDestructor *next; +}; + +static PUThreadDestructor * volatile pp_uthread_tls_destructors = NULL; +static PMutex *pp_uthread_tls_mutex = NULL; + +static void pp_uthread_get_os2_priority (PUThreadPriority prio, PULONG thr_class, PLONG thr_level); +static PULONG pp_uthread_get_tls_key (PUThreadKey *key); +static void pp_uthread_clean_destructors (void); +static void pp_uthread_os2_proxy (ppointer data); + +static void +pp_uthread_get_os2_priority (PUThreadPriority prio, PULONG thr_class, PLONG thr_level) +{ + switch (prio) { + case P_UTHREAD_PRIORITY_INHERIT: + { + APIRET ulrc; + PTIB ptib = NULL; + + if (P_UNLIKELY (DosGetInfoBlocks (&ptib, NULL) != NO_ERROR)) { + P_WARNING ("PUThread::pp_uthread_get_os2_priority: DosGetInfoBlocks() failed"); + *thr_class = PRTYC_REGULAR; + *thr_level = 0; + } else { + *thr_class = ((ptib->tib_ptib2->tib2_ulpri) >> 8) & 0x00FF; + *thr_level = (ptib->tib_ptib2->tib2_ulpri) & 0x001F; + } + + return; + } + case P_UTHREAD_PRIORITY_IDLE: + { + *thr_class = PRTYC_IDLETIME; + *thr_level = 0; + + return; + } + case P_UTHREAD_PRIORITY_LOWEST: + { + *thr_class = PRTYC_REGULAR; + *thr_level = PRTYD_MINIMUM; + + return; + } + case P_UTHREAD_PRIORITY_LOW: + { + *thr_class = PRTYC_REGULAR; + *thr_level = PRTYD_MINIMUM / 2; + + return; + } + case P_UTHREAD_PRIORITY_NORMAL: + { + *thr_class = PRTYC_REGULAR; + *thr_level = 0; + + return; + } + case P_UTHREAD_PRIORITY_HIGH: + { + *thr_class = PRTYC_REGULAR; + *thr_level = PRTYD_MAXIMUM / 2; + + return; + } + case P_UTHREAD_PRIORITY_HIGHEST: + { + *thr_class = PRTYC_REGULAR; + *thr_level = PRTYD_MAXIMUM; + + return; + } + case P_UTHREAD_PRIORITY_TIMECRITICAL: + { + *thr_class = PRTYC_TIMECRITICAL; + *thr_level = 0; + + return; + } + } +} + +static PULONG +pp_uthread_get_tls_key (PUThreadKey *key) +{ + PULONG thread_key; + + thread_key = (PULONG) p_atomic_pointer_get ((ppointer) &key->key); + + if (P_LIKELY (thread_key != NULL)) + return thread_key; + + p_mutex_lock (pp_uthread_tls_mutex); + + thread_key = key->key; + + if (P_LIKELY (thread_key == NULL)) { + PUThreadDestructor *destr = NULL; + + if (key->free_func != NULL) { + if (P_UNLIKELY ((destr = p_malloc0 (sizeof (PUThreadDestructor))) == NULL)) { + P_ERROR ("PUThread::pp_uthread_get_tls_key: failed to allocate memory"); + p_mutex_unlock (pp_uthread_tls_mutex); + return NULL; + } + } + + if (P_UNLIKELY (DosAllocThreadLocalMemory (1, &thread_key) != NO_ERROR)) { + P_ERROR ("PUThread::pp_uthread_get_tls_key: DosAllocThreadLocalMemory() failed"); + p_free (destr); + p_mutex_unlock (pp_uthread_tls_mutex); + return NULL; + } + + if (destr != NULL) { + destr->key = thread_key; + destr->free_func = key->free_func; + destr->next = pp_uthread_tls_destructors; + + /* At the same time thread exit could be performed at there is no + * lock for the global destructor list */ + if (P_UNLIKELY (p_atomic_pointer_compare_and_exchange ((void * volatile *) &pp_uthread_tls_destructors, + (void *) destr->next, + (void *) destr) == FALSE)) { + P_ERROR ("PUThread::pp_uthread_get_tls_key: p_atomic_pointer_compare_and_exchange() failed"); + + if (P_UNLIKELY (DosFreeThreadLocalMemory (thread_key) != NO_ERROR)) + P_ERROR ("PUThread::pp_uthread_get_tls_key: DosFreeThreadLocalMemory() failed"); + + p_free (destr); + p_mutex_unlock (pp_uthread_tls_mutex); + return NULL; + } + } + + key->key = thread_key; + } + + p_mutex_unlock (pp_uthread_tls_mutex); + + return thread_key; +} + +static void +pp_uthread_clean_destructors (void) +{ + pboolean was_called; + + do { + PUThreadDestructor *destr; + + was_called = FALSE; + + destr = (PUThreadDestructor *) p_atomic_pointer_get ((const void * volatile *) &pp_uthread_tls_destructors); + + while (destr != NULL) { + PULONG value; + + value = destr->key; + + if (value != NULL && ((ppointer) *value) != NULL && destr->free_func != NULL) { + *destr->key = (ULONG) NULL; + destr->free_func ((ppointer) *value); + was_called = TRUE; + } + + destr = destr->next; + } + } while (was_called); +} + +static void +pp_uthread_os2_proxy (ppointer data) +{ + PUThread *thread = data; + + thread->proxy (thread); + + pp_uthread_clean_destructors (); +} + +void +p_uthread_init_internal (void) +{ + if (P_LIKELY (pp_uthread_tls_mutex == NULL)) + pp_uthread_tls_mutex = p_mutex_new (); +} + +void +p_uthread_shutdown_internal (void) +{ + PUThreadDestructor *destr; + + pp_uthread_clean_destructors (); + + destr = pp_uthread_tls_destructors; + + while (destr != NULL) { + PUThreadDestructor *next_destr = destr->next; + + p_free (destr); + destr = next_destr; + } + + pp_uthread_tls_destructors = NULL; + + if (P_LIKELY (pp_uthread_tls_mutex != NULL)) { + p_mutex_free (pp_uthread_tls_mutex); + pp_uthread_tls_mutex = NULL; + } +} + +void +p_uthread_win32_thread_detach (void) +{ +} + +PUThread * +p_uthread_create_internal (PUThreadFunc func, + pboolean joinable, + PUThreadPriority prio, + psize stack_size) +{ + PUThread *ret; + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PUThread))) == NULL)) { + P_ERROR ("PUThread::p_uthread_create_internal: failed to allocate memory"); + return NULL; + } + + ret->base.joinable = joinable; + ret->proxy = func; + + if (P_UNLIKELY ((ret->hdl = _beginthread ((void (*) (void *)) pp_uthread_os2_proxy, + NULL, + (puint) stack_size, + ret)) <= 0)) { + P_ERROR ("PUThread::p_uthread_create_internal: _beginthread() failed"); + p_free (ret); + return NULL; + } + + ret->base.prio = P_UTHREAD_PRIORITY_INHERIT; + p_uthread_set_priority (ret, prio); + + return ret; +} + +void +p_uthread_exit_internal (void) +{ + pp_uthread_clean_destructors (); + _endthread (); +} + +void +p_uthread_wait_internal (PUThread *thread) +{ + APIRET ulrc; + + while ((ulrc = DosWaitThread (&thread->hdl, DCWW_WAIT)) == ERROR_INTERRUPT) + ; + + if (P_UNLIKELY (ulrc != NO_ERROR && ulrc != ERROR_INVALID_THREADID)) + P_ERROR ("PUThread::p_uthread_wait_internal: DosWaitThread() failed"); +} + +void +p_uthread_set_name_internal (PUThread *thread) +{ + P_UNUSED (thread); +} + +void +p_uthread_free_internal (PUThread *thread) +{ + p_free (thread); +} + +P_LIB_API void +p_uthread_yield (void) +{ + DosSleep (0); +} + +P_LIB_API pboolean +p_uthread_set_priority (PUThread *thread, + PUThreadPriority prio) +{ + APIRET ulrc; + PTIB ptib = NULL; + LONG cur_level; + LONG new_level; + ULONG new_class; + + if (P_UNLIKELY (thread == NULL)) + return FALSE; + + if (P_UNLIKELY (DosGetInfoBlocks (&ptib, NULL) != NO_ERROR)) { + P_WARNING ("PUThread::p_uthread_set_priority: DosGetInfoBlocks() failed"); + return FALSE; + } + + cur_level = (ptib->tib_ptib2->tib2_ulpri) & 0x001F; + + pp_uthread_get_os2_priority (prio, &new_class, &new_level); + + if (P_UNLIKELY (DosSetPriority (PRTYS_THREAD, new_class, new_level - cur_level, 0) != NO_ERROR)) { + P_WARNING ("PUThread::p_uthread_set_priority: DosSetPriority() failed"); + return FALSE; + } + + thread->base.prio = prio; + return TRUE; +} + +P_LIB_API P_HANDLE +p_uthread_current_id (void) +{ + return (P_HANDLE) (_gettid ()); +} + +P_LIB_API PUThreadKey * +p_uthread_local_new (PDestroyFunc free_func) +{ + PUThreadKey *ret; + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PUThreadKey))) == NULL)) { + P_ERROR ("PUThread::p_uthread_local_new: failed to allocate memory"); + return NULL; + } + + ret->free_func = free_func; + + return ret; +} + +P_LIB_API void +p_uthread_local_free (PUThreadKey *key) +{ + if (P_UNLIKELY (key == NULL)) + return; + + p_free (key); +} + +P_LIB_API ppointer +p_uthread_get_local (PUThreadKey *key) +{ + PULONG tls_key; + + if (P_UNLIKELY (key == NULL)) + return NULL; + + if (P_UNLIKELY ((tls_key = pp_uthread_get_tls_key (key)) == NULL)) + return NULL; + + return (ppointer) *tls_key; +} + +P_LIB_API void +p_uthread_set_local (PUThreadKey *key, + ppointer value) +{ + PULONG tls_key; + + if (P_UNLIKELY (key == NULL)) + return; + + tls_key = pp_uthread_get_tls_key (key); + + if (P_LIKELY (tls_key != NULL)) + *tls_key = (ULONG) value; +} + +P_LIB_API void +p_uthread_replace_local (PUThreadKey *key, + ppointer value) +{ + PULONG tls_key; + ppointer old_value; + + if (P_UNLIKELY (key == NULL)) + return; + + tls_key = pp_uthread_get_tls_key (key); + + if (P_UNLIKELY (tls_key == NULL)) + return; + + old_value = (ppointer) *tls_key; + + if (old_value != NULL && key->free_func != NULL) + key->free_func (old_value); + + *tls_key = (ULONG) value; +} diff --git a/3rdparty/plibsys/src/puthread-posix.c b/3rdparty/plibsys/src/puthread-posix.c new file mode 100644 index 0000000..90e14d5 --- /dev/null +++ b/3rdparty/plibsys/src/puthread-posix.c @@ -0,0 +1,580 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2019 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pmem.h" +#include "patomic.h" +#include "puthread.h" +#include "puthread-private.h" + +#ifdef P_OS_SCO +# include "pmutex.h" +#endif + +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <pthread.h> +#include <time.h> + +/* On some OS like OpenBSD it must follow <pthread.h> */ +#ifdef PLIBSYS_NEED_PTHREAD_NP_H +# include <pthread_np.h> +#endif + +#ifdef PLIBSYS_HAS_POSIX_SCHEDULING +# ifndef P_OS_VMS +# include <sched.h> +# endif +#endif + +#ifdef PLIBSYS_HAS_PTHREAD_PRCTL +# include <sys/prctl.h> +# include <linux/prctl.h> +#endif + +#ifdef P_OS_QNX6 +# include <sys/neutrino.h> +#endif + +#ifdef P_OS_HAIKU +# include <kernel/OS.h> +#endif + +/* Some systems without native pthreads may lack some of the constants, + * leave them zero as we are not going to use them anyway */ + +#ifndef PTHREAD_CREATE_JOINABLE +# define PTHREAD_CREATE_JOINABLE 0 +#endif + +#ifndef PTHREAD_CREATE_DETACHED +# define PTHREAD_CREATE_DETACHED 0 +#endif + +#ifdef PLIBSYS_HAS_POSIX_SCHEDULING +# ifndef PTHREAD_INHERIT_SCHED +# define PTHREAD_INHERIT_SCHED 0 +# endif + +# ifndef PTHREAD_EXPLICIT_SCHED +# define PTHREAD_EXPLICIT_SCHED 0 +# endif + +/* Old Linux kernels may lack a definition */ +# if defined (P_OS_LINUX) && !defined (SCHED_IDLE) +# define SCHED_IDLE 5 +# endif +#endif + +/* Max length of a thead name */ +#if defined(P_OS_LINUX) +# define PUTHREAD_MAX_NAME 16 +#elif defined(P_OS_NETBSD) +# define PUTHREAD_MAX_NAME PTHREAD_MAX_NAMELEN_NP +#elif defined(P_OS_DRAGONFLY) +# define PUTHREAD_MAX_NAME 16 +#elif defined(P_OS_SOLARIS) +# define PUTHREAD_MAX_NAME 32 +#elif defined(P_OS_TRU64) +# define PUTHREAD_MAX_NAME 32 +#elif defined(P_OS_VMS) +# define PUTHREAD_MAX_NAME 32 +#elif defined(P_OS_QNX6) +# define PUTHREAD_MAX_NAME _NTO_THREAD_NAME_MAX +#elif defined(P_OS_HAIKU) +# define PUTHREAD_MAX_NAME 32 +#endif + +typedef pthread_t puthread_hdl; + +struct PUThread_ { + PUThreadBase base; + puthread_hdl hdl; +}; + +struct PUThreadKey_ { + pthread_key_t *key; + PDestroyFunc free_func; +}; + +#ifdef P_OS_SCO +static PMutex *pp_uthread_tls_mutex = NULL; +#endif + +#ifdef PLIBSYS_HAS_POSIX_SCHEDULING +static pboolean pp_uthread_get_unix_priority (PUThreadPriority prio, int *sched_policy, int *sched_priority); +#endif + +static pthread_key_t * pp_uthread_get_tls_key (PUThreadKey *key); + +#ifdef PLIBSYS_HAS_POSIX_SCHEDULING +static pboolean +pp_uthread_get_unix_priority (PUThreadPriority prio, int *sched_policy, int *sched_priority) +{ + pint lowBound, upperBound; + pint prio_min, prio_max; + pint native_prio; + +#ifdef SCHED_IDLE + if (prio == P_UTHREAD_PRIORITY_IDLE) { + *sched_policy = SCHED_IDLE; + *sched_priority = 0; + return TRUE; + } + + lowBound = (pint) P_UTHREAD_PRIORITY_LOWEST; +#else + lowBound = (pint) P_UTHREAD_PRIORITY_IDLE; +#endif + upperBound = (pint) P_UTHREAD_PRIORITY_TIMECRITICAL; + + prio_min = sched_get_priority_min (*sched_policy); + prio_max = sched_get_priority_max (*sched_policy); + + if (P_UNLIKELY (prio_min == -1 || prio_max == -1)) + return FALSE; + + native_prio = ((pint) prio - lowBound) * (prio_max - prio_min) / upperBound + prio_min; + + if (P_UNLIKELY (native_prio > prio_max)) + native_prio = prio_max; + + if (P_UNLIKELY (native_prio < prio_min)) + native_prio = prio_min; + + *sched_priority = native_prio; + + return TRUE; +} +#endif + +static pthread_key_t * +pp_uthread_get_tls_key (PUThreadKey *key) +{ + pthread_key_t *thread_key; + + thread_key = (pthread_key_t *) p_atomic_pointer_get ((ppointer) &key->key); + + if (P_LIKELY (thread_key != NULL)) + return thread_key; + +#ifdef P_OS_SCO + p_mutex_lock (pp_uthread_tls_mutex); + + thread_key = key->key; + + if (P_LIKELY (thread_key == NULL)) { +#endif + if (P_UNLIKELY ((thread_key = p_malloc0 (sizeof (pthread_key_t))) == NULL)) { + P_ERROR ("PUThread::pp_uthread_get_tls_key: failed to allocate memory"); +#ifdef P_OS_SCO + p_mutex_unlock (pp_uthread_tls_mutex); +#endif + return NULL; + } + + if (P_UNLIKELY (pthread_key_create (thread_key, key->free_func) != 0)) { + P_ERROR ("PUThread::pp_uthread_get_tls_key: pthread_key_create() failed"); + p_free (thread_key); +#ifdef P_OS_SCO + p_mutex_unlock (pp_uthread_tls_mutex); +#endif + return NULL; + } + +#ifndef P_OS_SCO + if (P_UNLIKELY (p_atomic_pointer_compare_and_exchange ((ppointer) &key->key, + NULL, + (ppointer) thread_key) == FALSE)) { + if (P_UNLIKELY (pthread_key_delete (*thread_key) != 0)) { + P_ERROR ("PUThread::pp_uthread_get_tls_key: pthread_key_delete() failed"); + p_free (thread_key); + return NULL; + } + + p_free (thread_key); + + thread_key = key->key; + } +#else + key->key = thread_key; + } + + p_mutex_unlock (pp_uthread_tls_mutex); +#endif + + return thread_key; +} + +void +p_uthread_init_internal (void) +{ +#ifdef P_OS_SCO + if (P_LIKELY (pp_uthread_tls_mutex == NULL)) + pp_uthread_tls_mutex = p_mutex_new (); +#endif +} + +void +p_uthread_shutdown_internal (void) +{ +#ifdef P_OS_SCO + if (P_LIKELY (pp_uthread_tls_mutex != NULL)) { + p_mutex_free (pp_uthread_tls_mutex); + pp_uthread_tls_mutex = NULL; + } +#endif +} + +void +p_uthread_win32_thread_detach (void) +{ +} + +PUThread * +p_uthread_create_internal (PUThreadFunc func, + pboolean joinable, + PUThreadPriority prio, + psize stack_size) +{ + PUThread *ret; + pthread_attr_t attr; + pint create_code; +#ifdef PLIBSYS_HAS_POSIX_SCHEDULING + struct sched_param sched; + pint native_prio; + pint sched_policy; +#endif + +#if defined (PLIBSYS_HAS_POSIX_STACKSIZE) && defined (_SC_THREAD_STACK_MIN) + plong min_stack; +#endif + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PUThread))) == NULL)) { + P_ERROR ("PUThread::p_uthread_create_internal: failed to allocate memory"); + return NULL; + } + + ret->base.joinable = joinable; + + if (P_UNLIKELY (pthread_attr_init (&attr) != 0)) { + P_ERROR ("PUThread::p_uthread_create_internal: pthread_attr_init() failed"); + p_free (ret); + return NULL; + } + + if (P_UNLIKELY (pthread_attr_setdetachstate (&attr, + joinable ? PTHREAD_CREATE_JOINABLE + : PTHREAD_CREATE_DETACHED) != 0)) { + P_ERROR ("PUThread::p_uthread_create_internal: pthread_attr_setdetachstate() failed"); + pthread_attr_destroy (&attr); + p_free (ret); + return NULL; + } + +#ifdef PLIBSYS_HAS_POSIX_SCHEDULING + if (prio == P_UTHREAD_PRIORITY_INHERIT) { + if (P_UNLIKELY (pthread_attr_setinheritsched (&attr, PTHREAD_INHERIT_SCHED) != 0)) + P_WARNING ("PUThread::p_uthread_create_internal: pthread_attr_setinheritsched() failed"); + } else { + if (P_LIKELY (pthread_attr_getschedpolicy (&attr, &sched_policy) == 0)) { + if (P_LIKELY (pp_uthread_get_unix_priority (prio, + &sched_policy, + &native_prio) == TRUE)) { + memset (&sched, 0, sizeof (sched)); + sched.sched_priority = native_prio; + + if (P_LIKELY (pthread_attr_setinheritsched (&attr, PTHREAD_EXPLICIT_SCHED) != 0 || + pthread_attr_setschedpolicy (&attr, sched_policy) != 0 || + pthread_attr_setschedparam (&attr, &sched) != 0)) + P_WARNING ("PUThread::p_uthread_create_internal: failed to set priority"); + } else + P_WARNING ("PUThread::p_uthread_create_internal: pp_uthread_get_unix_priority() failed"); + } else + P_WARNING ("PUThread::p_uthread_create_internal: pthread_attr_getschedpolicy() failed"); + } +#endif + +#ifdef PLIBSYS_HAS_POSIX_STACKSIZE +# ifdef _SC_THREAD_STACK_MIN + if (stack_size > 0) { + min_stack = (plong) sysconf (_SC_THREAD_STACK_MIN); + + if (P_LIKELY (min_stack > 0)) { + if (P_UNLIKELY (stack_size < (psize) min_stack)) + stack_size = (psize) min_stack; + } else + P_WARNING ("PUThread::p_uthread_create_internal: sysconf() with _SC_THREAD_STACK_MIN failed"); + + if (P_UNLIKELY (pthread_attr_setstacksize (&attr, stack_size) != 0)) + P_WARNING ("PUThread::p_uthread_create_internal: pthread_attr_setstacksize() failed"); + } +# endif +#endif + + create_code = pthread_create (&ret->hdl, &attr, func, ret); + +#ifdef EPERM + if (create_code == EPERM) { +# ifdef PLIBSYS_HAS_POSIX_SCHEDULING + pthread_attr_setinheritsched (&attr, PTHREAD_INHERIT_SCHED); +# endif + create_code = pthread_create (&ret->hdl, &attr, func, ret); + } +#endif + + if (P_UNLIKELY (create_code != 0)) { + P_ERROR ("PUThread::p_uthread_create_internal: pthread_create() failed"); + pthread_attr_destroy (&attr); + p_free (ret); + return NULL; + } + + ret->base.prio = prio; + pthread_attr_destroy (&attr); + + return ret; +} + +void +p_uthread_exit_internal (void) +{ + pthread_exit (P_INT_TO_POINTER (0)); +} + +void +p_uthread_wait_internal (PUThread *thread) +{ + if (P_UNLIKELY (pthread_join (thread->hdl, NULL) != 0)) + P_ERROR ("PUThread::p_uthread_wait_internal: pthread_join() failed"); +} + +void +p_uthread_set_name_internal (PUThread *thread) +{ + pchar *thr_name = NULL; + pint res = 0; + pboolean is_alloc = FALSE; +#ifdef PUTHREAD_MAX_NAME + psize namelen = 0; +#endif + + thr_name = thread->base.name; + +#ifdef PUTHREAD_MAX_NAME + namelen = strlen (thr_name); + + if (namelen > PUTHREAD_MAX_NAME - 1) { + if (P_UNLIKELY ((thr_name = p_malloc0 (namelen + 1)) == NULL)) { + P_ERROR ("PUThread::p_uthread_set_name_internal: failed to allocate memory"); + return; + } + + memcpy (thr_name, thread->base.name, PUTHREAD_MAX_NAME - 1); + + is_alloc = TRUE; + } +#endif + +#if defined(PLIBSYS_HAS_PTHREAD_SETNAME) +# if defined(P_OS_MAC) + if (thread->hdl != pthread_self ()) + P_WARNING ("PUThread::p_uthread_set_name_internal: only calling thread is supported in macOS"); + else + res = pthread_setname_np (thr_name); +# elif defined(P_OS_NETBSD) + res = pthread_setname_np (thread->hdl, "%s", thr_name); +# elif defined(P_OS_TRU64) || defined(P_OS_VMS) + res = pthread_setname_np (thread->hdl, thr_name, 0); +# else + res = pthread_setname_np (thread->hdl, thr_name); +# endif +#elif defined(PLIBSYS_HAS_PTHREAD_SET_NAME) + pthread_set_name_np (thread->hdl, thr_name); +#elif defined(PLIBSYS_HAS_PTHREAD_PRCTL) + if (thread->hdl != pthread_self ()) + P_WARNING ("PUThread::p_uthread_set_name_internal: prctl() can be used on calling thread only"); + else + res = prctl (PR_SET_NAME, thr_name, NULL, NULL, NULL); +#elif defined(P_OS_HAIKU) + res = rename_thread (find_thread (NULL), thr_name); +#endif + + if (is_alloc == TRUE) + p_free (thr_name); + + if (res != 0) + P_WARNING ("PUThread::p_uthread_set_name_internal: failed to set thread system name"); +} + +void +p_uthread_free_internal (PUThread *thread) +{ + p_free (thread); +} + +P_LIB_API void +p_uthread_yield (void) +{ + sched_yield (); +} + +P_LIB_API pboolean +p_uthread_set_priority (PUThread *thread, + PUThreadPriority prio) +{ +#ifdef PLIBSYS_HAS_POSIX_SCHEDULING + struct sched_param sched; + pint policy; + pint native_prio; +#endif + + if (P_UNLIKELY (thread == NULL)) + return FALSE; + +#ifdef PLIBSYS_HAS_POSIX_SCHEDULING + if (P_UNLIKELY (pthread_getschedparam (thread->hdl, &policy, &sched) != 0)) { + P_ERROR ("PUThread::p_uthread_set_priority: pthread_getschedparam() failed"); + return FALSE; + } + + if (P_UNLIKELY (pp_uthread_get_unix_priority (prio, &policy, &native_prio) == FALSE)) { + P_ERROR ("PUThread::p_uthread_set_priority: pp_uthread_get_unix_priority() failed"); + return FALSE; + } + + memset (&sched, 0, sizeof (sched)); + sched.sched_priority = native_prio; + + if (P_UNLIKELY (pthread_setschedparam (thread->hdl, policy, &sched) != 0)) { + P_ERROR ("PUThread::p_uthread_set_priority: pthread_setschedparam() failed"); + return FALSE; + } +#endif + + thread->base.prio = prio; + return TRUE; +} + +P_LIB_API P_HANDLE +p_uthread_current_id (void) +{ + return (P_HANDLE) ((psize) pthread_self ()); +} + +P_LIB_API PUThreadKey * +p_uthread_local_new (PDestroyFunc free_func) +{ + PUThreadKey *ret; + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PUThreadKey))) == NULL)) { + P_ERROR ("PUThread::p_uthread_local_new: failed to allocate memory"); + return NULL; + } + + ret->free_func = free_func; + + return ret; +} + +P_LIB_API void +p_uthread_local_free (PUThreadKey *key) +{ + if (P_UNLIKELY (key == NULL)) + return; + + p_free (key); +} + +P_LIB_API ppointer +p_uthread_get_local (PUThreadKey *key) +{ + pthread_key_t *tls_key; +#ifdef P_OS_SCO + ppointer value; +#endif + + if (P_UNLIKELY (key == NULL)) + return NULL; + + if (P_UNLIKELY ((tls_key = pp_uthread_get_tls_key (key)) == NULL)) + return NULL; + +#ifdef P_OS_SCO + if (P_UNLIKELY (pthread_getspecific (*tls_key, &value) != 0)) + return NULL; + + return value; +#else + return pthread_getspecific (*tls_key); +#endif +} + +P_LIB_API void +p_uthread_set_local (PUThreadKey *key, + ppointer value) +{ + pthread_key_t *tls_key; + + if (P_UNLIKELY (key == NULL)) + return; + + tls_key = pp_uthread_get_tls_key (key); + + if (P_LIKELY (tls_key != NULL)) { + if (P_UNLIKELY (pthread_setspecific (*tls_key, value) != 0)) + P_ERROR ("PUThread::p_uthread_set_local: pthread_setspecific() failed"); + } +} + +P_LIB_API void +p_uthread_replace_local (PUThreadKey *key, + ppointer value) +{ + pthread_key_t *tls_key; + ppointer old_value; + + if (P_UNLIKELY (key == NULL)) + return; + + tls_key = pp_uthread_get_tls_key (key); + + if (P_UNLIKELY (tls_key == NULL)) + return; + +#ifdef P_OS_SCO + if (P_UNLIKELY (pthread_getspecific (*tls_key, &old_value) != 0)) + return; +#else + old_value = pthread_getspecific (*tls_key); +#endif + + if (old_value != NULL && key->free_func != NULL) + key->free_func (old_value); + + if (P_UNLIKELY (pthread_setspecific (*tls_key, value) != 0)) + P_ERROR ("PUThread::p_uthread_replace_local: pthread_setspecific() failed"); +} diff --git a/3rdparty/plibsys/src/puthread-private.h b/3rdparty/plibsys/src/puthread-private.h new file mode 100644 index 0000000..9b389d8 --- /dev/null +++ b/3rdparty/plibsys/src/puthread-private.h @@ -0,0 +1,53 @@ +/* + * The MIT License + * + * Copyright (C) 2016-2019 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION) +# error "Header files shouldn't be included directly, consider using <plibsys.h> instead." +#endif + +#ifndef PLIBSYS_HEADER_PUTHREAD_PRIVATE_H +#define PLIBSYS_HEADER_PUTHREAD_PRIVATE_H + +#include "pmacros.h" +#include "ptypes.h" +#include "puthread.h" + +P_BEGIN_DECLS + +/** Base thread structure */ +typedef struct PUThreadBase_ { + pint ref_count; /**< Reference counter. */ + pint ret_code; /**< Return code. */ + pboolean ours; /**< Our thread flag. */ + pboolean joinable; /**< Joinable flag. */ + PUThreadFunc func; /**< Thread routine. */ + ppointer data; /**< Thread input data. */ + PUThreadPriority prio; /**< Thread priority. */ + pchar *name; /**< Thread name */ +} PUThreadBase; + +P_END_DECLS + +#endif /* PLIBSYS_HEADER_PUTHREAD_PRIVATE_H */ diff --git a/3rdparty/plibsys/src/puthread-solaris.c b/3rdparty/plibsys/src/puthread-solaris.c new file mode 100644 index 0000000..80f9f7b --- /dev/null +++ b/3rdparty/plibsys/src/puthread-solaris.c @@ -0,0 +1,352 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2019 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pmem.h" +#include "patomic.h" +#include "puthread.h" +#include "puthread-private.h" + +#ifndef P_OS_UNIXWARE +# include "pmutex.h" +#endif + +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <thread.h> + +#ifdef P_OS_UNIXWARE +# define PLIBSYS_THREAD_MIN_PRIO 0 +# define PLIBSYS_THREAD_MAX_PRIO 126 +#else +# define PLIBSYS_THREAD_MIN_PRIO 0 +# define PLIBSYS_THREAD_MAX_PRIO 127 +#endif + +typedef thread_t puthread_hdl; + +struct PUThread_ { + PUThreadBase base; + puthread_hdl hdl; +}; + +struct PUThreadKey_ { + thread_key_t *key; + PDestroyFunc free_func; +}; + +#ifndef P_OS_UNIXWARE +static PMutex *pp_uthread_tls_mutex = NULL; +#endif + +static pint pp_uthread_get_unix_priority (PUThreadPriority prio); +static thread_key_t * pp_uthread_get_tls_key (PUThreadKey *key); + +static pint +pp_uthread_get_unix_priority (PUThreadPriority prio) +{ + pint lowBound, upperBound; + + lowBound = (pint) P_UTHREAD_PRIORITY_IDLE; + upperBound = (pint) P_UTHREAD_PRIORITY_TIMECRITICAL; + + return ((pint) prio - lowBound) * + (PLIBSYS_THREAD_MAX_PRIO - PLIBSYS_THREAD_MIN_PRIO) / upperBound + + PLIBSYS_THREAD_MIN_PRIO; +} + +static thread_key_t * +pp_uthread_get_tls_key (PUThreadKey *key) +{ + thread_key_t *thread_key; + + thread_key = (thread_key_t *) p_atomic_pointer_get ((ppointer) &key->key); + + if (P_LIKELY (thread_key != NULL)) + return thread_key; + +#ifndef P_OS_UNIXWARE + p_mutex_lock (pp_uthread_tls_mutex); + + thread_key = key->key; + + if (P_LIKELY (thread_key == NULL)) { +#endif + if (P_UNLIKELY ((thread_key = p_malloc0 (sizeof (thread_key_t))) == NULL)) { + P_ERROR ("PUThread::pp_uthread_get_tls_key: failed to allocate memory"); +#ifndef P_OS_UNIXWARE + p_mutex_unlock (pp_uthread_tls_mutex); +#endif + return NULL; + } + + if (P_UNLIKELY (thr_keycreate (thread_key, key->free_func) != 0)) { + P_ERROR ("PUThread::pp_uthread_get_tls_key: thr_keycreate() failed"); + p_free (thread_key); +#ifndef P_OS_UNIXWARE + p_mutex_unlock (pp_uthread_tls_mutex); +#endif + return NULL; + } +#ifdef P_OS_UNIXWARE + if (P_UNLIKELY (p_atomic_pointer_compare_and_exchange ((ppointer) &key->key, + NULL, + (ppointer) thread_key) == FALSE)) { + if (P_UNLIKELY (thr_keydelete (*thread_key) != 0)) { + P_ERROR ("PUThread::pp_uthread_get_tls_key: thr_keydelete() failed"); + p_free (thread_key); + return NULL; + } + + p_free (thread_key); + + thread_key = key->key; + } +#else + key->key = thread_key; + } + + p_mutex_unlock (pp_uthread_tls_mutex); +#endif + + return thread_key; +} + +void +p_uthread_init_internal (void) +{ +#ifndef P_OS_UNIXWARE + if (P_LIKELY (pp_uthread_tls_mutex == NULL)) + pp_uthread_tls_mutex = p_mutex_new (); +#endif +} + +void +p_uthread_shutdown_internal (void) +{ +#ifndef P_OS_UNIXWARE + if (P_LIKELY (pp_uthread_tls_mutex != NULL)) { + p_mutex_free (pp_uthread_tls_mutex); + pp_uthread_tls_mutex = NULL; + } +#endif +} + +void +p_uthread_win32_thread_detach (void) +{ +} + +PUThread * +p_uthread_create_internal (PUThreadFunc func, + pboolean joinable, + PUThreadPriority prio, + psize stack_size) +{ + PUThread *ret; + pint32 flags; + psize min_stack; + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PUThread))) == NULL)) { + P_ERROR ("PUThread::p_uthread_create_internal: failed to allocate memory"); + return NULL; + } + + if (stack_size > 0) { +#ifdef P_OS_UNIXWARE + min_stack = thr_minstack (); +#else + min_stack = thr_min_stack (); +#endif + + if (P_UNLIKELY (stack_size < min_stack)) + stack_size = min_stack; + } + + flags = THR_SUSPENDED; + flags |= joinable ? 0 : THR_DETACHED; + + if (P_UNLIKELY (thr_create (NULL, stack_size, func, ret, flags, &ret->hdl) != 0)) { + P_ERROR ("PUThread::p_uthread_create_internal: thr_create() failed"); + p_free (ret); + return NULL; + } + + if (P_UNLIKELY (thr_setprio (ret->hdl, pp_uthread_get_unix_priority (prio)) != 0)) + P_WARNING ("PUThread::p_uthread_create_internal: thr_setprio() failed"); + + if (P_UNLIKELY (thr_continue (ret->hdl) != 0)) { + P_ERROR ("PUThread::p_uthread_create_internal: thr_continue() failed"); + p_free (ret); + return NULL; + } + + ret->base.joinable = joinable; + ret->base.prio = prio; + + return ret; +} + +void +p_uthread_exit_internal (void) +{ + thr_exit (P_INT_TO_POINTER (0)); +} + +void +p_uthread_wait_internal (PUThread *thread) +{ + if (P_UNLIKELY (thr_join (thread->hdl, NULL, NULL) != 0)) + P_ERROR ("PUThread::p_uthread_wait_internal: thr_join() failed"); +} + +void +p_uthread_set_name_internal (PUThread *thread) +{ + P_UNUSED (thread); +} + +void +p_uthread_free_internal (PUThread *thread) +{ + p_free (thread); +} + +P_LIB_API void +p_uthread_yield (void) +{ + thr_yield (); +} + +P_LIB_API pboolean +p_uthread_set_priority (PUThread *thread, + PUThreadPriority prio) +{ + if (P_UNLIKELY (thread == NULL)) + return FALSE; + + if (P_UNLIKELY (thr_setprio (thread->hdl, pp_uthread_get_unix_priority (prio)) != 0)) { + P_WARNING ("PUThread::p_uthread_set_priority: thr_setprio() failed"); + return FALSE; + } + + thread->base.prio = prio; + + return TRUE; +} + +P_LIB_API P_HANDLE +p_uthread_current_id (void) +{ + return (P_HANDLE) ((psize) thr_self ()); +} + +P_LIB_API PUThreadKey * +p_uthread_local_new (PDestroyFunc free_func) +{ + PUThreadKey *ret; + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PUThreadKey))) == NULL)) { + P_ERROR ("PUThread::p_uthread_local_new: failed to allocate memory"); + return NULL; + } + + ret->free_func = free_func; + + return ret; +} + +P_LIB_API void +p_uthread_local_free (PUThreadKey *key) +{ + if (P_UNLIKELY (key == NULL)) + return; + + p_free (key); +} + +P_LIB_API ppointer +p_uthread_get_local (PUThreadKey *key) +{ + thread_key_t *tls_key; + ppointer ret = NULL; + + if (P_UNLIKELY (key == NULL)) + return ret; + + tls_key = pp_uthread_get_tls_key (key); + + if (P_LIKELY (tls_key != NULL)) { + if (P_UNLIKELY (thr_getspecific (*tls_key, &ret) != 0)) + P_ERROR ("PUThread::p_uthread_get_local: thr_getspecific() failed"); + } + + return ret; +} + +P_LIB_API void +p_uthread_set_local (PUThreadKey *key, + ppointer value) +{ + thread_key_t *tls_key; + + if (P_UNLIKELY (key == NULL)) + return; + + tls_key = pp_uthread_get_tls_key (key); + + if (P_LIKELY (tls_key != NULL)) { + if (P_UNLIKELY (thr_setspecific (*tls_key, value) != 0)) + P_ERROR ("PUThread::p_uthread_set_local: thr_setspecific() failed"); + } +} + +P_LIB_API void +p_uthread_replace_local (PUThreadKey *key, + ppointer value) +{ + thread_key_t *tls_key; + ppointer old_value = NULL; + + if (P_UNLIKELY (key == NULL)) + return; + + tls_key = pp_uthread_get_tls_key (key); + + if (P_UNLIKELY (tls_key == NULL)) + return; + + if (P_UNLIKELY (thr_getspecific (*tls_key, &old_value) != 0)) { + P_ERROR ("PUThread::p_uthread_replace_local: thr_getspecific() failed"); + return; + } + + if (old_value != NULL && key->free_func != NULL) + key->free_func (old_value); + + if (P_UNLIKELY (thr_setspecific (*tls_key, value) != 0)) + P_ERROR ("PUThread::p_uthread_replace_local: thr_setspecific() failed"); +} diff --git a/3rdparty/plibsys/src/puthread-win.c b/3rdparty/plibsys/src/puthread-win.c new file mode 100644 index 0000000..57604fd --- /dev/null +++ b/3rdparty/plibsys/src/puthread-win.c @@ -0,0 +1,511 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2019 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pmem.h" +#include "pmutex.h" +#include "patomic.h" +#include "puthread.h" +#include "puthread-private.h" + +#include <stdlib.h> +#include <time.h> +#include <string.h> + +#include <process.h> + +typedef HRESULT (WINAPI * PWin32SetThreadDescription) (HANDLE hThread, PCWSTR lpThreadDescription); +typedef HANDLE puthread_hdl; + +struct PUThread_ { + PUThreadBase base; + puthread_hdl hdl; + PUThreadFunc proxy; +}; + +struct PUThreadKey_ { + DWORD key_idx; + PDestroyFunc free_func; +}; + +typedef struct PUThreadDestructor_ PUThreadDestructor; + +struct PUThreadDestructor_ { + DWORD key_idx; + PDestroyFunc free_func; + PUThreadDestructor *next; +}; + +/* + * For thread names: + * https://docs.microsoft.com/en-us/visualstudio/debugger/how-to-set-a-thread-name-in-native-code + */ + +const DWORD MS_VC_THREAD_NAME_EXCEPTION = 0x406D1388; + +#pragma pack(push, 8) +typedef struct tagTHREADNAME_INFO +{ + DWORD dwType; /* Must be 0x1000. */ + LPCSTR szName; /* Pointer to name (in user addr space). */ + DWORD dwThreadID; /* Thread ID (-1 = caller thread). */ + DWORD dwFlags; /* Reserved for future use, must be zero. */ +} THREADNAME_INFO; +#pragma pack(pop) + +#ifndef P_CC_MSVC +static void *pp_uthread_name_veh_handle = NULL; + +static LONG __stdcall +pp_uthread_set_thread_name_veh (PEXCEPTION_POINTERS except_info) +{ + if (except_info->ExceptionRecord != NULL && + except_info->ExceptionRecord->ExceptionCode == MS_VC_THREAD_NAME_EXCEPTION) + return EXCEPTION_CONTINUE_EXECUTION; + + return EXCEPTION_CONTINUE_SEARCH; +} +#endif + +/* Rest of definitions */ + +static PWin32SetThreadDescription pp_uthread_set_descr_func = NULL; +static PUThreadDestructor * volatile pp_uthread_tls_destructors = NULL; +static PMutex *pp_uthread_tls_mutex = NULL; + +static DWORD pp_uthread_get_tls_key (PUThreadKey *key); +static puint __stdcall pp_uthread_win32_proxy (ppointer data); + +static DWORD +pp_uthread_get_tls_key (PUThreadKey *key) +{ + DWORD tls_key = key->key_idx; + + if (P_LIKELY (tls_key != TLS_OUT_OF_INDEXES)) + return tls_key; + + p_mutex_lock (pp_uthread_tls_mutex); + + tls_key = key->key_idx; + + if (P_LIKELY (tls_key == TLS_OUT_OF_INDEXES)) { + PUThreadDestructor *destr = NULL; + + tls_key = TlsAlloc (); + + if (P_UNLIKELY (tls_key == TLS_OUT_OF_INDEXES)) { + P_ERROR ("PUThread::pp_uthread_get_tls_key: TlsAlloc() failed"); + p_mutex_unlock (pp_uthread_tls_mutex); + return TLS_OUT_OF_INDEXES; + } + + if (key->free_func != NULL) { + if (P_UNLIKELY ((destr = p_malloc0 (sizeof (PUThreadDestructor))) == NULL)) { + P_ERROR ("PUThread::pp_uthread_get_tls_key: failed to allocate memory"); + + if (P_UNLIKELY (TlsFree (tls_key) == 0)) + P_ERROR ("PUThread::pp_uthread_get_tls_key: TlsFree() failed(1)"); + + p_mutex_unlock (pp_uthread_tls_mutex); + return TLS_OUT_OF_INDEXES; + } + + destr->key_idx = tls_key; + destr->free_func = key->free_func; + destr->next = pp_uthread_tls_destructors; + + /* At the same time thread exit could be performed at there is no + * lock for the global destructor list */ + if (P_UNLIKELY (p_atomic_pointer_compare_and_exchange ((PVOID volatile *) &pp_uthread_tls_destructors, + (PVOID) destr->next, + (PVOID) destr) == FALSE)) { + P_ERROR ("PUThread::pp_uthread_get_tls_key: p_atomic_pointer_compare_and_exchange() failed"); + + if (P_UNLIKELY (TlsFree (tls_key) == 0)) + P_ERROR ("PUThread::pp_uthread_get_tls_key: TlsFree() failed(2)"); + + p_free (destr); + + p_mutex_unlock (pp_uthread_tls_mutex); + return TLS_OUT_OF_INDEXES; + } + } + + key->key_idx = tls_key; + } + + p_mutex_unlock (pp_uthread_tls_mutex); + + return tls_key; +} + +static puint __stdcall +pp_uthread_win32_proxy (ppointer data) +{ + PUThread *thread = data; + + thread->proxy (thread); + + _endthreadex (0); + + return 0; +} + +void +p_uthread_win32_thread_detach (void) +{ + pboolean was_called; + + do { + PUThreadDestructor *destr; + + was_called = FALSE; + + destr = (PUThreadDestructor *) p_atomic_pointer_get ((const PVOID volatile *) &pp_uthread_tls_destructors); + + while (destr != NULL) { + ppointer value; + + value = TlsGetValue (destr->key_idx); + + if (value != NULL && destr->free_func != NULL) { + TlsSetValue (destr->key_idx, NULL); + destr->free_func (value); + was_called = TRUE; + } + + destr = destr->next; + } + } while (was_called); +} + +void +p_uthread_init_internal (void) +{ + HMODULE hmodule; + + if (P_LIKELY (pp_uthread_tls_mutex == NULL)) + pp_uthread_tls_mutex = p_mutex_new (); + + hmodule = GetModuleHandleA ("kernel32.dll"); + + if (P_UNLIKELY (hmodule == NULL)) { + P_ERROR ("PUThread::p_uthread_init_internal: failed to load kernel32.dll module"); + return; + } + + pp_uthread_set_descr_func = (PWin32SetThreadDescription) GetProcAddress (hmodule, "SetThreadDescription"); + +#ifndef P_CC_MSVC + pp_uthread_name_veh_handle = AddVectoredExceptionHandler (1, &pp_uthread_set_thread_name_veh); +#endif +} + +void +p_uthread_shutdown_internal (void) +{ + PUThreadDestructor *destr; + + p_uthread_win32_thread_detach (); + + destr = pp_uthread_tls_destructors; + + while (destr != NULL) { + PUThreadDestructor *next_destr = destr->next; + + TlsFree (destr->key_idx); + p_free (destr); + + destr = next_destr; + } + + pp_uthread_tls_destructors = NULL; + + if (P_LIKELY (pp_uthread_tls_mutex != NULL)) { + p_mutex_free (pp_uthread_tls_mutex); + pp_uthread_tls_mutex = NULL; + } + + pp_uthread_set_descr_func = NULL; + +#ifndef P_CC_MSVC + if (pp_uthread_name_veh_handle != NULL) { + RemoveVectoredExceptionHandler (pp_uthread_name_veh_handle); + pp_uthread_name_veh_handle = NULL; + } +#endif +} + +PUThread * +p_uthread_create_internal (PUThreadFunc func, + pboolean joinable, + PUThreadPriority prio, + psize stack_size) +{ + PUThread *ret; + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PUThread))) == NULL)) { + P_ERROR ("PUThread::p_uthread_create_internal: failed to allocate memory"); + return NULL; + } + + ret->proxy = func; + + if (P_UNLIKELY ((ret->hdl = (HANDLE) _beginthreadex (NULL, + (puint) stack_size, + pp_uthread_win32_proxy, + ret, + CREATE_SUSPENDED, + NULL)) == NULL)) { + P_ERROR ("PUThread::p_uthread_create_internal: _beginthreadex() failed"); + p_free (ret); + return NULL; + } + + ret->base.joinable = joinable; + + p_uthread_set_priority (ret, prio); + + if (P_UNLIKELY (ResumeThread (ret->hdl) == (DWORD) -1)) { + P_ERROR ("PUThread::p_uthread_create_internal: ResumeThread() failed"); + CloseHandle (ret->hdl); + p_free (ret); + } + + return ret; +} + +void +p_uthread_exit_internal (void) +{ + _endthreadex (0); +} + +void +p_uthread_wait_internal (PUThread *thread) +{ + if (P_UNLIKELY ((WaitForSingleObject (thread->hdl, INFINITE)) != WAIT_OBJECT_0)) + P_ERROR ("PUThread::p_uthread_wait_internal: WaitForSingleObject() failed"); +} + +void +p_uthread_set_name_internal (PUThread *thread) +{ + wchar_t *thr_wname = NULL; + psize namelen = 0; + HRESULT hres; + THREADNAME_INFO thr_info; + + if (pp_uthread_set_descr_func != NULL) { + namelen = strlen (thread->base.name); + + if (P_UNLIKELY ((thr_wname = p_malloc0 (sizeof (wchar_t) * (namelen + 1))) == NULL)) { + P_ERROR ("PUThread::p_uthread_set_name_internal: failed to allocate memory"); + return; + } + + mbstowcs (thr_wname, thread->base.name, namelen + 1); + + hres = pp_uthread_set_descr_func (thread->hdl, thr_wname); + + p_free (thr_wname); + + if (P_UNLIKELY (FAILED (hres))) { + P_ERROR ("PUThread::p_uthread_set_name_internal: failed to set thread description"); + return; + } + } + + if (!IsDebuggerPresent ()) + return; + + thr_info.dwType = 0x1000; + thr_info.szName = thread->base.name; + thr_info.dwThreadID = -1; + thr_info.dwFlags = 0; + +#ifdef P_CC_MSVC +# pragma warning(push) +# pragma warning(disable: 6320 6322) + __try { + RaiseException (MS_VC_THREAD_NAME_EXCEPTION, + 0, + sizeof (thr_info) / sizeof (ULONG_PTR), + (ULONG_PTR *) &thr_info); + } + __except (EXCEPTION_EXECUTE_HANDLER) {} +# pragma warning(pop) +#else + if (pp_uthread_name_veh_handle != NULL) + RaiseException (MS_VC_THREAD_NAME_EXCEPTION, + 0, + sizeof (thr_info) / sizeof (ULONG_PTR), + (ULONG_PTR *) &thr_info); +#endif +} + +void +p_uthread_free_internal (PUThread *thread) +{ + CloseHandle (thread->hdl); + p_free (thread); +} + +P_LIB_API void +p_uthread_yield (void) +{ + Sleep (0); +} + +P_LIB_API pboolean +p_uthread_set_priority (PUThread *thread, + PUThreadPriority prio) +{ + pint native_prio; + + if (P_UNLIKELY (thread == NULL)) + return FALSE; + + switch (prio) { + case P_UTHREAD_PRIORITY_IDLE: + native_prio = THREAD_PRIORITY_IDLE; + break; + case P_UTHREAD_PRIORITY_LOWEST: + native_prio = THREAD_PRIORITY_LOWEST; + break; + case P_UTHREAD_PRIORITY_LOW: + native_prio = THREAD_PRIORITY_BELOW_NORMAL; + break; + case P_UTHREAD_PRIORITY_NORMAL: + native_prio = THREAD_PRIORITY_NORMAL; + break; + case P_UTHREAD_PRIORITY_HIGH: + native_prio = THREAD_PRIORITY_ABOVE_NORMAL; + break; + case P_UTHREAD_PRIORITY_HIGHEST: + native_prio = THREAD_PRIORITY_HIGHEST; + break; + case P_UTHREAD_PRIORITY_TIMECRITICAL: + native_prio = THREAD_PRIORITY_TIME_CRITICAL; + break; + case P_UTHREAD_PRIORITY_INHERIT: + default: + native_prio = GetThreadPriority (GetCurrentThread ()); + break; + } + + if (P_UNLIKELY (SetThreadPriority (thread->hdl, native_prio) == 0)) { + P_ERROR ("PUThread::p_uthread_set_priority: SetThreadPriority() failed"); + return FALSE; + } + + thread->base.prio = prio; + + return TRUE; +} + +P_LIB_API P_HANDLE +p_uthread_current_id (void) +{ + return (P_HANDLE) ((psize) GetCurrentThreadId ()); +} + +P_LIB_API PUThreadKey * +p_uthread_local_new (PDestroyFunc free_func) +{ + PUThreadKey *ret; + + if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PUThreadKey))) == NULL)) { + P_ERROR ("PUThread::p_uthread_local_new: failed to allocate memory"); + return NULL; + } + + ret->key_idx = TLS_OUT_OF_INDEXES; + ret->free_func = free_func; + + return ret; +} + +P_LIB_API void +p_uthread_local_free (PUThreadKey *key) +{ + if (P_UNLIKELY (key == NULL)) + return; + + p_free (key); +} + +P_LIB_API ppointer +p_uthread_get_local (PUThreadKey *key) +{ + DWORD tls_idx; + + if (P_UNLIKELY (key == NULL)) + return NULL; + + tls_idx = pp_uthread_get_tls_key (key); + + return tls_idx == TLS_OUT_OF_INDEXES ? NULL : TlsGetValue (tls_idx); +} + +P_LIB_API void +p_uthread_set_local (PUThreadKey *key, + ppointer value) +{ + DWORD tls_idx; + + if (P_UNLIKELY (key == NULL)) + return; + + tls_idx = pp_uthread_get_tls_key (key); + + if (P_LIKELY (tls_idx != TLS_OUT_OF_INDEXES)) { + if (P_UNLIKELY (TlsSetValue (tls_idx, value) == 0)) + P_ERROR ("PUThread::p_uthread_set_local: TlsSetValue() failed"); + } +} + +P_LIB_API void +p_uthread_replace_local (PUThreadKey *key, + ppointer value) +{ + DWORD tls_idx; + ppointer old_value; + + if (P_UNLIKELY (key == NULL)) + return; + + tls_idx = pp_uthread_get_tls_key (key); + + if (P_UNLIKELY (tls_idx == TLS_OUT_OF_INDEXES)) + return; + + old_value = TlsGetValue (tls_idx); + + if (old_value != NULL && key->free_func != NULL) + key->free_func (old_value); + + if (P_UNLIKELY (TlsSetValue (tls_idx, value) == 0)) + P_ERROR ("PUThread::p_uthread_replace_local: TlsSetValue() failed"); +} diff --git a/3rdparty/plibsys/src/puthread.c b/3rdparty/plibsys/src/puthread.c new file mode 100644 index 0000000..ad72e78 --- /dev/null +++ b/3rdparty/plibsys/src/puthread.c @@ -0,0 +1,558 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2019 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "patomic.h" +#ifndef P_OS_WIN +# include "perror.h" +#endif +#include "pmem.h" +#include "pspinlock.h" +#include "pstring.h" +#include "puthread.h" +#include "puthread-private.h" + +#ifdef P_OS_OS2 +# define INCL_DOSPROCESS +# define INCL_DOSERRORS +# define INCL_DOSMISC +# include <os2.h> +#endif + +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#ifndef P_OS_WIN +# include <unistd.h> +#endif + +#ifdef P_OS_WIN +typedef void (WINAPI * SystemInfoFunc) (LPSYSTEM_INFO); +#endif + +#ifdef P_OS_HPUX +# include <sys/pstat.h> +#endif + +#ifdef P_OS_BSD4 +# include <sys/param.h> +# include <sys/types.h> +# include <sys/sysctl.h> +#endif + +#ifdef P_OS_VMS +# define __NEW_STARLET 1 +# include <starlet.h> +# include <ssdef.h> +# include <stsdef.h> +# include <efndef.h> +# include <iledef.h> +# include <iosbdef.h> +# include <syidef.h> +# include <tis.h> +# include <lib$routines.h> +#endif + +#ifdef P_OS_QNX6 +# include <sys/syspage.h> +#endif + +#ifdef P_OS_BEOS +# include <kernel/OS.h> +#endif + +#ifdef P_OS_SYLLABLE +# include <atheos/sysinfo.h> +#endif + +#if defined (P_OS_SCO) && !defined (_SC_NPROCESSORS_ONLN) +# include <sys/utsname.h> +#endif + +#ifdef P_OS_AMIGA +# include <clib/alib_protos.h> +# include <proto/dos.h> +# include <proto/exec.h> +#endif + +extern void p_uthread_init_internal (void); +extern void p_uthread_shutdown_internal (void); +extern void p_uthread_exit_internal (void); +extern void p_uthread_wait_internal (PUThread *thread); +extern void p_uthread_free_internal (PUThread *thread); +extern void p_uthread_set_name_internal (PUThread *thread); +extern PUThread * p_uthread_create_internal (PUThreadFunc func, + pboolean joinable, + PUThreadPriority prio, + psize stack_size); + +static void pp_uthread_cleanup (ppointer data); +static ppointer pp_uthread_proxy (ppointer data); + +#ifndef P_OS_WIN +# if !defined (PLIBSYS_HAS_CLOCKNANOSLEEP) && !defined (PLIBSYS_HAS_NANOSLEEP) +static pint pp_uthread_nanosleep (puint32 msec); +# endif +#endif + +static PUThreadKey * pp_uthread_specific_data = NULL; +static PSpinLock * pp_uthread_new_spin = NULL; + +static void +pp_uthread_cleanup (ppointer data) +{ + p_uthread_unref (data); +} + +static ppointer +pp_uthread_proxy (ppointer data) +{ + PUThreadBase *base_thread = data; + + p_uthread_set_local (pp_uthread_specific_data, data); + + p_spinlock_lock (pp_uthread_new_spin); + p_spinlock_unlock (pp_uthread_new_spin); + + if (base_thread->name != NULL) + p_uthread_set_name_internal ((PUThread *) base_thread); + + base_thread->func (base_thread->data); + + return NULL; +} + +void +p_uthread_init (void) +{ + if (P_LIKELY (pp_uthread_specific_data == NULL)) + pp_uthread_specific_data = p_uthread_local_new ((PDestroyFunc) pp_uthread_cleanup); + + if (P_LIKELY (pp_uthread_new_spin == NULL)) + pp_uthread_new_spin = p_spinlock_new (); + + p_uthread_init_internal (); +} + +void +p_uthread_shutdown (void) +{ + PUThread *cur_thread; + + if (P_LIKELY (pp_uthread_specific_data != NULL)) { + cur_thread = p_uthread_get_local (pp_uthread_specific_data); + + if (P_UNLIKELY (cur_thread != NULL)) { + p_uthread_unref (cur_thread); + p_uthread_set_local (pp_uthread_specific_data, NULL); + } + + p_uthread_local_free (pp_uthread_specific_data); + pp_uthread_specific_data = NULL; + } + + if (P_LIKELY (pp_uthread_new_spin != NULL)) { + p_spinlock_free (pp_uthread_new_spin); + pp_uthread_new_spin = NULL; + } + + p_uthread_shutdown_internal (); +} + +P_LIB_API PUThread * +p_uthread_create_full (PUThreadFunc func, + ppointer data, + pboolean joinable, + PUThreadPriority prio, + psize stack_size, + const pchar *name) +{ + PUThreadBase *base_thread; + + if (P_UNLIKELY (func == NULL)) + return NULL; + + p_spinlock_lock (pp_uthread_new_spin); + + base_thread = (PUThreadBase *) p_uthread_create_internal (pp_uthread_proxy, + joinable, + prio, + stack_size); + + if (P_LIKELY (base_thread != NULL)) { + base_thread->ref_count = 2; + base_thread->ours = TRUE; + base_thread->joinable = joinable; + base_thread->func = func; + base_thread->data = data; + base_thread->name = p_strdup (name); + } + + p_spinlock_unlock (pp_uthread_new_spin); + + return (PUThread *) base_thread; +} + +P_LIB_API PUThread * +p_uthread_create (PUThreadFunc func, + ppointer data, + pboolean joinable, + const pchar *name) +{ + /* All checks will be inside */ + return p_uthread_create_full (func, data, joinable, P_UTHREAD_PRIORITY_INHERIT, 0, name); +} + +P_LIB_API void +p_uthread_exit (pint code) +{ + PUThreadBase *base_thread = (PUThreadBase *) p_uthread_current (); + + if (P_UNLIKELY (base_thread == NULL)) + return; + + if (P_UNLIKELY (base_thread->ours == FALSE)) { + P_WARNING ("PUThread::p_uthread_exit: p_uthread_exit() cannot be called from an unknown thread"); + return; + } + + base_thread->ret_code = code; + + p_uthread_exit_internal (); +} + +P_LIB_API pint +p_uthread_join (PUThread *thread) +{ + PUThreadBase *base_thread; + + if (P_UNLIKELY (thread == NULL)) + return -1; + + base_thread = (PUThreadBase *) thread; + + if (base_thread->joinable == FALSE) + return -1; + + p_uthread_wait_internal (thread); + + return base_thread->ret_code; +} + +P_LIB_API PUThread * +p_uthread_current (void) +{ + PUThreadBase *base_thread = p_uthread_get_local (pp_uthread_specific_data); + + if (P_UNLIKELY (base_thread == NULL)) { + if (P_UNLIKELY ((base_thread = p_malloc0 (sizeof (PUThreadBase))) == NULL)) { + P_ERROR ("PUThread::p_uthread_current: failed to allocate memory"); + return NULL; + } + + base_thread->ref_count = 1; + + p_uthread_set_local (pp_uthread_specific_data, base_thread); + } + + return (PUThread *) base_thread; +} + +P_LIB_API pint +p_uthread_ideal_count (void) +{ +#if defined (P_OS_WIN) + SYSTEM_INFO sys_info; + SystemInfoFunc sys_info_func; + + sys_info_func = (SystemInfoFunc) GetProcAddress (GetModuleHandleA ("kernel32.dll"), + "GetNativeSystemInfo"); + + if (P_UNLIKELY (sys_info_func == NULL)) + sys_info_func = (SystemInfoFunc) GetProcAddress (GetModuleHandleA ("kernel32.dll"), + "GetSystemInfo"); + + if (P_UNLIKELY (sys_info_func == NULL)) { + P_ERROR ("PUThread::p_uthread_ideal_count: failed to get address of system info procedure"); + return 1; + } + + sys_info_func (&sys_info); + + return (pint) sys_info.dwNumberOfProcessors; +#elif defined (P_OS_HPUX) + struct pst_dynamic psd; + + if (P_LIKELY (pstat_getdynamic (&psd, sizeof (psd), 1, 0) != -1)) + return (pint) psd.psd_proc_cnt; + else { + P_WARNING ("PUThread::p_uthread_ideal_count: failed to call pstat_getdynamic()"); + return 1; + } +#elif defined (P_OS_IRIX) + pint cores; + + cores = sysconf (_SC_NPROC_ONLN); + + if (P_UNLIKELY (cores < 0)) { + P_WARNING ("PUThread::p_uthread_ideal_count: failed to call sysconf(_SC_NPROC_ONLN)"); + cores = 1; + } + + return cores; +#elif defined (P_OS_BSD4) + pint cores; + pint mib[2]; + size_t len = sizeof (cores); + + mib[0] = CTL_HW; + mib[1] = HW_NCPU; + + if (P_UNLIKELY (sysctl (mib, 2, &cores, &len, NULL, 0) == -1)) { + P_WARNING ("PUThread::p_uthread_ideal_count: failed to call sysctl()"); + return 1; + } + + return (pint) cores; +#elif defined (P_OS_VMS) + pint cores; + pint status; + puint efn; + IOSB iosb; +# if (PLIBSYS_SIZEOF_VOID_P == 4) + ILE3 itmlst[] = { { sizeof (cores), SYI$_AVAILCPU_CNT, &cores, NULL}, + { 0, 0, NULL, NULL} + }; +# else + ILEB_64 itmlst[] = { { 1, SYI$_AVAILCPU_CNT, -1, sizeof (cores), &cores, NULL}, + { 0, 0, 0, 0, NULL, NULL} + }; +# endif + + status = lib$get_ef (&efn); + + if (P_UNLIKELY (!$VMS_STATUS_SUCCESS (status))) { + P_WARNING ("PUThread::p_uthread_ideal_count: failed to call lib$get_ef()"); + return 1; + } + + status = sys$getsyi (efn, NULL, NULL, itmlst, &iosb, tis_io_complete, 0); + + if (P_UNLIKELY (!$VMS_STATUS_SUCCESS (status))) { + P_WARNING ("PUThread::p_uthread_ideal_count: failed to call sys$getsyiw()"); + lib$free_ef (&efn); + return 1; + } + + status = tis_synch (efn, &iosb); + + if (P_UNLIKELY (!$VMS_STATUS_SUCCESS (status))) { + P_WARNING ("PUThread::p_uthread_ideal_count: failed to call tis_synch()"); + lib$free_ef (&efn); + return 1; + } + + if (P_UNLIKELY (iosb.iosb$l_getxxi_status != SS$_NORMAL)) { + P_WARNING ("PUThread::p_uthread_ideal_count: l_getxxi_status is not normal"); + lib$free_ef (&efn); + return 1; + } + + lib$free_ef (&efn); + + return cores; +#elif defined (P_OS_OS2) + APIRET ulrc; + ULONG cores; + + if (P_UNLIKELY (DosQuerySysInfo (QSV_NUMPROCESSORS, + QSV_NUMPROCESSORS, + &cores, + sizeof (cores)) != NO_ERROR)) { + P_WARNING ("PUThread::p_uthread_ideal_count: failed to call DosQuerySysInfo()"); + return 1; + } + + return (pint) cores; +#elif defined (P_OS_QNX6) + return (pint) _syspage_ptr->num_cpu; +#elif defined (P_OS_BEOS) + system_info sys_info; + + get_system_info (&sys_info); + + return (pint) sys_info.cpu_count; +#elif defined (P_OS_SYLLABLE) + system_info sys_info; + + if (P_UNLIKELY (get_system_info_v (&sys_info, SYS_INFO_VERSION) != 0)) { + P_WARNING ("PUThread::p_uthread_ideal_count: failed to call get_system_info_v()"); + return 1; + } + + return (pint) sys_info.nCPUCount; +#elif defined (P_OS_AMIGA) + puint32 cores; + + IExec->GetCPUInfoTags (GCIT_NumberOfCPUs, &cores, TAG_END); + + return (pint) cores; +#elif defined (P_OS_SCO) && !defined (_SC_NPROCESSORS_ONLN) + struct scoutsname utsn; + + if (P_UNLIKELY (__scoinfo (&utsn, sizeof (utsn)) == -1)) { + P_ERROR ("PUThread::p_uthread_ideal_count: failed to call __scoinfo()"); + return 1; + } + + return (pint) utsn.numcpu; +#elif defined (_SC_NPROCESSORS_ONLN) + pint cores; + + cores = (pint) sysconf (_SC_NPROCESSORS_ONLN); + + if (P_UNLIKELY (cores == -1)) { + P_WARNING ("PUThread::p_uthread_ideal_count: failed to call sysconf(_SC_NPROCESSORS_ONLN)"); + return 1; + } + + return cores; +#else + return 1; +#endif +} + +P_LIB_API void +p_uthread_ref (PUThread *thread) +{ + if (P_UNLIKELY (thread == NULL)) + return; + + p_atomic_int_inc (&((PUThreadBase *) thread)->ref_count); +} + +P_LIB_API void +p_uthread_unref (PUThread *thread) +{ + PUThreadBase *base_thread; + + if (P_UNLIKELY (thread == NULL)) + return; + + base_thread = (PUThreadBase *) thread; + + if (p_atomic_int_dec_and_test (&base_thread->ref_count) == TRUE) { + p_free (base_thread->name); + + if (base_thread->ours == TRUE) + p_uthread_free_internal (thread); + else + p_free (thread); + } +} + +#ifndef P_OS_WIN +# include <errno.h> +# if !defined (PLIBSYS_HAS_CLOCKNANOSLEEP) && !defined (PLIBSYS_HAS_NANOSLEEP) +# include <sys/select.h> +# include <sys/time.h> +static pint pp_uthread_nanosleep (puint32 msec) +{ + pint rc; + struct timeval tstart, tstop, tremain, time2wait; + + time2wait.tv_sec = msec / 1000; + time2wait.tv_usec = (msec % 1000) * 1000; + + if (P_UNLIKELY (gettimeofday (&tstart, NULL) != 0)) + return -1; + + rc = -1; + + while (rc != 0) { + if (P_UNLIKELY ((rc = select (0, NULL, NULL, NULL, &time2wait)) != 0)) { + if (p_error_get_last_system () == EINTR) { + if (gettimeofday (&tstop, NULL) != 0) + return -1; + + tremain.tv_sec = time2wait.tv_sec - + (tstop.tv_sec - tstart.tv_sec); + tremain.tv_usec = time2wait.tv_usec - + (tstop.tv_usec - tstart.tv_usec); + tremain.tv_sec += tremain.tv_usec / 1000000L; + tremain.tv_usec %= 1000000L; + } else + return -1; + } + } + + return 0; +} +# endif +#endif + +P_LIB_API pint +p_uthread_sleep (puint32 msec) +{ +#if defined (P_OS_WIN) + Sleep (msec); + return 0; +#elif defined (P_OS_OS2) + return (DosSleep (msec) == NO_ERROR) ? 0 : -1; +#elif defined (P_OS_AMIGA) + return TimeDelay (0, msec / 1000, (msec % 1000) * 1000) == 0 ? 0 : -1; +#elif defined (PLIBSYS_HAS_CLOCKNANOSLEEP) || defined (PLIBSYS_HAS_NANOSLEEP) + pint result; + struct timespec time_req; + struct timespec time_rem; + + memset (&time_rem, 0, sizeof (struct timespec)); + + time_req.tv_nsec = (msec % 1000) * 1000000L; + time_req.tv_sec = (time_t) (msec / 1000); + + result = -1; + while (result != 0) { + /* Syllable has unimplemented clock_nanocleep() call */ +# if defined (PLIBSYS_HAS_CLOCKNANOSLEEP) && !defined (P_OS_SYLLABLE) + if (P_UNLIKELY ((result = clock_nanosleep (CLOCK_MONOTONIC, + 0, + &time_req, + &time_rem)) != 0)) { +# else + if (P_UNLIKELY ((result = nanosleep (&time_req, &time_rem)) != 0)) { +# endif + if (p_error_get_last_system () == EINTR) + time_req = time_rem; + else + return -1; + } + } + + return 0; +#else + return pp_uthread_nanosleep (msec); +#endif +} diff --git a/3rdparty/plibsys/src/puthread.h b/3rdparty/plibsys/src/puthread.h new file mode 100644 index 0000000..2c07ac5 --- /dev/null +++ b/3rdparty/plibsys/src/puthread.h @@ -0,0 +1,311 @@ +/* + * The MIT License + * + * Copyright (C) 2010-2019 Alexander Saprykin <saprykin.spb@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * @file puthread.h + * @brief Multithreading support + * @author Alexander Saprykin + * + * A thread is a system execution unit which is managed independently by the + * scheduler of the operating system. It allows to do things in parallel or + * concurrently. + * + * #PUThread provides a convinient way of multithreading support using native + * routines to provide the best performance on the target system. + * + * To create the thread use the p_uthread_create() or p_uthread_create_full() + * routines. Joinable threads allow to wait until their execution is finished + * before proceeding further. Thus you can synchronize threads' execution within + * the main thread. + * + * A reference counter mechanism is used to keep track of a #PUThread structure. + * It means that the structure will be freed automatically when the reference + * counter becomes zero. Use p_uthread_ref() to hold the structure and + * p_uthread_unref() to decrement the counter back. A running thread holds a + * reference to itself structure, so you do not require to hold a reference + * to the thread while it is running. + * + * Priorities (if supported) allow to tune scheduler behavior: threads with + * higher priority will be executed more frequently. Be careful that improper + * priorities may lead to negative effects when some threads may receive almost + * zero execution time. + * + * Thread priorities are unreliable: not all operating systems respect thread + * priorities in favour of process ones. Priorities may be ignored for bound + * threads (every thread bound to a kernel light-weight thread as 1:1), other + * systems may require administrative privileges to change the thread priority + * (i.e. Linux). Windows always respects thread priorities. + * + * To put the current thread (even if it was not created using the #PUThread + * routines) in a sleep state use p_uthread_sleep(). + * + * You can give a hint to the scheduler that the current thread do not need an + * execution time with the p_uthread_yield() routine. This is useful when some + * of the threads are in an idle state so you do not want to waste a CPU time. + * This only tells to the scheduler to skip the current scheduling cycle for the + * calling thread, though the scheduler can ingnore it. + * + * A thread local storage (TLS) is provided. The TLS key's value can be accessed + * through a reference key defined as a #PUThreadKey. A TLS reference key is + * some sort of a token which has an associated value. But every thread has its + * own token value though using the same token object. + * + * After creating the TLS reference key every thread can use it to access a + * local-specific value. Use the p_uthread_local_new() call to create the TLS + * reference key and pass it to every thread which needs local-specific values. + * You can also provide a destroy notification function which would be called + * upon a TLS key removal which is usually performed on the thread exit. + * + * There are two calls to set a TLS key's value: p_uthread_set_local() and + * p_uthread_replace_local(). The only difference is that the former one calls + * the provided destroy notification function before replacing the old value. + * + * Thread names are used on most of operating systems for debugging purposes, + * thereby some limitations for long name can be applied and too long names + * will be truncated automatically. + */ + +#if !defined (PLIBSYS_H_INSIDE) && !defined (PLIBSYS_COMPILATION) +# error "Header files shouldn't be included directly, consider using <plibsys.h> instead." +#endif + +#ifndef PLIBSYS_HEADER_PUTHREAD_H +#define PLIBSYS_HEADER_PUTHREAD_H + +#include <pmacros.h> +#include <ptypes.h> + +P_BEGIN_DECLS + +/** Typedef for a #PUThread running method. */ +typedef ppointer (*PUThreadFunc) (ppointer arg); + +/** Thread opaque data type. */ +typedef struct PUThread_ PUThread; + +/** TLS key opaque data type. */ +typedef struct PUThreadKey_ PUThreadKey; + +/** Thread priority. */ +typedef enum PUThreadPriority_ { + P_UTHREAD_PRIORITY_INHERIT = 0, /**< Inherits the caller thread priority. Default priority. */ + P_UTHREAD_PRIORITY_IDLE = 1, /**< Scheduled only when no other threads are running. */ + P_UTHREAD_PRIORITY_LOWEST = 2, /**< Scheduled less often than #P_UTHREAD_PRIORITY_LOW. */ + P_UTHREAD_PRIORITY_LOW = 3, /**< Scheduled less often than #P_UTHREAD_PRIORITY_NORMAL. */ + P_UTHREAD_PRIORITY_NORMAL = 4, /**< Operating system's default priority. */ + P_UTHREAD_PRIORITY_HIGH = 5, /**< Scheduled more often than #P_UTHREAD_PRIORITY_NORMAL. */ + P_UTHREAD_PRIORITY_HIGHEST = 6, /**< Scheduled more often than #P_UTHREAD_PRIORITY_HIGH. */ + P_UTHREAD_PRIORITY_TIMECRITICAL = 7 /**< Scheduled as often as possible. */ +} PUThreadPriority; + +/** + * @brief Creates a new #PUThread and starts it. + * @param func Main thread function to run. + * @param data Pointer to pass into the thread main function, may be NULL. + * @param joinable Whether to create a joinable thread or not. + * @param prio Thread priority. + * @param stack_size Thread stack size, in bytes. Leave zero to use a default + * value. + * @param name Thread name, maybe NULL. + * @return Pointer to #PUThread in case of success, NULL otherwise. + * @since 0.0.1 + * @note Unreference the returned value after use with p_uthread_unref(). You do + * not need to call p_uthread_ref() explicitly on the returned value. + */ +P_LIB_API PUThread * p_uthread_create_full (PUThreadFunc func, + ppointer data, + pboolean joinable, + PUThreadPriority prio, + psize stack_size, + const pchar *name); + +/** + * @brief Creates a #PUThread and starts it. A short version of + * p_uthread_create_full(). + * @param func Main thread function to run. + * @param data Pointer to pass into the thread main function, may be NULL. + * @param joinable Whether to create a joinable thread or not. + * @param name Thread name, maybe NULL. + * @return Pointer to #PUThread in case of success, NULL otherwise. + * @since 0.0.1 + * @note Unreference the returned value after use with p_uthread_unref(). You do + * not need to call p_uthread_ref() explicitly on the returned value. + */ +P_LIB_API PUThread * p_uthread_create (PUThreadFunc func, + ppointer data, + pboolean joinable, + const pchar *name); + +/** + * @brief Exits from the currently running (caller) thread. + * @param code Exit code. + * @since 0.0.1 + */ +P_LIB_API void p_uthread_exit (pint code); + +/** + * @brief Waits for the selected thread to become finished. + * @param thread Thread to wait for. + * @return Thread exit code in case of success, -1 otherwise. + * @since 0.0.1 + * @note Thread must be joinable to return the non-negative result. + */ +P_LIB_API pint p_uthread_join (PUThread *thread); + +/** + * @brief Sleeps the current thread (caller) for a specified amount of time. + * @param msec Milliseconds to sleep. + * @return 0 in case of success, -1 otherwise. + * @since 0.0.1 + */ +P_LIB_API pint p_uthread_sleep (puint32 msec); + +/** + * @brief Sets a thread priority. + * @param thread Thread to set the priority for. + * @param prio Priority to set. + * @return TRUE in case of success, FALSE otherwise. + * @since 0.0.1 + */ +P_LIB_API pboolean p_uthread_set_priority (PUThread *thread, + PUThreadPriority prio); + +/** + * @brief Tells the scheduler to skip the current (caller) thread in the current + * planning stage. + * @since 0.0.1 + * + * The scheduler shouldn't give time ticks for the current thread during the + * current period, but it may ignore this call. + */ +P_LIB_API void p_uthread_yield (void); + +/** + * @brief Gets an ID of the current (caller) thread. + * @return The ID of the current thread. + * @since 0.0.1 + * + * This is a platform-dependent type. You shouldn't treat it as a number, it + * only gives you the uniquer ID of the thread accross the system. + */ +P_LIB_API P_HANDLE p_uthread_current_id (void); + +/** + * @brief Gets a thread structure of the current (caller) thread. + * @return The thread structure of the current thread. + * @since 0.0.1 + * @note This call doesn't not increment the reference counter of the returned + * structure. + * + * A thread structure will be returned even for the thread which was created + * outside the library. But you should not use thread manipulation routines over + * that structure. + */ +P_LIB_API PUThread * p_uthread_current (void); + +/** + * @brief Gets the ideal number of threads for the system based on the number of + * avaialble CPUs and cores (physical and logical). + * @return Ideal number of threads, 1 in case of failed detection. + * @since 0.0.3 + */ +P_LIB_API pint p_uthread_ideal_count (void); + +/** + * @brief Increments a thread reference counter + * @param thread #PUThread to increment the reference counter. + * @since 0.0.1 + * @note The #PUThread object will not be removed until the reference counter is + * positive. + */ +P_LIB_API void p_uthread_ref (PUThread *thread); + +/** + * @brief Decrements a thread reference counter + * @param thread #PUThread to decrement the reference counter. + * @since 0.0.1 + * @note When the reference counter becomes zero the #PUThread is removed from + * the memory. + */ +P_LIB_API void p_uthread_unref (PUThread *thread); + +/** + * @brief Create a new TLS reference key. + * @param free_func TLS key destroy notification call, leave NULL if not need. + * @return New TLS reference key in case of success, NULL otherwise. + * @since 0.0.1 + */ +P_LIB_API PUThreadKey * p_uthread_local_new (PDestroyFunc free_func); + +/** + * @brief Frees a TLS reference key. + * @param key TLS reference key to free. + * @since 0.0.1 + * + * It doesn't remove the TLS key itself but only removes a reference used to + * access the TLS slot. + */ +P_LIB_API void p_uthread_local_free (PUThreadKey *key); + +/** + * @brief Gets a TLS value. + * @param key TLS reference key to get the value for. + * @return TLS value for the given key. + * @since 0.0.1 + * @note This call may fail only in case of abnormal use or program behavior, + * the NULL value will be returned to tolerance the failure. + */ +P_LIB_API ppointer p_uthread_get_local (PUThreadKey *key); + +/** + * @brief Sets a TLS value. + * @param key TLS reference key to set the value for. + * @param value TLS value to set. + * @since 0.0.1 + * @note This call may fail only in case of abnormal use or program behavior. + * + * It doesn't call the destructor notification function provided with + * p_uthread_local_new(). + */ +P_LIB_API void p_uthread_set_local (PUThreadKey *key, + ppointer value); + +/** + * @brief Replaces a TLS value. + * @param key TLS reference key to replace the value for. + * @param value TLS value to set. + * @since 0.0.1 + * @note This call may fail only in case of abnormal use or program behavior. + * + * This call does perform the notification function provided with + * p_uthread_local_new() on the old TLS value. This is the only difference with + * p_uthread_set_local(). + */ +P_LIB_API void p_uthread_replace_local (PUThreadKey *key, + ppointer value); + +P_END_DECLS + +#endif /* PLIBSYS_HEADER_PUTHREAD_H */ |