summaryrefslogtreecommitdiff
path: root/3rdparty/plibsys/src
diff options
context:
space:
mode:
authorsanine <sanine.not@pm.me>2022-08-27 23:52:56 -0500
committersanine <sanine.not@pm.me>2022-08-27 23:52:56 -0500
commita4dd0ad63c00f4dee3b86dfd3075d1d61b2b3180 (patch)
tree13bd5bfa15e6fea2a12f176bae79adf9c6fd0933 /3rdparty/plibsys/src
parentbde3e4f1bb7b8f8abca0884a7d994ee1c17a66b1 (diff)
add plibsys
Diffstat (limited to '3rdparty/plibsys/src')
-rw-r--r--3rdparty/plibsys/src/CMakeLists.txt996
-rw-r--r--3rdparty/plibsys/src/patomic-c11.c182
-rw-r--r--3rdparty/plibsys/src/patomic-decc.c286
-rw-r--r--3rdparty/plibsys/src/patomic-sim.c268
-rw-r--r--3rdparty/plibsys/src/patomic-sync.c190
-rw-r--r--3rdparty/plibsys/src/patomic-win.c272
-rw-r--r--3rdparty/plibsys/src/patomic.h310
-rw-r--r--3rdparty/plibsys/src/pcondvariable-amiga.c230
-rw-r--r--3rdparty/plibsys/src/pcondvariable-atheos.c234
-rw-r--r--3rdparty/plibsys/src/pcondvariable-beos.c233
-rw-r--r--3rdparty/plibsys/src/pcondvariable-none.c80
-rw-r--r--3rdparty/plibsys/src/pcondvariable-os2.c163
-rw-r--r--3rdparty/plibsys/src/pcondvariable-posix.c121
-rw-r--r--3rdparty/plibsys/src/pcondvariable-solaris.c122
-rw-r--r--3rdparty/plibsys/src/pcondvariable-win.c312
-rw-r--r--3rdparty/plibsys/src/pcondvariable.h138
-rw-r--r--3rdparty/plibsys/src/pcryptohash-gost3411.c484
-rw-r--r--3rdparty/plibsys/src/pcryptohash-gost3411.h53
-rw-r--r--3rdparty/plibsys/src/pcryptohash-md5.c273
-rw-r--r--3rdparty/plibsys/src/pcryptohash-md5.h51
-rw-r--r--3rdparty/plibsys/src/pcryptohash-sha1.c321
-rw-r--r--3rdparty/plibsys/src/pcryptohash-sha1.h51
-rw-r--r--3rdparty/plibsys/src/pcryptohash-sha2-256.c286
-rw-r--r--3rdparty/plibsys/src/pcryptohash-sha2-256.h59
-rw-r--r--3rdparty/plibsys/src/pcryptohash-sha2-512.c300
-rw-r--r--3rdparty/plibsys/src/pcryptohash-sha2-512.h59
-rw-r--r--3rdparty/plibsys/src/pcryptohash-sha3.c297
-rw-r--r--3rdparty/plibsys/src/pcryptohash-sha3.h79
-rw-r--r--3rdparty/plibsys/src/pcryptohash.c250
-rw-r--r--3rdparty/plibsys/src/pcryptohash.h188
-rw-r--r--3rdparty/plibsys/src/pdir-none.c124
-rw-r--r--3rdparty/plibsys/src/pdir-os2.c381
-rw-r--r--3rdparty/plibsys/src/pdir-posix.c378
-rw-r--r--3rdparty/plibsys/src/pdir-win.c291
-rw-r--r--3rdparty/plibsys/src/pdir.c37
-rw-r--r--3rdparty/plibsys/src/pdir.h177
-rw-r--r--3rdparty/plibsys/src/perror-private.h67
-rw-r--r--3rdparty/plibsys/src/perror.c878
-rw-r--r--3rdparty/plibsys/src/perror.h249
-rw-r--r--3rdparty/plibsys/src/perrortypes.h106
-rw-r--r--3rdparty/plibsys/src/pfile.c80
-rw-r--r--3rdparty/plibsys/src/pfile.h87
-rw-r--r--3rdparty/plibsys/src/phashtable.c243
-rw-r--r--3rdparty/plibsys/src/phashtable.h167
-rw-r--r--3rdparty/plibsys/src/pinifile.c503
-rw-r--r--3rdparty/plibsys/src/pinifile.h261
-rw-r--r--3rdparty/plibsys/src/pipc-private.h76
-rw-r--r--3rdparty/plibsys/src/pipc.c178
-rw-r--r--3rdparty/plibsys/src/plibraryloader-amiga.c583
-rw-r--r--3rdparty/plibsys/src/plibraryloader-beos.c142
-rw-r--r--3rdparty/plibsys/src/plibraryloader-none.c75
-rw-r--r--3rdparty/plibsys/src/plibraryloader-os2.c155
-rw-r--r--3rdparty/plibsys/src/plibraryloader-posix.c148
-rw-r--r--3rdparty/plibsys/src/plibraryloader-shl.c140
-rw-r--r--3rdparty/plibsys/src/plibraryloader-win.c140
-rw-r--r--3rdparty/plibsys/src/plibraryloader.h155
-rw-r--r--3rdparty/plibsys/src/plibsys-private.h86
-rw-r--r--3rdparty/plibsys/src/plibsys.h64
-rw-r--r--3rdparty/plibsys/src/plibsysconfig.h.in76
-rw-r--r--3rdparty/plibsys/src/plist.c174
-rw-r--r--3rdparty/plibsys/src/plist.h199
-rw-r--r--3rdparty/plibsys/src/pmacros.h302
-rw-r--r--3rdparty/plibsys/src/pmacroscompiler.h253
-rw-r--r--3rdparty/plibsys/src/pmacroscpu.h627
-rw-r--r--3rdparty/plibsys/src/pmacrosos.h500
-rw-r--r--3rdparty/plibsys/src/pmain.c132
-rw-r--r--3rdparty/plibsys/src/pmain.h209
-rw-r--r--3rdparty/plibsys/src/pmem.c357
-rw-r--r--3rdparty/plibsys/src/pmem.h191
-rw-r--r--3rdparty/plibsys/src/pmutex-amiga.c101
-rw-r--r--3rdparty/plibsys/src/pmutex-atheos.c111
-rw-r--r--3rdparty/plibsys/src/pmutex-beos.c110
-rw-r--r--3rdparty/plibsys/src/pmutex-none.c66
-rw-r--r--3rdparty/plibsys/src/pmutex-os2.c112
-rw-r--r--3rdparty/plibsys/src/pmutex-posix.c104
-rw-r--r--3rdparty/plibsys/src/pmutex-solaris.c105
-rw-r--r--3rdparty/plibsys/src/pmutex-win.c92
-rw-r--r--3rdparty/plibsys/src/pmutex.h136
-rw-r--r--3rdparty/plibsys/src/pprocess.c61
-rw-r--r--3rdparty/plibsys/src/pprocess.h69
-rw-r--r--3rdparty/plibsys/src/prwlock-general.c326
-rw-r--r--3rdparty/plibsys/src/prwlock-none.c103
-rw-r--r--3rdparty/plibsys/src/prwlock-posix.c151
-rw-r--r--3rdparty/plibsys/src/prwlock-solaris.c151
-rw-r--r--3rdparty/plibsys/src/prwlock-win.c548
-rw-r--r--3rdparty/plibsys/src/prwlock.h185
-rw-r--r--3rdparty/plibsys/src/psemaphore-amiga.c250
-rw-r--r--3rdparty/plibsys/src/psemaphore-none.c91
-rw-r--r--3rdparty/plibsys/src/psemaphore-os2.c91
-rw-r--r--3rdparty/plibsys/src/psemaphore-posix.c272
-rw-r--r--3rdparty/plibsys/src/psemaphore-sysv.c319
-rw-r--r--3rdparty/plibsys/src/psemaphore-win.c204
-rw-r--r--3rdparty/plibsys/src/psemaphore.h191
-rw-r--r--3rdparty/plibsys/src/pshm-amiga.c274
-rw-r--r--3rdparty/plibsys/src/pshm-none.c94
-rw-r--r--3rdparty/plibsys/src/pshm-os2.c350
-rw-r--r--3rdparty/plibsys/src/pshm-posix.c311
-rw-r--r--3rdparty/plibsys/src/pshm-sysv.c307
-rw-r--r--3rdparty/plibsys/src/pshm-win.c262
-rw-r--r--3rdparty/plibsys/src/pshm.h195
-rw-r--r--3rdparty/plibsys/src/pshmbuffer.c334
-rw-r--r--3rdparty/plibsys/src/pshmbuffer.h191
-rw-r--r--3rdparty/plibsys/src/psocket.c1644
-rw-r--r--3rdparty/plibsys/src/psocket.h779
-rw-r--r--3rdparty/plibsys/src/psocketaddress.c619
-rw-r--r--3rdparty/plibsys/src/psocketaddress.h284
-rw-r--r--3rdparty/plibsys/src/pspinlock-c11.c103
-rw-r--r--3rdparty/plibsys/src/pspinlock-decc.c87
-rw-r--r--3rdparty/plibsys/src/pspinlock-sim.c88
-rw-r--r--3rdparty/plibsys/src/pspinlock-sync.c83
-rw-r--r--3rdparty/plibsys/src/pspinlock-win.c83
-rw-r--r--3rdparty/plibsys/src/pspinlock.h142
-rw-r--r--3rdparty/plibsys/src/pstdarg.h318
-rw-r--r--3rdparty/plibsys/src/pstring.c206
-rw-r--r--3rdparty/plibsys/src/pstring.h117
-rw-r--r--3rdparty/plibsys/src/psysclose-darwin.c93
-rw-r--r--3rdparty/plibsys/src/psysclose-private.h47
-rw-r--r--3rdparty/plibsys/src/psysclose-unix.c54
-rw-r--r--3rdparty/plibsys/src/psysclose-win.c33
-rw-r--r--3rdparty/plibsys/src/ptimeprofiler-amiga.c70
-rw-r--r--3rdparty/plibsys/src/ptimeprofiler-beos.c51
-rw-r--r--3rdparty/plibsys/src/ptimeprofiler-generic.c58
-rw-r--r--3rdparty/plibsys/src/ptimeprofiler-mach.c73
-rw-r--r--3rdparty/plibsys/src/ptimeprofiler-os2.c107
-rw-r--r--3rdparty/plibsys/src/ptimeprofiler-posix.c109
-rw-r--r--3rdparty/plibsys/src/ptimeprofiler-private.h45
-rw-r--r--3rdparty/plibsys/src/ptimeprofiler-solaris.c53
-rw-r--r--3rdparty/plibsys/src/ptimeprofiler-win.c169
-rw-r--r--3rdparty/plibsys/src/ptimeprofiler.c70
-rw-r--r--3rdparty/plibsys/src/ptimeprofiler.h93
-rw-r--r--3rdparty/plibsys/src/ptree-avl.c481
-rw-r--r--3rdparty/plibsys/src/ptree-avl.h58
-rw-r--r--3rdparty/plibsys/src/ptree-bst.c140
-rw-r--r--3rdparty/plibsys/src/ptree-bst.h58
-rw-r--r--3rdparty/plibsys/src/ptree-private.h48
-rw-r--r--3rdparty/plibsys/src/ptree-rb.c484
-rw-r--r--3rdparty/plibsys/src/ptree-rb.h58
-rw-r--r--3rdparty/plibsys/src/ptree.c315
-rw-r--r--3rdparty/plibsys/src/ptree.h230
-rw-r--r--3rdparty/plibsys/src/ptypes.h1122
-rw-r--r--3rdparty/plibsys/src/puthread-amiga.c673
-rw-r--r--3rdparty/plibsys/src/puthread-atheos.c317
-rw-r--r--3rdparty/plibsys/src/puthread-beos.c431
-rw-r--r--3rdparty/plibsys/src/puthread-none.c142
-rw-r--r--3rdparty/plibsys/src/puthread-os2.c458
-rw-r--r--3rdparty/plibsys/src/puthread-posix.c580
-rw-r--r--3rdparty/plibsys/src/puthread-private.h53
-rw-r--r--3rdparty/plibsys/src/puthread-solaris.c352
-rw-r--r--3rdparty/plibsys/src/puthread-win.c511
-rw-r--r--3rdparty/plibsys/src/puthread.c558
-rw-r--r--3rdparty/plibsys/src/puthread.h311
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 */